package resolve import ( "fmt" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/remote" "edge-infra.dev/pkg/f8n/warehouse/oci" "edge-infra.dev/pkg/f8n/warehouse/oci/walk" "edge-infra.dev/pkg/f8n/warehouse/pallet" ) // Ref calls Resolve based on the input reference to an artifact. ResolveRef // retrieves a *remote.Descriptor, converts that to an artifact based on its // MediaType and passes that artifact to Resolve. func Ref(ref name.Reference) (map[string]pallet.Pallet, error) { desc, err := remote.Get(ref) if err != nil { return nil, err } switch { case desc.MediaType.IsIndex(): oart, err := desc.ImageIndex() if err != nil { return nil, fmt.Errorf("Invalid descriptor, failed conversion to ImageIndex") } return Resolve(oart) case desc.MediaType.IsImage(): oart, err := desc.Image() if err != nil { return nil, fmt.Errorf("Invalid descriptor, failed conversion to Image") } return Resolve(oart) } return nil, fmt.Errorf("Invalid descriptor, MediaType must be Image or ImageIndex") } // Resolve is used for handling and retrieving dependencies from the input // OCI artifact. Image Indexes are walked first, retrieving the digest from each // Image Index. Images are checked to confirm that they do not have a parent of // the same name, preventing redundant dependency retrieval. A map of all digests // is returned for use. func Resolve(a oci.Artifact, opts ...Option) (map[string]pallet.Pallet, error) { options := makeOptions(opts...) resolved := map[string]pallet.Pallet{} walker := &walk.Fns{ Index: func(imgIdx v1.ImageIndex, _ v1.ImageIndex) error { p, err := pallet.New(imgIdx) if err != nil { return err } digest, err := p.Digest() if err != nil { return err } // Check if the artifact already exists in the map and address // potential conflicts between digests. if existing, ok := resolved[p.Name()]; ok { if err := checkForConflicts(existing, digest, options); err != nil { return err } } resolved[p.Name()] = p return nil }, Image: func(img v1.Image, parent v1.ImageIndex) error { // if the image isn't a standalone package or we encounter an error // determining if it is, exit early. isPkg, err := oci.IsPackage(img, parent) if !isPkg || err != nil { return err } p, err := pallet.New(img) if err != nil { return err } digest, err := p.Digest() if err != nil { return err } // Check if the artifact already exists in the map and address // potential conflicts between digests. if existing, ok := resolved[p.Name()]; ok { if err := checkForConflicts(existing, digest, options); err != nil { return err } } resolved[p.Name()] = p return nil }, } if err := walk.Walk(a, walker); err != nil { return nil, err } return resolved, nil } func checkForConflicts(p pallet.Pallet, d v1.Hash, o options) error { palletD, err := p.Digest() if err != nil { return err } switch { case palletD == d: return nil case o.acceptFirst: return nil case o.strict: return fmt.Errorf("resolve: %w", oci.NewConflictErr(p.Name(), palletD, d)) default: return nil } }