package integration import ( "fmt" "testing" "github.com/davecgh/go-spew/spew" "gotest.tools/v3/assert" "gotest.tools/v3/assert/cmp" "gotest.tools/v3/poll" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/selection" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "edge-infra.dev/pkg/f8n/warehouse/capability" "edge-infra.dev/pkg/f8n/warehouse/cluster" whv1 "edge-infra.dev/pkg/f8n/warehouse/k8s/apis/v1alpha2" "edge-infra.dev/pkg/f8n/warehouse/oci/layer" "edge-infra.dev/pkg/f8n/warehouse/pallet" "edge-infra.dev/pkg/k8s/object" "edge-infra.dev/pkg/k8s/runtime/conditions" "edge-infra.dev/test/f2" "edge-infra.dev/test/f2/x/ktest" "edge-infra.dev/test/f2/x/warehouse" ) func TestShipmentController_Finalizers(t *testing.T) { var ( id string p1 testPallet p2 testPallet s = &whv1.Shipment{} children []client.Object ) fin := f2.NewFeature("Finalizers"). Setup("Create and push test packages", func(ctx f2.Context, t *testing.T) f2.Context { k := ktest.FromContextT(ctx, t) id = fmt.Sprintf("sfin-%s", ctx.RunID) p1 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-1", id), "v1", createLayer(t, layer.Runtime, minimalv1)) p2 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v1", createLayer(t, layer.Runtime, minimalv2)) s = shipment(ctx, t, id, []whv1.BaseArtifact{ {Digest: p1.digest, Name: p1.name}, {Digest: p2.digest, Name: p2.name}, }) assert.NilError(t, k.Client.Create(ctx, s)) k.WaitOn(t, k.Check(s, shipmentReady)) return ctx }). Test("Finalizer is added", func(ctx f2.Context, t *testing.T) f2.Context { if !controllerutil.ContainsFinalizer(s, whv1.WarehouseFinalizer) { t.Error("finalizer not added to unpackedpallet", spew.Sprintln(s)) } return ctx }). Test("Removes finalizer on delete", func(ctx f2.Context, t *testing.T) f2.Context { k := ktest.FromContextT(ctx, t) children = make([]client.Object, 2) for i, v := range []testPallet{p1, p2} { u := &whv1.UnpackedPallet{} assert.NilError(t, k.Client.Get( ctx, types.NamespacedName{Name: s.ChildResourceName(v.name)}, u, ), "%s never created by shipment", s.ChildResourceName(v.name)) children[i] = u } assert.NilError(t, k.Client.Delete(ctx, s)) // Object being deleted means that finalizer was successfully removed. k.WaitOn(t, k.ObjDeleted(s)) return ctx }). Test("Respects spec.prune", func(ctx f2.Context, t *testing.T) f2.Context { k := ktest.FromContextT(ctx, t) // Make sure objects weren't pruned previously k.WaitOn(t, k.ObjsExist(children)) s = shipment(ctx, t, fmt.Sprintf("%s-2", id), []whv1.BaseArtifact{ {Digest: p1.digest, Name: p1.name}, {Digest: p2.digest, Name: p2.name}, }, whv1.WithPrune()) assert.NilError(t, k.Client.Create(ctx, s)) k.WaitOn(t, k.Check(s, shipmentReady)) // Re-evaluate children with new name for i, v := range []testPallet{p1, p2} { u := &whv1.UnpackedPallet{} assert.NilError(t, k.Client.Get( ctx, types.NamespacedName{Name: s.ChildResourceName(v.name)}, u, ), "%s never created by shipment", s.ChildResourceName(v.name)) children[i] = u } assert.NilError(t, k.Client.Delete(ctx, s)) k.WaitOn(t, k.ObjsDeleted(children), poll.WithTimeout(k.Timeout*2)) return ctx }).Feature() f.Test(t, fin) } func TestShipmentController_Capability(t *testing.T) { var ( p1 testPallet p2 testPallet ) capability := f2.NewFeature("Runtime Capabilities"). Test("Capability not present in spec.pallets or spec.pallets's children", func(ctx f2.Context, t *testing.T) f2.Context { //nolint:dupl k := ktest.FromContextT(ctx, t) id := fmt.Sprintf("not-present-in-spec-pallets-%s", ctx.RunID) p1 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-1", id), "v1", createLayer(t, layer.Runtime, minimalv1)) p2 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v2", createLayer(t, layer.Runtime, minimalv2)) s := shipment(ctx, t, id, []whv1.BaseArtifact{ {Digest: p1.digest, Name: p1.name}, {Digest: p2.digest, Name: p2.name}, }, whv1.WithCapabilities("linkerd")) assert.NilError(t, k.Client.Create(ctx, s)) k.WaitOn(t, k.Check(s, shipmentStalled)) return ctx }).Test("Capability present in spec.pallets", func(ctx f2.Context, t *testing.T) f2.Context { //nolint:dupl k := ktest.FromContextT(ctx, t) id := fmt.Sprintf("present-in-spec-pallets-%s", ctx.RunID) l5did := fmt.Sprintf("%s-linkerd", id) l5d := createAndPushPallet(ctx, t, l5did, "latest", createLayer(t, layer.Runtime, linkerdCapablityProvider)) p1 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-1", id), "v1", createLayer(t, layer.Runtime, minimalv1)) p2 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v1", createLayer(t, layer.Runtime, meshConfig, layer.ForCapability(capability.Capability(l5did)))) s := shipment(ctx, t, id, []whv1.BaseArtifact{ {Digest: p1.digest, Name: p1.name}, {Digest: p2.digest, Name: p2.name}, {Digest: l5d.digest, Name: l5d.name}, }, whv1.WithCapabilities(l5did)) assert.NilError(t, k.Client.Create(ctx, s)) k.WaitOn(t, k.Check(s, shipmentReady)) return ctx }).Test("Capability present in spec.pallets's children", func(ctx f2.Context, t *testing.T) f2.Context { //nolint:dupl k := ktest.FromContextT(ctx, t) reg := warehouse.FromContextT(ctx, t) id := fmt.Sprintf("present-in-spec-pallets-children-%s", ctx.RunID) l5did := fmt.Sprintf("%s-linkerd", id) l5d := createAndPushPallet(ctx, t, l5did, "latest", createLayer(t, layer.Runtime, linkerdCapablityProvider)) p1img := createAndPushPallet(ctx, t, fmt.Sprintf("%s-1", id), "v1", createLayer(t, layer.Runtime, minimalv1)) p1, err := pallet.ImageIndex(pallet.Options{ Metadata: p1img.pallet.Metadata(), Capabilities: p1img.pallet.Capabilities(), ClusterProviders: cluster.Providers{cluster.Generic, cluster.GKE}, }, p1img.pallet, l5d.pallet) assert.NilError(t, err) assert.NilError(t, reg.Push(p1, p1.Name(), "v1")) p2 = createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v1", createLayer(t, layer.Runtime, minimalv2)) p1digest, err := p1.Digest() assert.NilError(t, err) s := shipment(ctx, t, id, []whv1.BaseArtifact{ {Digest: p1digest.String(), Name: p1.Name()}, {Digest: p2.digest, Name: p2.name}, }, whv1.WithCapabilities(l5did)) assert.NilError(t, k.Client.Create(ctx, s)) k.WaitOn(t, k.Check(s, shipmentReady)) return ctx }).Feature() f.Test(t, capability) } func TestShipmentController_PalletRepos(t *testing.T) { feat := f2.NewFeature("Pallet Repositories"). Test("Pallets are fetched from a single repository", func(ctx f2.Context, t *testing.T) f2.Context { k := ktest.FromContextT(ctx, t) // create somes pallets and include them as deps id := fmt.Sprintf("unpackedpallet-repo-%s", ctx.RunID) p10 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-10", id), "v1", createLayer(t, layer.Runtime, minimalv1)) p11 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-11", id), "v1", createLayer(t, layer.Runtime, minimalv1)) p1 := createAndPushPalletWithDeps(ctx, t, fmt.Sprintf("%s-1", id), "", p10.pallet, p11.pallet) p2 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v2", createLayer(t, layer.Runtime, minimalv2)) p0 := createAndPushPalletWithDeps(ctx, t, id, "", p1.pallet, p2.pallet) // shipment with single pallet s := shipment(ctx, t, id, []whv1.BaseArtifact{{Digest: p0.digest, Name: p0.name}}) s.Labels = map[string]string{"test-id": id} assert.NilError(t, k.Client.Create(ctx, s)) k.WaitOn(t, k.Check(s, shipmentReady)) req, err := labels.NewRequirement("test-id", selection.Equals, []string{id}) assert.NilError(t, err) // TODO(aw185176): Should be generic helper plts := &whv1.UnpackedPalletList{} assert.NilError(t, k.Client.List(ctx, plts, &client.ListOptions{ LabelSelector: labels.NewSelector().Add(*req), })) assert.Assert(t, len(plts.Items) == 3, "expected 3 unpackedpallets, got %d", len(plts.Items)) sameRepo := plts.Items[0].Spec.Repository for _, p := range plts.Items { assert.Equal(t, sameRepo, p.Spec.Repository, "expected Pallet's dependencies to be pulled from same repository", ) } return ctx }).Feature() f.Test(t, feat) } // nolint:dupl func TestShipmentController_Status(t *testing.T) { fetchCondFailed := func(o client.Object) cmp.Result { s := o.(*whv1.Shipment) if conditions.HasReason(s, whv1.FetchedArtifactCondition, whv1.FetchFailedReason) { return cmp.ResultSuccess } return cmp.ResultFailure(fmt.Sprintf("%s not stalled", object.FmtObject(s))) } hasResolvedArtifacts := func(pins []whv1.ResolvedArtifact) func(client.Object) cmp.Result { return func(o client.Object) cmp.Result { s := o.(*whv1.Shipment) if !cmp.DeepEqual(s.Status.LastApplied, pins)().Success() { return cmp.ResultFailure(fmt.Sprintf("lastApplied does not match. actual: %+v, expected: %+v", s.Status.LastApplied, pins)) } if !cmp.DeepEqual(s.Status.LastAttempted, pins)().Success() { return cmp.ResultFailure(fmt.Sprintf("lastAttempted does not match. actual: %+v, expected: %+v", s.Status.LastAttempted, pins)) } return cmp.ResultSuccess } } feat := f2.NewFeature("Shipment Status"). Test("FetchedArtifactCondition is set correctly for missing artifacts", func(ctx f2.Context, t *testing.T) f2.Context { k := ktest.FromContextT(ctx, t) // Shipment controller should report correct status condition for failed fetch id := fmt.Sprintf("testshipmentcontroller-status-%s", ctx.RunID) s := shipment(ctx, t, id, []whv1.BaseArtifact{{Tag: "latest", Name: "does-not-exist"}}) s.Labels = map[string]string{"test-id": id} assert.NilError(t, k.Client.Create(ctx, s)) k.WaitOn(t, k.Check(s, fetchCondFailed)) return ctx }). Test("Status contains information about composite pallet artifacts", func(ctx f2.Context, t *testing.T) f2.Context { k := ktest.FromContextT(ctx, t) // create somes pallets and include them as deps id := fmt.Sprintf("pinned-composite-status-%s", ctx.RunID) imgDep0 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-10", id), "v1", createLayer(t, layer.Runtime, minimalv1)) imgDep1 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-11", id), "v1", createLayer(t, layer.Runtime, minimalv1)) idxDep := createAndPushPalletWithDeps(ctx, t, fmt.Sprintf("%s-1", id), "", imgDep0.pallet, imgDep1.pallet) imgDep := createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v2", createLayer(t, layer.Runtime, minimalv2)) compIdx := createAndPushPalletWithDeps(ctx, t, id, "0.1.2", idxDep.pallet, imgDep.pallet) artifacts := []whv1.ResolvedArtifact{ {Name: imgDep0.name, Digest: imgDep0.digest, ResolvedDigest: imgDep0.digest, Version: fixtureSemVer}, {Name: imgDep1.name, Digest: imgDep1.digest, ResolvedDigest: imgDep1.digest, Version: fixtureSemVer}, {Name: idxDep.name, Digest: idxDep.digest, ResolvedDigest: idxDep.digest, Version: fixtureSemVer}, {Name: imgDep.name, Digest: imgDep.digest, ResolvedDigest: imgDep.digest, Version: fixtureSemVer}, {Name: compIdx.name, Digest: compIdx.digest, ResolvedDigest: compIdx.digest, Version: fixtureSemVer}, } // Shipment status should show digests for composite pallets s := shipment(ctx, t, id, []whv1.BaseArtifact{{Digest: compIdx.digest, Name: compIdx.name}}) s.Labels = map[string]string{"test-id": id} assert.NilError(t, k.Client.Create(ctx, s)) k.WaitOn(t, k.Check(s, shipmentReady)) k.WaitOn(t, k.Check(s, hasResolvedArtifacts(artifacts))) return ctx }). Test("Status contains version metadata about included pallets", func(ctx f2.Context, t *testing.T) f2.Context { k := ktest.FromContextT(ctx, t) // create somes pallets and include them as deps id := fmt.Sprintf("version-meta-status-%s", ctx.RunID) imgDep0 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-10", id), "v1", createLayer(t, layer.Runtime, minimalv1)) imgDep1 := createAndPushPallet(ctx, t, fmt.Sprintf("%s-11", id), "v1", createLayer(t, layer.Runtime, minimalv1)) idxDep := createAndPushPalletWithDeps(ctx, t, fmt.Sprintf("%s-1", id), "", imgDep0.pallet, imgDep1.pallet) imgDep := createAndPushPallet(ctx, t, fmt.Sprintf("%s-2", id), "v2", createLayer(t, layer.Runtime, minimalv2)) compIdx := createAndPushPalletWithDeps(ctx, t, id, "0.1.2", idxDep.pallet, imgDep.pallet) artifacts := []whv1.ResolvedArtifact{ {Name: imgDep0.name, Digest: imgDep0.digest, ResolvedDigest: imgDep0.digest, Version: fixtureSemVer}, {Name: imgDep1.name, Digest: imgDep1.digest, ResolvedDigest: imgDep1.digest, Version: fixtureSemVer}, {Name: idxDep.name, Digest: idxDep.digest, ResolvedDigest: idxDep.digest, Version: fixtureSemVer}, {Name: imgDep.name, Digest: imgDep.digest, ResolvedDigest: imgDep.digest, Version: fixtureSemVer}, {Name: compIdx.name, Digest: compIdx.digest, ResolvedDigest: compIdx.digest, Version: fixtureSemVer}, } // Shipment status should show digests for composite pallets s := shipment(ctx, t, id, []whv1.BaseArtifact{{Digest: compIdx.digest, Name: compIdx.name}}) s.Labels = map[string]string{"test-id": id} assert.NilError(t, k.Client.Create(ctx, s)) k.WaitOn(t, k.Check(s, shipmentReady)) k.WaitOn(t, k.Check(s, hasResolvedArtifacts(artifacts))) return ctx }).Feature() f.Test(t, feat) }