From 73a18b14c80eef6c4f44b82c0d3fbb9e04f31c28 Mon Sep 17 00:00:00 2001 From: Xiaoxuan Wang Date: Wed, 20 Dec 2023 12:43:28 +0800 Subject: [PATCH] generic implementation Signed-off-by: Xiaoxuan Wang --- content/oci/oci.go | 78 --------------------------------------- content/oci/oci_test.go | 4 +- registry/repository.go | 82 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 80 deletions(-) diff --git a/content/oci/oci.go b/content/oci/oci.go index d68117a77..5d4699a1f 100644 --- a/content/oci/oci.go +++ b/content/oci/oci.go @@ -36,7 +36,6 @@ import ( "oras.land/oras-go/v2/internal/descriptor" "oras.land/oras-go/v2/internal/graph" "oras.land/oras-go/v2/internal/resolver" - "oras.land/oras-go/v2/internal/spec" ) // Store implements `oras.Target`, and represents a content store @@ -376,83 +375,6 @@ func (s *Store) writeIndexFile() error { return os.WriteFile(s.indexPath, indexJSON, 0666) } -// Referrers lists the descriptors of image or artifact manifests directly -// referencing the given manifest descriptor. -// -// fn is called on the referrer results. If artifactType is not empty, only -// referrers of the same artifact type are fed to fn. -// -// Reference: https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc3/spec.md#listing-referrers -func (s *Store) Referrers(ctx context.Context, desc ocispec.Descriptor, artifactType string, fn func(referrers []ocispec.Descriptor) error) error { - s.sync.RLock() - defer s.sync.RUnlock() - - if !descriptor.IsManifest(desc) { - return fmt.Errorf("the descriptor %v is not a manifest", desc) - } - var results []ocispec.Descriptor - predecessors, err := s.Predecessors(ctx, desc) - if err != nil { - return err - } - for _, node := range predecessors { - switch node.MediaType { - case ocispec.MediaTypeImageManifest: - fetched, err := content.FetchAll(ctx, s, node) - if err != nil { - return err - } - var manifest ocispec.Manifest - if err := json.Unmarshal(fetched, &manifest); err != nil { - return err - } - if manifest.Subject == nil || !content.Equal(*manifest.Subject, desc) { - continue - } - if manifest.ArtifactType != "" { - node.ArtifactType = manifest.ArtifactType - } else { - node.ArtifactType = manifest.Config.MediaType - } - node.Annotations = manifest.Annotations - case ocispec.MediaTypeImageIndex: - fetched, err := content.FetchAll(ctx, s, node) - if err != nil { - return err - } - var index ocispec.Index - if err := json.Unmarshal(fetched, &index); err != nil { - return err - } - if index.Subject == nil || !content.Equal(*index.Subject, desc) { - continue - } - node.ArtifactType = index.ArtifactType - node.Annotations = index.Annotations - case spec.MediaTypeArtifactManifest: - fetched, err := content.FetchAll(ctx, s, node) - if err != nil { - return err - } - var artifact spec.Artifact - if err := json.Unmarshal(fetched, &artifact); err != nil { - return err - } - if artifact.Subject == nil || !content.Equal(*artifact.Subject, desc) { - continue - } - node.ArtifactType = artifact.ArtifactType - node.Annotations = artifact.Annotations - default: - continue - } - if artifactType == "" || artifactType == node.ArtifactType { - results = append(results, node) - } - } - return fn(results) -} - // validateReference validates ref. func validateReference(ref string) error { if ref == "" { diff --git a/content/oci/oci_test.go b/content/oci/oci_test.go index e59ce82c6..adc84a545 100644 --- a/content/oci/oci_test.go +++ b/content/oci/oci_test.go @@ -2364,7 +2364,7 @@ func TestStore_Referrers(t *testing.T) { for i := 4; i < len(wantedReferrers); i++ { want := wantedReferrers[i] var results []ocispec.Descriptor - err := s.Referrers(ctx, descs[i], "", func(referrers []ocispec.Descriptor) error { + err := registry.Referrers(ctx, s, descs[i], "", func(referrers []ocispec.Descriptor) error { results = append(results, referrers...) return nil }) @@ -2391,7 +2391,7 @@ func TestStore_Referrers(t *testing.T) { for i := 4; i < len(wantedReferrers); i++ { want := wantedReferrers[i] var results []ocispec.Descriptor - err := s.Referrers(ctx, descs[i], "image manifest", func(referrers []ocispec.Descriptor) error { + err := registry.Referrers(ctx, s, descs[i], "image manifest", func(referrers []ocispec.Descriptor) error { results = append(results, referrers...) return nil }) diff --git a/registry/repository.go b/registry/repository.go index b75b7b8ea..36f73cba1 100644 --- a/registry/repository.go +++ b/registry/repository.go @@ -17,10 +17,14 @@ package registry import ( "context" + "encoding/json" + "fmt" "io" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "oras.land/oras-go/v2/content" + "oras.land/oras-go/v2/internal/descriptor" + "oras.land/oras-go/v2/internal/spec" ) // Repository is an ORAS target and an union of the blob and the manifest CASs. @@ -134,3 +138,81 @@ func Tags(ctx context.Context, repo TagLister) ([]string, error) { } return res, nil } + +// Referrers lists the descriptors of image or artifact manifests directly +// referencing the given manifest descriptor. +// +// fn is called on the referrer results. If artifactType is not empty, only +// referrers of the same artifact type are fed to fn. +// +// Reference: https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc3/spec.md#listing-referrers +func Referrers(ctx context.Context, store content.ReadOnlyGraphStorage, desc ocispec.Descriptor, artifactType string, fn func(referrers []ocispec.Descriptor) error) error { + if !descriptor.IsManifest(desc) { + return fmt.Errorf("the descriptor %v is not a manifest", desc) + } + // use the Referrer API if it is available + if rf, ok := store.(ReferrerLister); ok { + return rf.Referrers(ctx, desc, artifactType, fn) + } + var results []ocispec.Descriptor + predecessors, err := store.Predecessors(ctx, desc) + if err != nil { + return err + } + for _, node := range predecessors { + switch node.MediaType { + case ocispec.MediaTypeImageManifest: + fetched, err := content.FetchAll(ctx, store, node) + if err != nil { + return err + } + var manifest ocispec.Manifest + if err := json.Unmarshal(fetched, &manifest); err != nil { + return err + } + if manifest.Subject == nil || !content.Equal(*manifest.Subject, desc) { + continue + } + if manifest.ArtifactType != "" { + node.ArtifactType = manifest.ArtifactType + } else { + node.ArtifactType = manifest.Config.MediaType + } + node.Annotations = manifest.Annotations + case ocispec.MediaTypeImageIndex: + fetched, err := content.FetchAll(ctx, store, node) + if err != nil { + return err + } + var index ocispec.Index + if err := json.Unmarshal(fetched, &index); err != nil { + return err + } + if index.Subject == nil || !content.Equal(*index.Subject, desc) { + continue + } + node.ArtifactType = index.ArtifactType + node.Annotations = index.Annotations + case spec.MediaTypeArtifactManifest: + fetched, err := content.FetchAll(ctx, store, node) + if err != nil { + return err + } + var artifact spec.Artifact + if err := json.Unmarshal(fetched, &artifact); err != nil { + return err + } + if artifact.Subject == nil || !content.Equal(*artifact.Subject, desc) { + continue + } + node.ArtifactType = artifact.ArtifactType + node.Annotations = artifact.Annotations + default: + continue + } + if artifactType == "" || artifactType == node.ArtifactType { + results = append(results, node) + } + } + return fn(results) +}