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  }