package validate import ( "fmt" v1 "github.com/google/go-containerregistry/pkg/v1" wh "edge-infra.dev/pkg/f8n/warehouse" "edge-infra.dev/pkg/f8n/warehouse/cluster" "edge-infra.dev/pkg/f8n/warehouse/oci" "edge-infra.dev/pkg/f8n/warehouse/oci/layer" "edge-infra.dev/pkg/f8n/warehouse/pallet" ) // Validate a Pallet artifact meets the OCI, Warehouse and Pallet specifications. func Pallet(p pallet.Pallet) error { // validate as a generic Warehouse artifact first if err := Warehouse(p); err != nil { return err } // perform specific Pallet validation err := Validate(p, &Fns{ Index: palletValidateIndex, Image: palletValidateImage, Layer: palletValidateLayer, }) if err != nil { return fmt.Errorf("invalid Pallet artifact: %v", err) } return nil } func palletValidateIndex(idx oci.Artifact, rootAnnotations map[string]string) error { annotations, err := oci.Annotations(idx) if err != nil { return err } // check top-level annotations are as expected for pallet artifact if err := Annotations(annotations, palletAnnotationValidators, palletOptionalAnnotationValidators); err != nil { return err } // check annotations values specified by image are present in root node if err := validatePalletAgainstRoot(annotations, rootAnnotations); err != nil { return err } idxName := annotations[wh.AnnotationName] palletClusterProviders, err := cluster.ProvidersFromAnnotations(annotations) if err != nil { return err } i := idx.(v1.ImageIndex) idxManifest, err := i.IndexManifest() if err != nil { return err } // check each descriptor expected annotations for pallet manifest for n, manifest := range idxManifest.Manifests { if err := validatePalletManifest(manifest, idxName, palletClusterProviders); err != nil { return fmt.Errorf("manifest %d is invalid: %v", n, err) } } return nil } func validatePalletManifest(manifest v1.Descriptor, idxName string, palletClusterProviders cluster.Providers) error { annotations := manifest.Annotations // check descriptor has expected annotations for Warehouse manifest if err := Annotations(annotations, palletManifestAnnotationValidators, nil); err != nil { return err } // if descriptor is a dependency, we are done if annotations[wh.AnnotationRefName] != idxName { return nil } // provider images should only have providers which match those in the pallet clusterProviders, err := cluster.ProvidersFromAnnotations(annotations) if err != nil { return err } for _, provider := range clusterProviders { if err := palletClusterProviders.IsValid(provider); err != nil { return err } } return nil } func palletValidateImage(img oci.Artifact, rootAnnotations map[string]string) error { annotations, err := oci.Annotations(img) if err != nil { return err } // check top-level annotations are as expected for Pallet artifact if err := Annotations(annotations, palletAnnotationValidators, palletOptionalAnnotationValidators); err != nil { return err } // check annotations values specified by image are present in root node if err := validatePalletAgainstRoot(annotations, rootAnnotations); err != nil { return err } i := img.(v1.Image) layers, err := layer.FromImage(i) if err != nil { return err } numBaseLayers := 0 numInfraLayers := 0 for _, l := range layers { layerAnnotations := l.Annotations() switch l.Type() { case layer.Infra: numInfraLayers++ case layer.Runtime: if _, ok := layerAnnotations[wh.AnnotationLayerRuntimeCapability]; !ok { numBaseLayers++ } } } // check there is a maximum of one base runtime layer, others must be runtime capabilities if numBaseLayers > 1 { return NewImageLayersError(errMultipleBaseLayers, numBaseLayers) } // check there is a maximum of one infrastructure layer if numInfraLayers > 1 { return NewImageLayersError(errMultipleInfraLayers, numInfraLayers) } return nil } func palletValidateLayer(l layer.Layer, _ map[string]string) error { // check we can convert the layer into unstructured K8s objects if _, err := l.Unstructured(); err != nil { return fmt.Errorf("%v: %v", errLayersMustBeK8sManifests, err) } return nil }