From 2d2b8533c60536c966eee61e7184629cf6006988 Mon Sep 17 00:00:00 2001 From: thesayyn Date: Mon, 18 Mar 2024 14:30:16 -0700 Subject: [PATCH] sourcesink --- cmd/crane/cmd/root.go | 2 +- pkg/crane/delete.go | 3 +- pkg/crane/get.go | 4 +- pkg/crane/options.go | 24 ++++++-- pkg/crane/push.go | 6 +- pkg/v1/layout/{pusher.go => sink.go} | 30 +++++----- .../layout/{pusher_test.go => sink_test.go} | 30 +++++----- pkg/v1/layout/{puller.go => source.go} | 29 +++++----- .../layout/{puller_test.go => source_test.go} | 24 ++++---- pkg/v1/partial/artifact.go | 4 +- pkg/v1/remote/options.go | 16 ------ pkg/v1/remote/puller.go | 56 +++++++------------ pkg/v1/remote/pusher.go | 28 ++++------ pkg/v1/sourcesink/sink.go | 15 +++++ pkg/v1/sourcesink/source.go | 15 +++++ 15 files changed, 144 insertions(+), 142 deletions(-) rename pkg/v1/layout/{pusher.go => sink.go} (84%) rename pkg/v1/layout/{pusher_test.go => sink_test.go} (88%) rename pkg/v1/layout/{puller.go => source.go} (78%) rename pkg/v1/layout/{puller_test.go => source_test.go} (85%) create mode 100644 pkg/v1/sourcesink/sink.go create mode 100644 pkg/v1/sourcesink/source.go diff --git a/cmd/crane/cmd/root.go b/cmd/crane/cmd/root.go index a0d210a5a..c754d5fad 100644 --- a/cmd/crane/cmd/root.go +++ b/cmd/crane/cmd/root.go @@ -72,7 +72,7 @@ func New(use, short string, options []crane.Option) *cobra.Command { } if uselocal != "" { p, _ := layout.FromPath(uselocal) - options = append(options, crane.WithPuller(layout.NewPuller(p)), crane.WithPusher(layout.NewPusher(p))) + options = append(options, crane.WithSource(layout.NewSource(p)), crane.WithSink(layout.NewSink(p))) } if Version != "" { binary := "crane" diff --git a/pkg/crane/delete.go b/pkg/crane/delete.go index 58a8be1f0..26189ffc3 100644 --- a/pkg/crane/delete.go +++ b/pkg/crane/delete.go @@ -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. @@ -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) } diff --git a/pkg/crane/get.go b/pkg/crane/get.go index d44064d41..b5a0234e3 100644 --- a/pkg/crane/get.go +++ b/pkg/crane/get.go @@ -38,7 +38,7 @@ func getArtifact(r string, opt ...Option) (partial.Artifact, error) { if err != nil { return nil, fmt.Errorf("parsing reference %q: %w", r, err) } - return remote.Artifact(ref, o.Remote...) + return o.source.Artifact(o.ctx, ref) } // Get calls remote.Get and returns an uninterpreted response. @@ -58,5 +58,5 @@ func Head(r string, opt ...Option) (*v1.Descriptor, error) { if err != nil { return nil, err } - return remote.Head(ref, o.Remote...) + return o.source.Head(o.ctx, ref) } diff --git a/pkg/crane/options.go b/pkg/crane/options.go index e7984b9be..525719a38 100644 --- a/pkg/crane/options.go +++ b/pkg/crane/options.go @@ -23,6 +23,7 @@ import ( "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/remote" + "github.com/google/go-containerregistry/pkg/v1/sourcesink" ) // Options hold the options that crane uses when calling other packages. @@ -38,6 +39,9 @@ type Options struct { jobs int noclobber bool ctx context.Context + + sink sourcesink.Sink + source sourcesink.Source } // GetOptions exposes the underlying []remote.Option, []name.Option, and @@ -62,6 +66,14 @@ func makeOptions(opts ...Option) Options { o(&opt) } + // By default use remote source and sink + if opt.sink == nil { + opt.sink, _ = remote.NewPusher(opt.Remote...) + } + if opt.source == nil { + opt.source, _ = remote.NewPuller(opt.Remote...) + } + // Allow for untrusted certificates if the user // passed Insecure but no custom transport. if opt.insecure && opt.Transport == nil { @@ -177,16 +189,16 @@ func WithNoClobber(noclobber bool) Option { } } -// WithPuller sets the puller for remote -func WithPuller(puller remote.Puller) Option { +// WithSink sets the sink +func WithSink(sink sourcesink.Sink) Option { return func(o *Options) { - o.Remote = append(o.Remote, remote.WithPuller(puller)) + o.sink = sink } } -// WithPuller sets the puller for remote -func WithPusher(pusher remote.Pusher) Option { +// WithSource sets the source +func WithSource(source sourcesink.Source) Option { return func(o *Options) { - o.Remote = append(o.Remote, remote.WithPusher(pusher)) + o.source = source } } diff --git a/pkg/crane/push.go b/pkg/crane/push.go index 90a058502..c83edbaf5 100644 --- a/pkg/crane/push.go +++ b/pkg/crane/push.go @@ -19,7 +19,6 @@ import ( "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" - "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/google/go-containerregistry/pkg/v1/tarball" ) @@ -50,7 +49,7 @@ func Push(img v1.Image, dst string, opt ...Option) error { if err != nil { return fmt.Errorf("parsing reference %q: %w", dst, err) } - return remote.Write(tag, img, o.Remote...) + return o.sink.Push(o.ctx, tag, img) } // Upload pushes the v1.Layer to a given repo. @@ -60,6 +59,5 @@ func Upload(layer v1.Layer, repo string, opt ...Option) error { if err != nil { return fmt.Errorf("parsing repo %q: %w", repo, err) } - - return remote.WriteLayer(ref, layer, o.Remote...) + return o.sink.Upload(o.ctx, ref, layer) } diff --git a/pkg/v1/layout/pusher.go b/pkg/v1/layout/sink.go similarity index 84% rename from pkg/v1/layout/pusher.go rename to pkg/v1/layout/sink.go index f159a83b7..a6959ffc2 100644 --- a/pkg/v1/layout/pusher.go +++ b/pkg/v1/layout/sink.go @@ -24,7 +24,7 @@ import ( "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/partial" - "github.com/google/go-containerregistry/pkg/v1/remote" + "github.com/google/go-containerregistry/pkg/v1/sourcesink" "github.com/google/go-containerregistry/pkg/v1/stream" "github.com/google/go-containerregistry/pkg/v1/types" specsv1 "github.com/opencontainers/image-spec/specs-go/v1" @@ -89,18 +89,16 @@ func unpackTaggable(t partial.WithRawManifest) ([]byte, *v1.Descriptor, error) { }, nil } -// Use partial.Artifact to unpack taggable. -// Duplication is not a concern here. -type pusher struct { +type sink struct { path Path } // Delete implements remote.Pusher. -func (lp *pusher) Delete(_ context.Context, _ name.Reference) error { +func (lp *sink) Delete(_ context.Context, _ name.Reference) error { return errors.New("unsupported operation") } -func (lp *pusher) writeLayer(l v1.Layer) error { +func (lp *sink) writeLayer(l v1.Layer) error { dg, err := l.Digest() if err != nil { return err @@ -116,7 +114,7 @@ func (lp *pusher) writeLayer(l v1.Layer) error { return nil } -func (lp *pusher) writeLayers(pctx context.Context, img v1.Image) error { +func (lp *sink) writeLayers(pctx context.Context, img v1.Image) error { ls, err := img.Layers() if err != nil { return err @@ -155,7 +153,7 @@ func (lp *pusher) writeLayers(pctx context.Context, img v1.Image) error { return g.Wait() } -func (lp *pusher) writeChildren(pctx context.Context, idx v1.ImageIndex) error { +func (lp *sink) writeChildren(pctx context.Context, idx v1.ImageIndex) error { children, err := partial.Manifests(idx) if err != nil { return err @@ -173,7 +171,7 @@ func (lp *pusher) writeChildren(pctx context.Context, idx v1.ImageIndex) error { return g.Wait() } -func (lp *pusher) writeDeps(ctx context.Context, m partial.Artifact) error { +func (lp *sink) writeDeps(ctx context.Context, m partial.Artifact) error { if img, ok := m.(v1.Image); ok { return lp.writeLayers(ctx, img) } @@ -186,7 +184,7 @@ func (lp *pusher) writeDeps(ctx context.Context, m partial.Artifact) error { return nil } -func (lp *pusher) writeManifest(ctx context.Context, t partial.WithRawManifest) error { +func (lp *sink) writeManifest(ctx context.Context, t partial.WithRawManifest) error { m, err := taggableToManifest(t) if err != nil { return err @@ -220,7 +218,7 @@ func (lp *pusher) writeManifest(ctx context.Context, t partial.WithRawManifest) return nil } -func (lp *pusher) writeChild(ctx context.Context, child partial.Describable, g *errgroup.Group) error { +func (lp *sink) writeChild(ctx context.Context, child partial.Describable, g *errgroup.Group) error { switch child := child.(type) { case v1.ImageIndex: // For recursive index, we want to do a depth-first launching of goroutines @@ -244,7 +242,7 @@ func (lp *pusher) writeChild(ctx context.Context, child partial.Describable, g * } // Push implements remote.Pusher. -func (lp *pusher) Push(ctx context.Context, ref name.Reference, t partial.WithRawManifest) error { +func (lp *sink) Push(ctx context.Context, ref name.Reference, t partial.WithRawManifest) error { err := lp.writeManifest(ctx, t) if err != nil { return err @@ -264,7 +262,7 @@ func (lp *pusher) Push(ctx context.Context, ref name.Reference, t partial.WithRa } // Upload implements remote.Pusher. -func (lp *pusher) Upload(_ context.Context, _ name.Repository, l v1.Layer) error { +func (lp *sink) Upload(_ context.Context, _ name.Repository, l v1.Layer) error { digest, err := l.Digest() if err != nil { return err @@ -276,10 +274,10 @@ func (lp *pusher) Upload(_ context.Context, _ name.Repository, l v1.Layer) error return lp.path.WriteBlob(digest, rc) } -func NewPusher(path Path) remote.Pusher { - return &pusher{ +func NewSink(path Path) sourcesink.Sink { + return &sink{ path, } } -var _ remote.Pusher = (*pusher)(nil) +var _ sourcesink.Sink = (*sink)(nil) diff --git a/pkg/v1/layout/pusher_test.go b/pkg/v1/layout/sink_test.go similarity index 88% rename from pkg/v1/layout/pusher_test.go rename to pkg/v1/layout/sink_test.go index 823055203..61cd874b4 100644 --- a/pkg/v1/layout/pusher_test.go +++ b/pkg/v1/layout/sink_test.go @@ -110,11 +110,11 @@ func TestCanPushRandomImage(t *testing.T) { t.Fatalf("random.Image() = %v", err) } path := mustOCILayout(t) - pusher := NewPusher(path) + sink := NewSink(path) ref := name.MustParseReference("local.random/image:latest") - err = pusher.Push(context.TODO(), ref, img) + err = sink.Push(context.TODO(), ref, img) if err != nil { - t.Errorf("pusher.Push() = %v", err) + t.Errorf("sink.Push() = %v", err) } mustHaveManifest(t, path, "local.random/image:latest") mustHaveBlobs(t, path, enumerateImageBlobs(t, img)) @@ -126,11 +126,11 @@ func TestCanPushRandomImageIndex(t *testing.T) { t.Fatalf("random.Index() = %v", err) } path := mustOCILayout(t) - pusher := NewPusher(path) + sink := NewSink(path) ref := name.MustParseReference("local.random/index:latest") - err = pusher.Push(context.TODO(), ref, idx) + err = sink.Push(context.TODO(), ref, idx) if err != nil { - t.Errorf("pusher.Push() = %v", err) + t.Errorf("sink.Push() = %v", err) } mustHaveManifest(t, path, "local.random/index:latest") mustHaveBlobs(t, path, enumerateImageIndexBlobs(t, idx)) @@ -147,12 +147,12 @@ func TestCanPushImageIndex(t *testing.T) { } path := mustOCILayout(t) - pusher := NewPusher(path) + sink := NewSink(path) ref := name.MustParseReference("local.repo/index:latest") - err = pusher.Push(context.TODO(), ref, img) + err = sink.Push(context.TODO(), ref, img) if err != nil { - t.Errorf("pusher.Push() = %v", err) + t.Errorf("sink.Push() = %v", err) } mustHaveManifest(t, path, "local.repo/index:latest") mustHaveBlobs(t, path, enumerateImageBlobs(t, img)) @@ -169,12 +169,12 @@ func TestCanPushImage(t *testing.T) { } path := mustOCILayout(t) - pusher := NewPusher(path) + sink := NewSink(path) ref := name.MustParseReference("local.repo/index:latest") - err = pusher.Push(context.TODO(), ref, img) + err = sink.Push(context.TODO(), ref, img) if err != nil { - t.Errorf("pusher.Push() = %v", err) + t.Errorf("sink.Push() = %v", err) } mustHaveManifest(t, path, "local.repo/index:latest") mustHaveBlobs(t, path, enumerateImageBlobs(t, img)) @@ -191,11 +191,11 @@ func TestCanPushImageWithLatestTag(t *testing.T) { } path := mustOCILayout(t) - pusher := NewPusher(path) + sink := NewSink(path) - err = pusher.Push(context.TODO(), name.MustParseReference("reg.local.repo/index"), img) + err = sink.Push(context.TODO(), name.MustParseReference("reg.local.repo/index"), img) if err != nil { - t.Errorf("pusher.Push() = %v", err) + t.Errorf("sink.Push() = %v", err) } mustHaveManifest(t, path, "reg.local.repo/index:latest") mustHaveBlobs(t, path, enumerateImageBlobs(t, img)) diff --git a/pkg/v1/layout/puller.go b/pkg/v1/layout/source.go similarity index 78% rename from pkg/v1/layout/puller.go rename to pkg/v1/layout/source.go index e3279d1d4..77abac7a7 100644 --- a/pkg/v1/layout/puller.go +++ b/pkg/v1/layout/source.go @@ -23,23 +23,24 @@ import ( "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/remote" + "github.com/google/go-containerregistry/pkg/v1/sourcesink" specsv1 "github.com/opencontainers/image-spec/specs-go/v1" ) -type puller struct { +type source struct { path Path } -func NewPuller(path Path) remote.Puller { - return &puller{ +func NewSource(path Path) sourcesink.Source { + return &source{ path, } } -var _ remote.Puller = (*puller)(nil) +var _ sourcesink.Source = (*source)(nil) // Artifact implements remote.Puller. -func (p *puller) getDescriptor(ref name.Reference) (*v1.Descriptor, error) { +func (p *source) getDescriptor(ref name.Reference) (*v1.Descriptor, error) { idx, err := p.path.ImageIndex() if err != nil { return nil, err @@ -61,7 +62,7 @@ func (p *puller) getDescriptor(ref name.Reference) (*v1.Descriptor, error) { } // Artifact implements remote.Puller. -func (p *puller) Artifact(_ context.Context, ref name.Reference) (partial.Artifact, error) { +func (p *source) Artifact(_ context.Context, ref name.Reference) (partial.Artifact, error) { desc, err := p.getDescriptor(ref) if err != nil { return nil, err @@ -81,12 +82,12 @@ func (p *puller) Artifact(_ context.Context, ref name.Reference) (partial.Artifa } // Head implements remote.Puller. -func (p *puller) Head(_ context.Context, ref name.Reference) (*v1.Descriptor, error) { +func (p *source) Head(_ context.Context, ref name.Reference) (*v1.Descriptor, error) { return p.getDescriptor(ref) } // Layer implements remote.Puller. -func (p *puller) Layer(_ context.Context, ref name.Digest) (v1.Layer, error) { +func (p *source) Layer(_ context.Context, ref name.Digest) (v1.Layer, error) { h, err := v1.NewHash(ref.Identifier()) if err != nil { return nil, err @@ -102,31 +103,31 @@ func (p *puller) Layer(_ context.Context, ref name.Digest) (v1.Layer, error) { } // List implements remote.Puller. -func (*puller) List(_ context.Context, _ name.Repository) ([]string, error) { +func (*source) List(_ context.Context, _ name.Repository) ([]string, error) { return nil, fmt.Errorf("unsupported operation") } // Get implements remote.Puller. -func (*puller) Get(_ context.Context, _ name.Reference) (*remote.Descriptor, error) { +func (*source) Get(_ context.Context, _ name.Reference) (*remote.Descriptor, error) { return nil, fmt.Errorf("unsupported operation") } // Lister implements remote.Puller. -func (*puller) Lister(_ context.Context, _ name.Repository) (*remote.Lister, error) { +func (*source) Lister(_ context.Context, _ name.Repository) (*remote.Lister, error) { return nil, fmt.Errorf("unsupported operation") } // Catalogger implements remote.Puller. -func (*puller) Catalogger(_ context.Context, _ name.Registry) (*remote.Catalogger, error) { +func (*source) Catalogger(_ context.Context, _ name.Registry) (*remote.Catalogger, error) { return nil, fmt.Errorf("unsupported operation") } // Catalog implements remote.Puller. -func (*puller) Catalog(_ context.Context, _ name.Registry) ([]string, error) { +func (*source) Catalog(_ context.Context, _ name.Registry) ([]string, error) { return nil, fmt.Errorf("unsupported operation") } // Referrers implements remote.Puller. -func (*puller) Referrers(_ context.Context, _ name.Digest, _ map[string]string) (v1.ImageIndex, error) { +func (*source) Referrers(_ context.Context, _ name.Digest, _ map[string]string) (v1.ImageIndex, error) { return nil, fmt.Errorf("unsupported operation") } diff --git a/pkg/v1/layout/puller_test.go b/pkg/v1/layout/source_test.go similarity index 85% rename from pkg/v1/layout/puller_test.go rename to pkg/v1/layout/source_test.go index 214e1df18..14e13cb05 100644 --- a/pkg/v1/layout/puller_test.go +++ b/pkg/v1/layout/source_test.go @@ -32,10 +32,10 @@ func TestPullerHeadWithDigest(t *testing.T) { t.Fatalf("FromPath() = %v", err) } digest := "sha256:32589985702551b6c56033bb3334432a0a513bf9d6aceda0f67c42b003850720" - puller := NewPuller(path) - desc, err := puller.Head(context.TODO(), name.MustParseReference("reg.local/repo2@sha256:32589985702551b6c56033bb3334432a0a513bf9d6aceda0f67c42b003850720")) + source := NewSource(path) + desc, err := source.Head(context.TODO(), name.MustParseReference("reg.local/repo2@sha256:32589985702551b6c56033bb3334432a0a513bf9d6aceda0f67c42b003850720")) if err != nil { - t.Fatalf("puller.Head() = %v", err) + t.Fatalf("source.Head() = %v", err) } if desc.Digest.String() != digest { @@ -49,10 +49,10 @@ func TestPullerHeadWithTag(t *testing.T) { t.Fatalf("FromPath() = %v", err) } digest := "sha256:05f95b26ed10668b7183c1e2da98610e91372fa9f510046d4ce5812addad86b5" - puller := NewPuller(path) - desc, err := puller.Head(context.TODO(), name.MustParseReference("reg.local/repo4:latest")) + source := NewSource(path) + desc, err := source.Head(context.TODO(), name.MustParseReference("reg.local/repo4:latest")) if err != nil { - t.Fatalf("puller.Head() = %v", err) + t.Fatalf("source.Head() = %v", err) } if desc.Digest.String() != digest { t.Fatalf("wrong descriptor returned, expected %s but got %s ", digest, desc.Digest) @@ -66,10 +66,10 @@ func TestPullerArtifact(t *testing.T) { } expectedDigest := "sha256:05f95b26ed10668b7183c1e2da98610e91372fa9f510046d4ce5812addad86b5" - puller := NewPuller(path) - desc, err := puller.Artifact(context.TODO(), name.MustParseReference("reg.local/repo4:latest")) + source := NewSource(path) + desc, err := source.Artifact(context.TODO(), name.MustParseReference("reg.local/repo4:latest")) if err != nil { - t.Fatalf("puller.Artifact() = %v", err) + t.Fatalf("source.Artifact() = %v", err) } digest, err := desc.Digest() @@ -98,11 +98,11 @@ func TestPullerLayer(t *testing.T) { } expectedDigest := "sha256:6e0b05049ed9c17d02e1a55e80d6599dbfcce7f4f4b022e3c673e685789c470e" - puller := NewPuller(path) + source := NewSource(path) - layer, err := puller.Layer(context.TODO(), name.MustParseReference("reg.local/repo4@sha256:6e0b05049ed9c17d02e1a55e80d6599dbfcce7f4f4b022e3c673e685789c470e").(name.Digest)) + layer, err := source.Layer(context.TODO(), name.MustParseReference("reg.local/repo4@sha256:6e0b05049ed9c17d02e1a55e80d6599dbfcce7f4f4b022e3c673e685789c470e").(name.Digest)) if err != nil { - t.Fatalf("puller.Layer() = %v", err) + t.Fatalf("source.Layer() = %v", err) } digest, err := layer.Digest() diff --git a/pkg/v1/partial/artifact.go b/pkg/v1/partial/artifact.go index 5d191c8a9..6ed48a540 100644 --- a/pkg/v1/partial/artifact.go +++ b/pkg/v1/partial/artifact.go @@ -14,7 +14,9 @@ package partial -import "github.com/google/go-containerregistry/pkg/v1/types" +import ( + types "github.com/google/go-containerregistry/pkg/v1/types" +) type Artifact interface { Describable diff --git a/pkg/v1/remote/options.go b/pkg/v1/remote/options.go index 63185b116..10b836d98 100644 --- a/pkg/v1/remote/options.go +++ b/pkg/v1/remote/options.go @@ -342,22 +342,6 @@ func WithFilter(key string, value string) Option { } } -// WithPuller sets puller for remote -func WithPuller(puller Puller) Option { - return func(o *options) error { - o.puller = &puller - return nil - } -} - -// WithPuller sets pusher for remote -func WithPusher(pusher Pusher) Option { - return func(o *options) error { - o.pusher = &pusher - return nil - } -} - // Reuse takes a Puller or Pusher and reuses it for remote interactions // rather than starting from a clean slate. For example, it will reuse token exchanges // when possible and avoid sending redundant HEAD requests. diff --git a/pkg/v1/remote/puller.go b/pkg/v1/remote/puller.go index fdd91fa68..29f3bf624 100644 --- a/pkg/v1/remote/puller.go +++ b/pkg/v1/remote/puller.go @@ -22,31 +22,20 @@ import ( "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/partial" + "github.com/google/go-containerregistry/pkg/v1/sourcesink" "github.com/google/go-containerregistry/pkg/v1/types" ) -type Puller interface { - Layer(ctx context.Context, ref name.Digest) (v1.Layer, error) - Head(ctx context.Context, ref name.Reference) (*v1.Descriptor, error) - List(ctx context.Context, repo name.Repository) ([]string, error) - Get(ctx context.Context, ref name.Reference) (*Descriptor, error) - Artifact(ctx context.Context, ref name.Reference) (partial.Artifact, error) - Lister(ctx context.Context, repo name.Repository) (*Lister, error) - Catalogger(ctx context.Context, reg name.Registry) (*Catalogger, error) - Catalog(ctx context.Context, reg name.Registry) ([]string, error) - Referrers(ctx context.Context, d name.Digest, filter map[string]string) (v1.ImageIndex, error) -} - -type puller struct { +type Puller struct { o *options // map[resource]*reader readers sync.Map } -var _ Puller = (*puller)(nil) +var _ sourcesink.Source = (*Puller)(nil) -func NewPuller(options ...Option) (Puller, error) { +func NewPuller(options ...Option) (*Puller, error) { o, err := makeOptions(options...) if err != nil { return nil, err @@ -54,11 +43,8 @@ func NewPuller(options ...Option) (Puller, error) { return newPuller(o), nil } -func newPuller(o *options) Puller { - if o.puller != nil { - return *o.puller - } - return &puller{ +func newPuller(o *options) *Puller { + return &Puller{ o: o, } } @@ -84,7 +70,7 @@ func (r *reader) init(ctx context.Context) error { return r.err } -func (p *puller) fetcher(ctx context.Context, target resource) (*fetcher, error) { +func (p *Puller) fetcher(ctx context.Context, target resource) (*fetcher, error) { v, _ := p.readers.LoadOrStore(target, &reader{ target: target, o: p.o, @@ -94,7 +80,7 @@ func (p *puller) fetcher(ctx context.Context, target resource) (*fetcher, error) } // Head is like remote.Head, but avoids re-authenticating when possible. -func (p *puller) Head(ctx context.Context, ref name.Reference) (*v1.Descriptor, error) { +func (p *Puller) Head(ctx context.Context, ref name.Reference) (*v1.Descriptor, error) { f, err := p.fetcher(ctx, ref.Context()) if err != nil { return nil, err @@ -104,11 +90,11 @@ func (p *puller) Head(ctx context.Context, ref name.Reference) (*v1.Descriptor, } // Get is like remote.Get, but avoids re-authenticating when possible. -func (p *puller) Get(ctx context.Context, ref name.Reference) (*Descriptor, error) { +func (p *Puller) Get(ctx context.Context, ref name.Reference) (*Descriptor, error) { return p.get(ctx, ref, p.o.acceptableMediaTypes, p.o.platform) } -func (p *puller) get(ctx context.Context, ref name.Reference, acceptable []types.MediaType, platform v1.Platform) (*Descriptor, error) { +func (p *Puller) get(ctx context.Context, ref name.Reference, acceptable []types.MediaType, platform v1.Platform) (*Descriptor, error) { f, err := p.fetcher(ctx, ref.Context()) if err != nil { return nil, err @@ -127,11 +113,11 @@ func (p *puller) get(ctx context.Context, ref name.Reference, acceptable []types }, nil } -func (p *puller) Artifact(ctx context.Context, ref name.Reference) (partial.Artifact, error) { +func (p *Puller) Artifact(ctx context.Context, ref name.Reference) (partial.Artifact, error) { return p.artifact(ctx, ref, p.o.acceptableMediaTypes, p.o.platform) } -func (p *puller) artifact(ctx context.Context, ref name.Reference, acceptable []types.MediaType, platform v1.Platform) (partial.Artifact, error) { +func (p *Puller) artifact(ctx context.Context, ref name.Reference, acceptable []types.MediaType, platform v1.Platform) (partial.Artifact, error) { desc, err := p.get(ctx, ref, acceptable, platform) if err != nil { return nil, err @@ -147,7 +133,7 @@ func (p *puller) artifact(ctx context.Context, ref name.Reference, acceptable [] } // Layer is like remote.Layer, but avoids re-authenticating when possible. -func (p *puller) Layer(ctx context.Context, ref name.Digest) (v1.Layer, error) { +func (p *Puller) Layer(ctx context.Context, ref name.Digest) (v1.Layer, error) { f, err := p.fetcher(ctx, ref.Context()) if err != nil { return nil, err @@ -172,7 +158,7 @@ func (p *puller) Layer(ctx context.Context, ref name.Digest) (v1.Layer, error) { } // List lists tags in a repo and handles pagination, returning the full list of tags. -func (p *puller) List(ctx context.Context, repo name.Repository) ([]string, error) { +func (p *Puller) List(ctx context.Context, repo name.Repository) ([]string, error) { lister, err := p.Lister(ctx, repo) if err != nil { return nil, err @@ -191,11 +177,11 @@ func (p *puller) List(ctx context.Context, repo name.Repository) ([]string, erro } // Lister lists tags in a repo and returns a Lister for paginating through the results. -func (p *puller) Lister(ctx context.Context, repo name.Repository) (*Lister, error) { +func (p *Puller) Lister(ctx context.Context, repo name.Repository) (*Lister, error) { return p.lister(ctx, repo, p.o.pageSize) } -func (p *puller) lister(ctx context.Context, repo name.Repository, pageSize int) (*Lister, error) { +func (p *Puller) lister(ctx context.Context, repo name.Repository, pageSize int) (*Lister, error) { f, err := p.fetcher(ctx, repo) if err != nil { return nil, err @@ -214,11 +200,11 @@ func (p *puller) lister(ctx context.Context, repo name.Repository, pageSize int) } // Catalog lists repos in a registry and handles pagination, returning the full list of repos. -func (p *puller) Catalog(ctx context.Context, reg name.Registry) ([]string, error) { +func (p *Puller) Catalog(ctx context.Context, reg name.Registry) ([]string, error) { return p.catalog(ctx, reg, p.o.pageSize) } -func (p *puller) catalog(ctx context.Context, reg name.Registry, pageSize int) ([]string, error) { +func (p *Puller) catalog(ctx context.Context, reg name.Registry, pageSize int) ([]string, error) { catalogger, err := p.catalogger(ctx, reg, pageSize) if err != nil { return nil, err @@ -235,11 +221,11 @@ func (p *puller) catalog(ctx context.Context, reg name.Registry, pageSize int) ( } // Catalogger lists repos in a registry and returns a Catalogger for paginating through the results. -func (p *puller) Catalogger(ctx context.Context, reg name.Registry) (*Catalogger, error) { +func (p *Puller) Catalogger(ctx context.Context, reg name.Registry) (*Catalogger, error) { return p.catalogger(ctx, reg, p.o.pageSize) } -func (p *puller) catalogger(ctx context.Context, reg name.Registry, pageSize int) (*Catalogger, error) { +func (p *Puller) catalogger(ctx context.Context, reg name.Registry, pageSize int) (*Catalogger, error) { f, err := p.fetcher(ctx, reg) if err != nil { return nil, err @@ -257,7 +243,7 @@ func (p *puller) catalogger(ctx context.Context, reg name.Registry, pageSize int }, nil } -func (p *puller) Referrers(ctx context.Context, d name.Digest, filter map[string]string) (v1.ImageIndex, error) { +func (p *Puller) Referrers(ctx context.Context, d name.Digest, filter map[string]string) (v1.ImageIndex, error) { f, err := p.fetcher(ctx, d.Context()) if err != nil { return nil, err diff --git a/pkg/v1/remote/pusher.go b/pkg/v1/remote/pusher.go index 0945993e9..28e7358ae 100644 --- a/pkg/v1/remote/pusher.go +++ b/pkg/v1/remote/pusher.go @@ -28,6 +28,7 @@ import ( v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/remote/transport" + "github.com/google/go-containerregistry/pkg/v1/sourcesink" "github.com/google/go-containerregistry/pkg/v1/stream" "github.com/google/go-containerregistry/pkg/v1/types" "golang.org/x/sync/errgroup" @@ -90,22 +91,16 @@ func (w *workers) Stream(layer v1.Layer, f func() error) error { return v.(error) } -type Pusher interface { - Delete(ctx context.Context, ref name.Reference) error - Push(ctx context.Context, ref name.Reference, t partial.WithRawManifest) error - Upload(ctx context.Context, repo name.Repository, l v1.Layer) error -} - -var _ Pusher = (*pusher)(nil) +var _ sourcesink.Sink = (*Pusher)(nil) -type pusher struct { +type Pusher struct { o *options // map[name.Repository]*repoWriter writers sync.Map } -func NewPusher(options ...Option) (Pusher, error) { +func NewPusher(options ...Option) (*Pusher, error) { o, err := makeOptions(options...) if err != nil { return nil, err @@ -113,16 +108,13 @@ func NewPusher(options ...Option) (Pusher, error) { return newPusher(o), nil } -func newPusher(o *options) Pusher { - if o.pusher != nil { - return *o.pusher - } - return &pusher{ +func newPusher(o *options) *Pusher { + return &Pusher{ o: o, } } -func (p *pusher) writer(ctx context.Context, repo name.Repository, o *options) (*repoWriter, error) { +func (p *Pusher) writer(ctx context.Context, repo name.Repository, o *options) (*repoWriter, error) { v, _ := p.writers.LoadOrStore(repo, &repoWriter{ repo: repo, o: o, @@ -131,7 +123,7 @@ func (p *pusher) writer(ctx context.Context, repo name.Repository, o *options) ( return rw, rw.init(ctx) } -func (p *pusher) Push(ctx context.Context, ref name.Reference, t partial.WithRawManifest) error { +func (p *Pusher) Push(ctx context.Context, ref name.Reference, t partial.WithRawManifest) error { w, err := p.writer(ctx, ref.Context(), p.o) if err != nil { return err @@ -139,7 +131,7 @@ func (p *pusher) Push(ctx context.Context, ref name.Reference, t partial.WithRaw return w.writeManifest(ctx, ref, t) } -func (p *pusher) Upload(ctx context.Context, repo name.Repository, l v1.Layer) error { +func (p *Pusher) Upload(ctx context.Context, repo name.Repository, l v1.Layer) error { w, err := p.writer(ctx, repo, p.o) if err != nil { return err @@ -147,7 +139,7 @@ func (p *pusher) Upload(ctx context.Context, repo name.Repository, l v1.Layer) e return w.writeLayer(ctx, l) } -func (p *pusher) Delete(ctx context.Context, ref name.Reference) error { +func (p *Pusher) Delete(ctx context.Context, ref name.Reference) error { w, err := p.writer(ctx, ref.Context(), p.o) if err != nil { return err diff --git a/pkg/v1/sourcesink/sink.go b/pkg/v1/sourcesink/sink.go new file mode 100644 index 000000000..393c8b186 --- /dev/null +++ b/pkg/v1/sourcesink/sink.go @@ -0,0 +1,15 @@ +package sourcesink + +import ( + "context" + + "github.com/google/go-containerregistry/pkg/name" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/partial" +) + +type Sink interface { + Delete(ctx context.Context, ref name.Reference) error + Push(ctx context.Context, ref name.Reference, t partial.WithRawManifest) error + Upload(ctx context.Context, repo name.Repository, l v1.Layer) error +} diff --git a/pkg/v1/sourcesink/source.go b/pkg/v1/sourcesink/source.go new file mode 100644 index 000000000..72f539de7 --- /dev/null +++ b/pkg/v1/sourcesink/source.go @@ -0,0 +1,15 @@ +package sourcesink + +import ( + "context" + + "github.com/google/go-containerregistry/pkg/name" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/partial" +) + +type Source interface { + Layer(ctx context.Context, ref name.Digest) (v1.Layer, error) + Head(ctx context.Context, ref name.Reference) (*v1.Descriptor, error) + Artifact(ctx context.Context, ref name.Reference) (partial.Artifact, error) +}