...

Source file src/edge-infra.dev/pkg/f8n/warehouse/oci/mutate/mutate.go

Documentation: edge-infra.dev/pkg/f8n/warehouse/oci/mutate

     1  // Package mutate implements Warehouse-specific mutation logic and helpers for
     2  // mutating Warehouse OCI artifacts.
     3  package mutate
     4  
     5  import (
     6  	"fmt"
     7  
     8  	v1 "github.com/google/go-containerregistry/pkg/v1"
     9  	"github.com/google/go-containerregistry/pkg/v1/mutate"
    10  
    11  	wh "edge-infra.dev/pkg/f8n/warehouse"
    12  	"edge-infra.dev/pkg/f8n/warehouse/oci"
    13  )
    14  
    15  // TODO: make AppendManifests do the work of parsing the descriptors and preserving media type etc?
    16  // TODO: AppendManifests probably has to change since it does some pallet-specific annotation carry over
    17  
    18  // AppendManifests appends the OCI artifacts to the input ImageIndex by creating
    19  // a new ImageIndex with the appropriate descriptors for each artifact. Some
    20  // annotations defined by the Warehouse spec on input artifacts will be
    21  // persisted or mutated onto the result descriptor that is appended to the
    22  // ImageIndex.
    23  //
    24  // NOTE: Although AppendManifests implements Warehouse-specific behavior by
    25  // adding annotations to the descriptors, no verification that the input
    26  // ImageIndex is itself a valid Pallet is done.
    27  func AppendManifests(idx v1.ImageIndex, artifacts ...oci.Artifact) (v1.ImageIndex, error) {
    28  	if len(artifacts) == 0 {
    29  		return nil, fmt.Errorf("mutate.AppendManifests: at least one artifact is required")
    30  	}
    31  	adds, err := artifactsToAddendums(artifacts)
    32  	if err != nil {
    33  		return nil, fmt.Errorf("mutate.AppendManifests: %w", err)
    34  	}
    35  
    36  	return mutate.AppendManifests(idx, adds...), nil
    37  }
    38  
    39  // artifactsToAddendums converts the input artifacts into something that can be
    40  // appended to ImageIndex
    41  func artifactsToAddendums(aa []oci.Artifact) ([]mutate.IndexAddendum, error) {
    42  	var adds []mutate.IndexAddendum
    43  	for _, a := range aa {
    44  		add, err := artifactToAddendum(a)
    45  		if err != nil {
    46  			return nil, err
    47  		}
    48  		adds = append(adds, add)
    49  	}
    50  	return adds, nil
    51  }
    52  
    53  // artifactToAddendum converts the input artifact into something that can be
    54  // appended to an ImageIndex, preserving or mapping annotations with semantic
    55  // value in the Warehouse spec to the embedded descriptors
    56  func artifactToAddendum(a oci.Artifact) (mutate.IndexAddendum, error) {
    57  	desc := v1.Descriptor{}
    58  	switch a := a.(type) {
    59  	// if this is one of our implementations, call the function again with the
    60  	// unwrapped ggcr type
    61  	case oci.Unwrapper:
    62  		return artifactToAddendum(a.Unwrap())
    63  	case v1.Image:
    64  		manifest, err := a.Manifest()
    65  		if err != nil {
    66  			return mutate.IndexAddendum{}, fmt.Errorf("failed to parse image manifest: %w", err)
    67  		}
    68  		desc.Annotations = descAnnotations(manifest.Annotations)
    69  	case v1.ImageIndex:
    70  		manifest, err := a.IndexManifest()
    71  		if err != nil {
    72  			return mutate.IndexAddendum{}, fmt.Errorf("failed to parse image index manifest: %w", err)
    73  		}
    74  		desc.Annotations = descAnnotations(manifest.Annotations)
    75  	default:
    76  		// TODO: try to lazily evaluate annotations? see mutate code in ggcr for example
    77  		// TODO: contextual error, dont have pallet name available anymore
    78  		return mutate.IndexAddendum{}, fmt.Errorf("%w", oci.ErrInvalidArtifact)
    79  	}
    80  
    81  	return mutate.IndexAddendum{Add: a, Descriptor: desc}, nil
    82  }
    83  
    84  // descAnnotations adds the required annotations for a descriptor based on the
    85  // manifests of the target it is referencing
    86  func descAnnotations(annos map[string]string) map[string]string {
    87  	m := map[string]string{}
    88  	for k, v := range annos {
    89  		switch k {
    90  		case wh.AnnotationName:
    91  			m[wh.AnnotationRefName] = v
    92  		case wh.AnnotationClusterProviders:
    93  			m[wh.AnnotationClusterProviders] = v
    94  		case wh.AnnotationKind:
    95  			m[wh.AnnotationKind] = v
    96  		}
    97  	}
    98  	return m
    99  }
   100  

View as plain text