1 package integration
2
3 import (
4 "context"
5 _ "embed"
6 "fmt"
7 "os"
8 "path"
9 "strings"
10 "testing"
11
12 "github.com/golang/mock/gomock"
13 "github.com/stretchr/testify/assert"
14 "github.com/stretchr/testify/require"
15
16 "edge-infra.dev/pkg/edge/api/mocks"
17 "edge-infra.dev/pkg/f8n/warehouse/cluster"
18 "edge-infra.dev/pkg/f8n/warehouse/oci"
19 "edge-infra.dev/pkg/f8n/warehouse/oci/layer"
20 "edge-infra.dev/pkg/f8n/warehouse/packagelock"
21 "edge-infra.dev/pkg/f8n/warehouse/pallet"
22 "edge-infra.dev/pkg/f8n/warehouse/promote"
23 "edge-infra.dev/pkg/k8s/object"
24 "edge-infra.dev/pkg/k8s/runtime/inventory"
25 "edge-infra.dev/pkg/k8s/unstructured"
26 "edge-infra.dev/test/f2"
27 "edge-infra.dev/test/f2/x/warehouse"
28 )
29
30
31 var Minimalv1 []byte
32
33
34 var Minimalv2 []byte
35
36
37 var Minimalv3 []byte
38
39 type testPallet struct {
40 inv *inventory.ResourceInventory
41 digest string
42 name string
43 tag string
44 pallet pallet.Pallet
45 }
46
47 var (
48 f f2.Framework
49
50 lumperctl = "lumper-controller"
51 banner = "banner-infra-cluster"
52 missing = "missingpkg"
53
54 stage1 = "ret-edge-stage1-foreman"
55 stage2 = "ret-edge-stage2-foreman"
56
57 fakeHash = "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e"
58 fakeDigest = fmt.Sprintf("sha256:%s", fakeHash)
59
60 pl packagelock.PackageLock
61 plMissingDigest packagelock.PackageLock
62 plWithMissing packagelock.PackageLock
63 plMixedValidMissing packagelock.PackageLock
64 )
65
66 func createMockPubSubService(t *testing.T) *mocks.MockPubSubService {
67 mockCtrl := gomock.NewController(t)
68 return mocks.NewMockPubSubService(mockCtrl)
69 }
70
71 func TestMain(m *testing.M) {
72 f = f2.New(context.Background(),
73 f2.WithExtensions(
74 &warehouse.Registry{},
75 ),
76 ).Setup(func(ctx f2.Context) (f2.Context, error) {
77 return ctx, nil
78 })
79 os.Exit(f.Run(m))
80 }
81
82 func TestPromotion(t *testing.T) {
83 var (
84 lumperctlLatestHash string
85 lumperctl014Hash string
86 lumperctl013Hash string
87 bannerLatestHash string
88 banner014Hash string
89 banner013Hash string
90 lumperTagHashes map[string]string
91 bannerTagHashes map[string]string
92
93 testSourceRepo = promote.Repository{
94 Name: promote.WarehouseRepo,
95 ProjectID: stage1,
96 }
97 testDestinationRepo = promote.Repository{
98 Name: promote.WarehouseRepo,
99 ProjectID: stage2,
100 Registry: "us-east1-docker.pkg.dev",
101 Ref: "us-east1-docker.pkg.dev/ret-edge-stage2-foreman/warehouse",
102 }
103 )
104
105 inv := f2.NewFeature("Package promotion").
106 Setup("Setup test registry", func(ctx f2.Context, t *testing.T) f2.Context {
107
108 url := warehouse.FromContextT(ctx, t).URL
109 host := strings.Split(url, "/")[0]
110
111 testSourceRepo.Registry = host
112 testSourceRepo.Ref = path.Join(url, stage1, promote.WarehouseRepo)
113
114
115 prefix := path.Join(stage1, promote.WarehouseRepo)
116 lumperctlLatestHash = createAndPushPallet(ctx, t, path.Join(prefix, lumperctl), promote.Latest, createLayer(t, layer.Runtime, Minimalv1))
117 lumperctl014Hash = createAndPushPallet(ctx, t, path.Join(prefix, lumperctl), "0.14", createLayer(t, layer.Runtime, Minimalv1))
118 lumperctl013Hash = createAndPushPallet(ctx, t, path.Join(prefix, lumperctl), "0.13", createLayer(t, layer.Runtime, Minimalv2))
119 bannerLatestHash = createAndPushPallet(ctx, t, path.Join(prefix, banner), promote.Latest, createLayer(t, layer.Runtime, Minimalv1))
120 banner014Hash = createAndPushPallet(ctx, t, path.Join(prefix, banner), "0.14", createLayer(t, layer.Runtime, Minimalv1))
121 banner013Hash = createAndPushPallet(ctx, t, path.Join(prefix, banner), "0.13", createLayer(t, layer.Runtime, Minimalv2))
122 lumperTagHashes = map[string]string{
123 "latest": lumperctlLatestHash,
124 "0.14": lumperctl014Hash,
125 "0.13": lumperctl013Hash,
126 }
127 bannerTagHashes = map[string]string{
128 "latest": bannerLatestHash,
129 "0.14": banner014Hash,
130 "0.13": banner013Hash,
131 }
132
133 lumperVersions := []packagelock.Version{
134 {Digest: lumperctl014Hash, Tags: []string{"0.14", "latest"}},
135 {Digest: lumperctl013Hash, Tags: []string{"0.13"}},
136 }
137
138 bannerVersions := []packagelock.Version{
139 {Digest: banner014Hash, Tags: []string{"0.14", "latest"}},
140 {Digest: banner013Hash, Tags: []string{"0.13"}},
141 }
142
143 bannerVersionsMissingDigest := []packagelock.Version{
144 {Digest: banner014Hash, Tags: []string{"0.14", "latest"}},
145 {Digest: banner013Hash, Tags: []string{"0.13"}},
146 {Digest: fakeDigest, Tags: []string{"0.12"}},
147 }
148
149 missingVersions := []packagelock.Version{
150 {Digest: fakeDigest, Tags: []string{"0.14"}},
151 }
152
153 pl = packagelock.PackageLock{
154 Packages: []packagelock.LockPackage{
155 {Name: lumperctl, Versions: lumperVersions},
156 {Name: banner, Versions: bannerVersions},
157 },
158 }
159
160 plMissingDigest = packagelock.PackageLock{
161 Packages: []packagelock.LockPackage{
162 {Name: lumperctl, Versions: lumperVersions},
163 {Name: banner, Versions: bannerVersionsMissingDigest},
164 },
165 }
166
167 plWithMissing = packagelock.PackageLock{
168 Packages: []packagelock.LockPackage{
169 {Name: missing, Versions: missingVersions},
170 },
171 }
172
173 plMixedValidMissing = packagelock.PackageLock{
174 Packages: []packagelock.LockPackage{
175 {Name: lumperctl, Versions: lumperVersions},
176 {Name: banner, Versions: bannerVersions},
177 {Name: missing, Versions: missingVersions},
178 },
179 }
180
181 return ctx
182 }).
183 Test("All packages in a valid package lock file are promoted", func(ctx f2.Context, t *testing.T) f2.Context {
184 testLockFile := "testdata/promote-test-lock/promote-test-lock.yaml"
185 cfg := &promote.Config{
186 LockFile: testLockFile,
187 SourceRepo: testSourceRepo,
188 DestinationRepo: testDestinationRepo,
189 }
190
191
192 ps := createMockPubSubService(t)
193 expectedLumperLatestMessage := fmt.Sprintf(
194 `{"action":"%s","digest":"%s","tag":"%s","projectId":"%s","registry":"%s","promotion":"%s"}`,
195 promote.InsertAction,
196 fmt.Sprintf("%s/%s@%s", cfg.SourceRepo.Ref, lumperctl, lumperTagHashes["latest"]),
197 fmt.Sprintf("%s/%s:%s", cfg.DestinationRepo.Ref, lumperctl, promote.Latest),
198 cfg.DestinationRepo.ProjectID,
199 cfg.DestinationRepo.Name,
200 promote.PromotionTrue,
201 )
202 expectedLumper013Message := fmt.Sprintf(
203 `{"action":"%s","digest":"%s","tag":"%s","projectId":"%s","registry":"%s","promotion":"%s"}`,
204 promote.InsertAction,
205 fmt.Sprintf("%s/%s@%s", cfg.SourceRepo.Ref, lumperctl, lumperTagHashes["0.13"]),
206 fmt.Sprintf("%s/%s:%s", cfg.DestinationRepo.Ref, lumperctl, "0.13"),
207 cfg.DestinationRepo.ProjectID,
208 cfg.DestinationRepo.Name,
209 promote.PromotionTrue,
210 )
211 expectedLumper014Message := fmt.Sprintf(
212 `{"action":"%s","digest":"%s","tag":"%s","projectId":"%s","registry":"%s","promotion":"%s"}`,
213 promote.InsertAction,
214 fmt.Sprintf("%s/%s@%s", cfg.SourceRepo.Ref, lumperctl, lumperTagHashes["0.14"]),
215 fmt.Sprintf("%s/%s:%s", cfg.DestinationRepo.Ref, lumperctl, "0.14"),
216 cfg.DestinationRepo.ProjectID,
217 cfg.DestinationRepo.Name,
218 promote.PromotionTrue,
219 )
220 expectedBannerLatestMessage := fmt.Sprintf(
221 `{"action":"%s","digest":"%s","tag":"%s","projectId":"%s","registry":"%s","promotion":"%s"}`,
222 promote.InsertAction,
223 fmt.Sprintf("%s/%s@%s", cfg.SourceRepo.Ref, banner, bannerTagHashes["latest"]),
224 fmt.Sprintf("%s/%s:%s", cfg.DestinationRepo.Ref, banner, promote.Latest),
225 cfg.DestinationRepo.ProjectID,
226 cfg.DestinationRepo.Name,
227 promote.PromotionTrue,
228 )
229 expectedBanner013Message := fmt.Sprintf(
230 `{"action":"%s","digest":"%s","tag":"%s","projectId":"%s","registry":"%s","promotion":"%s"}`,
231 promote.InsertAction,
232 fmt.Sprintf("%s/%s@%s", cfg.SourceRepo.Ref, banner, bannerTagHashes["0.13"]),
233 fmt.Sprintf("%s/%s:%s", cfg.DestinationRepo.Ref, banner, "0.13"),
234 cfg.DestinationRepo.ProjectID,
235 cfg.DestinationRepo.Name,
236 promote.PromotionTrue,
237 )
238 expectedBanner014Message := fmt.Sprintf(
239 `{"action":"%s","digest":"%s","tag":"%s","projectId":"%s","registry":"%s","promotion":"%s"}`,
240 promote.InsertAction,
241 fmt.Sprintf("%s/%s@%s", cfg.SourceRepo.Ref, banner, bannerTagHashes["0.14"]),
242 fmt.Sprintf("%s/%s:%s", cfg.DestinationRepo.Ref, banner, "0.14"),
243 cfg.DestinationRepo.ProjectID,
244 cfg.DestinationRepo.Name,
245 promote.PromotionTrue,
246 )
247
248
249 ps.EXPECT().Send(gomock.Any(), promote.PromotionsTopic, []byte(expectedLumper013Message), promote.Attributes).Return(nil)
250 ps.EXPECT().Send(gomock.Any(), promote.PromotionsTopic, []byte(expectedLumper014Message), promote.Attributes).Return(nil)
251 ps.EXPECT().Send(gomock.Any(), promote.PromotionsTopic, []byte(expectedLumperLatestMessage), promote.Attributes).Return(nil)
252 ps.EXPECT().Send(gomock.Any(), promote.PromotionsTopic, []byte(expectedBanner013Message), promote.Attributes).Return(nil)
253 ps.EXPECT().Send(gomock.Any(), promote.PromotionsTopic, []byte(expectedBanner014Message), promote.Attributes).Return(nil)
254 ps.EXPECT().Send(gomock.Any(), promote.PromotionsTopic, []byte(expectedBannerLatestMessage), promote.Attributes).Return(nil)
255
256 digests, err := promote.Promote(ctx, cfg, pl, ps)
257 fmt.Println("printing digests")
258 fmt.Println(digests)
259 assert.NoError(t, err)
260 return ctx
261 }).
262 Test("A missing package is caught prior to promotion executing", func(ctx f2.Context, t *testing.T) f2.Context {
263 cfg := &promote.Config{
264 SourceRepo: testSourceRepo,
265 DestinationRepo: testDestinationRepo,
266 }
267
268
269 ps := createMockPubSubService(t)
270
271 _, err := promote.Promote(ctx, cfg, plWithMissing, ps)
272 fmt.Println("should catch error")
273 fmt.Println(err)
274 assert.Error(t, err)
275 return ctx
276 }).
277 Test("A lockfile with both valid and invalid packages is caught prior to promotion executing", func(ctx f2.Context, t *testing.T) f2.Context {
278 cfg := &promote.Config{
279 SourceRepo: testSourceRepo,
280 DestinationRepo: testDestinationRepo,
281 }
282
283
284 ps := createMockPubSubService(t)
285
286 _, err := promote.Promote(ctx, cfg, plMixedValidMissing, ps)
287 fmt.Println("should catch error")
288 fmt.Println(err)
289 assert.Error(t, err)
290 return ctx
291 }).
292 Test("A lockfile with a valid package is missing a digest/tag", func(ctx f2.Context, t *testing.T) f2.Context {
293 cfg := &promote.Config{
294 SourceRepo: testSourceRepo,
295 DestinationRepo: testDestinationRepo,
296 }
297
298
299 ps := createMockPubSubService(t)
300
301 _, err := promote.Promote(ctx, cfg, plMixedValidMissing, ps)
302 fmt.Println("should catch error")
303 fmt.Println(err)
304 assert.Error(t, err)
305 return ctx
306 }).
307 Feature()
308
309 f.Test(t, inv)
310 }
311
312
313 func createAndPushPallet(ctx f2.Context, t *testing.T, name, tag string, layers ...layer.Layer) string {
314 t.Helper()
315
316 p := testPallet{name: name, tag: tag}
317 var objs []*unstructured.Unstructured
318 for i, l := range layers {
319 r, err := l.Uncompressed()
320 require.NoError(t, err)
321 lobjs, err := object.ReadObjects(r)
322 require.NoError(t, err, "failed to read test pallet objects", name)
323 objs = append(objs, lobjs...)
324 layers[i] = l
325 }
326 p.inv = inventory.New(inventory.FromUnstructured(objs...))
327
328 a, err := pallet.Image(pallet.Options{
329 Metadata: palletMeta(name),
330 ClusterProviders: cluster.BuiltInProviders(),
331 }, layers...)
332 require.NoError(t, err, "failed to create test pallet", name)
333 p.pallet = a
334
335 hash, err := a.Digest()
336 require.NoError(t, err, "failed to get test pallet digest", name)
337 p.digest = hash.String()
338
339 pushArtifactToRegistry(ctx, t, a, name, tag)
340 return hash.String()
341 }
342
343 func palletMeta(name string) pallet.Metadata {
344 return pallet.Metadata{
345 Name: name,
346 Team: "@ncrvoyix-swt-retail/f8n",
347 BuildInfo: pallet.BuildInfo{
348 Created: "yesterday",
349 Source: "https://github.com/ncrvoyix-swt-retail/edge-infra",
350 Revision: "d34db33f",
351 Version: "0.2.5",
352 },
353 }
354 }
355
356 func pushArtifactToRegistry(ctx f2.Context, t *testing.T, a oci.Artifact, name, tag string) {
357 t.Helper()
358 err := warehouse.FromContextT(ctx, t).Push(a, name, tag)
359 require.NoError(t, err, "failed to push test pallet", name)
360 }
361
362
363 func createLayer(t *testing.T, lt layer.Type, yaml []byte, opts ...layer.Option) layer.Layer {
364 t.Helper()
365 l, err := layer.New(lt, yaml, opts...)
366 require.NoError(t, err, "failed to create test pallet layer")
367 return l
368 }
369
View as plain text