// 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 }