...

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

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

     1  package validate
     2  
     3  import (
     4  	"fmt"
     5  
     6  	v1 "github.com/google/go-containerregistry/pkg/v1"
     7  
     8  	wh "edge-infra.dev/pkg/f8n/warehouse"
     9  	"edge-infra.dev/pkg/f8n/warehouse/cluster"
    10  	"edge-infra.dev/pkg/f8n/warehouse/oci"
    11  	"edge-infra.dev/pkg/f8n/warehouse/oci/layer"
    12  	"edge-infra.dev/pkg/f8n/warehouse/pallet"
    13  )
    14  
    15  // Validate a Pallet artifact meets the OCI, Warehouse and Pallet specifications.
    16  func Pallet(p pallet.Pallet) error {
    17  	// validate as a generic Warehouse artifact first
    18  	if err := Warehouse(p); err != nil {
    19  		return err
    20  	}
    21  
    22  	// perform specific Pallet validation
    23  	err := Validate(p, &Fns{
    24  		Index: palletValidateIndex,
    25  		Image: palletValidateImage,
    26  		Layer: palletValidateLayer,
    27  	})
    28  	if err != nil {
    29  		return fmt.Errorf("invalid Pallet artifact: %v", err)
    30  	}
    31  	return nil
    32  }
    33  
    34  func palletValidateIndex(idx oci.Artifact, rootAnnotations map[string]string) error {
    35  	annotations, err := oci.Annotations(idx)
    36  	if err != nil {
    37  		return err
    38  	}
    39  
    40  	// check top-level annotations are as expected for pallet artifact
    41  	if err := Annotations(annotations, palletAnnotationValidators, palletOptionalAnnotationValidators); err != nil {
    42  		return err
    43  	}
    44  
    45  	// check annotations values specified by image are present in root node
    46  	if err := validatePalletAgainstRoot(annotations, rootAnnotations); err != nil {
    47  		return err
    48  	}
    49  
    50  	idxName := annotations[wh.AnnotationName]
    51  	palletClusterProviders, err := cluster.ProvidersFromAnnotations(annotations)
    52  	if err != nil {
    53  		return err
    54  	}
    55  
    56  	i := idx.(v1.ImageIndex)
    57  	idxManifest, err := i.IndexManifest()
    58  	if err != nil {
    59  		return err
    60  	}
    61  
    62  	// check each descriptor expected annotations for pallet manifest
    63  	for n, manifest := range idxManifest.Manifests {
    64  		if err := validatePalletManifest(manifest, idxName, palletClusterProviders); err != nil {
    65  			return fmt.Errorf("manifest %d is invalid: %v", n, err)
    66  		}
    67  	}
    68  
    69  	return nil
    70  }
    71  
    72  func validatePalletManifest(manifest v1.Descriptor, idxName string, palletClusterProviders cluster.Providers) error {
    73  	annotations := manifest.Annotations
    74  
    75  	// check descriptor has expected annotations for Warehouse manifest
    76  	if err := Annotations(annotations, palletManifestAnnotationValidators, nil); err != nil {
    77  		return err
    78  	}
    79  
    80  	// if descriptor is a dependency, we are done
    81  	if annotations[wh.AnnotationRefName] != idxName {
    82  		return nil
    83  	}
    84  
    85  	// provider images should only have providers which match those in the pallet
    86  	clusterProviders, err := cluster.ProvidersFromAnnotations(annotations)
    87  	if err != nil {
    88  		return err
    89  	}
    90  	for _, provider := range clusterProviders {
    91  		if err := palletClusterProviders.IsValid(provider); err != nil {
    92  			return err
    93  		}
    94  	}
    95  
    96  	return nil
    97  }
    98  
    99  func palletValidateImage(img oci.Artifact, rootAnnotations map[string]string) error {
   100  	annotations, err := oci.Annotations(img)
   101  	if err != nil {
   102  		return err
   103  	}
   104  
   105  	// check top-level annotations are as expected for Pallet artifact
   106  	if err := Annotations(annotations, palletAnnotationValidators, palletOptionalAnnotationValidators); err != nil {
   107  		return err
   108  	}
   109  
   110  	// check annotations values specified by image are present in root node
   111  	if err := validatePalletAgainstRoot(annotations, rootAnnotations); err != nil {
   112  		return err
   113  	}
   114  
   115  	i := img.(v1.Image)
   116  	layers, err := layer.FromImage(i)
   117  	if err != nil {
   118  		return err
   119  	}
   120  
   121  	numBaseLayers := 0
   122  	numInfraLayers := 0
   123  	for _, l := range layers {
   124  		layerAnnotations := l.Annotations()
   125  		switch l.Type() {
   126  		case layer.Infra:
   127  			numInfraLayers++
   128  		case layer.Runtime:
   129  			if _, ok := layerAnnotations[wh.AnnotationLayerRuntimeCapability]; !ok {
   130  				numBaseLayers++
   131  			}
   132  		}
   133  	}
   134  
   135  	// check there is a maximum of one base runtime layer, others must be runtime capabilities
   136  	if numBaseLayers > 1 {
   137  		return NewImageLayersError(errMultipleBaseLayers, numBaseLayers)
   138  	}
   139  
   140  	// check there is a maximum of one infrastructure layer
   141  	if numInfraLayers > 1 {
   142  		return NewImageLayersError(errMultipleInfraLayers, numInfraLayers)
   143  	}
   144  
   145  	return nil
   146  }
   147  
   148  func palletValidateLayer(l layer.Layer, _ map[string]string) error {
   149  	// check we can convert the layer into unstructured K8s objects
   150  	if _, err := l.Unstructured(); err != nil {
   151  		return fmt.Errorf("%v: %v", errLayersMustBeK8sManifests, err)
   152  	}
   153  
   154  	return nil
   155  }
   156  

View as plain text