...

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

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

     1  package validate
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"maps"
     8  
     9  	v1 "github.com/google/go-containerregistry/pkg/v1"
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	wh "edge-infra.dev/pkg/f8n/warehouse"
    14  	"edge-infra.dev/pkg/f8n/warehouse/cluster"
    15  	"edge-infra.dev/pkg/f8n/warehouse/oci"
    16  	"edge-infra.dev/pkg/f8n/warehouse/oci/layer"
    17  	"edge-infra.dev/pkg/f8n/warehouse/pallet"
    18  )
    19  
    20  var providers = cluster.BuiltInProviders()
    21  
    22  var palletFns = &Fns{
    23  	Index: palletValidateIndex,
    24  	Image: palletValidateImage,
    25  	Layer: palletValidateLayer,
    26  }
    27  
    28  var testPalletRefs = []string{
    29  	"cert-manager:latest",
    30  	"shoot:latest",
    31  	"redpanda-system:latest",
    32  	"prometheus-operator:latest",
    33  	"external-secrets:latest",
    34  }
    35  
    36  func getPallet(reference string) (pallet.Pallet, error) {
    37  	a, err := getArtifact(reference)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  
    42  	return pallet.New(a)
    43  }
    44  
    45  func makeMeta() pallet.Metadata {
    46  	return pallet.Metadata{
    47  		Name:   "shoot",
    48  		Vendor: "NCR",
    49  		Team:   "@ncrvoyix-swt-retail/edge-waterboys",
    50  		BuildInfo: pallet.BuildInfo{
    51  			Created:  "2023-06-21T14:12:59+00:00",
    52  			Source:   "https://gothub.com/ncrvoyix-swt-retail/edge-infra",
    53  			Revision: "d34db33f",
    54  			Version:  "0.2.5",
    55  		},
    56  	}
    57  }
    58  
    59  func TestValidatePallet(t *testing.T) {
    60  	for _, ref := range testPalletRefs {
    61  		p, err := getPallet(ref)
    62  		require.NoError(t, err)
    63  
    64  		assert.NoError(t, Pallet(p))
    65  	}
    66  }
    67  
    68  func TestPalletAgainstRoot(t *testing.T) {
    69  	p, err := getPallet("cert-manager:latest")
    70  	require.NoError(t, err)
    71  
    72  	img := p.Unwrap()
    73  	annos, err := oci.Annotations(img)
    74  	require.NoError(t, err)
    75  
    76  	// validating against its own annotations as root is valid
    77  	assert.NoError(t, palletFns.validateImage(img, annos))
    78  
    79  	// root with no providers makes image invalid as cert-manager has providers
    80  	annosWithoutProviders := maps.Clone(annos)
    81  	annosWithoutProviders[wh.AnnotationClusterProviders] = ""
    82  	err = palletFns.validateImage(img, annosWithoutProviders)
    83  	assert.ErrorContains(t, err, fmt.Sprintf("invalid v1.Image: descriptor has '%s' but root does not", wh.AnnotationClusterProviders))
    84  
    85  	// provider not in root makes image invalid as cert-manager has 'sds'
    86  	annosWithOnlyGKEProvider := maps.Clone(annos)
    87  	annosWithOnlyGKEProvider[wh.AnnotationClusterProviders] = "gke"
    88  	err = palletFns.validateImage(img, annosWithOnlyGKEProvider)
    89  	assert.ErrorContains(t, err, fmt.Sprintf("invalid v1.Image: descriptor has '%s'=", wh.AnnotationClusterProviders))
    90  	assert.ErrorContains(t, err, "which is not in root set '[gke]'")
    91  }
    92  
    93  func TestPalletAnnotations(t *testing.T) {
    94  	p, err := getPallet("cert-manager:latest")
    95  	require.NoError(t, err)
    96  
    97  	annos, err := oci.Annotations(p)
    98  	require.NoError(t, err)
    99  
   100  	// check valid annotations return no errors
   101  	assert.NoError(t, Annotations(annos, palletAnnotationValidators, nil))
   102  
   103  	// missing optional annotation returns no errors
   104  	annosWithMissingOptional := maps.Clone(annos)
   105  	delete(annosWithMissingOptional, wh.AnnotationTitle)
   106  	assert.NoError(t, Annotations(annosWithMissingOptional, palletAnnotationValidators, palletOptionalAnnotationValidators))
   107  
   108  	// non-pallet kind annotation returns error
   109  	annosWithWrongKind := maps.Clone(annos)
   110  	annosWithWrongKind[wh.AnnotationKind] = "cluster"
   111  	err = Annotations(annosWithWrongKind, palletAnnotationValidators, nil)
   112  	assert.ErrorContains(t, err, wh.AnnotationKind)
   113  	assert.ErrorContains(t, err, errInvalidPalletKindAnnotation.Error())
   114  
   115  	// documentation annotation not valid URL returns error
   116  	annosWithInvalidDocumentation := maps.Clone(annos)
   117  	annosWithInvalidDocumentation[wh.AnnotationDocumentation] = "pkg/f8n/warehouse/README.md"
   118  	err = Annotations(annosWithInvalidDocumentation, palletAnnotationValidators, palletOptionalAnnotationValidators)
   119  	assert.ErrorContains(t, err, wh.AnnotationDocumentation)
   120  	assert.ErrorContains(t, err, errInvalidURLAnnotation.Error())
   121  
   122  	// documentation annotation not valid URL returns error
   123  	annosWithNonBinaryRender := maps.Clone(annos)
   124  	annosWithNonBinaryRender[wh.AnnotationRender] = "yes"
   125  	err = Annotations(annosWithNonBinaryRender, palletAnnotationValidators, palletOptionalAnnotationValidators)
   126  	assert.ErrorContains(t, err, wh.AnnotationRender)
   127  	assert.ErrorContains(t, err, errInvalidBinaryAnnotation.Error())
   128  }
   129  
   130  func TestManifestWithInvalidProviderReturnsError(t *testing.T) {
   131  	p, err := getPallet("shoot:latest")
   132  	require.NoError(t, err)
   133  
   134  	// remove 'gke' from shoot providers
   135  	annos, err := oci.Annotations(p)
   136  	require.NoError(t, err)
   137  	annosWithoutGKEProvider := maps.Clone(annos)
   138  	annosWithoutGKEProvider[wh.AnnotationClusterProviders] = "sds,generic"
   139  	idx := p.Unwrap().(v1.ImageIndex)
   140  	idx = oci.Annotate(idx, annosWithoutGKEProvider).(v1.ImageIndex)
   141  
   142  	// check manifests are now invalid, as the 'gke' provider descriptor is still present
   143  	err = palletFns.indexValidator(idx, annosWithoutGKEProvider)
   144  	assert.ErrorContains(t, err, "manifest")
   145  	assert.ErrorContains(t, err, "invalid cluster provider gke")
   146  }
   147  
   148  func TestMultipleBaseLayersReturnsError(t *testing.T) {
   149  	meta := makeMeta()
   150  	contents := []byte("")
   151  	layers := []layer.Layer{}
   152  	for n := 0; n < 2; n++ {
   153  		l, err := layer.New(layer.Runtime, contents)
   154  		require.NoError(t, err)
   155  		layers = append(layers, l)
   156  	}
   157  
   158  	img, err := pallet.Image(pallet.Options{
   159  		ClusterProviders: providers,
   160  		Metadata:         meta,
   161  	}, layers...)
   162  	require.NoError(t, err)
   163  
   164  	err = palletFns.imageValidator(img.Unwrap(), providers.OCIAnnotations())
   165  	assert.ErrorContains(t, err, errMultipleBaseLayers.Error())
   166  }
   167  
   168  func TestMultipleInfraLayersReturnsError(t *testing.T) {
   169  	meta := makeMeta()
   170  	contents := []byte("")
   171  	layers := []layer.Layer{}
   172  	for n := 0; n < 2; n++ {
   173  		l, err := layer.New(layer.Infra, contents)
   174  		require.NoError(t, err)
   175  		layers = append(layers, l)
   176  	}
   177  
   178  	img, err := pallet.Image(pallet.Options{
   179  		ClusterProviders: providers,
   180  		Metadata:         meta,
   181  	}, layers...)
   182  	require.NoError(t, err)
   183  
   184  	err = palletFns.imageValidator(img.Unwrap(), providers.OCIAnnotations())
   185  	assert.ErrorContains(t, err, errMultipleInfraLayers.Error())
   186  }
   187  
   188  func TestNonK8sLayerReturnsError(t *testing.T) {
   189  	contents := []byte("invalid-k8s-manifest")
   190  	l, err := layer.New(layer.Runtime, contents)
   191  	require.NoError(t, err)
   192  
   193  	err = palletFns.layerValidator(l, map[string]string{})
   194  	assert.ErrorContains(t, err, errLayersMustBeK8sManifests.Error())
   195  }
   196  

View as plain text