summaryrefslogtreecommitdiffstats
path: root/pkg/v1/daemon/image.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/v1/daemon/image.go')
-rw-r--r--pkg/v1/daemon/image.go203
1 files changed, 203 insertions, 0 deletions
diff --git a/pkg/v1/daemon/image.go b/pkg/v1/daemon/image.go
new file mode 100644
index 0000000..55ba833
--- /dev/null
+++ b/pkg/v1/daemon/image.go
@@ -0,0 +1,203 @@
+// Copyright 2018 Google LLC All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package daemon
+
+import (
+ "bytes"
+ "context"
+ "io"
+ "sync"
+
+ "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
+ id *v1.Hash
+
+ once sync.Once
+ err error
+}
+
+type imageOpener struct {
+ ref name.Reference
+ ctx context.Context
+
+ buffered bool
+ client Client
+
+ once sync.Once
+ bytes []byte
+ err error
+}
+
+func (i *imageOpener) saveImage() (io.ReadCloser, error) {
+ return i.client.ImageSave(i.ctx, []string{i.ref.Name()})
+}
+
+func (i *imageOpener) bufferedOpener() (io.ReadCloser, error) {
+ // Store the tarball in memory and return a new reader into the bytes each time we need to access something.
+ i.once.Do(func() {
+ i.bytes, i.err = func() ([]byte, error) {
+ rc, err := i.saveImage()
+ if err != nil {
+ return nil, err
+ }
+ defer rc.Close()
+
+ return io.ReadAll(rc)
+ }()
+ })
+
+ // Wrap the bytes in a ReadCloser so it looks like an opened file.
+ return io.NopCloser(bytes.NewReader(i.bytes)), i.err
+}
+
+func (i *imageOpener) opener() tarball.Opener {
+ if i.buffered {
+ return i.bufferedOpener
+ }
+
+ // To avoid storing the tarball in memory, do a save every time we need to access something.
+ return i.saveImage
+}
+
+// Image provides access to an image reference from the Docker daemon,
+// applying functional options to the underlying imageOpener before
+// resolving the reference into a v1.Image.
+func Image(ref name.Reference, options ...Option) (v1.Image, error) {
+ o, err := makeOptions(options...)
+ if err != nil {
+ return nil, err
+ }
+
+ i := &imageOpener{
+ ref: ref,
+ buffered: o.buffered,
+ client: o.client,
+ ctx: o.ctx,
+ }
+
+ img := &image{
+ ref: ref,
+ opener: i,
+ }
+
+ // Eagerly fetch Image ID to ensure it actually exists.
+ // https://github.com/google/go-containerregistry/issues/1186
+ id, err := img.ConfigName()
+ if err != nil {
+ return nil, err
+ }
+ img.id = &id
+
+ return img, nil
+}
+
+func (i *image) initialize() error {
+ // Don't re-initialize tarball if already initialized.
+ if i.tarballImage == nil {
+ i.once.Do(func() {
+ i.tarballImage, i.err = tarball.Image(i.opener.opener(), nil)
+ })
+ }
+ return i.err
+}
+
+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) {
+ if i.id != nil {
+ return *i.id, nil
+ }
+ 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)
+}