summaryrefslogtreecommitdiffstats
path: root/pkg/crane/pull.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/crane/pull.go')
-rw-r--r--pkg/crane/pull.go142
1 files changed, 142 insertions, 0 deletions
diff --git a/pkg/crane/pull.go b/pkg/crane/pull.go
new file mode 100644
index 0000000..7e6e5b7
--- /dev/null
+++ b/pkg/crane/pull.go
@@ -0,0 +1,142 @@
+// 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 crane
+
+import (
+ "fmt"
+ "os"
+
+ legacy "github.com/google/go-containerregistry/pkg/legacy/tarball"
+ "github.com/google/go-containerregistry/pkg/name"
+ v1 "github.com/google/go-containerregistry/pkg/v1"
+ "github.com/google/go-containerregistry/pkg/v1/empty"
+ "github.com/google/go-containerregistry/pkg/v1/layout"
+ "github.com/google/go-containerregistry/pkg/v1/remote"
+ "github.com/google/go-containerregistry/pkg/v1/tarball"
+)
+
+// Tag applied to images that were pulled by digest. This denotes that the
+// image was (probably) never tagged with this, but lets us avoid applying the
+// ":latest" tag which might be misleading.
+const iWasADigestTag = "i-was-a-digest"
+
+// Pull returns a v1.Image of the remote image src.
+func Pull(src string, opt ...Option) (v1.Image, error) {
+ o := makeOptions(opt...)
+ ref, err := name.ParseReference(src, o.Name...)
+ if err != nil {
+ return nil, fmt.Errorf("parsing reference %q: %w", src, err)
+ }
+
+ return remote.Image(ref, o.Remote...)
+}
+
+// Save writes the v1.Image img as a tarball at path with tag src.
+func Save(img v1.Image, src, path string) error {
+ imgMap := map[string]v1.Image{src: img}
+ return MultiSave(imgMap, path)
+}
+
+// MultiSave writes collection of v1.Image img with tag as a tarball.
+func MultiSave(imgMap map[string]v1.Image, path string, opt ...Option) error {
+ o := makeOptions(opt...)
+ tagToImage := map[name.Tag]v1.Image{}
+
+ for src, img := range imgMap {
+ ref, err := name.ParseReference(src, o.Name...)
+ if err != nil {
+ return fmt.Errorf("parsing ref %q: %w", src, err)
+ }
+
+ // WriteToFile wants a tag to write to the tarball, but we might have
+ // been given a digest.
+ // If the original ref was a tag, use that. Otherwise, if it was a
+ // digest, tag the image with :i-was-a-digest instead.
+ tag, ok := ref.(name.Tag)
+ if !ok {
+ d, ok := ref.(name.Digest)
+ if !ok {
+ return fmt.Errorf("ref wasn't a tag or digest")
+ }
+ tag = d.Repository.Tag(iWasADigestTag)
+ }
+ tagToImage[tag] = img
+ }
+ // no progress channel (for now)
+ return tarball.MultiWriteToFile(path, tagToImage)
+}
+
+// PullLayer returns the given layer from a registry.
+func PullLayer(ref string, opt ...Option) (v1.Layer, error) {
+ o := makeOptions(opt...)
+ digest, err := name.NewDigest(ref, o.Name...)
+ if err != nil {
+ return nil, err
+ }
+
+ return remote.Layer(digest, o.Remote...)
+}
+
+// SaveLegacy writes the v1.Image img as a legacy tarball at path with tag src.
+func SaveLegacy(img v1.Image, src, path string) error {
+ imgMap := map[string]v1.Image{src: img}
+ return MultiSave(imgMap, path)
+}
+
+// MultiSaveLegacy writes collection of v1.Image img with tag as a legacy tarball.
+func MultiSaveLegacy(imgMap map[string]v1.Image, path string) error {
+ refToImage := map[name.Reference]v1.Image{}
+
+ for src, img := range imgMap {
+ ref, err := name.ParseReference(src)
+ if err != nil {
+ return fmt.Errorf("parsing ref %q: %w", src, err)
+ }
+ refToImage[ref] = img
+ }
+
+ w, err := os.Create(path)
+ if err != nil {
+ return err
+ }
+ defer w.Close()
+
+ return legacy.MultiWrite(refToImage, w)
+}
+
+// SaveOCI writes the v1.Image img as an OCI Image Layout at path. If a layout
+// already exists at that path, it will add the image to the index.
+func SaveOCI(img v1.Image, path string) error {
+ imgMap := map[string]v1.Image{"": img}
+ return MultiSaveOCI(imgMap, path)
+}
+
+// MultiSaveOCI writes collection of v1.Image img as an OCI Image Layout at path. If a layout
+// already exists at that path, it will add the image to the index.
+func MultiSaveOCI(imgMap map[string]v1.Image, path string) error {
+ p, err := layout.FromPath(path)
+ if err != nil {
+ p, err = layout.Write(path, empty.Index)
+ if err != nil {
+ return err
+ }
+ }
+ for _, img := range imgMap {
+ if err = p.AppendImage(img); err != nil {
+ return err
+ }
+ }
+ return nil
+}