107 lines
3.2 KiB
Go
107 lines
3.2 KiB
Go
/*
|
|
Copyright The ORAS Authors.
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package content
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
|
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"oras.land/oras-go/v2/internal/docker"
|
|
)
|
|
|
|
// PredecessorFinder finds out the nodes directly pointing to a given node of a
|
|
// directed acyclic graph.
|
|
// In other words, returns the "parents" of the current descriptor.
|
|
// PredecessorFinder is an extension of Storage.
|
|
type PredecessorFinder interface {
|
|
// Predecessors returns the nodes directly pointing to the current node.
|
|
Predecessors(ctx context.Context, node ocispec.Descriptor) ([]ocispec.Descriptor, error)
|
|
}
|
|
|
|
// GraphStorage represents a CAS that supports direct predecessor node finding.
|
|
type GraphStorage interface {
|
|
Storage
|
|
PredecessorFinder
|
|
}
|
|
|
|
// ReadOnlyGraphStorage represents a read-only GraphStorage.
|
|
type ReadOnlyGraphStorage interface {
|
|
ReadOnlyStorage
|
|
PredecessorFinder
|
|
}
|
|
|
|
// Successors returns the nodes directly pointed by the current node.
|
|
// In other words, returns the "children" of the current descriptor.
|
|
func Successors(ctx context.Context, fetcher Fetcher, node ocispec.Descriptor) ([]ocispec.Descriptor, error) {
|
|
switch node.MediaType {
|
|
case docker.MediaTypeManifest:
|
|
content, err := FetchAll(ctx, fetcher, node)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// OCI manifest schema can be used to marshal docker manifest
|
|
var manifest ocispec.Manifest
|
|
if err := json.Unmarshal(content, &manifest); err != nil {
|
|
return nil, err
|
|
}
|
|
return append([]ocispec.Descriptor{manifest.Config}, manifest.Layers...), nil
|
|
case ocispec.MediaTypeImageManifest:
|
|
content, err := FetchAll(ctx, fetcher, node)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var manifest ocispec.Manifest
|
|
if err := json.Unmarshal(content, &manifest); err != nil {
|
|
return nil, err
|
|
}
|
|
var nodes []ocispec.Descriptor
|
|
if manifest.Subject != nil {
|
|
nodes = append(nodes, *manifest.Subject)
|
|
}
|
|
nodes = append(nodes, manifest.Config)
|
|
return append(nodes, manifest.Layers...), nil
|
|
case docker.MediaTypeManifestList, ocispec.MediaTypeImageIndex:
|
|
content, err := FetchAll(ctx, fetcher, node)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// docker manifest list and oci index are equivalent for successors.
|
|
var index ocispec.Index
|
|
if err := json.Unmarshal(content, &index); err != nil {
|
|
return nil, err
|
|
}
|
|
return index.Manifests, nil
|
|
case ocispec.MediaTypeArtifactManifest:
|
|
content, err := FetchAll(ctx, fetcher, node)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var manifest ocispec.Artifact
|
|
if err := json.Unmarshal(content, &manifest); err != nil {
|
|
return nil, err
|
|
}
|
|
var nodes []ocispec.Descriptor
|
|
if manifest.Subject != nil {
|
|
nodes = append(nodes, *manifest.Subject)
|
|
}
|
|
return append(nodes, manifest.Blobs...), nil
|
|
}
|
|
return nil, nil
|
|
}
|