Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement local puller and pusher #1871

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion cmd/crane/cmd/append.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,5 @@ container image.`,

appendCmd.MarkFlagsMutuallyExclusive("oci-empty-base", "base")
appendCmd.MarkFlagRequired("new_tag")
appendCmd.MarkFlagRequired("new_layer")
return appendCmd
}
16 changes: 5 additions & 11 deletions cmd/crane/cmd/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,20 +70,14 @@ func NewCmdExport(options *[]crane.Option) *cobra.Command {
return fmt.Errorf("reading tarball from stdin: %w", err)
}
} else {
desc, err := crane.Get(src, *options...)
desc, err := crane.Artifact(src, *options...)
if err != nil {
return fmt.Errorf("pulling %s: %w", src, err)
}
if desc.MediaType.IsSchema1() {
img, err = desc.Schema1()
if err != nil {
return fmt.Errorf("pulling schema 1 image %s: %w", src, err)
}
} else {
img, err = desc.Image()
if err != nil {
return fmt.Errorf("pulling Image %s: %w", src, err)
}
var ok bool
thesayyn marked this conversation as resolved.
Show resolved Hide resolved
img, ok = desc.(v1.Image)
if !ok {
return fmt.Errorf("pulling schema 1 image %s: %w", src, err)
}
}

Expand Down
21 changes: 9 additions & 12 deletions cmd/crane/cmd/flatten.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,26 +91,23 @@ func NewCmdFlatten(options *[]crane.Option) *cobra.Command {
}

func flatten(ref name.Reference, repo name.Repository, use string, o crane.Options) (partial.Describable, error) {
desc, err := remote.Get(ref, o.Remote...)
desc, err := remote.Artifact(ref, o.Remote...)
if err != nil {
return nil, fmt.Errorf("pulling %s: %w", ref, err)
}

if desc.MediaType.IsIndex() {
idx, err := desc.ImageIndex()
if err != nil {
return nil, err
}
if idx, ok := desc.(v1.ImageIndex); ok {
return flattenIndex(idx, repo, use, o)
} else if desc.MediaType.IsImage() {
img, err := desc.Image()
if err != nil {
return nil, err
}
} else if img, ok := desc.(v1.Image); ok {
return flattenImage(img, repo, use, o)
}

return nil, fmt.Errorf("can't flatten %s", desc.MediaType)
mt, err := desc.MediaType()
if err != nil {
return nil, err
}

return nil, fmt.Errorf("can't flatten %s", mt)
}

func push(flat partial.Describable, ref name.Reference, o crane.Options) error {
Expand Down
49 changes: 24 additions & 25 deletions cmd/crane/cmd/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,19 @@ func NewCmdIndexFilter(options *[]crane.Option) *cobra.Command {
if err != nil {
return err
}
desc, err := remote.Get(ref, o.Remote...)
desc, err := remote.Artifact(ref, o.Remote...)
if err != nil {
return fmt.Errorf("pulling %s: %w", baseRef, err)
}
if !desc.MediaType.IsIndex() {
return fmt.Errorf("expected %s to be an index, got %q", baseRef, desc.MediaType)
}
base, err := desc.ImageIndex()
mt, err := desc.MediaType()
if err != nil {
return nil
return err
}

var base v1.ImageIndex
var ok bool
if base, ok = desc.(v1.ImageIndex); !ok {
return fmt.Errorf("expected %s to be an index, got %q", baseRef, mt)
}

idx := filterIndex(base, platforms.platforms)
Expand Down Expand Up @@ -142,6 +145,7 @@ The platform for appended manifests is inferred from the config file or omitted
var (
base v1.ImageIndex
err error
ok bool
ref name.Reference
)

Expand All @@ -160,16 +164,16 @@ The platform for appended manifests is inferred from the config file or omitted
if err != nil {
return err
}
desc, err := remote.Get(ref, o.Remote...)
desc, err := remote.Artifact(ref, o.Remote...)
if err != nil {
return fmt.Errorf("pulling %s: %w", baseRef, err)
}
if !desc.MediaType.IsIndex() {
return fmt.Errorf("expected %s to be an index, got %q", baseRef, desc.MediaType)
}
base, err = desc.ImageIndex()
mt, err := desc.MediaType()
if err != nil {
return err
return fmt.Errorf("getting media type %s: %w", baseRef, err)
}
if base, ok = desc.(v1.ImageIndex); !ok {
return fmt.Errorf("expected %s to be an index, got %q", baseRef, mt)
}
}

Expand All @@ -180,16 +184,11 @@ The platform for appended manifests is inferred from the config file or omitted
if err != nil {
return err
}
desc, err := remote.Get(ref, o.Remote...)
desc, err := remote.Artifact(ref, o.Remote...)
if err != nil {
return err
}
if desc.MediaType.IsImage() {
img, err := desc.Image()
if err != nil {
return err
}

if img, ok := desc.(v1.Image); ok {
cf, err := img.ConfigFile()
if err != nil {
return err
Expand All @@ -203,11 +202,7 @@ The platform for appended manifests is inferred from the config file or omitted
Add: img,
Descriptor: *newDesc,
})
} else if desc.MediaType.IsIndex() {
idx, err := desc.ImageIndex()
if err != nil {
return err
}
} else if idx, ok := desc.(v1.ImageIndex); ok {
if flatten {
im, err := idx.IndexManifest()
if err != nil {
Expand Down Expand Up @@ -243,7 +238,11 @@ The platform for appended manifests is inferred from the config file or omitted
})
}
} else {
return fmt.Errorf("saw unexpected MediaType %q for %q", desc.MediaType, m)
mt, err := desc.MediaType()
if err != nil {
return fmt.Errorf("getting media type %s: %w", baseRef, err)
}
return fmt.Errorf("saw unexpected MediaType %q for %q", mt, m)
}
}

Expand Down
9 changes: 8 additions & 1 deletion cmd/crane/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/google/go-containerregistry/internal/cmd"
"github.com/google/go-containerregistry/pkg/crane"
"github.com/google/go-containerregistry/pkg/logs"
"github.com/google/go-containerregistry/pkg/v1/layout"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/spf13/cobra"
)
Expand All @@ -47,6 +48,7 @@ func New(use, short string, options []crane.Option) *cobra.Command {
insecure := false
ndlayers := false
platform := &platformValue{}
uselocal := ""

wt := &warnTransport{}

Expand All @@ -68,6 +70,10 @@ func New(use, short string, options []crane.Option) *cobra.Command {
if ndlayers {
options = append(options, crane.WithNondistributable())
}
if uselocal != "" {
p, _ := layout.FromPath(uselocal)
options = append(options, crane.WithSource(layout.NewSource(p)), crane.WithSink(layout.NewSink(p)))
}
if Version != "" {
binary := "crane"
if len(os.Args[0]) != 0 {
Expand Down Expand Up @@ -137,7 +143,8 @@ func New(use, short string, options []crane.Option) *cobra.Command {
root.PersistentFlags().BoolVar(&insecure, "insecure", false, "Allow image references to be fetched without TLS")
root.PersistentFlags().BoolVar(&ndlayers, "allow-nondistributable-artifacts", false, "Allow pushing non-distributable (foreign) layers")
root.PersistentFlags().Var(platform, "platform", "Specifies the platform in the form os/arch[/variant][:osversion] (e.g. linux/amd64).")

root.PersistentFlags().StringVar(&uselocal, "local", "", "Use a local oci-layout as remote registry")
root.PersistentFlags().MarkHidden("local")
return root
}

Expand Down
26 changes: 15 additions & 11 deletions cmd/crane/cmd/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"fmt"

"github.com/google/go-containerregistry/pkg/crane"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/tarball"
"github.com/google/go-containerregistry/pkg/v1/validate"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -52,7 +53,7 @@ func NewCmdValidate(options *[]crane.Option) *cobra.Command {
}

if remoteRef != "" {
rmt, err := crane.Get(remoteRef, *options...)
rmt, err := crane.Artifact(remoteRef, *options...)
if err != nil {
return fmt.Errorf("failed to read image %s: %w", remoteRef, err)
}
Expand All @@ -63,24 +64,27 @@ func NewCmdValidate(options *[]crane.Option) *cobra.Command {
if fast {
opt = append(opt, validate.Fast)
}
if rmt.MediaType.IsIndex() && o.Platform == nil {
idx, err := rmt.ImageIndex()
if err != nil {
return fmt.Errorf("reading index: %w", err)
}
var (
idx v1.ImageIndex
img v1.Image
ok bool
)
if idx, ok = rmt.(v1.ImageIndex); ok && o.Platform == nil {
if err := validate.Index(idx, opt...); err != nil {
fmt.Fprintf(cmd.OutOrStdout(), "FAIL: %s: %v\n", remoteRef, err)
return err
}
} else {
img, err := rmt.Image()
if err != nil {
return fmt.Errorf("reading image: %w", err)
}
} else if img, ok = rmt.(v1.Image); ok {
if err := validate.Image(img, opt...); err != nil {
fmt.Fprintf(cmd.OutOrStdout(), "FAIL: %s: %v\n", remoteRef, err)
return err
}
} else {
mt, err := rmt.MediaType()
if err != nil {
return err
}
return fmt.Errorf("failed to validate ref %s: uknown media type %s", remoteRef, mt)
}
fmt.Fprintf(cmd.OutOrStdout(), "PASS: %s\n", remoteRef)
}
Expand Down
15 changes: 12 additions & 3 deletions internal/cmd/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,14 +280,19 @@ func editManifest(in io.Reader, out io.Writer, src string, dst string, mt string
return nil, err
}

desc, err := remote.Get(ref, o.Remote...)
desc, err := remote.Artifact(ref, o.Remote...)
if err != nil {
return nil, err
}

manifest, err := desc.RawManifest()
if err != nil {
return nil, err
}

var edited []byte
if interactive(in, out) {
edited, err = editor.Edit(bytes.NewReader(desc.Manifest), ".json")
edited, err = editor.Edit(bytes.NewReader(manifest), ".json")
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -317,7 +322,11 @@ func editManifest(in io.Reader, out io.Writer, src string, dst string, mt string

if mt == "" {
// If --media-type is unset, use Content-Type by default.
mt = string(desc.MediaType)
mediatype, err := desc.MediaType()
if err != nil {
return nil, err
}
mt = string(mediatype)

// If document contains mediaType, default to that.
wmt := withMediaType{}
Expand Down
2 changes: 1 addition & 1 deletion pkg/crane/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ package crane

// Config returns the config file for the remote image ref.
func Config(ref string, opt ...Option) ([]byte, error) {
i, _, err := getImage(ref, opt...)
i, err := GetImage(ref, opt...)
if err != nil {
return nil, err
}
Expand Down
3 changes: 1 addition & 2 deletions pkg/crane/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"fmt"

"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
)

// Delete deletes the remote reference at src.
Expand All @@ -29,5 +28,5 @@ func Delete(src string, opt ...Option) error {
return fmt.Errorf("parsing reference %q: %w", src, err)
}

return remote.Delete(ref, o.Remote...)
return o.sink.Delete(o.ctx, ref)
}
25 changes: 11 additions & 14 deletions pkg/crane/digest.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,19 @@

package crane

import "github.com/google/go-containerregistry/pkg/logs"
import (
"github.com/google/go-containerregistry/pkg/logs"
)

// Digest returns the sha256 hash of the remote image at ref.
func Digest(ref string, opt ...Option) (string, error) {
o := makeOptions(opt...)
if o.Platform != nil {
desc, err := getManifest(ref, opt...)
desc, err := getArtifact(ref, opt...)
if err != nil {
return "", err
}
if !desc.MediaType.IsIndex() {
return desc.Digest.String(), nil
}

// TODO: does not work for indexes which contain schema v1 manifests
img, err := desc.Image()
if err != nil {
return "", err
}
digest, err := img.Digest()
digest, err := desc.Digest()
if err != nil {
return "", err
}
Expand All @@ -42,11 +35,15 @@ func Digest(ref string, opt ...Option) (string, error) {
desc, err := Head(ref, opt...)
if err != nil {
logs.Warn.Printf("HEAD request failed, falling back on GET: %v", err)
rdesc, err := getManifest(ref, opt...)
rdesc, err := getArtifact(ref, opt...)
if err != nil {
return "", err
}
hash, err := rdesc.Digest()
if err != nil {
return "", err
}
return rdesc.Digest.String(), nil
return hash.String(), nil
}
return desc.Digest.String(), nil
}
Loading
Loading