1 package pack
2
3 import (
4 "embed"
5 "io/fs"
6 "strings"
7 "testing"
8
9 "github.com/stretchr/testify/assert"
10 "gopkg.in/yaml.v3"
11
12 "edge-infra.dev/pkg/f8n/warehouse"
13 "edge-infra.dev/pkg/f8n/warehouse/lift"
14 "edge-infra.dev/pkg/f8n/warehouse/lift/pack/types"
15 "edge-infra.dev/pkg/f8n/warehouse/oci"
16 "edge-infra.dev/pkg/f8n/warehouse/oci/validate"
17 "edge-infra.dev/pkg/f8n/warehouse/pallet"
18 "edge-infra.dev/pkg/k8s/kustomize"
19 "edge-infra.dev/pkg/lib/build"
20 "edge-infra.dev/test/fixtures"
21 )
22
23 var (
24 b = pallet.BuildInfo{
25 Created: "2023-06-21T15:00:00Z",
26 Source: "https://github.com/ncrvoyix-swt-retail/edge-infra",
27 Revision: "abcde1234",
28 Version: "0.0.1",
29 }
30
31 data embed.FS
32 )
33
34 func TestParameterValidation(t *testing.T) {
35 validParameters := []string{"cluster_uuid", "cluster_hash", "gcp_project_id", "foreman_gcp_project_id"}
36 tcs := map[string]struct {
37 data string
38 parameters []string
39 }{
40 "without parameters": {
41 data: `apiVersion: v1
42 kind: ServiceAccount
43 metadata:
44 name: shoot`,
45 parameters: nil,
46 },
47 "with invalid parameters": {
48 data: `apiVersion: v1
49 kind: ServiceAccount
50 metadata:
51 name: ${var:=invalid%)!@}`,
52 parameters: nil,
53 },
54 "with valid parameters": {
55 data: `apiVersion: iam.cnrm.cloud.google.com/v1beta1
56 kind: IAMPolicyMember
57 metadata:
58 name: shoot-publisher
59 spec:
60 member: serviceAccount:shoot-${cluster_hash}@${gcp_project_id}.iam.gserviceaccount.com
61 resourceRef:
62 apiVersion: pubsub.cnrm.cloud.google.com/v1beta1
63 kind: PubSubTopic
64 external: projects/${foreman_gcp_project_id}/topics/data-sync-e2c
65 role: roles/pubsub.publisher`,
66 parameters: []string{
67 "cluster_hash",
68 "gcp_project_id",
69 "foreman_gcp_project_id",
70 },
71 },
72 "with valid and invalid parameters": {
73 data: `apiVersion: iam.cnrm.cloud.google.com/v1beta1
74 kind: IAMPolicyMember
75 metadata:
76 name: ${var:=default}
77 spec:
78 member: serviceAccount:shoot-${cluster_hash}@${gcp_project_id}.iam.gserviceaccount.com
79 resourceRef:
80 apiVersion: pubsub.cnrm.cloud.google.com/v1beta1
81 kind: PubSubTopic
82 external: projects/${foreman_gcp_project_id}/topics/data-sync-e2c
83 role: roles/pubsub.publisher`,
84 parameters: []string{
85 "cluster_hash",
86 "gcp_project_id",
87 "foreman_gcp_project_id",
88 },
89 },
90 "with multiple $ prefix": {
91 data: `apiVersion: iam.cnrm.cloud.google.com/v1beta1
92 kind: IAMPolicyMember
93 metadata:
94 name: $${NOPE}
95 spec:
96 member: serviceAccount:shoot-$$${cluster_hash}@${gcp_project_id}.iam.gserviceaccount.com
97 resourceRef:
98 apiVersion: pubsub.cnrm.cloud.google.com/v1beta1
99 kind: PubSubTopic
100 external: projects/${foreman_gcp_project_id}/topics/data-sync-e2c
101 role: roles/pubsub.publisher`,
102 parameters: []string{
103 "gcp_project_id",
104 "foreman_gcp_project_id",
105 },
106 },
107 }
108
109 for test, tc := range tcs {
110 t.Run(test, func(t *testing.T) {
111 parameters := getParameters(tc.data)
112 assert.Equal(t, tc.parameters, parameters, "Expected parameters do not match actual parameters: %s", test)
113 })
114 t.Run(test, func(t *testing.T) {
115 parameters := getParameters(tc.data)
116 for _, p := range parameters {
117 valid := false
118 for _, vp := range validParameters {
119 if p == vp {
120
121 valid = true
122 continue
123 }
124 }
125 assert.True(t, valid, "rendering parameter %s is invalid: expected one of %v",
126 p, validParameters)
127 }
128 })
129 }
130 }
131
132
133 func TestPacker(t *testing.T) {
134 packer := fixturesFS(t)
135
136 t.Run("Version", func(t *testing.T) {
137
138 testPallet := types.Pallet{}
139 inferredVersion := packer.pkgMeta(testPallet)
140 assert.Contains(t, inferredVersion.Version, b.Version)
141
142 testPallet.Version = build.Version{
143 Commit: "abcd1234",
144 ReleaseCandidate: false,
145 SemVer: "9.9.9",
146 Timestamp: 1686062747,
147 }
148
149
150 declaredVersion := packer.pkgMeta(testPallet)
151 assert.Contains(t, declaredVersion.Version, testPallet.Version.SemVer)
152 })
153
154
155
156 t.Run("ParameterAnnotation", func(t *testing.T) {
157
158 plt, err := packer.Pack("pallets/real/stores/store")
159 assert.NoError(t, err)
160 packedParameters := plt.Parameters()
161 assert.NotEmpty(t, packedParameters)
162
163
164 a := plt.Unwrap()
165 annos, err := oci.Annotations(a)
166 assert.NoError(t, err)
167
168
169
170 assert.Equal(t, strings.Join(packedParameters, ","), annos[warehouse.AnnotationParameters])
171 })
172
173
174 t.Run("Artifacts", func(t *testing.T) {
175 packedPallet, err := packer.Pack("pallets/test-pallets/cert-manager-test")
176 assert.NoError(t, err)
177 packedParameters := packedPallet.Parameters()
178 assert.NotEmpty(t, packedParameters)
179
180 fetchedPallets := packer.Artifacts()
181 assert.NotEmpty(t, fetchedPallets)
182 assert.Contains(t, fetchedPallets, packedPallet)
183 })
184
185
186 t.Run("ValidArtifact", func(t *testing.T) {
187 plt, err := packer.Pack("pallets/real/stores/store")
188 assert.NoError(t, err)
189
190 err = validate.Pallet(plt)
191 assert.NoError(t, err)
192
193
194 a := plt.Unwrap()
195 err = validate.Warehouse(a)
196 assert.NoError(t, err)
197 })
198 }
199
200
201
202
203
204
205
206
207
208
209
210
211 func TestReproducibleBuilds(t *testing.T) {
212 packer := fixturesFS(t)
213
214
215 ctrlPallet, err := packer.Pack("pallets/real/external-secrets")
216 assert.NoError(t, err)
217
218 ctrlManifest, err := ctrlPallet.RawManifest()
219 assert.NoError(t, err)
220
221
222 for i := 0; i < 10; i++ {
223 t.Run("RepeatBuild", func(t *testing.T) {
224
225 packer.cache = make(map[string]pallet.Pallet)
226
227
228 testPallet, err := packer.Pack("pallets/real/external-secrets")
229 assert.NoError(t, err)
230
231
232 testManifest, err := testPallet.RawManifest()
233 assert.NoError(t, err)
234 assert.Equal(t, string(ctrlManifest), string(testManifest))
235 })
236 }
237 }
238
239
240 func fixturesFS(t *testing.T) *Packer {
241 t.Helper()
242 t.Setenv("WAREHOUSE_PATH", "/")
243 t.Setenv("WAREHOUSE_CACHE", "/cache")
244
245
246 testFS, err := fs.Sub(fixtures.PalletFixtures, "warehouse/src")
247 if err != nil {
248 t.Fatalf("failed to reroot test fs: %s", err)
249 }
250 packerFS := &kustomize.FS{FS: testFS}
251
252 return packerFromFS(t, packerFS)
253 }
254
255 func testdataFS(t *testing.T) *Packer {
256 t.Helper()
257 t.Setenv("WAREHOUSE_PATH", "/")
258 t.Setenv("WAREHOUSE_CACHE", "/cache")
259 s, err := fs.Sub(data, "testdata/fs")
260 if err != nil {
261 t.Fatalf("failed to reroot test fs: %s", err)
262 }
263 packerFS := &kustomize.FS{FS: s}
264
265 return packerFromFS(t, packerFS)
266 }
267
268 func packerFromFS(t *testing.T, packerFS *kustomize.FS) *Packer {
269
270 testCfgBytes, err := packerFS.ReadFile(".warehouse.yaml")
271 if err != nil {
272 t.Fatalf("failed to read .warehouse.yaml in test fs: %s", err)
273 }
274 testCfg := lift.Config{}
275 err = yaml.Unmarshal(testCfgBytes, &testCfg)
276 if err != nil {
277 t.Fatalf("failed to unmarshal test cfg: %s", err)
278 }
279
280
281 cfg, err := lift.NewConfig()
282 if err != nil {
283 t.Fatal(err)
284 }
285 cfg, err = cfg.FromConfig(testCfg)
286 if err != nil {
287 t.Fatalf("failed to merge test cfg: %s", err)
288 }
289
290
291 c := Context{
292 Config: &cfg,
293 FS: packerFS,
294 }
295
296 packer, err := New(c, b)
297 if err != nil {
298 t.Fatal(err)
299 }
300
301 return packer
302 }
303
304 func TestRawLayers(t *testing.T) {
305 packer := testdataFS(t)
306 packedRawPallet, err := packer.Pack("cert-manager-test")
307 assert.NoError(t, err)
308 rawMeta := packedRawPallet.Metadata()
309 packedKustomizePallet, err := packer.Pack("cert-manager-kustomize")
310 assert.NoError(t, err)
311 kustomizeMeta := packedKustomizePallet.Metadata()
312 assert.Equal(t, rawMeta, kustomizeMeta)
313 }
314
View as plain text