1 package integration
2
3 import (
4 "fmt"
5 "testing"
6
7 "github.com/davecgh/go-spew/spew"
8 "gotest.tools/v3/assert"
9 "gotest.tools/v3/assert/cmp"
10 "gotest.tools/v3/poll"
11 "k8s.io/apimachinery/pkg/labels"
12 "k8s.io/apimachinery/pkg/selection"
13 "k8s.io/apimachinery/pkg/types"
14 "sigs.k8s.io/controller-runtime/pkg/client"
15 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
16
17 "edge-infra.dev/pkg/f8n/warehouse/capability"
18 "edge-infra.dev/pkg/f8n/warehouse/cluster"
19 whv1 "edge-infra.dev/pkg/f8n/warehouse/k8s/apis/v1alpha2"
20 "edge-infra.dev/pkg/f8n/warehouse/oci/layer"
21 "edge-infra.dev/pkg/f8n/warehouse/pallet"
22 "edge-infra.dev/pkg/k8s/object"
23 "edge-infra.dev/pkg/k8s/runtime/conditions"
24 "edge-infra.dev/test/f2"
25 "edge-infra.dev/test/f2/x/ktest"
26 "edge-infra.dev/test/f2/x/warehouse"
27 )
28
29 func TestShipmentController_Finalizers(t *testing.T) {
30 var (
31 id string
32 p1 testPallet
33 p2 testPallet
34 s = &whv1.Shipment{}
35 children []client.Object
36 )
37
38 fin := f2.NewFeature("Finalizers").
39 Setup("Create and push test packages", func(ctx f2.Context, t *testing.T) f2.Context {
40 k := ktest.FromContextT(ctx, t)
41
42 id = fmt.Sprintf("sfin-%s", ctx.RunID)
43 p1 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-1", id), "v1",
44 createLayer(t, layer.Runtime, minimalv1))
45 p2 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v1",
46 createLayer(t, layer.Runtime, minimalv2))
47 s = shipment(ctx, t, id, []whv1.BaseArtifact{
48 {Digest: p1.digest, Name: p1.name},
49 {Digest: p2.digest, Name: p2.name},
50 })
51
52 assert.NilError(t, k.Client.Create(ctx, s))
53 k.WaitOn(t, k.Check(s, shipmentReady))
54
55 return ctx
56 }).
57 Test("Finalizer is added", func(ctx f2.Context, t *testing.T) f2.Context {
58 if !controllerutil.ContainsFinalizer(s, whv1.WarehouseFinalizer) {
59 t.Error("finalizer not added to unpackedpallet", spew.Sprintln(s))
60 }
61
62 return ctx
63 }).
64 Test("Removes finalizer on delete", func(ctx f2.Context, t *testing.T) f2.Context {
65 k := ktest.FromContextT(ctx, t)
66
67 children = make([]client.Object, 2)
68 for i, v := range []testPallet{p1, p2} {
69 u := &whv1.UnpackedPallet{}
70 assert.NilError(t, k.Client.Get(
71 ctx, types.NamespacedName{Name: s.ChildResourceName(v.name)}, u,
72 ), "%s never created by shipment", s.ChildResourceName(v.name))
73 children[i] = u
74 }
75
76 assert.NilError(t, k.Client.Delete(ctx, s))
77
78 k.WaitOn(t, k.ObjDeleted(s))
79
80 return ctx
81 }).
82 Test("Respects spec.prune", func(ctx f2.Context, t *testing.T) f2.Context {
83 k := ktest.FromContextT(ctx, t)
84
85
86 k.WaitOn(t, k.ObjsExist(children))
87
88 s = shipment(ctx, t, fmt.Sprintf("%s-2", id), []whv1.BaseArtifact{
89 {Digest: p1.digest, Name: p1.name},
90 {Digest: p2.digest, Name: p2.name},
91 }, whv1.WithPrune())
92 assert.NilError(t, k.Client.Create(ctx, s))
93 k.WaitOn(t, k.Check(s, shipmentReady))
94
95
96 for i, v := range []testPallet{p1, p2} {
97 u := &whv1.UnpackedPallet{}
98 assert.NilError(t, k.Client.Get(
99 ctx, types.NamespacedName{Name: s.ChildResourceName(v.name)}, u,
100 ), "%s never created by shipment", s.ChildResourceName(v.name))
101 children[i] = u
102 }
103
104 assert.NilError(t, k.Client.Delete(ctx, s))
105 k.WaitOn(t, k.ObjsDeleted(children), poll.WithTimeout(k.Timeout*2))
106
107 return ctx
108 }).Feature()
109
110 f.Test(t, fin)
111 }
112
113 func TestShipmentController_Capability(t *testing.T) {
114 var (
115 p1 testPallet
116 p2 testPallet
117 )
118
119 capability := f2.NewFeature("Runtime Capabilities").
120 Test("Capability not present in spec.pallets or spec.pallets's children", func(ctx f2.Context, t *testing.T) f2.Context {
121 k := ktest.FromContextT(ctx, t)
122
123 id := fmt.Sprintf("not-present-in-spec-pallets-%s", ctx.RunID)
124 p1 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-1", id), "v1",
125 createLayer(t, layer.Runtime, minimalv1))
126 p2 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v2",
127 createLayer(t, layer.Runtime, minimalv2))
128 s := shipment(ctx, t, id, []whv1.BaseArtifact{
129 {Digest: p1.digest, Name: p1.name},
130 {Digest: p2.digest, Name: p2.name},
131 }, whv1.WithCapabilities("linkerd"))
132
133 assert.NilError(t, k.Client.Create(ctx, s))
134 k.WaitOn(t, k.Check(s, shipmentStalled))
135
136 return ctx
137 }).Test("Capability present in spec.pallets", func(ctx f2.Context, t *testing.T) f2.Context {
138 k := ktest.FromContextT(ctx, t)
139
140 id := fmt.Sprintf("present-in-spec-pallets-%s", ctx.RunID)
141 l5did := fmt.Sprintf("%s-linkerd", id)
142 l5d := createAndPushPallet(ctx, t, l5did, "latest",
143 createLayer(t, layer.Runtime, linkerdCapablityProvider))
144 p1 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-1", id), "v1",
145 createLayer(t, layer.Runtime, minimalv1))
146 p2 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v1",
147 createLayer(t, layer.Runtime, meshConfig, layer.ForCapability(capability.Capability(l5did))))
148 s := shipment(ctx, t, id, []whv1.BaseArtifact{
149 {Digest: p1.digest, Name: p1.name},
150 {Digest: p2.digest, Name: p2.name},
151 {Digest: l5d.digest, Name: l5d.name},
152 }, whv1.WithCapabilities(l5did))
153
154 assert.NilError(t, k.Client.Create(ctx, s))
155 k.WaitOn(t, k.Check(s, shipmentReady))
156
157 return ctx
158 }).Test("Capability present in spec.pallets's children", func(ctx f2.Context, t *testing.T) f2.Context {
159 k := ktest.FromContextT(ctx, t)
160 reg := warehouse.FromContextT(ctx, t)
161
162 id := fmt.Sprintf("present-in-spec-pallets-children-%s", ctx.RunID)
163 l5did := fmt.Sprintf("%s-linkerd", id)
164 l5d := createAndPushPallet(ctx, t, l5did, "latest",
165 createLayer(t, layer.Runtime, linkerdCapablityProvider))
166
167 p1img := createAndPushPallet(ctx, t, fmt.Sprintf("%s-1", id), "v1",
168 createLayer(t, layer.Runtime, minimalv1))
169 p1, err := pallet.ImageIndex(pallet.Options{
170 Metadata: p1img.pallet.Metadata(),
171 Capabilities: p1img.pallet.Capabilities(),
172 ClusterProviders: cluster.Providers{cluster.Generic, cluster.GKE},
173 }, p1img.pallet, l5d.pallet)
174 assert.NilError(t, err)
175 assert.NilError(t, reg.Push(p1, p1.Name(), "v1"))
176
177 p2 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v1",
178 createLayer(t, layer.Runtime, minimalv2))
179
180 p1digest, err := p1.Digest()
181 assert.NilError(t, err)
182
183 s := shipment(ctx, t, id, []whv1.BaseArtifact{
184 {Digest: p1digest.String(), Name: p1.Name()},
185 {Digest: p2.digest, Name: p2.name},
186 }, whv1.WithCapabilities(l5did))
187
188 assert.NilError(t, k.Client.Create(ctx, s))
189 k.WaitOn(t, k.Check(s, shipmentReady))
190
191 return ctx
192 }).Feature()
193
194 f.Test(t, capability)
195 }
196
197 func TestShipmentController_PalletRepos(t *testing.T) {
198 feat := f2.NewFeature("Pallet Repositories").
199 Test("Pallets are fetched from a single repository", func(ctx f2.Context, t *testing.T) f2.Context {
200 k := ktest.FromContextT(ctx, t)
201
202
203 id := fmt.Sprintf("unpackedpallet-repo-%s", ctx.RunID)
204 p10 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-10", id), "v1",
205 createLayer(t, layer.Runtime, minimalv1))
206 p11 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-11", id), "v1",
207 createLayer(t, layer.Runtime, minimalv1))
208 p1 := createAndPushPalletWithDeps(ctx, t, fmt.Sprintf("%s-1", id), "", p10.pallet, p11.pallet)
209 p2 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v2",
210 createLayer(t, layer.Runtime, minimalv2))
211 p0 := createAndPushPalletWithDeps(ctx, t, id, "", p1.pallet, p2.pallet)
212
213
214 s := shipment(ctx, t, id, []whv1.BaseArtifact{{Digest: p0.digest, Name: p0.name}})
215 s.Labels = map[string]string{"test-id": id}
216 assert.NilError(t, k.Client.Create(ctx, s))
217 k.WaitOn(t, k.Check(s, shipmentReady))
218
219 req, err := labels.NewRequirement("test-id", selection.Equals, []string{id})
220 assert.NilError(t, err)
221
222
223 plts := &whv1.UnpackedPalletList{}
224 assert.NilError(t, k.Client.List(ctx, plts, &client.ListOptions{
225 LabelSelector: labels.NewSelector().Add(*req),
226 }))
227 assert.Assert(t, len(plts.Items) == 3, "expected 3 unpackedpallets, got %d", len(plts.Items))
228 sameRepo := plts.Items[0].Spec.Repository
229 for _, p := range plts.Items {
230 assert.Equal(t, sameRepo, p.Spec.Repository,
231 "expected Pallet's dependencies to be pulled from same repository",
232 )
233 }
234
235 return ctx
236 }).Feature()
237
238 f.Test(t, feat)
239 }
240
241
242 func TestShipmentController_Status(t *testing.T) {
243 fetchCondFailed := func(o client.Object) cmp.Result {
244 s := o.(*whv1.Shipment)
245 if conditions.HasReason(s, whv1.FetchedArtifactCondition, whv1.FetchFailedReason) {
246 return cmp.ResultSuccess
247 }
248 return cmp.ResultFailure(fmt.Sprintf("%s not stalled", object.FmtObject(s)))
249 }
250
251 hasResolvedArtifacts := func(pins []whv1.ResolvedArtifact) func(client.Object) cmp.Result {
252 return func(o client.Object) cmp.Result {
253 s := o.(*whv1.Shipment)
254 if !cmp.DeepEqual(s.Status.LastApplied, pins)().Success() {
255 return cmp.ResultFailure(fmt.Sprintf("lastApplied does not match. actual: %+v, expected: %+v", s.Status.LastApplied, pins))
256 }
257 if !cmp.DeepEqual(s.Status.LastAttempted, pins)().Success() {
258 return cmp.ResultFailure(fmt.Sprintf("lastAttempted does not match. actual: %+v, expected: %+v", s.Status.LastAttempted, pins))
259 }
260
261 return cmp.ResultSuccess
262 }
263 }
264
265 feat := f2.NewFeature("Shipment Status").
266 Test("FetchedArtifactCondition is set correctly for missing artifacts", func(ctx f2.Context, t *testing.T) f2.Context {
267 k := ktest.FromContextT(ctx, t)
268
269
270 id := fmt.Sprintf("testshipmentcontroller-status-%s", ctx.RunID)
271 s := shipment(ctx, t, id, []whv1.BaseArtifact{{Tag: "latest", Name: "does-not-exist"}})
272 s.Labels = map[string]string{"test-id": id}
273 assert.NilError(t, k.Client.Create(ctx, s))
274 k.WaitOn(t, k.Check(s, fetchCondFailed))
275
276 return ctx
277 }).
278 Test("Status contains information about composite pallet artifacts", func(ctx f2.Context, t *testing.T) f2.Context {
279 k := ktest.FromContextT(ctx, t)
280
281
282 id := fmt.Sprintf("pinned-composite-status-%s", ctx.RunID)
283 imgDep0 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-10", id), "v1",
284 createLayer(t, layer.Runtime, minimalv1))
285 imgDep1 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-11", id), "v1",
286 createLayer(t, layer.Runtime, minimalv1))
287 idxDep := createAndPushPalletWithDeps(ctx, t, fmt.Sprintf("%s-1", id), "", imgDep0.pallet, imgDep1.pallet)
288 imgDep := createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v2",
289 createLayer(t, layer.Runtime, minimalv2))
290 compIdx := createAndPushPalletWithDeps(ctx, t, id, "0.1.2", idxDep.pallet, imgDep.pallet)
291
292 artifacts := []whv1.ResolvedArtifact{
293 {Name: imgDep0.name, Digest: imgDep0.digest, ResolvedDigest: imgDep0.digest, Version: fixtureSemVer},
294 {Name: imgDep1.name, Digest: imgDep1.digest, ResolvedDigest: imgDep1.digest, Version: fixtureSemVer},
295 {Name: idxDep.name, Digest: idxDep.digest, ResolvedDigest: idxDep.digest, Version: fixtureSemVer},
296 {Name: imgDep.name, Digest: imgDep.digest, ResolvedDigest: imgDep.digest, Version: fixtureSemVer},
297 {Name: compIdx.name, Digest: compIdx.digest, ResolvedDigest: compIdx.digest, Version: fixtureSemVer},
298 }
299
300
301 s := shipment(ctx, t, id, []whv1.BaseArtifact{{Digest: compIdx.digest, Name: compIdx.name}})
302 s.Labels = map[string]string{"test-id": id}
303 assert.NilError(t, k.Client.Create(ctx, s))
304 k.WaitOn(t, k.Check(s, shipmentReady))
305 k.WaitOn(t, k.Check(s, hasResolvedArtifacts(artifacts)))
306
307 return ctx
308 }).
309 Test("Status contains version metadata about included pallets", func(ctx f2.Context, t *testing.T) f2.Context {
310 k := ktest.FromContextT(ctx, t)
311
312
313 id := fmt.Sprintf("version-meta-status-%s", ctx.RunID)
314 imgDep0 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-10", id), "v1",
315 createLayer(t, layer.Runtime, minimalv1))
316 imgDep1 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-11", id), "v1",
317 createLayer(t, layer.Runtime, minimalv1))
318 idxDep := createAndPushPalletWithDeps(ctx, t, fmt.Sprintf("%s-1", id), "", imgDep0.pallet, imgDep1.pallet)
319 imgDep := createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v2",
320 createLayer(t, layer.Runtime, minimalv2))
321 compIdx := createAndPushPalletWithDeps(ctx, t, id, "0.1.2", idxDep.pallet, imgDep.pallet)
322
323 artifacts := []whv1.ResolvedArtifact{
324 {Name: imgDep0.name, Digest: imgDep0.digest, ResolvedDigest: imgDep0.digest, Version: fixtureSemVer},
325 {Name: imgDep1.name, Digest: imgDep1.digest, ResolvedDigest: imgDep1.digest, Version: fixtureSemVer},
326 {Name: idxDep.name, Digest: idxDep.digest, ResolvedDigest: idxDep.digest, Version: fixtureSemVer},
327 {Name: imgDep.name, Digest: imgDep.digest, ResolvedDigest: imgDep.digest, Version: fixtureSemVer},
328 {Name: compIdx.name, Digest: compIdx.digest, ResolvedDigest: compIdx.digest, Version: fixtureSemVer},
329 }
330
331
332 s := shipment(ctx, t, id, []whv1.BaseArtifact{{Digest: compIdx.digest, Name: compIdx.name}})
333 s.Labels = map[string]string{"test-id": id}
334 assert.NilError(t, k.Client.Create(ctx, s))
335 k.WaitOn(t, k.Check(s, shipmentReady))
336 k.WaitOn(t, k.Check(s, hasResolvedArtifacts(artifacts)))
337
338 return ctx
339 }).Feature()
340
341 f.Test(t, feat)
342 }
343
View as plain text