// Copyright 2020 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 package e2e import ( "context" "fmt" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "sigs.k8s.io/cli-utils/pkg/apply" "sigs.k8s.io/cli-utils/pkg/apply/event" "sigs.k8s.io/cli-utils/pkg/inventory" "sigs.k8s.io/cli-utils/pkg/object" "sigs.k8s.io/cli-utils/pkg/testutil" "sigs.k8s.io/controller-runtime/pkg/client" ) func dependsOnTest(c client.Client, invConfig InventoryConfig, inventoryName, namespaceName string) { By("apply resources in order based on depends-on annotation") applier := invConfig.ApplierFactoryFunc() inv := invConfig.InvWrapperFunc(invConfig.InventoryFactoryFunc(inventoryName, namespaceName, "test")) pod1Obj := withDependsOn(withNamespace(manifestToUnstructured(pod1), namespaceName), fmt.Sprintf("/namespaces/%s/Pod/pod3", namespaceName)) pod2Obj := withNamespace(manifestToUnstructured(pod2), namespaceName) pod3Obj := withDependsOn(withNamespace(manifestToUnstructured(pod3), namespaceName), fmt.Sprintf("/namespaces/%s/Pod/pod2", namespaceName)) // Dependency order: pod1 -> pod3 -> pod2 // Apply order: pod2, pod3, pod1 resources := []*unstructured.Unstructured{ pod1Obj, pod2Obj, pod3Obj, } applierEvents := runCollect(applier.Run(context.TODO(), inv, resources, apply.Options{ EmitStatusEvents: false, })) expEvents := []testutil.ExpEvent{ { // InitTask EventType: event.InitType, InitEvent: &testutil.ExpInitEvent{}, }, { // InvAddTask start EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.InventoryAction, GroupName: "inventory-add-0", Type: event.Started, }, }, { // InvAddTask finished EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.InventoryAction, GroupName: "inventory-add-0", Type: event.Finished, }, }, { // ApplyTask start EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.ApplyAction, GroupName: "apply-0", Type: event.Started, }, }, { // Apply Pod2 first EventType: event.ApplyType, ApplyEvent: &testutil.ExpApplyEvent{ GroupName: "apply-0", Operation: event.Created, Identifier: object.UnstructuredToObjMetaOrDie(pod2Obj), Error: nil, }, }, { // ApplyTask finished EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.ApplyAction, GroupName: "apply-0", Type: event.Finished, }, }, { // WaitTask start EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.WaitAction, GroupName: "wait-0", Type: event.Started, }, }, { // Pod2 reconcile Pending. EventType: event.WaitType, WaitEvent: &testutil.ExpWaitEvent{ GroupName: "wait-0", Operation: event.ReconcilePending, Identifier: object.UnstructuredToObjMetaOrDie(pod2Obj), }, }, { // Pod2 confirmed Current. EventType: event.WaitType, WaitEvent: &testutil.ExpWaitEvent{ GroupName: "wait-0", Operation: event.Reconciled, Identifier: object.UnstructuredToObjMetaOrDie(pod2Obj), }, }, { // WaitTask finished EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.WaitAction, GroupName: "wait-0", Type: event.Finished, }, }, { // ApplyTask start EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.ApplyAction, GroupName: "apply-1", Type: event.Started, }, }, { // Apply Pod3 second EventType: event.ApplyType, ApplyEvent: &testutil.ExpApplyEvent{ GroupName: "apply-1", Operation: event.Created, Identifier: object.UnstructuredToObjMetaOrDie(pod3Obj), Error: nil, }, }, { // ApplyTask finished EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.ApplyAction, GroupName: "apply-1", Type: event.Finished, }, }, { // WaitTask start EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.WaitAction, GroupName: "wait-1", Type: event.Started, }, }, { // Pod3 reconcile Pending. EventType: event.WaitType, WaitEvent: &testutil.ExpWaitEvent{ GroupName: "wait-1", Operation: event.ReconcilePending, Identifier: object.UnstructuredToObjMetaOrDie(pod3Obj), }, }, { // Pod3 confirmed Current. EventType: event.WaitType, WaitEvent: &testutil.ExpWaitEvent{ GroupName: "wait-1", Operation: event.Reconciled, Identifier: object.UnstructuredToObjMetaOrDie(pod3Obj), }, }, { // WaitTask finished EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.WaitAction, GroupName: "wait-1", Type: event.Finished, }, }, { // ApplyTask start EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.ApplyAction, GroupName: "apply-2", Type: event.Started, }, }, { // Apply Pod1 third EventType: event.ApplyType, ApplyEvent: &testutil.ExpApplyEvent{ GroupName: "apply-2", Operation: event.Created, Identifier: object.UnstructuredToObjMetaOrDie(pod1Obj), Error: nil, }, }, { // ApplyTask finished EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.ApplyAction, GroupName: "apply-2", Type: event.Finished, }, }, { // WaitTask start EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.WaitAction, GroupName: "wait-2", Type: event.Started, }, }, { // Pod1 reconcile Pending. EventType: event.WaitType, WaitEvent: &testutil.ExpWaitEvent{ GroupName: "wait-2", Operation: event.ReconcilePending, Identifier: object.UnstructuredToObjMetaOrDie(pod1Obj), }, }, { // Pod1 confirmed Current. EventType: event.WaitType, WaitEvent: &testutil.ExpWaitEvent{ GroupName: "wait-2", Operation: event.Reconciled, Identifier: object.UnstructuredToObjMetaOrDie(pod1Obj), }, }, { // WaitTask finished EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.WaitAction, GroupName: "wait-2", Type: event.Finished, }, }, { // InvSetTask start EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.InventoryAction, GroupName: "inventory-set-0", Type: event.Started, }, }, { // InvSetTask finished EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.InventoryAction, GroupName: "inventory-set-0", Type: event.Finished, }, }, } Expect(testutil.EventsToExpEvents(applierEvents)).To(testutil.Equal(expEvents)) By("verify pod1 created and ready") result := assertUnstructuredExists(c, pod1Obj) podIP, found, err := testutil.NestedField(result.Object, "status", "podIP") Expect(err).NotTo(HaveOccurred()) Expect(found).To(BeTrue()) Expect(podIP).NotTo(BeEmpty()) // use podIP as proxy for readiness By("verify pod2 created and ready") result = assertUnstructuredExists(c, pod2Obj) podIP, found, err = testutil.NestedField(result.Object, "status", "podIP") Expect(err).NotTo(HaveOccurred()) Expect(found).To(BeTrue()) Expect(podIP).NotTo(BeEmpty()) // use podIP as proxy for readiness By("verify pod3 created and ready") result = assertUnstructuredExists(c, pod3Obj) podIP, found, err = testutil.NestedField(result.Object, "status", "podIP") Expect(err).NotTo(HaveOccurred()) Expect(found).To(BeTrue()) Expect(podIP).NotTo(BeEmpty()) // use podIP as proxy for readiness By("destroy resources in opposite order") destroyer := invConfig.DestroyerFactoryFunc() options := apply.DestroyerOptions{InventoryPolicy: inventory.AdoptIfNoInventory} destroyerEvents := runCollect(destroyer.Run(context.TODO(), inv, options)) expEvents = []testutil.ExpEvent{ { // InitTask EventType: event.InitType, InitEvent: &testutil.ExpInitEvent{}, }, { // PruneTask start EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.DeleteAction, GroupName: "prune-0", Type: event.Started, }, }, { // Delete pod1 first EventType: event.DeleteType, DeleteEvent: &testutil.ExpDeleteEvent{ GroupName: "prune-0", Operation: event.Deleted, Identifier: object.UnstructuredToObjMetaOrDie(pod1Obj), Error: nil, }, }, { // PruneTask finished EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.DeleteAction, GroupName: "prune-0", Type: event.Finished, }, }, { // WaitTask start EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.WaitAction, GroupName: "wait-0", Type: event.Started, }, }, { // Pod1 reconcile Pending. EventType: event.WaitType, WaitEvent: &testutil.ExpWaitEvent{ GroupName: "wait-0", Operation: event.ReconcilePending, Identifier: object.UnstructuredToObjMetaOrDie(pod1Obj), }, }, { // Pod1 confirmed NotFound. EventType: event.WaitType, WaitEvent: &testutil.ExpWaitEvent{ GroupName: "wait-0", Operation: event.Reconciled, Identifier: object.UnstructuredToObjMetaOrDie(pod1Obj), }, }, { // WaitTask finished EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.WaitAction, GroupName: "wait-0", Type: event.Finished, }, }, { // PruneTask start EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.DeleteAction, GroupName: "prune-1", Type: event.Started, }, }, { // Delete pod3 second EventType: event.DeleteType, DeleteEvent: &testutil.ExpDeleteEvent{ GroupName: "prune-1", Operation: event.Deleted, Identifier: object.UnstructuredToObjMetaOrDie(pod3Obj), Error: nil, }, }, { // PruneTask finished EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.DeleteAction, GroupName: "prune-1", Type: event.Finished, }, }, { // WaitTask start EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.WaitAction, GroupName: "wait-1", Type: event.Started, }, }, { // Pod3 reconcile Pending. EventType: event.WaitType, WaitEvent: &testutil.ExpWaitEvent{ GroupName: "wait-1", Operation: event.ReconcilePending, Identifier: object.UnstructuredToObjMetaOrDie(pod3Obj), }, }, { // Pod3 confirmed NotFound. EventType: event.WaitType, WaitEvent: &testutil.ExpWaitEvent{ GroupName: "wait-1", Operation: event.Reconciled, Identifier: object.UnstructuredToObjMetaOrDie(pod3Obj), }, }, { // WaitTask finished EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.WaitAction, GroupName: "wait-1", Type: event.Finished, }, }, { // PruneTask start EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.DeleteAction, GroupName: "prune-2", Type: event.Started, }, }, { // Delete pod2 third EventType: event.DeleteType, DeleteEvent: &testutil.ExpDeleteEvent{ GroupName: "prune-2", Operation: event.Deleted, Identifier: object.UnstructuredToObjMetaOrDie(pod2Obj), Error: nil, }, }, { // PruneTask finished EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.DeleteAction, GroupName: "prune-2", Type: event.Finished, }, }, { // WaitTask start EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.WaitAction, GroupName: "wait-2", Type: event.Started, }, }, { // Pod2 reconcile Pending. EventType: event.WaitType, WaitEvent: &testutil.ExpWaitEvent{ GroupName: "wait-2", Operation: event.ReconcilePending, Identifier: object.UnstructuredToObjMetaOrDie(pod2Obj), }, }, { // Pod2 confirmed NotFound. EventType: event.WaitType, WaitEvent: &testutil.ExpWaitEvent{ GroupName: "wait-2", Operation: event.Reconciled, Identifier: object.UnstructuredToObjMetaOrDie(pod2Obj), }, }, { // WaitTask finished EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.WaitAction, GroupName: "wait-2", Type: event.Finished, }, }, { // DeleteInvTask start EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.InventoryAction, GroupName: "delete-inventory-0", Type: event.Started, }, }, { // DeleteInvTask finished EventType: event.ActionGroupType, ActionGroupEvent: &testutil.ExpActionGroupEvent{ Action: event.InventoryAction, GroupName: "delete-inventory-0", Type: event.Finished, }, }, } Expect(testutil.EventsToExpEvents(destroyerEvents)).To(testutil.Equal(expEvents)) By("verify pod1 deleted") assertUnstructuredDoesNotExist(c, pod1Obj) By("verify pod2 deleted") assertUnstructuredDoesNotExist(c, pod2Obj) By("verify pod3 deleted") assertUnstructuredDoesNotExist(c, pod3Obj) }