...

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

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

     1  package validate
     2  
     3  import (
     4  	"fmt"
     5  	"net/url"
     6  	"regexp"
     7  	"strings"
     8  	"time"
     9  
    10  	"go.uber.org/multierr"
    11  
    12  	wh "edge-infra.dev/pkg/f8n/warehouse"
    13  	"edge-infra.dev/pkg/f8n/warehouse/pallet"
    14  	"edge-infra.dev/pkg/lib/build/semver"
    15  )
    16  
    17  var revisionHashRegex = "^[a-f0-9]{7,40}$"
    18  
    19  // Validators for required Pallet annotations
    20  var palletAnnotationValidators = map[string]AnnotationValidator{
    21  	wh.AnnotationKind:     validatePalletKind,
    22  	wh.AnnotationTeam:     validateNotEmpty,
    23  	wh.AnnotationRevision: validateRevision,
    24  	wh.AnnotationSource:   validateSource,
    25  	wh.AnnotationVersion:  validateVersion,
    26  	wh.AnnotationCreated:  validateCreated,
    27  	wh.AnnotationVendor:   validateNotEmpty,
    28  }
    29  
    30  // Validators for optional Pallet annotations
    31  var palletOptionalAnnotationValidators = map[string]AnnotationValidator{
    32  	wh.AnnotationTitle:         validateNotEmpty,
    33  	wh.AnnotationDescription:   validateNotEmpty,
    34  	wh.AnnotationDocumentation: validateDocumentation,
    35  	wh.AnnotationRender:        validateRender,
    36  	wh.AnnotationCapabilities:  validateCapabilities,
    37  }
    38  
    39  // Validators for required Pallet manifest annotations
    40  var palletManifestAnnotationValidators = map[string]AnnotationValidator{
    41  	wh.AnnotationKind:             validateNotEmpty,
    42  	wh.AnnotationRefName:          validateNotEmpty,
    43  	wh.AnnotationClusterProviders: validateClusterProviders,
    44  }
    45  
    46  // BuildInfo validates the components of pallet.BuildInfo prior to packer instantiation.
    47  func BuildInfo(bi pallet.BuildInfo) error {
    48  	return multierr.Combine(
    49  		validateSource(bi.Source),
    50  		validateVersion(bi.Version),
    51  		validateRevision(bi.Revision),
    52  		validateCreated(bi.Created),
    53  	)
    54  }
    55  
    56  // com.ncr.warehouse.kind should be "pallet".
    57  func validatePalletKind(kind string) error {
    58  	if kind != wh.PalletKind {
    59  		return errInvalidPalletKindAnnotation
    60  	}
    61  	return nil
    62  }
    63  
    64  // org.opencontainers.image.revision should be the hash of the artifact.
    65  func validateRevision(revision string) error {
    66  	match, _ := regexp.MatchString(revisionHashRegex, revision)
    67  	if !match {
    68  		return errInvalidRevisionAnnotation
    69  	}
    70  	return nil
    71  }
    72  
    73  // org.opencontainers.image.source should be the URL path to the relevant source code.
    74  func validateSource(sourceURL string) error {
    75  	_, err := url.ParseRequestURI(sourceURL)
    76  	if err != nil {
    77  		return fmt.Errorf("%v: %v", errInvalidURLAnnotation, err)
    78  	}
    79  	return nil
    80  }
    81  
    82  // org.opencontainers.image.version should be a valid SemVer version.
    83  func validateVersion(version string) error {
    84  	if err := semver.IsValidSemver(version); err != nil {
    85  		return fmt.Errorf("%v: %v", errInvalidVersionAnnotation, err)
    86  	}
    87  	return nil
    88  }
    89  
    90  // org.opencontainers.image.created should be timestamp as defined by RFC3339.
    91  func validateCreated(created string) error {
    92  	_, err := time.Parse(time.RFC3339, created)
    93  	if err != nil {
    94  		return fmt.Errorf("%v: %v", errInvalidTimestampAnnotation, err)
    95  	}
    96  	return nil
    97  }
    98  
    99  // org.opencontainers.image.documentation should be the URL path to the relevant documentation.
   100  func validateDocumentation(documentationURL string) error {
   101  	_, err := url.ParseRequestURI(documentationURL)
   102  	if err != nil {
   103  		return fmt.Errorf("%v: %v", errInvalidURLAnnotation, err)
   104  	}
   105  	return nil
   106  }
   107  
   108  // com.ncr.warehouse.pallet.render should be either "true" or "false".
   109  func validateRender(render string) error {
   110  	if !(render == "true" || render == "false") {
   111  		return errInvalidBinaryAnnotation
   112  	}
   113  	return nil
   114  }
   115  
   116  // com.ncr.warehouse.pallet.capabilities should be a comma separated list of runtime capabilities.
   117  func validateCapabilities(capabilities string) error {
   118  	match, _ := regexp.MatchString(csvListRegex, capabilities)
   119  	if !match {
   120  		return errInvalidListAnnotation
   121  	}
   122  	return nil
   123  }
   124  
   125  // Checks the annotation values for capabilities, providers and parameters are all present in the root node.
   126  func validatePalletAgainstRoot(annotations, rootAnnotations map[string]string) error {
   127  	// validate all descriptor runtime capabilities are specified by root
   128  	capabilities := strings.Split(annotations[wh.AnnotationCapabilities], ",")
   129  	rootCapabilities := strings.Split(rootAnnotations[wh.AnnotationCapabilities], ",")
   130  	for _, capability := range capabilities {
   131  		if !isValueInRoot(capability, rootCapabilities) {
   132  			return NewDescriptorError(wh.AnnotationCapabilities, capability, rootCapabilities)
   133  		}
   134  	}
   135  
   136  	// validate all descriptor cluster providers are specified by root
   137  	providers := strings.Split(annotations[wh.AnnotationClusterProviders], ",")
   138  	rootProviders := strings.Split(rootAnnotations[wh.AnnotationClusterProviders], ",")
   139  	for _, provider := range providers {
   140  		if !isValueInRoot(provider, rootProviders) {
   141  			return NewDescriptorError(wh.AnnotationClusterProviders, provider, rootProviders)
   142  		}
   143  	}
   144  
   145  	// validate all descriptor parameters are specified by root
   146  	parameters := strings.Split(annotations[wh.AnnotationParameters], ",")
   147  	rootParameters := strings.Split(rootAnnotations[wh.AnnotationParameters], ",")
   148  	for _, parameter := range parameters {
   149  		if !isValueInRoot(parameter, rootParameters) {
   150  			return NewDescriptorError(wh.AnnotationParameters, parameter, rootParameters)
   151  		}
   152  	}
   153  
   154  	return nil
   155  }
   156  
   157  // Returns an error if a given argument is not one of the root arguments or root arguments are empty.
   158  func isValueInRoot(value string, rootValues []string) bool {
   159  	if value == "" {
   160  		return true
   161  	}
   162  	if len(rootValues) == 0 || rootValues[0] == "" {
   163  		return false
   164  	}
   165  	for _, rootValue := range rootValues {
   166  		if value == rootValue {
   167  			return true
   168  		}
   169  	}
   170  	return false
   171  }
   172  

View as plain text