oras.land/oras-go/v2@v2.5.1-0.20240520045656-aef90e4d04c4/content/graph.go (about) 1 /* 2 Copyright The ORAS Authors. 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14 */ 15 16 package content 17 18 import ( 19 "context" 20 "encoding/json" 21 22 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 23 "oras.land/oras-go/v2/internal/docker" 24 "oras.land/oras-go/v2/internal/spec" 25 ) 26 27 // PredecessorFinder finds out the nodes directly pointing to a given node of a 28 // directed acyclic graph. 29 // In other words, returns the "parents" of the current descriptor. 30 // PredecessorFinder is an extension of Storage. 31 type PredecessorFinder interface { 32 // Predecessors returns the nodes directly pointing to the current node. 33 Predecessors(ctx context.Context, node ocispec.Descriptor) ([]ocispec.Descriptor, error) 34 } 35 36 // GraphStorage represents a CAS that supports direct predecessor node finding. 37 type GraphStorage interface { 38 Storage 39 PredecessorFinder 40 } 41 42 // ReadOnlyGraphStorage represents a read-only GraphStorage. 43 type ReadOnlyGraphStorage interface { 44 ReadOnlyStorage 45 PredecessorFinder 46 } 47 48 // Successors returns the nodes directly pointed by the current node. 49 // In other words, returns the "children" of the current descriptor. 50 func Successors(ctx context.Context, fetcher Fetcher, node ocispec.Descriptor) ([]ocispec.Descriptor, error) { 51 switch node.MediaType { 52 case docker.MediaTypeManifest: 53 content, err := FetchAll(ctx, fetcher, node) 54 if err != nil { 55 return nil, err 56 } 57 // OCI manifest schema can be used to marshal docker manifest 58 var manifest ocispec.Manifest 59 if err := json.Unmarshal(content, &manifest); err != nil { 60 return nil, err 61 } 62 return append([]ocispec.Descriptor{manifest.Config}, manifest.Layers...), nil 63 case ocispec.MediaTypeImageManifest: 64 content, err := FetchAll(ctx, fetcher, node) 65 if err != nil { 66 return nil, err 67 } 68 var manifest ocispec.Manifest 69 if err := json.Unmarshal(content, &manifest); err != nil { 70 return nil, err 71 } 72 var nodes []ocispec.Descriptor 73 if manifest.Subject != nil { 74 nodes = append(nodes, *manifest.Subject) 75 } 76 nodes = append(nodes, manifest.Config) 77 return append(nodes, manifest.Layers...), nil 78 case docker.MediaTypeManifestList: 79 content, err := FetchAll(ctx, fetcher, node) 80 if err != nil { 81 return nil, err 82 } 83 84 // OCI manifest index schema can be used to marshal docker manifest list 85 var index ocispec.Index 86 if err := json.Unmarshal(content, &index); err != nil { 87 return nil, err 88 } 89 return index.Manifests, nil 90 case ocispec.MediaTypeImageIndex: 91 content, err := FetchAll(ctx, fetcher, node) 92 if err != nil { 93 return nil, err 94 } 95 96 var index ocispec.Index 97 if err := json.Unmarshal(content, &index); err != nil { 98 return nil, err 99 } 100 var nodes []ocispec.Descriptor 101 if index.Subject != nil { 102 nodes = append(nodes, *index.Subject) 103 } 104 return append(nodes, index.Manifests...), nil 105 case spec.MediaTypeArtifactManifest: 106 content, err := FetchAll(ctx, fetcher, node) 107 if err != nil { 108 return nil, err 109 } 110 111 var manifest spec.Artifact 112 if err := json.Unmarshal(content, &manifest); err != nil { 113 return nil, err 114 } 115 var nodes []ocispec.Descriptor 116 if manifest.Subject != nil { 117 nodes = append(nodes, *manifest.Subject) 118 } 119 return append(nodes, manifest.Blobs...), nil 120 } 121 return nil, nil 122 }