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
77 assert.NoError(t, palletFns.validateImage(img, annos))
78
79
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
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
101 assert.NoError(t, Annotations(annos, palletAnnotationValidators, nil))
102
103
104 annosWithMissingOptional := maps.Clone(annos)
105 delete(annosWithMissingOptional, wh.AnnotationTitle)
106 assert.NoError(t, Annotations(annosWithMissingOptional, palletAnnotationValidators, palletOptionalAnnotationValidators))
107
108
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
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
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
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
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