diff --git a/remotes/file.go b/remotes/file.go index ee4aff7..de39618 100644 --- a/remotes/file.go +++ b/remotes/file.go @@ -60,12 +60,22 @@ var ErrNotFound = errors.New("not found") func NewFileProvider(file File) StoreProvider { return &fileProvider{ file: file, + preferZSTD: true, downloader: NewDownloadGZIPFromZSTD(file), } } // NewFileProviderWithDownloader create a new file provider use custom Downloader func NewFileProviderWithDownloader(file File, downloader Downloader) StoreProvider { + return &fileProvider{ + file: file, + preferZSTD: true, + downloader: downloader, + } +} + +// NewFileProviderWithDownloaderPreferGZIP prefers to use gzip format images +func NewFileProviderWithDownloaderPreferGZIP(file File, downloader Downloader) StoreProvider { return &fileProvider{ file: file, downloader: downloader, @@ -77,6 +87,7 @@ func NewFileProviderWithDownloader(file File, downloader Downloader) StoreProvid // - https://github.com/opencontainers/image-spec/blob/main/image-layout.md type fileProvider struct { file File + preferZSTD bool // nice to use zstd when preferZSTD downloader Downloader client *containerd.Client } @@ -196,7 +207,7 @@ func (p *fileProvider) selectImagesFromMap(ctx context.Context, imagesMap map[st return 1 // unknown } } - return supportArchiveTypeZstd != (getImageArchiveType(targetImages[i]) < getImageArchiveType(targetImages[j])) + return (supportArchiveTypeZstd && p.preferZSTD) != (getImageArchiveType(targetImages[i]) < getImageArchiveType(targetImages[j])) }) imageList = append(imageList, targetImages[0]) } diff --git a/remotes/file_test.go b/remotes/file_test.go index d6f986f..2935272 100644 --- a/remotes/file_test.go +++ b/remotes/file_test.go @@ -106,6 +106,71 @@ func TestFileProviderGet(t *testing.T) { }) } +func TestFileProviderGetPreferGZIP(t *testing.T) { + ctx := context.Background() + + t.Run("should get file gzip image archive format", func(t *testing.T) { + RegisterTestingT(t) + + p := newFileProviderPreferGZIP("testdata/example-noop-1.0.0-gzip.tar") + image, err := p.Get(ctx, "example.com/example/noop:1.0.0") + Expect(err).ShouldNot(HaveOccurred()) + Expect(string(image.Target.Digest)).Should(Equal("sha256:cbd3ccbe91459729a64eea12be3ae561d18883b0ad1a034c0ffac5cd2ab49746")) + }) + + t.Run("should get file zstd image archive format", func(t *testing.T) { + RegisterTestingT(t) + + p := newFileProviderPreferGZIP("testdata/example-noop-1.0.0-zstd.tar") + image, err := p.Get(ctx, "example.com/example/noop:1.0.0") + Expect(err).ShouldNot(HaveOccurred()) + Expect(string(image.Target.Digest)).Should(Equal("sha256:37378d19f032cd586790c085aa4f8878a6c51472740e74de16c56e5443a38f21")) + }) + + t.Run("should get file multi image archive format", func(t *testing.T) { + t.Run("without containerd client", func(t *testing.T) { + RegisterTestingT(t) + + p := newFileProviderPreferGZIP("testdata/example-noop-1.0.0-multi.tar") + image, err := p.Get(ctx, "example.com/example/noop:1.0.0") + Expect(err).ShouldNot(HaveOccurred()) + Expect(string(image.Target.Digest)).Should(Equal("sha256:cbd3ccbe91459729a64eea12be3ae561d18883b0ad1a034c0ffac5cd2ab49746")) + }) + + t.Run("with containerd version < 1.5.0", func(t *testing.T) { + RegisterTestingT(t) + + p := newFileProviderPreferGZIP("testdata/example-noop-1.0.0-multi.tar") + err := p.(remotes.ContainerdClientInjectable).WithContainerdClient(ctx, &containerd.Client{}) + Expect(err).ShouldNot(HaveOccurred()) + + patches := gomonkey.NewPatches() + defer patches.Reset() + patches.ApplyMethodFunc(version.NewVersionClient(nil), "Version", patchVersionMethod("1.4.6")) + + image, err := p.Get(ctx, "example.com/example/noop:1.0.0") + Expect(err).ShouldNot(HaveOccurred()) + Expect(string(image.Target.Digest)).Should(Equal("sha256:cbd3ccbe91459729a64eea12be3ae561d18883b0ad1a034c0ffac5cd2ab49746")) + }) + + t.Run("with containerd version >= 1.5.0", func(t *testing.T) { + RegisterTestingT(t) + + p := newFileProviderPreferGZIP("testdata/example-noop-1.0.0-multi.tar") + err := p.(remotes.ContainerdClientInjectable).WithContainerdClient(ctx, &containerd.Client{}) + Expect(err).ShouldNot(HaveOccurred()) + + patches := gomonkey.NewPatches() + defer patches.Reset() + patches.ApplyMethodFunc(version.NewVersionClient(nil), "Version", patchVersionMethod("1.5.6")) + + image, err := p.Get(ctx, "example.com/example/noop:1.0.0") + Expect(err).ShouldNot(HaveOccurred()) + Expect(string(image.Target.Digest)).Should(Equal("sha256:cbd3ccbe91459729a64eea12be3ae561d18883b0ad1a034c0ffac5cd2ab49746")) + }) + }) +} + func TestFileProviderResolve(t *testing.T) { ctx := context.Background() @@ -206,6 +271,13 @@ func newFileProvider(filePath string) remotes.StoreProvider { return remotes.NewFileProvider(remotes.OpenFunc(func() (io.ReadCloser, error) { return os.Open(filePath) })) } +func newFileProviderPreferGZIP(filePath string) remotes.StoreProvider { + return remotes.NewFileProviderWithDownloaderPreferGZIP( + remotes.OpenFunc(func() (io.ReadCloser, error) { return os.Open(filePath) }), + remotes.NewDownloadGZIPFromZSTD(remotes.OpenFunc(func() (io.ReadCloser, error) { return os.Open(filePath) })), + ) +} + func patchVersionMethod(v string) func(ctx context.Context, in *ptypes.Empty, opts ...grpc.CallOption) (*version.VersionResponse, error) { return func(ctx context.Context, in *ptypes.Empty, opts ...grpc.CallOption) (*version.VersionResponse, error) { return &version.VersionResponse{Version: v}, nil