From f0983da3297cf2e88939a5db926ce60d8535b21b Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Sun, 19 Sep 2021 22:41:06 +0300 Subject: [PATCH] feat(daemon): lazy image saving (#1121) * feat(daemon): tarball lazy loading * fix tests * fix: replace LayerByDigest with LayerByDiffID * test: add validate.Image --- pkg/v1/daemon/image.go | 102 +++++++++++++++++++++++++++++++++++- pkg/v1/daemon/image_test.go | 27 ++++++++-- pkg/v1/daemon/options.go | 1 + 3 files changed, 126 insertions(+), 4 deletions(-) diff --git a/pkg/v1/daemon/image.go b/pkg/v1/daemon/image.go index 5013c3d30..b912c4478 100644 --- a/pkg/v1/daemon/image.go +++ b/pkg/v1/daemon/image.go @@ -24,8 +24,15 @@ import ( "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/tarball" + "github.com/google/go-containerregistry/pkg/v1/types" ) +type image struct { + ref name.Reference + opener *imageOpener + tarballImage v1.Image +} + type imageOpener struct { ref name.Reference ctx context.Context @@ -85,5 +92,98 @@ func Image(ref name.Reference, options ...Option) (v1.Image, error) { ctx: o.ctx, } - return tarball.Image(i.opener(), nil) + return &image{ + ref: ref, + opener: i, + }, nil +} + +func (i *image) initialize() error { + // Don't re-initialize tarball if already initialized. + if i.tarballImage == nil { + var err error + i.tarballImage, err = tarball.Image(i.opener.opener(), nil) + if err != nil { + return err + } + } + return nil +} + +func (i *image) Layers() ([]v1.Layer, error) { + if err := i.initialize(); err != nil { + return nil, err + } + return i.tarballImage.Layers() +} + +func (i image) MediaType() (types.MediaType, error) { + if err := i.initialize(); err != nil { + return "", err + } + return i.tarballImage.MediaType() +} + +func (i *image) Size() (int64, error) { + if err := i.initialize(); err != nil { + return 0, err + } + return i.tarballImage.Size() +} + +func (i *image) ConfigName() (v1.Hash, error) { + res, _, err := i.opener.client.ImageInspectWithRaw(i.opener.ctx, i.ref.String()) + if err != nil { + return v1.Hash{}, err + } + return v1.NewHash(res.ID) +} + +func (i image) ConfigFile() (*v1.ConfigFile, error) { + if err := i.initialize(); err != nil { + return nil, err + } + return i.tarballImage.ConfigFile() +} + +func (i image) RawConfigFile() ([]byte, error) { + if err := i.initialize(); err != nil { + return nil, err + } + return i.tarballImage.RawConfigFile() +} + +func (i image) Digest() (v1.Hash, error) { + if err := i.initialize(); err != nil { + return v1.Hash{}, err + } + return i.tarballImage.Digest() +} + +func (i image) Manifest() (*v1.Manifest, error) { + if err := i.initialize(); err != nil { + return nil, err + } + return i.tarballImage.Manifest() +} + +func (i image) RawManifest() ([]byte, error) { + if err := i.initialize(); err != nil { + return nil, err + } + return i.tarballImage.RawManifest() +} + +func (i image) LayerByDigest(h v1.Hash) (v1.Layer, error) { + if err := i.initialize(); err != nil { + return nil, err + } + return i.tarballImage.LayerByDigest(h) +} + +func (i image) LayerByDiffID(h v1.Hash) (v1.Layer, error) { + if err := i.initialize(); err != nil { + return nil, err + } + return i.tarballImage.LayerByDiffID(h) } diff --git a/pkg/v1/daemon/image_test.go b/pkg/v1/daemon/image_test.go index c65565771..07e73acb1 100644 --- a/pkg/v1/daemon/image_test.go +++ b/pkg/v1/daemon/image_test.go @@ -24,10 +24,11 @@ import ( "strings" "testing" + "github.com/docker/docker/api/types" "github.com/google/go-containerregistry/internal/compare" "github.com/google/go-containerregistry/pkg/name" - "github.com/google/go-containerregistry/pkg/v1/tarball" + "github.com/google/go-containerregistry/pkg/v1/validate" ) var imagePath = "../tarball/testdata/test_image_1.tar" @@ -62,6 +63,12 @@ func (m *MockClient) ImageSave(_ context.Context, _ []string) (io.ReadCloser, er return m.saveBody, m.saveErr } +func (m *MockClient) ImageInspectWithRaw(context.Context, string) (types.ImageInspect, []byte, error) { + return types.ImageInspect{ + ID: "sha256:6e0b05049ed9c17d02e1a55e80d6599dbfcce7f4f4b022e3c673e685789c470e", + }, nil, nil +} + func TestImage(t *testing.T) { for _, tc := range []struct { name string @@ -114,8 +121,22 @@ func TestImage(t *testing.T) { } return } - if err := compare.Images(img, dmn); err != nil { - t.Errorf("compare.Images: %v", err) + err = compare.Images(img, dmn) + if err != nil { + if tc.wantErr == "" { + t.Errorf("compare.Images: %v", err) + } else if !strings.Contains(err.Error(), tc.wantErr) { + t.Errorf("wanted %s to contain %s", err.Error(), tc.wantErr) + } + } + + err = validate.Image(dmn) + if err != nil { + if tc.wantErr == "" { + t.Errorf("validate.Image: %v", err) + } else if !strings.Contains(err.Error(), tc.wantErr) { + t.Errorf("wanted %s to contain %s", err.Error(), tc.wantErr) + } } } diff --git a/pkg/v1/daemon/options.go b/pkg/v1/daemon/options.go index c3a0ac66b..e8a5a1e5d 100644 --- a/pkg/v1/daemon/options.go +++ b/pkg/v1/daemon/options.go @@ -99,4 +99,5 @@ type Client interface { ImageSave(context.Context, []string) (io.ReadCloser, error) ImageLoad(context.Context, io.Reader, bool) (types.ImageLoadResponse, error) ImageTag(context.Context, string, string) error + ImageInspectWithRaw(context.Context, string) (types.ImageInspect, []byte, error) }