diff --git a/cmd/oras/root/cp.go b/cmd/oras/root/cp.go index 31d63a85e..28d61f275 100644 --- a/cmd/oras/root/cp.go +++ b/cmd/oras/root/cp.go @@ -28,6 +28,7 @@ import ( "github.com/spf13/cobra" "oras.land/oras-go/v2" "oras.land/oras-go/v2/content" + "oras.land/oras-go/v2/registry" "oras.land/oras-go/v2/registry/remote/auth" "oras.land/oras/cmd/oras/internal/argument" "oras.land/oras/cmd/oras/internal/display" @@ -153,7 +154,7 @@ func doCopy(ctx context.Context, src oras.ReadOnlyGraphTarget, dst oras.GraphTar extendedCopyOptions := oras.DefaultExtendedCopyOptions extendedCopyOptions.Concurrency = opts.concurrency extendedCopyOptions.FindPredecessors = func(ctx context.Context, src content.ReadOnlyGraphStorage, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { - return graph.Referrers(ctx, src, desc, "") + return registry.Referrers(ctx, src, desc, "") } const ( diff --git a/cmd/oras/root/discover.go b/cmd/oras/root/discover.go index ec67d839c..6b98d4a22 100644 --- a/cmd/oras/root/discover.go +++ b/cmd/oras/root/discover.go @@ -28,10 +28,10 @@ import ( "gopkg.in/yaml.v3" "oras.land/oras-go/v2" + "oras.land/oras-go/v2/registry" "oras.land/oras/cmd/oras/internal/argument" oerrors "oras.land/oras/cmd/oras/internal/errors" "oras.land/oras/cmd/oras/internal/option" - "oras.land/oras/internal/graph" "oras.land/oras/internal/tree" ) @@ -119,7 +119,7 @@ func runDiscover(ctx context.Context, opts discoverOptions) error { return tree.Print(root) } - refs, err := graph.Referrers(ctx, repo, desc, opts.artifactType) + refs, err := registry.Referrers(ctx, repo, desc, opts.artifactType) if err != nil { return err } @@ -141,7 +141,7 @@ func runDiscover(ctx context.Context, opts discoverOptions) error { } func fetchAllReferrers(ctx context.Context, repo oras.ReadOnlyGraphTarget, desc ocispec.Descriptor, artifactType string, node *tree.Node, opts *discoverOptions) error { - results, err := graph.Referrers(ctx, repo, desc, artifactType) + results, err := registry.Referrers(ctx, repo, desc, artifactType) if err != nil { return err } diff --git a/go.mod b/go.mod index b716a4181..86c49450d 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( golang.org/x/sync v0.5.0 golang.org/x/term v0.15.0 gopkg.in/yaml.v3 v3.0.1 - oras.land/oras-go/v2 v2.3.1-0.20231121114731-79a08b452e76 + oras.land/oras-go/v2 v2.3.1-0.20231227022511-1d9ad6c409b3 ) require ( diff --git a/go.sum b/go.sum index 063c2a68f..22f13b4b9 100644 --- a/go.sum +++ b/go.sum @@ -39,7 +39,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -oras.land/oras-go/v2 v2.3.1-0.20231026053328-062ed0e058f8 h1:MOs3GufGAl0KY0e19iZ0aYjFQFSFqY9+a/Elt8ThzdI= -oras.land/oras-go/v2 v2.3.1-0.20231026053328-062ed0e058f8/go.mod h1:5AQXVEu1X/FKp1F9DMOb5ZItZBOa0y5dha0yCm4NR9c= -oras.land/oras-go/v2 v2.3.1-0.20231121114731-79a08b452e76 h1:U3oxpjdj2zkw35OjhBaa7xLloGXPPGjwpC8Czp5+zTk= -oras.land/oras-go/v2 v2.3.1-0.20231121114731-79a08b452e76/go.mod h1:W2rj9/rGtsTh9lmU3S0uq3iwvR6wNvUuD8nmOFmWBUY= +oras.land/oras-go/v2 v2.3.1-0.20231227022511-1d9ad6c409b3 h1:cF1ErV93cAw4i5/MNF4pW6ULHa27ig3oYGlxouzymY8= +oras.land/oras-go/v2 v2.3.1-0.20231227022511-1d9ad6c409b3/go.mod h1:W2rj9/rGtsTh9lmU3S0uq3iwvR6wNvUuD8nmOFmWBUY= diff --git a/internal/graph/graph.go b/internal/graph/graph.go index e5323124d..acaf42171 100644 --- a/internal/graph/graph.go +++ b/internal/graph/graph.go @@ -24,7 +24,6 @@ import ( "golang.org/x/sync/errgroup" "oras.land/oras-go/v2" "oras.land/oras-go/v2/content" - "oras.land/oras-go/v2/registry" "oras.land/oras/internal/docker" ) @@ -104,94 +103,6 @@ func Successors(ctx context.Context, fetcher content.Fetcher, node ocispec.Descr return } -// Referrers returns referrer nodes of desc in target. -func Referrers(ctx context.Context, target content.ReadOnlyGraphStorage, desc ocispec.Descriptor, artifactType string) ([]ocispec.Descriptor, error) { - var results []ocispec.Descriptor - if repo, ok := target.(registry.ReferrerLister); ok { - // get referrers directly - err := repo.Referrers(ctx, desc, artifactType, func(referrers []ocispec.Descriptor) error { - results = append(results, referrers...) - return nil - }) - if err != nil { - return nil, err - } - return results, nil - } - - // find matched referrers in all predecessors - predecessors, err := target.Predecessors(ctx, desc) - if err != nil { - return nil, err - } - for _, node := range predecessors { - switch node.MediaType { - case MediaTypeArtifactManifest: - fetched, err := fetchBytes(ctx, target, node) - if err != nil { - return nil, err - } - var artifact Artifact - if err := json.Unmarshal(fetched, &artifact); err != nil { - return nil, err - } - if artifact.Subject == nil || !content.Equal(*artifact.Subject, desc) { - continue - } - node.ArtifactType = artifact.ArtifactType - node.Annotations = artifact.Annotations - case ocispec.MediaTypeImageManifest: - fetched, err := fetchBytes(ctx, target, node) - if err != nil { - return nil, err - } - var image ocispec.Manifest - if err := json.Unmarshal(fetched, &image); err != nil { - return nil, err - } - if image.Subject == nil || !content.Equal(*image.Subject, desc) { - continue - } - node.ArtifactType = image.ArtifactType - if node.ArtifactType == "" { - node.ArtifactType = image.Config.MediaType - } - node.Annotations = image.Annotations - case ocispec.MediaTypeImageIndex: - fetched, err := fetchBytes(ctx, target, node) - if err != nil { - return nil, err - } - var index ocispec.Index - if err := json.Unmarshal(fetched, &index); err != nil { - return nil, err - } - if index.Subject == nil || !content.Equal(*index.Subject, desc) { - continue - } - node.ArtifactType = index.ArtifactType - node.Annotations = index.Annotations - default: - continue - } - if artifactType == "" || artifactType == node.ArtifactType { - // the field artifactType in referrers descriptor is allowed to be empty - // https://github.com/opencontainers/distribution-spec/issues/458 - results = append(results, node) - } - } - return results, nil -} - -func fetchBytes(ctx context.Context, fetcher content.Fetcher, desc ocispec.Descriptor) ([]byte, error) { - rc, err := fetcher.Fetch(ctx, desc) - if err != nil { - return nil, err - } - defer rc.Close() - return content.ReadAll(rc, desc) -} - // FindPredecessors returns all predecessors of descs in src concurrently. func FindPredecessors(ctx context.Context, src oras.ReadOnlyGraphTarget, descs []ocispec.Descriptor, opts oras.ExtendedCopyOptions) ([]ocispec.Descriptor, error) { var referrers []ocispec.Descriptor diff --git a/internal/graph/graph_test.go b/internal/graph/graph_test.go index d33c4ad8c..85c23cbfc 100644 --- a/internal/graph/graph_test.go +++ b/internal/graph/graph_test.go @@ -19,150 +19,20 @@ import ( "bytes" "context" "encoding/json" - "errors" "reflect" "testing" "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "oras.land/oras-go/v2" "oras.land/oras-go/v2/content" "oras.land/oras-go/v2/content/memory" "oras.land/oras/internal/docker" ) -type errLister struct { - oras.ReadOnlyGraphTarget -} - -func (e *errLister) Referrers(ctx context.Context, desc ocispec.Descriptor, artifactType string, fn func(referrers []ocispec.Descriptor) error) error { - return errors.New("") -} - -type refLister struct { - referrers []ocispec.Descriptor - oras.ReadOnlyGraphTarget -} - -func (m *refLister) Referrers(ctx context.Context, desc ocispec.Descriptor, artifactType string, fn func(referrers []ocispec.Descriptor) error) error { - return fn(m.referrers) -} - -type predecessorFinder struct { - *memory.Store -} - type fetcher struct { content.Fetcher } -func TestReferrers(t *testing.T) { - ctx := context.Background() - var blobs [][]byte - var descs []ocispec.Descriptor - appendBlob := func(mediaType string, blob []byte) { - blobs = append(blobs, blob) - descs = append(descs, ocispec.Descriptor{ - MediaType: mediaType, - Digest: digest.FromBytes(blob), - Size: int64(len(blob)), - }) - } - generateImage := func(subject *ocispec.Descriptor, annotations map[string]string, config ocispec.Descriptor, layers ...ocispec.Descriptor) { - manifest := ocispec.Manifest{ - Subject: subject, - Config: config, - Layers: layers, - Annotations: annotations, - } - manifestJSON, err := json.Marshal(manifest) - if err != nil { - t.Fatal(err) - } - appendBlob(ocispec.MediaTypeImageManifest, manifestJSON) - } - generateIndex := func(manifests ...ocispec.Descriptor) { - index := ocispec.Index{ - Manifests: manifests, - } - manifestJSON, err := json.Marshal(index) - if err != nil { - t.Fatal(err) - } - appendBlob(ocispec.MediaTypeImageIndex, manifestJSON) - } - const ( - blob = iota - imgConfig - subject - image - index - ) - anno := map[string]string{"test": "foo"} - appendBlob(ocispec.MediaTypeImageLayer, []byte("blob")) - imageType := "test.image" - appendBlob(imageType, []byte("config content")) - generateImage(nil, nil, descs[imgConfig], descs[blob]) - generateImage(&descs[subject], anno, descs[imgConfig], descs[blob]) - imageDesc := descs[image] - imageDesc.Annotations = anno - imageDesc.ArtifactType = imageType - generateIndex(descs[subject]) - - referrers := []ocispec.Descriptor{descs[image], descs[image]} - memory := memory.New() - for i := range descs { - if err := memory.Push(ctx, descs[i], bytes.NewReader(blobs[i])); err != nil { - t.Errorf("Error pushing %v\n", err) - } - } - finder := &predecessorFinder{Store: memory} - - type args struct { - ctx context.Context - target oras.ReadOnlyGraphTarget - desc ocispec.Descriptor - artifactType string - } - tests := []struct { - name string - args args - want []ocispec.Descriptor - wantErr bool - }{ - {"should fail when a referrer lister failed to get referrers", args{ctx, &errLister{}, ocispec.Descriptor{}, ""}, nil, true}, - {"should return referrers when target is a referrer lister", args{ctx, &refLister{referrers: referrers}, ocispec.Descriptor{}, ""}, referrers, false}, - {"should return nil for index node", args{ctx, finder, descs[index], ""}, nil, false}, - {"should return nil for config node", args{ctx, finder, descs[imgConfig], ""}, nil, false}, - {"should return nil for blob/layer node", args{ctx, finder, descs[blob], ""}, nil, false}, - {"should find filtered image referrer", args{ctx, finder, descs[subject], imageType}, []ocispec.Descriptor{imageDesc}, false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := Referrers(tt.args.ctx, tt.args.target, tt.args.desc, tt.args.artifactType) - if (err != nil) != tt.wantErr { - t.Errorf("Referrers() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Referrers() = %v, want %v", got, tt.want) - } - }) - } - - t.Run("should find referrers in predecessors", func(t *testing.T) { - want := []ocispec.Descriptor{imageDesc} - got, err := Referrers(ctx, finder, descs[subject], "") - if err != nil { - t.Errorf("Referrers() error = %v", err) - return - } - if !reflect.DeepEqual(got, want) { - t.Errorf("Referrers() = %v, want %v", got, want) - } - }) -} - func TestSuccessors(t *testing.T) { var blobs [][]byte var descs []ocispec.Descriptor