From 97604555d62ce81d9ef8cb8c4d39b700d98900a7 Mon Sep 17 00:00:00 2001 From: Isteb4k Date: Tue, 20 May 2025 21:18:13 +0200 Subject: [PATCH 1/3] chore: refactore the e2e tests to run in parallel Signed-off-by: Isteb4k --- tests/e2e/Taskfile.yaml | 21 ++ tests/e2e/affinity_toleration_test.go | 335 ++++++++--------- tests/e2e/complex_test.go | 176 ++++----- tests/e2e/config/config.go | 9 +- tests/e2e/image_hotplug_test.go | 237 ++++++------ tests/e2e/images_creation_test.go | 21 +- tests/e2e/importer_network_policy_test.go | 11 +- tests/e2e/sizing_policy_test.go | 107 +++--- .../affinity-toleration/vm/base/vm.yaml | 2 +- .../e2e/testdata/affinity-toleration/vmc.yaml | 2 +- .../overlays/vm-a-not-b/vm.vmclass.patch.yaml | 2 +- .../overlays/vm-b-not-a/vm.vmclass.patch.yaml | 2 +- .../overlays/vm-c-and-a/vm.vmclass.patch.yaml | 2 +- tests/e2e/testdata/complex-test/vmc.yaml | 2 +- .../existing-vmclass/kustomization.yaml | 2 +- tests/e2e/testdata/sizing-policy/vmc.yaml | 2 +- tests/e2e/tests_suite_test.go | 216 ++++++----- tests/e2e/util_test.go | 64 ++-- tests/e2e/vd_snapshots_test.go | 183 +++++----- tests/e2e/vm_configuration_test.go | 211 +++++------ tests/e2e/vm_connectivity_test.go | 187 +++++----- tests/e2e/vm_disk_attachment_test.go | 183 +++++----- tests/e2e/vm_disk_resizing_test.go | 337 +++++++++--------- tests/e2e/vm_evacuation_test.go | 17 +- tests/e2e/vm_label_annotation_test.go | 185 +++++----- tests/e2e/vm_migration_cancel_test.go | 25 +- tests/e2e/vm_migration_test.go | 38 +- tests/e2e/vm_version_test.go | 15 +- 28 files changed, 1307 insertions(+), 1287 deletions(-) diff --git a/tests/e2e/Taskfile.yaml b/tests/e2e/Taskfile.yaml index 4ec3de1daa..9c36d738b3 100644 --- a/tests/e2e/Taskfile.yaml +++ b/tests/e2e/Taskfile.yaml @@ -91,6 +91,27 @@ tasks: echo "EOF" >> $GITHUB_ENV exit $EXIT_CODE' + runp: + desc: "Run e2e tests" + deps: + - copy + - ginkgo + - kubectl + - d8 + cmds: + - | + ginkgo -v \ + -p --procs=12 \ + --race \ + {{if .TIMEOUT -}} + --timeout={{ .TIMEOUT }} \ + {{else -}} + --timeout=2h \ + {{end -}} + {{if .FOCUS -}} + --focus "{{ .FOCUS }}" + {{end -}} + run: desc: "Run e2e tests" deps: diff --git a/tests/e2e/affinity_toleration_test.go b/tests/e2e/affinity_toleration_test.go index e69b3dbe01..cc4098094c 100644 --- a/tests/e2e/affinity_toleration_test.go +++ b/tests/e2e/affinity_toleration_test.go @@ -35,142 +35,7 @@ import ( kc "github.com/deckhouse/virtualization/tests/e2e/kubectl" ) -func ExpectVirtualMachineIsMigratable(vmObj *virtv2.VirtualMachine) { - GinkgoHelper() - for _, c := range vmObj.Status.Conditions { - if c.Type == string(vmcondition.TypeMigratable) { - Expect(c.Status).Should(Equal(metav1.ConditionTrue), - "the `VirtualMachine` %s should be %q", - vmObj.Name, - vmcondition.TypeMigratable, - ) - } - } -} - -func DefineTargetNode(sourceNode string, targetLabel map[string]string) (string, error) { - nodes := &corev1.NodeList{} - err := GetObjects(kc.ResourceNode, nodes, kc.GetOptions{ - Labels: targetLabel, - }) - if err != nil { - return "", err - } - for _, n := range nodes.Items { - if n.Name != sourceNode { - for _, c := range n.Status.Conditions { - if c.Type == corev1.NodeReady && c.Status == corev1.ConditionTrue { - return n.Name, nil - } - } - } - } - return "", fmt.Errorf("failed to define a target node") -} - -func GetVirtualMachineObjByLabel(namespace string, label map[string]string) (*virtv2.VirtualMachine, error) { - vmObjects := virtv2.VirtualMachineList{} - err := GetObjects(kc.ResourceVM, &vmObjects, kc.GetOptions{ - Labels: label, - Namespace: namespace, - }) - if len(vmObjects.Items) != 1 { - return nil, fmt.Errorf("there is only one `VirtualMachine` with the %q label in this case", label) - } - if err != nil { - return nil, fmt.Errorf("failed to obtain the %q `VirtualMachine`", label) - } - return &vmObjects.Items[0], nil -} - -func GenerateNodeAffinityPatch(key string, operator corev1.NodeSelectorOperator, values []string) ([]byte, error) { - vmAffinity := &virtv2.VMAffinity{ - NodeAffinity: &corev1.NodeAffinity{ - RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{ - NodeSelectorTerms: []corev1.NodeSelectorTerm{ - { - MatchExpressions: []corev1.NodeSelectorRequirement{ - { - Key: key, - Operator: operator, - Values: values, - }, - }, - }, - }, - }, - }, - } - - b, err := json.Marshal(vmAffinity) - if err != nil { - return nil, err - } - return b, nil -} - -func GenerateVirtualMachineAndPodAntiAffinityPatch(key, topologyKey string, operator metav1.LabelSelectorOperator, values []string) ([]byte, error) { - vmAndPodAntiAffinity := &virtv2.VirtualMachineAndPodAntiAffinity{ - RequiredDuringSchedulingIgnoredDuringExecution: []virtv2.VirtualMachineAndPodAffinityTerm{ - { - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: key, - Operator: operator, - Values: values, - }, - }, - }, - TopologyKey: topologyKey, - }, - }, - } - - b, err := json.Marshal(vmAndPodAntiAffinity) - if err != nil { - return nil, err - } - return b, nil -} - -func GenerateVirtualMachineAndPodAffinityPatch(key, topologyKey string, operator metav1.LabelSelectorOperator, values []string) ([]byte, error) { - vmAndPodAffinity := &virtv2.VirtualMachineAndPodAffinity{ - RequiredDuringSchedulingIgnoredDuringExecution: []virtv2.VirtualMachineAndPodAffinityTerm{ - { - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: key, - Operator: operator, - Values: values, - }, - }, - }, - TopologyKey: topologyKey, - }, - }, - } - - b, err := json.Marshal(vmAndPodAffinity) - if err != nil { - return nil, err - } - return b, nil -} - var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2ETestDecorators(), func() { - BeforeAll(func() { - if config.IsReusable() { - Skip("Test not available in REUSABLE mode: not supported yet.") - } - - kustomization := fmt.Sprintf("%s/%s", conf.TestData.AffinityToleration, "kustomization.yaml") - ns, err := kustomize.GetNamespace(kustomization) - Expect(err).NotTo(HaveOccurred(), "%w", err) - conf.SetNamespace(ns) - }) - const ( nodeLabelKey = "kubernetes.io/hostname" masterLabelKey = "node.deckhouse.io/group" @@ -188,8 +53,20 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E vmNodeAffinity = map[string]string{"vm": "vm-node-affinity"} workerNodeLabel = map[string]string{"node.deckhouse.io/group": "worker"} masterNodeLabel = map[string]string{"node.deckhouse.io/group": "master"} + ns string ) + BeforeAll(func() { + if config.IsReusable() { + Skip("Test not available in REUSABLE mode: not supported yet.") + } + + kustomization := fmt.Sprintf("%s/%s", conf.TestData.AffinityToleration, "kustomization.yaml") + var err error + ns, err = kustomize.GetNamespace(kustomization) + Expect(err).NotTo(HaveOccurred(), "%w", err) + }) + AfterEach(func() { if CurrentSpecReport().Failed() { SaveTestResources(testCaseLabel, CurrentSpecReport().LeafNodeText) @@ -209,28 +86,28 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E By(fmt.Sprintf("`VirtualImages` should be in the %q phase", virtv2.ImageReady), func() { WaitPhaseByLabel(kc.ResourceVI, PhaseReady, kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) By(fmt.Sprintf("`VirtualMachineClasses` should be in %s phases", virtv2.ClassPhaseReady), func() { WaitPhaseByLabel(kc.ResourceVMClass, PhaseReady, kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) By(fmt.Sprintf("`VirtualDisks` should be in the %q phase", virtv2.DiskReady), func() { WaitPhaseByLabel(kc.ResourceVD, PhaseReady, kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) By("`VirtualMachines` agents should be ready", func() { WaitVMAgentReady(kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -247,13 +124,13 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E err error ) By("Obtain the `VirtualMachine` objects", func() { - vmObjA, err = GetVirtualMachineObjByLabel(conf.Namespace, vmA) + vmObjA, err = GetVirtualMachineObjByLabel(ns, vmA) Expect(err).NotTo(HaveOccurred(), "failed to obtain the %q `VirtualMachine`", vmA) - vmObjB, err = GetVirtualMachineObjByLabel(conf.Namespace, vmB) + vmObjB, err = GetVirtualMachineObjByLabel(ns, vmB) Expect(err).NotTo(HaveOccurred(), "failed to obtain the %q `VirtualMachine`", vmB) - vmObjC, err = GetVirtualMachineObjByLabel(conf.Namespace, vmC) + vmObjC, err = GetVirtualMachineObjByLabel(ns, vmC) Expect(err).NotTo(HaveOccurred(), "failed to obtain the %q `VirtualMachine`", vmC) - vmObjD, err = GetVirtualMachineObjByLabel(conf.Namespace, vmD) + vmObjD, err = GetVirtualMachineObjByLabel(ns, vmD) Expect(err).NotTo(HaveOccurred(), "failed to obtain the %q `VirtualMachine`", vmD) }) By("Set affinity when creating the `VirtualMachines`.`: `vm-a` and `vm-c` should be running on the same node", func() { @@ -290,7 +167,7 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E Eventually(func() error { updatedVMObjC := &virtv2.VirtualMachine{} err := GetObject(virtv2.VirtualMachineResource, vmObjC.Name, updatedVMObjC, kc.GetOptions{ - Namespace: conf.Namespace, + Namespace: ns, }) if err != nil { return err @@ -306,19 +183,19 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E jsonPatchAdd, jsonPatchRemove, }, - Namespace: conf.Namespace, + Namespace: ns, }) Expect(res.Error()).NotTo(HaveOccurred(), "failed to patch the %q `VirtualMachine`", vmC) wg.Wait() WaitVMAgentReady(kc.WaitOptions{ Labels: vmC, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) updatedVMObjC := &virtv2.VirtualMachine{} err = GetObject(virtv2.VirtualMachineResource, vmObjC.Name, updatedVMObjC, kc.GetOptions{ - Namespace: conf.Namespace, + Namespace: ns, }) Expect(err).NotTo(HaveOccurred(), "failed to obtain the %q `VirtualMachine` object", vmC) Expect(updatedVMObjC.Status.MigrationState.Source.Node).Should(Equal(vmObjC.Status.Node)) @@ -330,7 +207,7 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E updatedVMObjC := &virtv2.VirtualMachine{} err = GetObject(virtv2.VirtualMachineResource, vmObjC.Name, updatedVMObjC, kc.GetOptions{ - Namespace: conf.Namespace, + Namespace: ns, }) ExpectVirtualMachineIsMigratable(updatedVMObjC) @@ -352,7 +229,7 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E jsonPatchAdd, jsonPatchRemove, }, - Namespace: conf.Namespace, + Namespace: ns, }) Expect(res.Error()).NotTo(HaveOccurred(), "failed to patch the %q `VirtualMachine`", vmC) @@ -363,7 +240,7 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E Eventually(func() error { updatedVMObjC = &virtv2.VirtualMachine{} err = GetObject(virtv2.VirtualMachineResource, vmObjC.Name, updatedVMObjC, kc.GetOptions{ - Namespace: conf.Namespace, + Namespace: ns, }) if err != nil { return err @@ -393,7 +270,7 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E WaitVMAgentReady(kc.WaitOptions{ Labels: vmC, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -409,20 +286,20 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E ) vmObj := &virtv2.VirtualMachine{} By("Sets the `spec.nodeSelector` with the `status.nodeSelector` value", func() { - vmObj, err = GetVirtualMachineObjByLabel(conf.Namespace, vmNodeSelector) + vmObj, err = GetVirtualMachineObjByLabel(ns, vmNodeSelector) Expect(err).NotTo(HaveOccurred(), "failed to obtain the %q `VirtualMachine` object", vmNodeSelector) ExpectVirtualMachineIsMigratable(vmObj) sourceNode = vmObj.Status.Node Expect(sourceNode).ShouldNot(BeEmpty(), "the `vm.status.nodeName` should have a value") mergePatch := fmt.Sprintf(`{"spec":{"nodeSelector":{%q:%q}}}`, nodeLabelKey, sourceNode) - err = MergePatchResource(kc.ResourceVM, vmObj.Name, mergePatch) + err = MergePatchResource(kc.ResourceVM, ns, vmObj.Name, mergePatch) Expect(err).NotTo(HaveOccurred(), "failed to patch the %q `VirtualMachine`", vmNodeSelector) }) By("The `VirtualMachine` should not be migrated", func() { time.Sleep(20 * time.Second) updatedVMObj := &virtv2.VirtualMachine{} err := GetObject(virtv2.VirtualMachineResource, vmObj.Name, updatedVMObj, kc.GetOptions{ - Namespace: conf.Namespace, + Namespace: ns, }) Expect(err).NotTo(HaveOccurred(), "failed to obtain the %q `VirtualMachine` object", vmNodeSelector) for _, c := range updatedVMObj.Status.Conditions { @@ -438,7 +315,7 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E updatedVMObj := &virtv2.VirtualMachine{} err := GetObject(virtv2.VirtualMachineResource, vmObj.Name, updatedVMObj, kc.GetOptions{ - Namespace: conf.Namespace, + Namespace: ns, }) Expect(err).NotTo(HaveOccurred(), "failed to obtain the %q `VirtualMachine` object", vmNodeSelector) Expect(updatedVMObj.Status.MigrationState).Should(BeNil()) @@ -455,7 +332,7 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E Eventually(func() error { updatedVMObj := &virtv2.VirtualMachine{} err := GetObject(virtv2.VirtualMachineResource, vmObj.Name, updatedVMObj, kc.GetOptions{ - Namespace: conf.Namespace, + Namespace: ns, }) if err != nil { return err @@ -467,19 +344,19 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E }).WithTimeout(Timeout).WithPolling(migratingStatusPollingInterval).Should(Succeed()) }() mergePatch := fmt.Sprintf(`{"spec":{"nodeSelector":{%q:%q}}}`, nodeLabelKey, targetNode) - err = MergePatchResource(kc.ResourceVM, vmObj.Name, mergePatch) + err = MergePatchResource(kc.ResourceVM, ns, vmObj.Name, mergePatch) Expect(err).NotTo(HaveOccurred(), "failed to patch the %q `VirtualMachine`", vmNodeSelector) wg.Wait() }) By("The `VirtualMachine` should be migrated", func() { WaitVMAgentReady(kc.WaitOptions{ Labels: vmNodeSelector, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) updatedVMObj := &virtv2.VirtualMachine{} err := GetObject(virtv2.VirtualMachineResource, vmObj.Name, updatedVMObj, kc.GetOptions{ - Namespace: conf.Namespace, + Namespace: ns, }) Expect(err).NotTo(HaveOccurred(), "failed to obtain the %q `VirtualMachine` object", vmNodeSelector) Expect(updatedVMObj.Status.MigrationState.Source.Node).Should(Equal(sourceNode)) @@ -498,7 +375,7 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E ) vmObj := &virtv2.VirtualMachine{} By("Sets the `spec.affinity.nodeAffinity` with the `status.nodeSelector` value", func() { - vmObj, err = GetVirtualMachineObjByLabel(conf.Namespace, vmNodeAffinity) + vmObj, err = GetVirtualMachineObjByLabel(ns, vmNodeAffinity) Expect(err).NotTo(HaveOccurred()) ExpectVirtualMachineIsMigratable(vmObj) sourceNode = vmObj.Status.Node @@ -507,14 +384,14 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E p, err := GenerateNodeAffinityPatch(nodeLabelKey, corev1.NodeSelectorOpIn, []string{sourceNode}) Expect(err).NotTo(HaveOccurred()) mergePatch := fmt.Sprintf(`{"spec":{"affinity":%s}}`, p) - err = MergePatchResource(kc.ResourceVM, vmObj.Name, mergePatch) + err = MergePatchResource(kc.ResourceVM, ns, vmObj.Name, mergePatch) Expect(err).NotTo(HaveOccurred(), "failed to patch the %q `VirtualMachine`", vmNodeAffinity) }) By("The `VirtualMachine` should not be migrated", func() { time.Sleep(20 * time.Second) updatedVMObj := &virtv2.VirtualMachine{} err := GetObject(virtv2.VirtualMachineResource, vmObj.Name, updatedVMObj, kc.GetOptions{ - Namespace: conf.Namespace, + Namespace: ns, }) Expect(err).NotTo(HaveOccurred(), "failed to obtain the %q `VirtualMachine` object", vmNodeAffinity) for _, c := range updatedVMObj.Status.Conditions { @@ -530,7 +407,7 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E updatedVMObj := &virtv2.VirtualMachine{} err := GetObject(virtv2.VirtualMachineResource, vmObj.Name, updatedVMObj, kc.GetOptions{ - Namespace: conf.Namespace, + Namespace: ns, }) Expect(err).NotTo(HaveOccurred(), "failed to obtain the %q `VirtualMachine` object", vmNodeAffinity) Expect(updatedVMObj.Status.MigrationState).Should(BeNil()) @@ -550,7 +427,7 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E Eventually(func() error { updatedVMObj := &virtv2.VirtualMachine{} err := GetObject(virtv2.VirtualMachineResource, vmObj.Name, updatedVMObj, kc.GetOptions{ - Namespace: conf.Namespace, + Namespace: ns, }) if err != nil { return err @@ -562,19 +439,19 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E }).WithTimeout(Timeout).WithPolling(migratingStatusPollingInterval).Should(Succeed()) }() mergePatch := fmt.Sprintf(`{"spec":{"affinity":%s}}`, p) - err = MergePatchResource(kc.ResourceVM, vmObj.Name, mergePatch) + err = MergePatchResource(kc.ResourceVM, ns, vmObj.Name, mergePatch) Expect(err).NotTo(HaveOccurred(), "failed to patch the %q `VirtualMachine`", vmNodeAffinity) wg.Wait() }) By("The `VirtualMachine` should be migrated", func() { WaitVMAgentReady(kc.WaitOptions{ Labels: vmNodeAffinity, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) updatedVMObj := &virtv2.VirtualMachine{} err := GetObject(virtv2.VirtualMachineResource, vmObj.Name, updatedVMObj, kc.GetOptions{ - Namespace: conf.Namespace, + Namespace: ns, }) Expect(err).NotTo(HaveOccurred(), "failed to obtain the %q `VirtualMachine` object", vmNodeAffinity) Expect(updatedVMObj.Status.MigrationState.Source.Node).Should(Equal(sourceNode)) @@ -590,3 +467,127 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E }) }) }) + +func ExpectVirtualMachineIsMigratable(vmObj *virtv2.VirtualMachine) { + GinkgoHelper() + for _, c := range vmObj.Status.Conditions { + if c.Type == string(vmcondition.TypeMigratable) { + Expect(c.Status).Should(Equal(metav1.ConditionTrue), + "the `VirtualMachine` %s should be %q", + vmObj.Name, + vmcondition.TypeMigratable, + ) + } + } +} + +func DefineTargetNode(sourceNode string, targetLabel map[string]string) (string, error) { + nodes := &corev1.NodeList{} + err := GetObjects(kc.ResourceNode, nodes, kc.GetOptions{ + Labels: targetLabel, + }) + if err != nil { + return "", err + } + for _, n := range nodes.Items { + if n.Name != sourceNode { + for _, c := range n.Status.Conditions { + if c.Type == corev1.NodeReady && c.Status == corev1.ConditionTrue { + return n.Name, nil + } + } + } + } + return "", fmt.Errorf("failed to define a target node") +} + +func GetVirtualMachineObjByLabel(namespace string, label map[string]string) (*virtv2.VirtualMachine, error) { + vmObjects := virtv2.VirtualMachineList{} + err := GetObjects(kc.ResourceVM, &vmObjects, kc.GetOptions{ + Labels: label, + Namespace: namespace, + }) + if len(vmObjects.Items) != 1 { + return nil, fmt.Errorf("there is only one `VirtualMachine` with the %q label in this case", label) + } + if err != nil { + return nil, fmt.Errorf("failed to obtain the %q `VirtualMachine`", label) + } + return &vmObjects.Items[0], nil +} + +func GenerateNodeAffinityPatch(key string, operator corev1.NodeSelectorOperator, values []string) ([]byte, error) { + vmAffinity := &virtv2.VMAffinity{ + NodeAffinity: &corev1.NodeAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{ + NodeSelectorTerms: []corev1.NodeSelectorTerm{ + { + MatchExpressions: []corev1.NodeSelectorRequirement{ + { + Key: key, + Operator: operator, + Values: values, + }, + }, + }, + }, + }, + }, + } + + b, err := json.Marshal(vmAffinity) + if err != nil { + return nil, err + } + return b, nil +} + +func GenerateVirtualMachineAndPodAntiAffinityPatch(key, topologyKey string, operator metav1.LabelSelectorOperator, values []string) ([]byte, error) { + vmAndPodAntiAffinity := &virtv2.VirtualMachineAndPodAntiAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: []virtv2.VirtualMachineAndPodAffinityTerm{ + { + LabelSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: key, + Operator: operator, + Values: values, + }, + }, + }, + TopologyKey: topologyKey, + }, + }, + } + + b, err := json.Marshal(vmAndPodAntiAffinity) + if err != nil { + return nil, err + } + return b, nil +} + +func GenerateVirtualMachineAndPodAffinityPatch(key, topologyKey string, operator metav1.LabelSelectorOperator, values []string) ([]byte, error) { + vmAndPodAffinity := &virtv2.VirtualMachineAndPodAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: []virtv2.VirtualMachineAndPodAffinityTerm{ + { + LabelSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: key, + Operator: operator, + Values: values, + }, + }, + }, + TopologyKey: topologyKey, + }, + }, + } + + b, err := json.Marshal(vmAndPodAffinity) + if err != nil { + return nil, err + } + return b, nil +} diff --git a/tests/e2e/complex_test.go b/tests/e2e/complex_test.go index 274915f25e..20be396197 100644 --- a/tests/e2e/complex_test.go +++ b/tests/e2e/complex_test.go @@ -30,43 +30,13 @@ import ( kc "github.com/deckhouse/virtualization/tests/e2e/kubectl" ) -func AssignIPToVMIP(name string) error { - assignErr := fmt.Sprintf("cannot patch VMIP %q with unnassigned IP address", name) - unassignedIP, err := FindUnassignedIP(mc.Spec.Settings.VirtualMachineCIDRs) - if err != nil { - return fmt.Errorf("%s\n%w", assignErr, err) - } - patch := fmt.Sprintf("{\"spec\":{\"staticIP\":%q}}", unassignedIP) - err = MergePatchResource(kc.ResourceVMIP, name, patch) - if err != nil { - return fmt.Errorf("%s\n%w", assignErr, err) - } - vmip := virtv2.VirtualMachineIPAddress{} - err = GetObject(kc.ResourceVMIP, name, &vmip, kc.GetOptions{ - Namespace: conf.Namespace, - }) - if err != nil { - return fmt.Errorf("%s\n%w", assignErr, err) - } - jsonPath := fmt.Sprintf("'jsonpath={.status.phase}=%s'", PhaseAttached) - waitOpts := kc.WaitOptions{ - Namespace: conf.Namespace, - For: jsonPath, - Timeout: ShortWaitDuration, - } - res := kubectl.WaitResources(kc.ResourceVMIP, waitOpts, name) - if res.Error() != nil { - return fmt.Errorf("%s\n%s", assignErr, res.StdErr()) - } - return nil -} - var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { var ( testCaseLabel = map[string]string{"testcase": "complex-test"} hasNoConsumerLabel = map[string]string{"hasNoConsumer": "complex-test"} alwaysOnLabel = map[string]string{"alwaysOn": "complex-test"} notAlwaysOnLabel = map[string]string{"notAlwaysOn": "complex-test"} + ns string ) AfterEach(func() { @@ -78,9 +48,10 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { Context("Preparing the environment", func() { It("sets the namespace", func() { kustomization := fmt.Sprintf("%s/%s", conf.TestData.ComplexTest, "kustomization.yaml") - ns, err := kustomize.GetNamespace(kustomization) + var err error + ns, err = kustomize.GetNamespace(kustomization) Expect(err).NotTo(HaveOccurred(), "%w", err) - conf.SetNamespace(ns) + Expect(ns).NotTo(BeEmpty()) }) }) @@ -89,7 +60,7 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { if config.IsReusable() { res := kubectl.List(kc.ResourceVM, kc.GetOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Output: "jsonpath='{.items[*].metadata.name}'", }) Expect(res.Error()).NotTo(HaveOccurred(), res.StdErr()) @@ -112,7 +83,7 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { By(fmt.Sprintf("VIs should be in %s phases", PhaseReady)) WaitPhaseByLabel(kc.ResourceVI, PhaseReady, kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -123,7 +94,7 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { By(fmt.Sprintf("CVIs should be in %s phases", PhaseReady)) WaitPhaseByLabel(kc.ResourceCVI, PhaseReady, kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -134,7 +105,7 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { By(fmt.Sprintf("VMClasses should be in %s phases", PhaseReady)) WaitPhaseByLabel(kc.ResourceVMClass, PhaseReady, kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -144,7 +115,7 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { It("patches custom VMIP with unassigned address", func() { vmipName := fmt.Sprintf("%s-%s", namePrefix, "vm-custom-ip") Eventually(func() error { - return AssignIPToVMIP(vmipName) + return AssignIPToVMIP(ns, vmipName) }).WithTimeout(LongWaitDuration).WithPolling(Interval).Should(Succeed()) }) @@ -152,7 +123,7 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { By(fmt.Sprintf("VMIPs should be in %s phases", PhaseAttached)) WaitPhaseByLabel(kc.ResourceVMIP, PhaseAttached, kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -164,7 +135,7 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { WaitPhaseByLabel(kc.ResourceVD, PhaseReady, kc.WaitOptions{ ExcludedLabels: []string{"hasNoConsumer"}, Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -173,7 +144,7 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { By(fmt.Sprintf("VDs should be in %s phases", phaseByVolumeBindingMode)) WaitPhaseByLabel(kc.ResourceVD, phaseByVolumeBindingMode, kc.WaitOptions{ Labels: hasNoConsumerLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -184,7 +155,7 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { By("Virtual machine agents should be ready") WaitVMAgentReady(kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -195,7 +166,7 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { By(fmt.Sprintf("VMBDAs should be in %s phases", PhaseAttached)) WaitPhaseByLabel(kc.ResourceVMBDA, PhaseAttached, kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -206,14 +177,14 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { It("checks VMs external connectivity", func() { res := kubectl.List(kc.ResourceVM, kc.GetOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Output: "jsonpath='{.items[*].metadata.name}'", }) Expect(res.Error()).NotTo(HaveOccurred(), res.StdErr()) vms := strings.Split(res.StdOut(), " ") - CheckCiliumAgents(kubectl, vms...) - CheckExternalConnection(externalHost, httpStatusOk, vms...) + CheckCiliumAgents(kubectl, ns, vms...) + CheckExternalConnection(externalHost, httpStatusOk, ns, vms...) }) }) }) @@ -231,7 +202,7 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { var vmList virtv2.VirtualMachineList err := GetObjects(kc.ResourceVM, &vmList, kc.GetOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, }) Expect(err).ShouldNot(HaveOccurred()) @@ -246,37 +217,37 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { } By("Trying to stop AlwaysOn VMs") - StopVirtualMachinesByVMOP(alwaysOnLabel, alwaysOnVMs...) + StopVirtualMachinesByVMOP(alwaysOnLabel, ns, alwaysOnVMs...) By("Trying to stop not AlwaysOn VMs") - StopVirtualMachinesByVMOP(notAlwaysOnLabel, notAlwaysOnVMs...) + StopVirtualMachinesByVMOP(notAlwaysOnLabel, ns, notAlwaysOnVMs...) }) It("checks VMOPs and VMs phases", func() { By(fmt.Sprintf("AlwaysOn VM VMOPs should be in %s phases", virtv2.VMOPPhaseFailed)) WaitResourcesByPhase(alwaysOnVMStopVMOPs, kc.ResourceVMOP, string(virtv2.VMOPPhaseFailed), kc.WaitOptions{ - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) By(fmt.Sprintf("Not AlwaysOn VM VMOPs should be in %s phases", virtv2.VMOPPhaseCompleted)) WaitResourcesByPhase(notAlwaysOnVMStopVMs, kc.ResourceVMOP, string(virtv2.VMOPPhaseCompleted), kc.WaitOptions{ - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) By(fmt.Sprintf("AlwaysOn VMs should be in %s phases", virtv2.MachineRunning)) WaitResourcesByPhase(alwaysOnVMs, kc.ResourceVM, string(virtv2.MachineRunning), kc.WaitOptions{ - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) By(fmt.Sprintf("Not AlwaysOn VMs should be in %s phases", virtv2.MachineStopped)) WaitResourcesByPhase(notAlwaysOnVMs, kc.ResourceVM, string(virtv2.MachineStopped), kc.WaitOptions{ - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) It("cleanup AlwaysOn VM VMOPs", func() { res := kubectl.Delete(kc.DeleteOptions{ - Namespace: conf.Namespace, + Namespace: ns, Labels: alwaysOnLabel, IgnoreNotFound: true, Resource: kc.ResourceVMOP, @@ -289,7 +260,7 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { It("starts VMs by VMOP", func() { var vms virtv2.VirtualMachineList err := GetObjects(kc.ResourceVM, &vms, kc.GetOptions{ - Namespace: conf.Namespace, + Namespace: ns, Labels: testCaseLabel, }) Expect(err).NotTo(HaveOccurred()) @@ -301,20 +272,20 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { } } - StartVirtualMachinesByVMOP(testCaseLabel, notAlwaysOnVMs...) + StartVirtualMachinesByVMOP(testCaseLabel, ns, notAlwaysOnVMs...) }) It("checks VMs and VMOPs phases", func() { By(fmt.Sprintf("VMOPs should be in %s phases", virtv2.VMOPPhaseCompleted)) WaitPhaseByLabel(kc.ResourceVMOP, string(virtv2.VMOPPhaseCompleted), kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) By("Virtual machine agents should be ready") WaitVMAgentReady(kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -325,7 +296,7 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { var vmList virtv2.VirtualMachineList err := GetObjects(kc.ResourceVM, &vmList, kc.GetOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, }) Expect(err).ShouldNot(HaveOccurred()) @@ -342,37 +313,37 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { vms = append(vms, alwaysOnVMs...) vms = append(vms, notAlwaysOnVMs...) - StopVirtualMachinesBySSH(vms...) + StopVirtualMachinesBySSH(ns, vms...) }) It("checks VMs phases", func() { By(fmt.Sprintf("Not AlwaysOn VMs should be in %s phases", virtv2.MachineStopped)) WaitResourcesByPhase(notAlwaysOnVMs, kc.ResourceVM, string(virtv2.MachineStopped), kc.WaitOptions{ - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) By(fmt.Sprintf("AlwaysOn VMs should be in %s phases", virtv2.MachineRunning)) WaitResourcesByPhase(alwaysOnVMs, kc.ResourceVM, string(virtv2.MachineRunning), kc.WaitOptions{ - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) It("start not AlwaysOn VMs", func() { - CreateAndApplyVMOPsWithSuffix(testCaseLabel, "-after-ssh-stopping", virtv2.VMOPTypeStart, notAlwaysOnVMs...) + CreateAndApplyVMOPsWithSuffix(testCaseLabel, "-after-ssh-stopping", virtv2.VMOPTypeStart, ns, notAlwaysOnVMs...) }) It("checks VMs and VMOPs phases", func() { By(fmt.Sprintf("VMOPs should be in %s phases", virtv2.VMOPPhaseCompleted)) WaitPhaseByLabel(kc.ResourceVMOP, string(virtv2.VMOPPhaseCompleted), kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) By("Virtual machine agents should be ready") WaitVMAgentReady(kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -382,27 +353,27 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { It("reboot VMs by VMOP", func() { res := kubectl.List(kc.ResourceVM, kc.GetOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Output: "jsonpath='{.items[*].metadata.name}'", }) Expect(res.Error()).NotTo(HaveOccurred(), res.StdErr()) vms := strings.Split(res.StdOut(), " ") - RebootVirtualMachinesByVMOP(testCaseLabel, vms...) + RebootVirtualMachinesByVMOP(testCaseLabel, ns, vms...) }) It("checks VMs and VMOPs phases", func() { By(fmt.Sprintf("VMOPs should be in %s phases", virtv2.VMOPPhaseCompleted)) WaitPhaseByLabel(kc.ResourceVMOP, string(virtv2.VMOPPhaseCompleted), kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) By("Virtual machine agents should be ready") WaitVMAgentReady(kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -419,7 +390,7 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { defer wg.Done() WaitPhaseByLabel(kc.ResourceVM, string(virtv2.MachineStopped), kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }() @@ -427,19 +398,19 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { res := kubectl.List(kc.ResourceVM, kc.GetOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Output: "jsonpath='{.items[*].metadata.name}'", }) Expect(res.Error()).NotTo(HaveOccurred(), res.StdErr()) vms := strings.Split(res.StdOut(), " ") - RebootVirtualMachinesBySSH(vms...) + RebootVirtualMachinesBySSH(ns, vms...) By("Virtual machines agent should be ready", func() { WaitVMAgentReady(kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -458,7 +429,7 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { defer wg.Done() WaitPhaseByLabel(kc.ResourceVM, string(virtv2.MachineStopped), kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }() @@ -467,7 +438,7 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { res := kubectl.Delete(kc.DeleteOptions{ IgnoreNotFound: true, Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Resource: kc.ResourcePod, }) Expect(res.Error()).NotTo(HaveOccurred()) @@ -475,7 +446,7 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { By("Virtual machines agent should be ready", func() { WaitVMAgentReady(kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -486,15 +457,15 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { It("checks VMs external connection after reboot", func() { res := kubectl.List(kc.ResourceVM, kc.GetOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Output: "jsonpath='{.items[*].metadata.name}'", }) Expect(res.Error()).NotTo(HaveOccurred(), res.StdErr()) vms := strings.Split(res.StdOut(), " ") - CheckCiliumAgents(kubectl, vms...) - CheckExternalConnection(externalHost, httpStatusOk, vms...) + CheckCiliumAgents(kubectl, ns, vms...) + CheckExternalConnection(externalHost, httpStatusOk, ns, vms...) }) }) }) @@ -504,14 +475,14 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { It("starts migrations", func() { res := kubectl.List(kc.ResourceVM, kc.GetOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Output: "jsonpath='{.items[*].metadata.name}'", }) Expect(res.Error()).NotTo(HaveOccurred(), res.StdErr()) vms := strings.Split(res.StdOut(), " ") - MigrateVirtualMachines(testCaseLabel, vms...) + MigrateVirtualMachines(testCaseLabel, ns, vms...) }) }) @@ -520,13 +491,13 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { By(fmt.Sprintf("VMOPs should be in %s phases", virtv2.VMOPPhaseCompleted)) WaitPhaseByLabel(kc.ResourceVMOP, string(virtv2.VMOPPhaseCompleted), kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) By("Virtual machines should be migrated") WaitByLabel(kc.ResourceVM, kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, For: "'jsonpath={.status.migrationState.result}=Succeeded'", }) @@ -535,15 +506,15 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { It("checks VMs external connection after migrations", func() { res := kubectl.List(kc.ResourceVM, kc.GetOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Output: "jsonpath='{.items[*].metadata.name}'", }) Expect(res.Error()).NotTo(HaveOccurred(), res.StdErr()) vms := strings.Split(res.StdOut(), " ") - CheckCiliumAgents(kubectl, vms...) - CheckExternalConnection(externalHost, httpStatusOk, vms...) + CheckCiliumAgents(kubectl, ns, vms...) + CheckExternalConnection(externalHost, httpStatusOk, ns, vms...) }) }) }) @@ -563,7 +534,38 @@ var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { resourcesToDelete.KustomizationDir = conf.TestData.ComplexTest } - DeleteTestCaseResources(resourcesToDelete) + DeleteTestCaseResources(ns, resourcesToDelete) }) }) }) + +func AssignIPToVMIP(vmipNamespace, vmipName string) error { + assignErr := fmt.Sprintf("cannot patch VMIP %q with unnassigned IP address", vmipName) + unassignedIP, err := FindUnassignedIP(mc.Spec.Settings.VirtualMachineCIDRs) + if err != nil { + return fmt.Errorf("%s\n%w", assignErr, err) + } + patch := fmt.Sprintf("{\"spec\":{\"staticIP\":%q}}", unassignedIP) + err = MergePatchResource(kc.ResourceVMIP, vmipNamespace, vmipName, patch) + if err != nil { + return fmt.Errorf("%s\n%w", assignErr, err) + } + vmip := virtv2.VirtualMachineIPAddress{} + err = GetObject(kc.ResourceVMIP, vmipName, &vmip, kc.GetOptions{ + Namespace: vmipNamespace, + }) + if err != nil { + return fmt.Errorf("%s\n%w", assignErr, err) + } + jsonPath := fmt.Sprintf("'jsonpath={.status.phase}=%s'", PhaseAttached) + waitOpts := kc.WaitOptions{ + Namespace: vmipNamespace, + For: jsonPath, + Timeout: ShortWaitDuration, + } + res := kubectl.WaitResources(kc.ResourceVMIP, waitOpts, vmipName) + if res.Error() != nil { + return fmt.Errorf("%s\n%s", assignErr, res.StdErr()) + } + return nil +} diff --git a/tests/e2e/config/config.go b/tests/e2e/config/config.go index d6f9cf4c56..7533c03ca8 100644 --- a/tests/e2e/config/config.go +++ b/tests/e2e/config/config.go @@ -128,7 +128,7 @@ type Config struct { VM VMConf `yaml:"vm"` Ipam IpamConf `yaml:"ipam"` HelperImages HelperImages `yaml:"helperImages"` - Namespace string `yaml:"namespaceSuffix"` + NamespaceSuffix string `yaml:"namespaceSuffix"` TestData TestData `yaml:"testData"` LogFilter []string `yaml:"logFilter"` CleanupResources []string `yaml:"cleanupResources"` @@ -191,9 +191,6 @@ type HelperImages struct { } func (c *Config) setEnvs() error { - if e, ok := os.LookupEnv("E2E_NAMESPACE"); ok { - c.Namespace = e - } // ClusterTransport if e, ok := os.LookupEnv("E2E_CLUSTERTRANSPORT_KUBECONFIG"); ok { c.ClusterTransport.KubeConfig = e @@ -274,10 +271,6 @@ func GetNamePrefix() (string, error) { return commitHash, nil } -func (c *Config) SetNamespace(name string) { - c.Namespace = name -} - func (k *Kustomize) SetParams(filePath, namespace, namePrefix string) error { var kustomizeFile Kustomize diff --git a/tests/e2e/image_hotplug_test.go b/tests/e2e/image_hotplug_test.go index 0c7e8f0a52..9ce1d6003f 100644 --- a/tests/e2e/image_hotplug_test.go +++ b/tests/e2e/image_hotplug_test.go @@ -28,104 +28,11 @@ import ( virtv2 "github.com/deckhouse/virtualization/api/core/v1alpha2" "github.com/deckhouse/virtualization/tests/e2e/config" - d8 "github.com/deckhouse/virtualization/tests/e2e/d8" + "github.com/deckhouse/virtualization/tests/e2e/d8" "github.com/deckhouse/virtualization/tests/e2e/ginkgoutil" kc "github.com/deckhouse/virtualization/tests/e2e/kubectl" ) -type Image struct { - Kind string - Name string -} - -func IsBlockDeviceCdRom(vmName, blockDeviceName string) (bool, error) { - var blockDevices *BlockDevices - bdIDPath := fmt.Sprintf("/dev/disk/by-id/%s-%s", CdRomIDPrefix, blockDeviceName) - cmd := fmt.Sprintf("lsblk --json --nodeps --output name,type %s", bdIDPath) - res := d8Virtualization.SSHCommand(vmName, cmd, d8.SSHOptions{ - Namespace: conf.Namespace, - Username: conf.TestData.SSHUser, - IdenityFile: conf.TestData.Sshkey, - }) - if res.Error() != nil { - return false, errors.New(res.StdErr()) - } - err := json.Unmarshal(res.StdOutBytes(), &blockDevices) - if err != nil { - return false, err - } - if len(blockDevices.BlockDevices) != 1 { - return false, fmt.Errorf("`blockDevices` length should be 1") - } - blockDevice := &blockDevices.BlockDevices[0] - return blockDevice.Type == "rom", nil -} - -func MountBlockDevice(vmName, blockDeviceID string) error { - bdIDPath := fmt.Sprintf("/dev/disk/by-id/%s", blockDeviceID) - cmd := fmt.Sprintf("sudo mount --read-only %s /mnt", bdIDPath) - res := d8Virtualization.SSHCommand(vmName, cmd, d8.SSHOptions{ - Namespace: conf.Namespace, - Username: conf.TestData.SSHUser, - IdenityFile: conf.TestData.Sshkey, - }) - if res.Error() != nil { - return errors.New(res.StdErr()) - } - return nil -} - -func IsBlockDeviceReadOnly(vmName, blockDeviceID string) (bool, error) { - bdIDPath := fmt.Sprintf("/dev/disk/by-id/%s", blockDeviceID) - cmd := fmt.Sprintf("findmnt --noheadings --output options %s", bdIDPath) - res := d8Virtualization.SSHCommand(vmName, cmd, d8.SSHOptions{ - Namespace: conf.Namespace, - Username: conf.TestData.SSHUser, - IdenityFile: conf.TestData.Sshkey, - }) - if res.Error() != nil { - return false, errors.New(res.StdErr()) - } - options := strings.Split(res.StdOut(), ",") - if len(options) == 0 { - return false, fmt.Errorf("list of options is empty: %s", options) - } - roOpt := options[0] - return roOpt == "ro", nil -} - -type Counter struct { - Current int - Expected int -} - -type ReusableResources map[kc.Resource]*Counter - -// Useful when require to check the created resources in `REUSABLE` mode. -// -// Static output option: `jsonpath='{.items[*].metadata.name}'`. -func CheckReusableResources(resources ReusableResources, opts kc.GetOptions) { - GinkgoHelper() - opts.Output = "jsonpath='{.items[*].metadata.name}'" - for r, c := range resources { - res := kubectl.List(r, opts) - Expect(res.Error()).NotTo(HaveOccurred(), "failed to check the reusable resources %q: %s", r, res.StdErr()) - c.Current = len(strings.Split(res.StdOut(), " ")) - } - - isReusableResourcesExist := false - for _, c := range resources { - if c.Current == c.Expected { - isReusableResourcesExist = true - } else { - isReusableResourcesExist = false - } - } - if isReusableResourcesExist { - return - } -} - var _ = Describe("Image hotplug", ginkgoutil.CommonE2ETestDecorators(), func() { const ( viCount = 2 @@ -141,6 +48,7 @@ var _ = Describe("Image hotplug", ginkgoutil.CommonE2ETestDecorators(), func() { disksBefore Disks disksAfter Disks testCaseLabel = map[string]string{"testcase": "image-hotplug"} + ns string ) AfterEach(func() { @@ -151,9 +59,9 @@ var _ = Describe("Image hotplug", ginkgoutil.CommonE2ETestDecorators(), func() { BeforeAll(func() { kustomization := fmt.Sprintf("%s/%s", conf.TestData.ImageHotplug, "kustomization.yaml") - ns, err := kustomize.GetNamespace(kustomization) + var err error + ns, err = kustomize.GetNamespace(kustomization) Expect(err).NotTo(HaveOccurred(), "%w", err) - conf.SetNamespace(ns) res := kubectl.Delete(kc.DeleteOptions{ IgnoreNotFound: true, @@ -184,7 +92,7 @@ var _ = Describe("Image hotplug", ginkgoutil.CommonE2ETestDecorators(), func() { }, }, kc.GetOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, IgnoreNotFound: true, }) } @@ -200,28 +108,28 @@ var _ = Describe("Image hotplug", ginkgoutil.CommonE2ETestDecorators(), func() { By(fmt.Sprintf("`VirtualImages` should be in the %q phase", virtv2.ImageReady), func() { WaitPhaseByLabel(kc.ResourceVI, PhaseReady, kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) By(fmt.Sprintf("`ClusterVirtualImages` should be in the %q phase", virtv2.ImageReady), func() { WaitPhaseByLabel(kc.ResourceCVI, PhaseReady, kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) By(fmt.Sprintf("`VirtualDisk` should be in the %q phase", virtv2.DiskReady), func() { WaitPhaseByLabel(kc.ResourceVD, PhaseReady, kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) By("`VirtualMachine` agent should be ready", func() { WaitVMAgentReady(kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -236,7 +144,7 @@ var _ = Describe("Image hotplug", ginkgoutil.CommonE2ETestDecorators(), func() { vmObjs := &virtv2.VirtualMachineList{} err := GetObjects(virtv2.VirtualMachineResource, vmObjs, kc.GetOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, }) Expect(err).NotTo(HaveOccurred(), "failed to get `VirtualMachines`: %s", err) Expect(len(vmObjs.Items)).To(Equal(vmCount), "there is only %d `VirtualMachine` in this test case", vmCount) @@ -246,7 +154,7 @@ var _ = Describe("Image hotplug", ginkgoutil.CommonE2ETestDecorators(), func() { viObjs := &virtv2.VirtualImageList{} err := GetObjects(virtv2.VirtualImageResource, viObjs, kc.GetOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, }) Expect(err).NotTo(HaveOccurred(), "failed to get `VirtualImages`: %s", err) @@ -261,7 +169,7 @@ var _ = Describe("Image hotplug", ginkgoutil.CommonE2ETestDecorators(), func() { cviObjs := &virtv2.ClusterVirtualImageList{} err := GetObjects(virtv2.ClusterVirtualImageResource, cviObjs, kc.GetOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, }) Expect(err).NotTo(HaveOccurred(), "failed to get `ClusterVirtualImages`: %s", err) @@ -276,14 +184,14 @@ var _ = Describe("Image hotplug", ginkgoutil.CommonE2ETestDecorators(), func() { It("retrieves the disk count before the images attachment", func() { Eventually(func() error { - return GetDisksMetadata(vmObj.Name, &disksBefore) + return GetDisksMetadata(ns, vmObj.Name, &disksBefore) }).WithTimeout(Timeout).WithPolling(Interval).ShouldNot(HaveOccurred(), "virtualMachine: %s", vmObj.Name) }) It("attaches the images into the `VirtualMachine`", func() { for _, bd := range imageBlockDevices { By(bd.Name, func() { - AttachBlockDevice(vmObj.Name, bd.Name, virtv2.VMBDAObjectRefKind(bd.Kind), testCaseLabel, conf.TestData.ImageHotplug) + AttachBlockDevice(ns, vmObj.Name, bd.Name, virtv2.VMBDAObjectRefKind(bd.Kind), testCaseLabel, conf.TestData.ImageHotplug) }) } }) @@ -292,26 +200,26 @@ var _ = Describe("Image hotplug", ginkgoutil.CommonE2ETestDecorators(), func() { By(fmt.Sprintf("`VirtualMachineBlockDeviceAttachments` should be in the %q phase", virtv2.BlockDeviceAttachmentPhaseAttached), func() { WaitPhaseByLabel(kc.ResourceVMBDA, PhaseAttached, kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) By("`VirtualMachine` should be ready", func() { WaitVMAgentReady(kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) By("`BlockDevices` should be attached", func() { - WaitBlockDeviceRefsAttached(conf.Namespace, vmObj.Name) + WaitBlockDeviceRefsAttached(ns, vmObj.Name) }) }) It("compares the disk count before and after attachment", func() { diskCountBefore := len(disksBefore.BlockDevices) Eventually(func() (int, error) { - err := GetDisksMetadata(vmObj.Name, &disksAfter) + err := GetDisksMetadata(ns, vmObj.Name, &disksAfter) if err != nil { return unacceptableCount, err } @@ -327,7 +235,7 @@ var _ = Describe("Image hotplug", ginkgoutil.CommonE2ETestDecorators(), func() { ) intVirtVmi := &virtv1.VirtualMachineInstance{} err := GetObject(kc.ResourceKubevirtVMI, vmObj.Name, intVirtVmi, kc.GetOptions{ - Namespace: conf.Namespace, + Namespace: ns, }) Expect(err).NotTo(HaveOccurred(), "failed to get `InternalVirtualMachineInstance`: %s", err) for _, disk := range intVirtVmi.Spec.Domain.Devices.Disks { @@ -337,7 +245,7 @@ var _ = Describe("Image hotplug", ginkgoutil.CommonE2ETestDecorators(), func() { } } Expect(isolockDeviceCount).To(Equal(1), "there is only one `ISO` block device in this case") - isCdRom, err := IsBlockDeviceCdRom(vmObj.Name, isoBlockDeviceName) + isCdRom, err := IsBlockDeviceCdRom(ns, vmObj.Name, isoBlockDeviceName) Expect(err).NotTo(HaveOccurred(), "failed to get `BlockDeviceType` of %q: %s", isoBlockDeviceName, err) Expect(isCdRom).Should(BeTrue(), "wrong type of the block device: %s", isoBlockDeviceName) }) @@ -346,7 +254,7 @@ var _ = Describe("Image hotplug", ginkgoutil.CommonE2ETestDecorators(), func() { imgs := make(map[string]string, imgCount) intVirtVmi := &virtv1.VirtualMachineInstance{} err := GetObject(kc.ResourceKubevirtVMI, vmObj.Name, intVirtVmi, kc.GetOptions{ - Namespace: conf.Namespace, + Namespace: ns, }) Expect(err).NotTo(HaveOccurred(), "failed to get `InternalVirtulMachineInstance`: %s", err) for _, disk := range intVirtVmi.Spec.Domain.Devices.Disks { @@ -360,9 +268,9 @@ var _ = Describe("Image hotplug", ginkgoutil.CommonE2ETestDecorators(), func() { Expect(len(imgs)).To(Equal(imgCount), "there are only %d `blockDevices` in this case", imgCount) for img, diskID := range imgs { - err := MountBlockDevice(vmObj.Name, diskID) + err := MountBlockDevice(ns, vmObj.Name, diskID) Expect(err).NotTo(HaveOccurred(), "failed to mount %q into the `VirtualMachine`: %s", img, err) - isReadOnly, err := IsBlockDeviceReadOnly(vmObj.Name, diskID) + isReadOnly, err := IsBlockDeviceReadOnly(ns, vmObj.Name, diskID) Expect(err).NotTo(HaveOccurred(), "failed to check the `ReadOnly` status: %s", img) Expect(isReadOnly).Should(BeTrue(), "the mounted disk should be `ReadOnly`") } @@ -372,7 +280,7 @@ var _ = Describe("Image hotplug", ginkgoutil.CommonE2ETestDecorators(), func() { res := kubectl.Delete(kc.DeleteOptions{ FilenameOption: kc.Filename, Filename: []string{fmt.Sprintf("%s/vmbda", conf.TestData.ImageHotplug)}, - Namespace: conf.Namespace, + Namespace: ns, Labels: testCaseLabel, }) Expect(res.Error()).NotTo(HaveOccurred(), "failed to delete `VirtualMachineBlockDeviceAttachments`: %s", res.StdErr()) @@ -382,7 +290,7 @@ var _ = Describe("Image hotplug", ginkgoutil.CommonE2ETestDecorators(), func() { diskCountBefore := len(disksBefore.BlockDevices) Expect(diskCountBefore).NotTo(BeZero(), "the disk count `before` should not be zero") Eventually(func() (int, error) { - err := GetDisksMetadata(vmObj.Name, &disksAfter) + err := GetDisksMetadata(ns, vmObj.Name, &disksAfter) if err != nil { return unacceptableCount, err } @@ -407,7 +315,100 @@ var _ = Describe("Image hotplug", ginkgoutil.CommonE2ETestDecorators(), func() { resourcesToDelete.KustomizationDir = conf.TestData.ImageHotplug } - DeleteTestCaseResources(resourcesToDelete) + DeleteTestCaseResources(ns, resourcesToDelete) }) }) }) + +type Image struct { + Kind string + Name string +} + +func IsBlockDeviceCdRom(vmNamespace, vmName, blockDeviceName string) (bool, error) { + var blockDevices *BlockDevices + bdIDPath := fmt.Sprintf("/dev/disk/by-id/%s-%s", CdRomIDPrefix, blockDeviceName) + cmd := fmt.Sprintf("lsblk --json --nodeps --output name,type %s", bdIDPath) + res := d8Virtualization.SSHCommand(vmName, cmd, d8.SSHOptions{ + Namespace: vmNamespace, + Username: conf.TestData.SSHUser, + IdenityFile: conf.TestData.Sshkey, + }) + if res.Error() != nil { + return false, errors.New(res.StdErr()) + } + err := json.Unmarshal(res.StdOutBytes(), &blockDevices) + if err != nil { + return false, err + } + if len(blockDevices.BlockDevices) != 1 { + return false, fmt.Errorf("`blockDevices` length should be 1") + } + blockDevice := &blockDevices.BlockDevices[0] + return blockDevice.Type == "rom", nil +} + +func MountBlockDevice(vmNamespace, vmName, blockDeviceID string) error { + bdIDPath := fmt.Sprintf("/dev/disk/by-id/%s", blockDeviceID) + cmd := fmt.Sprintf("sudo mount --read-only %s /mnt", bdIDPath) + res := d8Virtualization.SSHCommand(vmName, cmd, d8.SSHOptions{ + Namespace: vmNamespace, + Username: conf.TestData.SSHUser, + IdenityFile: conf.TestData.Sshkey, + }) + if res.Error() != nil { + return errors.New(res.StdErr()) + } + return nil +} + +func IsBlockDeviceReadOnly(vmNamespace, vmName, blockDeviceID string) (bool, error) { + bdIDPath := fmt.Sprintf("/dev/disk/by-id/%s", blockDeviceID) + cmd := fmt.Sprintf("findmnt --noheadings --output options %s", bdIDPath) + res := d8Virtualization.SSHCommand(vmName, cmd, d8.SSHOptions{ + Namespace: vmNamespace, + Username: conf.TestData.SSHUser, + IdenityFile: conf.TestData.Sshkey, + }) + if res.Error() != nil { + return false, errors.New(res.StdErr()) + } + options := strings.Split(res.StdOut(), ",") + if len(options) == 0 { + return false, fmt.Errorf("list of options is empty: %s", options) + } + roOpt := options[0] + return roOpt == "ro", nil +} + +type Counter struct { + Current int + Expected int +} + +type ReusableResources map[kc.Resource]*Counter + +// Useful when require to check the created resources in `REUSABLE` mode. +// +// Static output option: `jsonpath='{.items[*].metadata.name}'`. +func CheckReusableResources(resources ReusableResources, opts kc.GetOptions) { + GinkgoHelper() + opts.Output = "jsonpath='{.items[*].metadata.name}'" + for r, c := range resources { + res := kubectl.List(r, opts) + Expect(res.Error()).NotTo(HaveOccurred(), "failed to check the reusable resources %q: %s", r, res.StdErr()) + c.Current = len(strings.Split(res.StdOut(), " ")) + } + + isReusableResourcesExist := false + for _, c := range resources { + if c.Current == c.Expected { + isReusableResourcesExist = true + } else { + isReusableResourcesExist = false + } + } + if isReusableResourcesExist { + return + } +} diff --git a/tests/e2e/images_creation_test.go b/tests/e2e/images_creation_test.go index 0fa0d13158..1c035175eb 100644 --- a/tests/e2e/images_creation_test.go +++ b/tests/e2e/images_creation_test.go @@ -29,8 +29,11 @@ import ( kc "github.com/deckhouse/virtualization/tests/e2e/kubectl" ) -var _ = Describe("VirtualImagesCreation", ginkgoutil.CommonE2ETestDecorators(), func() { - testCaseLabel := map[string]string{"testcase": "images-creation"} +var _ = Describe("VirtualImageCreation", ginkgoutil.CommonE2ETestDecorators(), func() { + var ( + testCaseLabel = map[string]string{"testcase": "images-creation"} + ns string + ) BeforeAll(func() { if config.IsReusable() { @@ -38,9 +41,9 @@ var _ = Describe("VirtualImagesCreation", ginkgoutil.CommonE2ETestDecorators(), } kustomization := fmt.Sprintf("%s/%s", conf.TestData.ImagesCreation, "kustomization.yaml") - ns, err := kustomize.GetNamespace(kustomization) + var err error + ns, err = kustomize.GetNamespace(kustomization) Expect(err).NotTo(HaveOccurred(), "%w", err) - conf.SetNamespace(ns) Expect(conf.StorageClass.ImmediateStorageClass).NotTo(BeNil(), "immediate storage class cannot be nil; please set up the immediate storage class in the cluster") @@ -87,7 +90,7 @@ var _ = Describe("VirtualImagesCreation", ginkgoutil.CommonE2ETestDecorators(), By(fmt.Sprintf("VD should be in %s phase", virtv2.DiskReady)) WaitPhaseByLabel(kc.ResourceVD, string(virtv2.DiskReady), kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -96,7 +99,7 @@ var _ = Describe("VirtualImagesCreation", ginkgoutil.CommonE2ETestDecorators(), By(fmt.Sprintf("VDSnapshot should be in %s phase", virtv2.VirtualDiskSnapshotPhaseReady)) WaitPhaseByLabel(kc.ResourceVDSnapshot, string(virtv2.VirtualDiskSnapshotPhaseReady), kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -107,7 +110,7 @@ var _ = Describe("VirtualImagesCreation", ginkgoutil.CommonE2ETestDecorators(), By(fmt.Sprintf("VIs should be in %s phases", virtv2.ImageReady)) WaitPhaseByLabel(kc.ResourceVI, string(virtv2.ImageReady), kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -116,7 +119,7 @@ var _ = Describe("VirtualImagesCreation", ginkgoutil.CommonE2ETestDecorators(), By(fmt.Sprintf("CVIs should be in %s phases", virtv2.ImageReady)) WaitPhaseByLabel(kc.ResourceCVI, string(virtv2.ImageReady), kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -124,7 +127,7 @@ var _ = Describe("VirtualImagesCreation", ginkgoutil.CommonE2ETestDecorators(), Context("When test is completed", func() { It("deletes test case resources", func() { - DeleteTestCaseResources(ResourcesToDelete{ + DeleteTestCaseResources(ns, ResourcesToDelete{ KustomizationDir: conf.TestData.ImagesCreation, }) }) diff --git a/tests/e2e/importer_network_policy_test.go b/tests/e2e/importer_network_policy_test.go index 3610357114..e57c6a02b8 100644 --- a/tests/e2e/importer_network_policy_test.go +++ b/tests/e2e/importer_network_policy_test.go @@ -28,12 +28,13 @@ import ( kc "github.com/deckhouse/virtualization/tests/e2e/kubectl" ) -var _ = Describe("Importer network policy", ginkgoutil.CommonE2ETestDecorators(), func() { +var _ = Describe("ImporterNetworkPolicy", ginkgoutil.CommonE2ETestDecorators(), func() { testCaseLabel := map[string]string{"testcase": "importer-network-policy"} + var ns string AfterAll(func() { By("Delete manifests") - DeleteTestCaseResources(ResourcesToDelete{KustomizationDir: conf.TestData.ImporterNetworkPolicy}) + DeleteTestCaseResources(ns, ResourcesToDelete{KustomizationDir: conf.TestData.ImporterNetworkPolicy}) }) BeforeEach(func() { @@ -51,9 +52,9 @@ var _ = Describe("Importer network policy", ginkgoutil.CommonE2ETestDecorators() Context("Preparing the environment", func() { It("sets the namespace", func() { kustomization := fmt.Sprintf("%s/%s", conf.TestData.ImporterNetworkPolicy, "kustomization.yaml") - ns, err := kustomize.GetNamespace(kustomization) + var err error + ns, err = kustomize.GetNamespace(kustomization) Expect(err).NotTo(HaveOccurred(), "%w", err) - conf.SetNamespace(ns) }) It("project apply", func() { @@ -91,7 +92,7 @@ var _ = Describe("Importer network policy", ginkgoutil.CommonE2ETestDecorators() By(fmt.Sprintf("%ss should be in %s phases", resourceShortName, phase)) WaitPhaseByLabel(resource, phase, kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }, diff --git a/tests/e2e/sizing_policy_test.go b/tests/e2e/sizing_policy_test.go index dae993c5d0..7d7905bc43 100644 --- a/tests/e2e/sizing_policy_test.go +++ b/tests/e2e/sizing_policy_test.go @@ -34,37 +34,7 @@ import ( kc "github.com/deckhouse/virtualization/tests/e2e/kubectl" ) -func ValidateVirtualMachineByClass(virtualMachineClass *virtv2.VirtualMachineClass, virtualMachine *virtv2.VirtualMachine) { - var sizingPolicy virtv2.SizingPolicy - for _, p := range virtualMachineClass.Spec.SizingPolicies { - if virtualMachine.Spec.CPU.Cores >= p.Cores.Min && virtualMachine.Spec.CPU.Cores <= p.Cores.Max { - sizingPolicy = *p.DeepCopy() - break - } - } - - checkMinMemory := virtualMachine.Spec.Memory.Size.Value() >= sizingPolicy.Memory.Min.Value() - checkMaxMemory := virtualMachine.Spec.Memory.Size.Value() <= sizingPolicy.Memory.Max.Value() - checkMemory := checkMinMemory && checkMaxMemory - Expect(checkMemory).To(BeTrue(), fmt.Errorf("memory size outside of possible interval '%v - %v': %v", sizingPolicy.Memory.Min, sizingPolicy.Memory.Max, virtualMachine.Spec.Memory.Size)) - - coreFraction, err := strconv.Atoi(strings.ReplaceAll(virtualMachine.Spec.CPU.CoreFraction, "%", "")) - Expect(err).NotTo(HaveOccurred(), "cannot convert CoreFraction value to integer: %s", err) - checkCoreFraction := slices.Contains(sizingPolicy.CoreFractions, virtv2.CoreFractionValue(coreFraction)) - Expect(checkCoreFraction).To(BeTrue(), fmt.Errorf("sizing policy core fraction list does not contain value from spec: %s\n%v", virtualMachine.Spec.CPU.CoreFraction, sizingPolicy.CoreFractions)) -} - -func CompareVirtualMachineClassReadyStatus(vmName string, expectedStatus metav1.ConditionStatus) { - GinkgoHelper() - vm := virtv2.VirtualMachine{} - err := GetObject(kc.ResourceVM, vmName, &vm, kc.GetOptions{Namespace: conf.Namespace}) - Expect(err).NotTo(HaveOccurred(), "%v", err) - status, err := GetConditionStatus(&vm, vmcondition.TypeClassReady.String()) - Expect(err).NotTo(HaveOccurred(), "%v", err) - Expect(status).To(Equal(expectedStatus), fmt.Sprintf("VirtualMachineClassReady status should be '%s'", expectedStatus)) -} - -var _ = Describe("Sizing policy", ginkgoutil.CommonE2ETestDecorators(), func() { +var _ = Describe("SizingPolicy", ginkgoutil.CommonE2ETestDecorators(), func() { BeforeEach(func() { if config.IsReusable() { Skip("Test not available in REUSABLE mode: not supported yet.") @@ -81,6 +51,7 @@ var _ = Describe("Sizing policy", ginkgoutil.CommonE2ETestDecorators(), func() { notExistingVMClassCreating = map[string]string{"vm": "not-existing-vmclass-with-creating"} existingVMClass = map[string]string{"vm": "existing-vmclass"} testCaseLabel = map[string]string{"testcase": "sizing-policy"} + ns string ) AfterEach(func() { @@ -92,15 +63,15 @@ var _ = Describe("Sizing policy", ginkgoutil.CommonE2ETestDecorators(), func() { Context("Preparing the environment", func() { vmNotValidSizingPolicyChanging = fmt.Sprintf("%s-vm-%s", namePrefix, notExistingVMClassChanging["vm"]) vmNotValidSizingPolicyCreating = fmt.Sprintf("%s-vm-%s", namePrefix, notExistingVMClassCreating["vm"]) - vmClassDiscovery = fmt.Sprintf("%s-discovery", namePrefix) - vmClassDiscoveryCopy = fmt.Sprintf("%s-discovery-copy", namePrefix) + vmClassDiscovery = fmt.Sprintf("%s-sizing-policy-discovery", namePrefix) + vmClassDiscoveryCopy = fmt.Sprintf("%s-sizing-policy-discovery-copy", namePrefix) newVMClassFilePath = fmt.Sprintf("%s/vmc-copy.yaml", conf.TestData.SizingPolicy) It("sets the namespace", func() { kustomization := fmt.Sprintf("%s/%s", conf.TestData.SizingPolicy, "kustomization.yaml") - ns, err := kustomize.GetNamespace(kustomization) + var err error + ns, err = kustomize.GetNamespace(kustomization) Expect(err).NotTo(HaveOccurred(), "%w", err) - conf.SetNamespace(ns) }) }) @@ -119,7 +90,7 @@ var _ = Describe("Sizing policy", ginkgoutil.CommonE2ETestDecorators(), func() { By(fmt.Sprintf("VIs should be in %s phases", PhaseReady)) WaitPhaseByLabel(kc.ResourceVI, PhaseReady, kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -130,12 +101,12 @@ var _ = Describe("Sizing policy", ginkgoutil.CommonE2ETestDecorators(), func() { By(fmt.Sprintf("VDs should be in %s phases", phaseByVolumeBindingMode)) WaitPhaseByLabel(kc.ResourceVD, phaseByVolumeBindingMode, kc.WaitOptions{ Labels: notExistingVMClassChanging, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) WaitPhaseByLabel(kc.ResourceVD, phaseByVolumeBindingMode, kc.WaitOptions{ Labels: notExistingVMClassCreating, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -144,7 +115,7 @@ var _ = Describe("Sizing policy", ginkgoutil.CommonE2ETestDecorators(), func() { By(fmt.Sprintf("VDs should be in %s phases", PhaseReady)) WaitPhaseByLabel(kc.ResourceVD, PhaseReady, kc.WaitOptions{ Labels: existingVMClass, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -155,12 +126,12 @@ var _ = Describe("Sizing policy", ginkgoutil.CommonE2ETestDecorators(), func() { By(fmt.Sprintf("VMs should be in %s phases", PhasePending)) WaitPhaseByLabel(kc.ResourceVM, PhasePending, kc.WaitOptions{ Labels: notExistingVMClassChanging, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) WaitPhaseByLabel(kc.ResourceVM, PhasePending, kc.WaitOptions{ Labels: notExistingVMClassCreating, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -169,7 +140,7 @@ var _ = Describe("Sizing policy", ginkgoutil.CommonE2ETestDecorators(), func() { By("Virtual machine agents should be ready") WaitVMAgentReady(kc.WaitOptions{ Labels: existingVMClass, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -179,12 +150,12 @@ var _ = Describe("Sizing policy", ginkgoutil.CommonE2ETestDecorators(), func() { Context(fmt.Sprintf("When virtual machine with label %s in phase %s", notExistingVMClassChanging, PhasePending), func() { It("checks condition status before changing 'virtulaMachineCLass` field with existing class", func() { By(fmt.Sprintf("VirtualMachineClassReady status should be '%s' before changing", metav1.ConditionFalse)) - CompareVirtualMachineClassReadyStatus(vmNotValidSizingPolicyChanging, metav1.ConditionFalse) + CompareVirtualMachineClassReadyStatus(ns, vmNotValidSizingPolicyChanging, metav1.ConditionFalse) }) It("changes VMClassName in VM specification with existing VMClass", func() { mergePatch := fmt.Sprintf("{\"spec\":{\"virtualMachineClassName\":%q}}", vmClassDiscovery) - err := MergePatchResource(kc.ResourceVM, vmNotValidSizingPolicyChanging, mergePatch) + err := MergePatchResource(kc.ResourceVM, ns, vmNotValidSizingPolicyChanging, mergePatch) Expect(err).NotTo(HaveOccurred()) }) @@ -192,23 +163,23 @@ var _ = Describe("Sizing policy", ginkgoutil.CommonE2ETestDecorators(), func() { By("VM should be ready") WaitVMAgentReady(kc.WaitOptions{ Labels: notExistingVMClassChanging, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) By(fmt.Sprintf("VirtualMachineClassReady status should be '%s' after changing", metav1.ConditionTrue)) - CompareVirtualMachineClassReadyStatus(vmNotValidSizingPolicyChanging, metav1.ConditionTrue) + CompareVirtualMachineClassReadyStatus(ns, vmNotValidSizingPolicyChanging, metav1.ConditionTrue) }) }) Context(fmt.Sprintf("When virtual machine with label %s in phase %s", notExistingVMClassCreating, PhasePending), func() { It("checks condition status before creating `VirtualMachineClass`", func() { By(fmt.Sprintf("VirtualMachineClassReady status should be '%s' before creating", metav1.ConditionFalse)) - CompareVirtualMachineClassReadyStatus(vmNotValidSizingPolicyCreating, metav1.ConditionFalse) + CompareVirtualMachineClassReadyStatus(ns, vmNotValidSizingPolicyCreating, metav1.ConditionFalse) }) It("changes VMClassName in VM specification with not existing VMClass which have correct prefix for creating", func() { mergePatch := fmt.Sprintf("{\"spec\":{\"virtualMachineClassName\":%q}}", vmClassDiscoveryCopy) - err := MergePatchResource(kc.ResourceVM, vmNotValidSizingPolicyCreating, mergePatch) + err := MergePatchResource(kc.ResourceVM, ns, vmNotValidSizingPolicyCreating, mergePatch) Expect(err).NotTo(HaveOccurred()) }) @@ -231,11 +202,11 @@ var _ = Describe("Sizing policy", ginkgoutil.CommonE2ETestDecorators(), func() { By("VM should be ready") WaitVMAgentReady(kc.WaitOptions{ Labels: notExistingVMClassCreating, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) By(fmt.Sprintf("VirtualMachineClassReady status should be '%s' after creating", metav1.ConditionTrue)) - CompareVirtualMachineClassReadyStatus(vmNotValidSizingPolicyCreating, metav1.ConditionTrue) + CompareVirtualMachineClassReadyStatus(ns, vmNotValidSizingPolicyCreating, metav1.ConditionTrue) }) }) }) @@ -244,7 +215,7 @@ var _ = Describe("Sizing policy", ginkgoutil.CommonE2ETestDecorators(), func() { It("checks sizing policy match", func() { res := kubectl.List(kc.ResourceVM, kc.GetOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Output: "jsonpath='{.items[*].metadata.name}'", }) Expect(res.Error()).NotTo(HaveOccurred(), res.StdErr()) @@ -257,7 +228,7 @@ var _ = Describe("Sizing policy", ginkgoutil.CommonE2ETestDecorators(), func() { for _, vm := range vms { By(fmt.Sprintf("Check virtual machine: %s", vm)) vmObj := virtv2.VirtualMachine{} - err := GetObject(kc.ResourceVM, vm, &vmObj, kc.GetOptions{Namespace: conf.Namespace}) + err := GetObject(kc.ResourceVM, vm, &vmObj, kc.GetOptions{Namespace: ns}) Expect(err).NotTo(HaveOccurred()) ValidateVirtualMachineByClass(&vmClass, &vmObj) } @@ -266,10 +237,40 @@ var _ = Describe("Sizing policy", ginkgoutil.CommonE2ETestDecorators(), func() { Context("When test is completed", func() { It("deletes test case resources", func() { - DeleteTestCaseResources(ResourcesToDelete{ + DeleteTestCaseResources(ns, ResourcesToDelete{ KustomizationDir: conf.TestData.SizingPolicy, Files: []string{newVMClassFilePath}, }) }) }) }) + +func ValidateVirtualMachineByClass(virtualMachineClass *virtv2.VirtualMachineClass, virtualMachine *virtv2.VirtualMachine) { + var sizingPolicy virtv2.SizingPolicy + for _, p := range virtualMachineClass.Spec.SizingPolicies { + if virtualMachine.Spec.CPU.Cores >= p.Cores.Min && virtualMachine.Spec.CPU.Cores <= p.Cores.Max { + sizingPolicy = *p.DeepCopy() + break + } + } + + checkMinMemory := virtualMachine.Spec.Memory.Size.Value() >= sizingPolicy.Memory.Min.Value() + checkMaxMemory := virtualMachine.Spec.Memory.Size.Value() <= sizingPolicy.Memory.Max.Value() + checkMemory := checkMinMemory && checkMaxMemory + Expect(checkMemory).To(BeTrue(), fmt.Errorf("memory size outside of possible interval '%v - %v': %v", sizingPolicy.Memory.Min, sizingPolicy.Memory.Max, virtualMachine.Spec.Memory.Size)) + + coreFraction, err := strconv.Atoi(strings.ReplaceAll(virtualMachine.Spec.CPU.CoreFraction, "%", "")) + Expect(err).NotTo(HaveOccurred(), "cannot convert CoreFraction value to integer: %s", err) + checkCoreFraction := slices.Contains(sizingPolicy.CoreFractions, virtv2.CoreFractionValue(coreFraction)) + Expect(checkCoreFraction).To(BeTrue(), fmt.Errorf("sizing policy core fraction list does not contain value from spec: %s\n%v", virtualMachine.Spec.CPU.CoreFraction, sizingPolicy.CoreFractions)) +} + +func CompareVirtualMachineClassReadyStatus(vmNamespace, vmName string, expectedStatus metav1.ConditionStatus) { + GinkgoHelper() + vm := virtv2.VirtualMachine{} + err := GetObject(kc.ResourceVM, vmName, &vm, kc.GetOptions{Namespace: vmNamespace}) + Expect(err).NotTo(HaveOccurred(), "%v", err) + status, err := GetConditionStatus(&vm, vmcondition.TypeClassReady.String()) + Expect(err).NotTo(HaveOccurred(), "%v", err) + Expect(status).To(Equal(expectedStatus), fmt.Sprintf("VirtualMachineClassReady status should be '%s'", expectedStatus)) +} diff --git a/tests/e2e/testdata/affinity-toleration/vm/base/vm.yaml b/tests/e2e/testdata/affinity-toleration/vm/base/vm.yaml index b34bd9a8c6..19d4eedcd0 100644 --- a/tests/e2e/testdata/affinity-toleration/vm/base/vm.yaml +++ b/tests/e2e/testdata/affinity-toleration/vm/base/vm.yaml @@ -4,7 +4,7 @@ metadata: name: vm spec: bootloader: EFI - virtualMachineClassName: affinity-discovery + virtualMachineClassName: affinity-toleration-discovery cpu: cores: 1 coreFraction: 5% diff --git a/tests/e2e/testdata/affinity-toleration/vmc.yaml b/tests/e2e/testdata/affinity-toleration/vmc.yaml index 0a075c1fce..d5a4a38b29 100644 --- a/tests/e2e/testdata/affinity-toleration/vmc.yaml +++ b/tests/e2e/testdata/affinity-toleration/vmc.yaml @@ -1,7 +1,7 @@ apiVersion: virtualization.deckhouse.io/v1alpha2 kind: VirtualMachineClass metadata: - name: affinity-discovery + name: affinity-toleration-discovery spec: cpu: type: Discovery diff --git a/tests/e2e/testdata/complex-test/vm/overlays/vm-a-not-b/vm.vmclass.patch.yaml b/tests/e2e/testdata/complex-test/vm/overlays/vm-a-not-b/vm.vmclass.patch.yaml index 6974a7d82e..cd9dac0ead 100644 --- a/tests/e2e/testdata/complex-test/vm/overlays/vm-a-not-b/vm.vmclass.patch.yaml +++ b/tests/e2e/testdata/complex-test/vm/overlays/vm-a-not-b/vm.vmclass.patch.yaml @@ -3,4 +3,4 @@ kind: VirtualMachine metadata: name: vm spec: - virtualMachineClassName: affinity-discovery + virtualMachineClassName: complex-discovery diff --git a/tests/e2e/testdata/complex-test/vm/overlays/vm-b-not-a/vm.vmclass.patch.yaml b/tests/e2e/testdata/complex-test/vm/overlays/vm-b-not-a/vm.vmclass.patch.yaml index 6974a7d82e..cd9dac0ead 100644 --- a/tests/e2e/testdata/complex-test/vm/overlays/vm-b-not-a/vm.vmclass.patch.yaml +++ b/tests/e2e/testdata/complex-test/vm/overlays/vm-b-not-a/vm.vmclass.patch.yaml @@ -3,4 +3,4 @@ kind: VirtualMachine metadata: name: vm spec: - virtualMachineClassName: affinity-discovery + virtualMachineClassName: complex-discovery diff --git a/tests/e2e/testdata/complex-test/vm/overlays/vm-c-and-a/vm.vmclass.patch.yaml b/tests/e2e/testdata/complex-test/vm/overlays/vm-c-and-a/vm.vmclass.patch.yaml index 6974a7d82e..cd9dac0ead 100644 --- a/tests/e2e/testdata/complex-test/vm/overlays/vm-c-and-a/vm.vmclass.patch.yaml +++ b/tests/e2e/testdata/complex-test/vm/overlays/vm-c-and-a/vm.vmclass.patch.yaml @@ -3,4 +3,4 @@ kind: VirtualMachine metadata: name: vm spec: - virtualMachineClassName: affinity-discovery + virtualMachineClassName: complex-discovery diff --git a/tests/e2e/testdata/complex-test/vmc.yaml b/tests/e2e/testdata/complex-test/vmc.yaml index 0a075c1fce..7ef9aaf78b 100644 --- a/tests/e2e/testdata/complex-test/vmc.yaml +++ b/tests/e2e/testdata/complex-test/vmc.yaml @@ -1,7 +1,7 @@ apiVersion: virtualization.deckhouse.io/v1alpha2 kind: VirtualMachineClass metadata: - name: affinity-discovery + name: complex-discovery spec: cpu: type: Discovery diff --git a/tests/e2e/testdata/sizing-policy/overlays/existing-vmclass/kustomization.yaml b/tests/e2e/testdata/sizing-policy/overlays/existing-vmclass/kustomization.yaml index d9f8ae4fb4..279d8aa88c 100644 --- a/tests/e2e/testdata/sizing-policy/overlays/existing-vmclass/kustomization.yaml +++ b/tests/e2e/testdata/sizing-policy/overlays/existing-vmclass/kustomization.yaml @@ -7,7 +7,7 @@ patches: - patch: |- - op: replace path: /spec/virtualMachineClassName - value: discovery + value: sizing-policy-discovery target: kind: VirtualMachine name: vm diff --git a/tests/e2e/testdata/sizing-policy/vmc.yaml b/tests/e2e/testdata/sizing-policy/vmc.yaml index 3d5c38a4e5..48baa39771 100644 --- a/tests/e2e/testdata/sizing-policy/vmc.yaml +++ b/tests/e2e/testdata/sizing-policy/vmc.yaml @@ -1,7 +1,7 @@ apiVersion: virtualization.deckhouse.io/v1alpha2 kind: VirtualMachineClass metadata: - name: discovery + name: sizing-policy-discovery spec: cpu: discovery: diff --git a/tests/e2e/tests_suite_test.go b/tests/e2e/tests_suite_test.go index a267306d8e..98ccbf8e5b 100644 --- a/tests/e2e/tests_suite_test.go +++ b/tests/e2e/tests_suite_test.go @@ -17,6 +17,7 @@ limitations under the License. package e2e import ( + "errors" "fmt" "log" "reflect" @@ -26,9 +27,10 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "golang.org/x/sync/errgroup" corev1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - apiruntime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" @@ -50,7 +52,7 @@ const ( Timeout = 90 * time.Second ShortWaitDuration = 60 * time.Second LongWaitDuration = 300 * time.Second - MaxWaitTimeout = 600 * time.Second + MaxWaitTimeout = 1000 * time.Second PhaseAttached = "Attached" PhaseReady = "Ready" PhaseBound = "Bound" @@ -117,7 +119,7 @@ func init() { log.Fatal(err) } - scheme := apiruntime.NewScheme() + scheme := runtime.NewScheme() err = virtv2.AddToScheme(scheme) if err != nil { log.Fatal(err) @@ -142,43 +144,7 @@ func init() { log.Fatal(err) } ChmodFile(conf.TestData.Sshkey, 0o600) - conf.Namespace = fmt.Sprintf("%s-%s", namePrefix, conf.Namespace) phaseByVolumeBindingMode = GetPhaseByVolumeBindingMode(conf.StorageClass.DefaultStorageClass) - // TODO: get kustomization files from testdata directory when all tests will be refactored - var kustomizationFiles []string - v := reflect.ValueOf(conf.TestData) - t := reflect.TypeOf(conf.TestData) - - if v.Kind() == reflect.Struct { - for i := 0; i < v.NumField(); i++ { - field := v.Field(i) - fieldType := t.Field(i) - - // Ignore - if fieldType.Name == "Sshkey" || fieldType.Name == "SSHUser" { - continue - } - - if field.Kind() == reflect.String { - path := fmt.Sprintf("%s/%s", field.String(), "kustomization.yaml") - kustomizationFiles = append(kustomizationFiles, path) - } - } - } - for _, filePath := range kustomizationFiles { - if err = kustomize.SetParams(filePath, conf.Namespace, namePrefix); err != nil { - log.Fatal(err) - } - } - - if !config.IsReusable() { - errs := Cleanup() - if len(errs) != 0 { - log.Fatal(errs) - } - } else { - log.Println("Run test in REUSABLE mode") - } } func newRestConfig(transport config.ClusterTransport) (*rest.Config, error) { @@ -209,18 +175,52 @@ func TestTests(t *testing.T) { if (ginkgoutil.FailureBehaviourEnvSwitcher{}).IsStopOnFailure() || !config.IsCleanUpNeeded() { return } +} - err := Cleanup() - if len(err) != 0 { - log.Fatal(err) +var _ = SynchronizedBeforeSuite(func() { + // TODO: get kustomization files from testdata directory when all tests will be refactored + var kustomizationFiles []string + v := reflect.ValueOf(conf.TestData) + t := reflect.TypeOf(conf.TestData) + + if v.Kind() == reflect.Struct { + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + fieldType := t.Field(i) + + // Ignore + if fieldType.Name == "Sshkey" || fieldType.Name == "SSHUser" { + continue + } + + if field.Kind() == reflect.String { + path := fmt.Sprintf("%s/%s", field.String(), "kustomization.yaml") + kustomizationFiles = append(kustomizationFiles, path) + } + } } -} -var _ = BeforeSuite(func() { - StartV12nControllerLogStream(logStreamByV12nControllerPod) + ns := fmt.Sprintf("%s-%s", namePrefix, conf.NamespaceSuffix) + for _, filePath := range kustomizationFiles { + err := kustomize.SetParams(filePath, ns, namePrefix) + if err != nil { + log.Fatal(err) + } + } + + if !config.IsReusable() { + err := Cleanup() + if err != nil { + log.Fatal(err) + } + } else { + log.Println("Run test in REUSABLE mode") + } +}, func() { + // StartV12nControllerLogStream(logStreamByV12nControllerPod) }) -var _ = AfterSuite(func() { +var _ = SynchronizedAfterSuite(func() { errs := make([]error, 0) checkErrs := CheckV12nControllerRestarts(logStreamByV12nControllerPod) if len(checkErrs) != 0 { @@ -231,64 +231,21 @@ var _ = AfterSuite(func() { errs = append(errs, stopErrs...) } Expect(errs).Should(BeEmpty()) -}) - -func Cleanup() []error { - cleanupErrs := make([]error, 0) - - res := kubectl.Delete(kc.DeleteOptions{ - IgnoreNotFound: true, - Labels: map[string]string{"id": namePrefix}, - Resource: kc.ResourceProject, - }) - if res.Error() != nil { - cleanupErrs = append( - cleanupErrs, fmt.Errorf("cmd: %s\nstderr: %s", res.GetCmd(), res.StdErr()), - ) - } - - testCases, err := conf.GetTestCases() +}, func() { + err := Cleanup() if err != nil { - cleanupErrs = append(cleanupErrs, err) - return cleanupErrs + log.Fatal(err) } +}) - for _, tc := range testCases { - kustomizeFilePath := fmt.Sprintf("%s/kustomization.yaml", tc) - namespace, err := kustomize.GetNamespace(kustomizeFilePath) - if err != nil { - cleanupErrs = append( - cleanupErrs, fmt.Errorf("cannot cleanup namespace %q: %w", namespace, err), - ) - continue - } - res := kubectl.Delete(kc.DeleteOptions{ - Filename: []string{namespace}, - IgnoreNotFound: true, - Resource: kc.ResourceNamespace, - }) - if res.Error() != nil { - cleanupErrs = append( - cleanupErrs, fmt.Errorf("cmd: %s\nstderr: %s", res.GetCmd(), res.StdErr()), - ) - continue - } - } +func Cleanup() error { + var eg errgroup.Group - for _, r := range conf.CleanupResources { - res = kubectl.Delete(kc.DeleteOptions{ - IgnoreNotFound: true, - Labels: map[string]string{"id": namePrefix}, - Resource: kc.Resource(r), - }) - if res.Error() != nil { - cleanupErrs = append( - cleanupErrs, fmt.Errorf("cmd: %s\nstderr: %s", res.GetCmd(), res.StdErr()), - ) - } - } + eg.Go(deleteProject) + eg.Go(deleteNamespaces) + eg.Go(deleteResources) - return cleanupErrs + return eg.Wait() } // This function is used to detect `v12n-controller` errors while the test suite is running. @@ -387,3 +344,64 @@ func CheckV12nControllerRestarts(logStreamByPod map[string]*el.LogStream) []erro } return errs } + +func deleteProject() error { + res := kubectl.Delete(kc.DeleteOptions{ + IgnoreNotFound: true, + Labels: map[string]string{"id": namePrefix}, + Resource: kc.ResourceProject, + }) + if res.Error() != nil { + return fmt.Errorf("cmd: %s\nstderr: %s", res.GetCmd(), res.StdErr()) + } + + return nil +} + +func deleteNamespaces() error { + testCases, cleanupErr := conf.GetTestCases() + if cleanupErr != nil { + return cleanupErr + } + + var eg errgroup.Group + + for _, tc := range testCases { + eg.Go(func() error { + kustomizeFilePath := fmt.Sprintf("%s/kustomization.yaml", tc) + namespace, err := kustomize.GetNamespace(kustomizeFilePath) + if err != nil { + return fmt.Errorf("cannot cleanup namespace %q: %w", namespace, err) + } + res := kubectl.Delete(kc.DeleteOptions{ + Filename: []string{namespace}, + IgnoreNotFound: true, + Resource: kc.ResourceNamespace, + }) + if res.Error() != nil { + return fmt.Errorf("cmd: %s\nstderr: %s", res.GetCmd(), res.StdErr()) + } + + return nil + }) + } + + return eg.Wait() +} + +func deleteResources() error { + var cleanupErr error + + for _, r := range conf.CleanupResources { + res := kubectl.Delete(kc.DeleteOptions{ + IgnoreNotFound: true, + Labels: map[string]string{"id": namePrefix}, + Resource: kc.Resource(r), + }) + if res.Error() != nil { + cleanupErr = errors.Join(cleanupErr, fmt.Errorf("cmd: %s\nstderr: %s", res.GetCmd(), res.StdErr())) + } + } + + return cleanupErr +} diff --git a/tests/e2e/util_test.go b/tests/e2e/util_test.go index 81fd0394cf..37530772cf 100644 --- a/tests/e2e/util_test.go +++ b/tests/e2e/util_test.go @@ -139,46 +139,46 @@ func ItCheckStatusFromFile(filepath, output, compareField string) { } } -func WaitResource(resource kc.Resource, name, waitFor string, timeout time.Duration) { +func WaitResource(resource kc.Resource, ns, name, waitFor string, timeout time.Duration) { GinkgoHelper() waitOpts := kc.WaitOptions{ - Namespace: conf.Namespace, + Namespace: ns, For: waitFor, Timeout: timeout, } res := kubectl.WaitResources(resource, waitOpts, name) - Expect(res.Error()).NotTo(HaveOccurred(), "wait failed %s %s/%s.\n%s", resource, conf.Namespace, name, res.StdErr()) + Expect(res.Error()).NotTo(HaveOccurred(), "wait failed %s %s/%s.\n%s", resource, ns, name, res.StdErr()) } -func PatchResource(resource kc.Resource, name string, patch []*kc.JSONPatch) { +func PatchResource(resource kc.Resource, ns, name string, patch []*kc.JSONPatch) { GinkgoHelper() res := kubectl.PatchResource(resource, name, kc.PatchOptions{ - Namespace: conf.Namespace, + Namespace: ns, JSONPatch: patch, }) - Expect(res.Error()).NotTo(HaveOccurred(), "patch failed %s %s/%s.\n%s", resource, conf.Namespace, name, + Expect(res.Error()).NotTo(HaveOccurred(), "patch failed %s %s/%s.\n%s", resource, ns, name, res.StdErr()) } -func MergePatchResource(resource kc.Resource, name, patch string) error { +func MergePatchResource(resource kc.Resource, ns, name, patch string) error { GinkgoHelper() res := kubectl.PatchResource(resource, name, kc.PatchOptions{ - Namespace: conf.Namespace, + Namespace: ns, MergePatch: patch, }) if res.Error() != nil { - return fmt.Errorf("patch failed %s %s/%s.\n%s", resource, conf.Namespace, name, res.StdErr()) + return fmt.Errorf("patch failed %s %s/%s.\n%s", resource, ns, name, res.StdErr()) } return nil } -func CheckField(resource kc.Resource, name, output, compareValue string) { +func CheckField(resource kc.Resource, ns, name, output, compareValue string) { GinkgoHelper() res := kubectl.GetResource(resource, name, kc.GetOptions{ - Namespace: conf.Namespace, + Namespace: ns, Output: output, }) - Expect(res.Error()).NotTo(HaveOccurred(), "get failed %s %s/%s.\n%s", resource, conf.Namespace, name, res.StdErr()) + Expect(res.Error()).NotTo(HaveOccurred(), "get failed %s %s/%s.\n%s", resource, ns, name, res.StdErr()) Expect(res.StdOut()).To(Equal(compareValue)) } @@ -539,7 +539,7 @@ type ResourcesToDelete struct { } // This function checks that all resources in test case can be deleted correctly. -func DeleteTestCaseResources(resources ResourcesToDelete) { +func DeleteTestCaseResources(ns string, resources ResourcesToDelete) { By("Response on deletion request should be successful", func() { const errMessage = "cannot delete test case resources" @@ -558,7 +558,7 @@ func DeleteTestCaseResources(resources ResourcesToDelete) { for _, r := range resources.AdditionalResources { res := kubectl.Delete(kc.DeleteOptions{ Labels: r.Labels, - Namespace: conf.Namespace, + Namespace: ns, Resource: r.Resource, }) Expect(res.Error()).NotTo(HaveOccurred(), fmt.Sprintf("%s\ncmd: %s\nstderr: %s", errMessage, res.GetCmd(), res.StdErr())) @@ -574,31 +574,31 @@ func DeleteTestCaseResources(resources ResourcesToDelete) { }) } -func RebootVirtualMachinesByVMOP(label map[string]string, virtualMachines ...string) { +func RebootVirtualMachinesByVMOP(label map[string]string, vmNamespace string, vmNames ...string) { GinkgoHelper() - CreateAndApplyVMOPs(label, virtv2.VMOPTypeRestart, virtualMachines...) + CreateAndApplyVMOPs(label, virtv2.VMOPTypeRestart, vmNamespace, vmNames...) } -func StopVirtualMachinesByVMOP(label map[string]string, virtualMachines ...string) { +func StopVirtualMachinesByVMOP(label map[string]string, vmNamespace string, vmNames ...string) { GinkgoHelper() - CreateAndApplyVMOPs(label, virtv2.VMOPTypeStop, virtualMachines...) + CreateAndApplyVMOPs(label, virtv2.VMOPTypeStop, vmNamespace, vmNames...) } -func StartVirtualMachinesByVMOP(label map[string]string, virtualMachines ...string) { +func StartVirtualMachinesByVMOP(label map[string]string, vmNamespace string, vmNames ...string) { GinkgoHelper() - CreateAndApplyVMOPs(label, virtv2.VMOPTypeStart, virtualMachines...) + CreateAndApplyVMOPs(label, virtv2.VMOPTypeStart, vmNamespace, vmNames...) } -func CreateAndApplyVMOPs(label map[string]string, vmopType virtv2.VMOPType, virtualMachines ...string) { - CreateAndApplyVMOPsWithSuffix(label, "", vmopType, virtualMachines...) +func CreateAndApplyVMOPs(label map[string]string, vmopType virtv2.VMOPType, vmNamespace string, vmNames ...string) { + CreateAndApplyVMOPsWithSuffix(label, "", vmopType, vmNamespace, vmNames...) } -func CreateAndApplyVMOPsWithSuffix(label map[string]string, suffix string, vmopType virtv2.VMOPType, virtualMachines ...string) { - for _, vm := range virtualMachines { - vmop, err := yaml.Marshal(GenerateVMOPWithSuffix(vm, suffix, label, vmopType)) +func CreateAndApplyVMOPsWithSuffix(label map[string]string, suffix string, vmopType virtv2.VMOPType, vmNamespace string, vmNames ...string) { + for _, vmName := range vmNames { + vmop, err := yaml.Marshal(GenerateVMOPWithSuffix(vmName, suffix, label, vmopType)) Expect(err).NotTo(HaveOccurred()) var cmd strings.Builder - cmd.WriteString(fmt.Sprintf("-n %s create -f - </dev/null &", d8.SSHOptions{ - Namespace: conf.Namespace, + Namespace: ns, Username: conf.TestData.SSHUser, IdenityFile: conf.TestData.Sshkey, Timeout: ShortTimeout, @@ -99,13 +100,13 @@ var _ = Describe("Virtual machine cancel migration", SIGMigration(), ginkgoutil. time.Sleep(20 * time.Second) By("Starting migrations for virtual machines") - MigrateVirtualMachines(testCaseLabel, vmNames...) + MigrateVirtualMachines(testCaseLabel, ns, vmNames...) someCompleted := false Eventually(func() error { vmops := &virtv2.VirtualMachineOperationList{} - err := GetObjects(kc.ResourceVMOP, vmops, kc.GetOptions{Labels: testCaseLabel, Namespace: conf.Namespace}) + err := GetObjects(kc.ResourceVMOP, vmops, kc.GetOptions{Labels: testCaseLabel, Namespace: ns}) if err != nil { return err } @@ -115,7 +116,7 @@ var _ = Describe("Virtual machine cancel migration", SIGMigration(), ginkgoutil. } kvvmis := &virtv1.VirtualMachineInstanceList{} - err = GetObjects(kc.ResourceKubevirtVMI, kvvmis, kc.GetOptions{Labels: testCaseLabel, Namespace: conf.Namespace}) + err = GetObjects(kc.ResourceKubevirtVMI, kvvmis, kc.GetOptions{Labels: testCaseLabel, Namespace: ns}) if err != nil { return err } @@ -169,7 +170,7 @@ var _ = Describe("Virtual machine cancel migration", SIGMigration(), ginkgoutil. Eventually(func() error { kvvmis := &virtv1.VirtualMachineInstanceList{} - err = GetObjects(kc.ResourceKubevirtVMI, kvvmis, kc.GetOptions{Labels: testCaseLabel, Namespace: conf.Namespace}) + err = GetObjects(kc.ResourceKubevirtVMI, kvvmis, kc.GetOptions{Labels: testCaseLabel, Namespace: ns}) if err != nil { return err } diff --git a/tests/e2e/vm_migration_test.go b/tests/e2e/vm_migration_test.go index 65ff8f3d43..5586a54057 100644 --- a/tests/e2e/vm_migration_test.go +++ b/tests/e2e/vm_migration_test.go @@ -29,13 +29,9 @@ import ( kc "github.com/deckhouse/virtualization/tests/e2e/kubectl" ) -func MigrateVirtualMachines(label map[string]string, virtualMachines ...string) { - GinkgoHelper() - CreateAndApplyVMOPs(label, virtv2.VMOPTypeEvict, virtualMachines...) -} - -var _ = Describe("Virtual machine migration", SIGMigration(), ginkgoutil.CommonE2ETestDecorators(), func() { +var _ = Describe("VirtualMachineMigration", SIGMigration(), ginkgoutil.CommonE2ETestDecorators(), func() { testCaseLabel := map[string]string{"testcase": "vm-migration"} + var ns string AfterEach(func() { if CurrentSpecReport().Failed() { @@ -46,9 +42,10 @@ var _ = Describe("Virtual machine migration", SIGMigration(), ginkgoutil.CommonE Context("Preparing the environment", func() { It("sets the namespace", func() { kustomization := fmt.Sprintf("%s/%s", conf.TestData.VMMigration, "kustomization.yaml") - ns, err := kustomize.GetNamespace(kustomization) + var err error + ns, err = kustomize.GetNamespace(kustomization) Expect(err).NotTo(HaveOccurred(), "%w", err) - conf.SetNamespace(ns) + Expect(ns).NotTo(BeEmpty()) }) }) @@ -57,7 +54,7 @@ var _ = Describe("Virtual machine migration", SIGMigration(), ginkgoutil.CommonE if config.IsReusable() { res := kubectl.List(kc.ResourceVM, kc.GetOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Output: "jsonpath='{.items[*].metadata.name}'", }) Expect(res.Error()).NotTo(HaveOccurred(), res.StdErr()) @@ -80,7 +77,7 @@ var _ = Describe("Virtual machine migration", SIGMigration(), ginkgoutil.CommonE By("Virtual machine agents should be ready") WaitVMAgentReady(kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -90,13 +87,13 @@ var _ = Describe("Virtual machine migration", SIGMigration(), ginkgoutil.CommonE It("starts migrations", func() { res := kubectl.List(kc.ResourceVM, kc.GetOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Output: "jsonpath='{.items[*].metadata.name}'", }) Expect(res.WasSuccess()).To(Equal(true), res.StdErr()) vms := strings.Split(res.StdOut(), " ") - MigrateVirtualMachines(testCaseLabel, vms...) + MigrateVirtualMachines(testCaseLabel, ns, vms...) }) }) @@ -105,13 +102,13 @@ var _ = Describe("Virtual machine migration", SIGMigration(), ginkgoutil.CommonE By(fmt.Sprintf("VMOPs should be in %s phases", virtv2.VMOPPhaseCompleted)) WaitPhaseByLabel(kc.ResourceVMOP, string(virtv2.VMOPPhaseCompleted), kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) By("Virtual machines should be migrated") WaitByLabel(kc.ResourceVM, kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, For: "'jsonpath={.status.migrationState.result}=Succeeded'", }) @@ -120,14 +117,14 @@ var _ = Describe("Virtual machine migration", SIGMigration(), ginkgoutil.CommonE It("checks VMs external connection after migrations", func() { res := kubectl.List(kc.ResourceVM, kc.GetOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Output: "jsonpath='{.items[*].metadata.name}'", }) Expect(res.WasSuccess()).To(Equal(true), res.StdErr()) vms := strings.Split(res.StdOut(), " ") - CheckCiliumAgents(kubectl, vms...) - CheckExternalConnection(externalHost, httpStatusOk, vms...) + CheckCiliumAgents(kubectl, ns, vms...) + CheckExternalConnection(externalHost, httpStatusOk, ns, vms...) }) }) @@ -146,7 +143,12 @@ var _ = Describe("Virtual machine migration", SIGMigration(), ginkgoutil.CommonE resourcesToDelete.KustomizationDir = conf.TestData.VMMigration } - DeleteTestCaseResources(resourcesToDelete) + DeleteTestCaseResources(ns, resourcesToDelete) }) }) }) + +func MigrateVirtualMachines(label map[string]string, vmNamespace string, vmNames ...string) { + GinkgoHelper() + CreateAndApplyVMOPs(label, virtv2.VMOPTypeEvict, vmNamespace, vmNames...) +} diff --git a/tests/e2e/vm_version_test.go b/tests/e2e/vm_version_test.go index 9e8207fb7f..cdb488fd53 100644 --- a/tests/e2e/vm_version_test.go +++ b/tests/e2e/vm_version_test.go @@ -28,7 +28,7 @@ import ( kc "github.com/deckhouse/virtualization/tests/e2e/kubectl" ) -var _ = Describe("Virtual machine versions", ginkgoutil.CommonE2ETestDecorators(), func() { +var _ = Describe("VirtualMachineVersions", ginkgoutil.CommonE2ETestDecorators(), func() { BeforeEach(func() { if config.IsReusable() { Skip("Test not available in REUSABLE mode: not supported yet.") @@ -36,6 +36,7 @@ var _ = Describe("Virtual machine versions", ginkgoutil.CommonE2ETestDecorators( }) testCaseLabel := map[string]string{"testcase": "vm-versions"} + var ns string AfterEach(func() { if CurrentSpecReport().Failed() { @@ -46,9 +47,9 @@ var _ = Describe("Virtual machine versions", ginkgoutil.CommonE2ETestDecorators( Context("Preparing the environment", func() { It("sets the namespace", func() { kustomization := fmt.Sprintf("%s/%s", conf.TestData.VMVersions, "kustomization.yaml") - ns, err := kustomize.GetNamespace(kustomization) + var err error + ns, err = kustomize.GetNamespace(kustomization) Expect(err).NotTo(HaveOccurred(), "%w", err) - conf.SetNamespace(ns) }) }) @@ -67,7 +68,7 @@ var _ = Describe("Virtual machine versions", ginkgoutil.CommonE2ETestDecorators( By(fmt.Sprintf("VIs should be in %s phase", PhaseReady)) WaitPhaseByLabel(kc.ResourceVI, PhaseReady, kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -78,7 +79,7 @@ var _ = Describe("Virtual machine versions", ginkgoutil.CommonE2ETestDecorators( By(fmt.Sprintf("VM should be in %s phase", PhaseRunning)) WaitPhaseByLabel(kc.ResourceVM, PhaseRunning, kc.WaitOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, Timeout: MaxWaitTimeout, }) }) @@ -89,7 +90,7 @@ var _ = Describe("Virtual machine versions", ginkgoutil.CommonE2ETestDecorators( var vms virtv2.VirtualMachineList err := GetObjects(kc.ResourceVM, &vms, kc.GetOptions{ Labels: testCaseLabel, - Namespace: conf.Namespace, + Namespace: ns, }) Expect(err).NotTo(HaveOccurred()) @@ -111,7 +112,7 @@ var _ = Describe("Virtual machine versions", ginkgoutil.CommonE2ETestDecorators( Context("When test is completed", func() { It("deletes test case resources", func() { - DeleteTestCaseResources(ResourcesToDelete{ + DeleteTestCaseResources(ns, ResourcesToDelete{ KustomizationDir: conf.TestData.VMVersions, }) }) From 6da1c00cc0c8ab48b381dd62d7d18ee24e05ac98 Mon Sep 17 00:00:00 2001 From: Roman Sysoev Date: Tue, 8 Jul 2025 16:03:36 +0300 Subject: [PATCH 2/3] chore(test): fix some problems - increase context timeout in the ipam test - run complex-test in a single process - run log streamer in a single instance - ignore not found when deleting kustomized resources - fix some typos Signed-off-by: Roman Sysoev --- tests/e2e/affinity_toleration_test.go | 2 +- tests/e2e/complex_test.go | 2 +- tests/e2e/go.mod | 2 +- tests/e2e/ipam_test.go | 2 +- tests/e2e/tests_suite_test.go | 27 +++++++++++++++------------ tests/e2e/util_test.go | 1 + tests/e2e/vd_snapshots_test.go | 2 +- tests/e2e/vm_configuration_test.go | 2 +- tests/e2e/vm_connectivity_test.go | 4 ++-- tests/e2e/vm_version_test.go | 4 ++-- 10 files changed, 26 insertions(+), 22 deletions(-) diff --git a/tests/e2e/affinity_toleration_test.go b/tests/e2e/affinity_toleration_test.go index cc4098094c..417ddc5e36 100644 --- a/tests/e2e/affinity_toleration_test.go +++ b/tests/e2e/affinity_toleration_test.go @@ -463,7 +463,7 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E Context("When test is completed", func() { It("deletes test case resources", func() { - DeleteTestCaseResources(ResourcesToDelete{KustomizationDir: conf.TestData.AffinityToleration}) + DeleteTestCaseResources(ns, ResourcesToDelete{KustomizationDir: conf.TestData.AffinityToleration}) }) }) }) diff --git a/tests/e2e/complex_test.go b/tests/e2e/complex_test.go index 20be396197..d65c3618e0 100644 --- a/tests/e2e/complex_test.go +++ b/tests/e2e/complex_test.go @@ -30,7 +30,7 @@ import ( kc "github.com/deckhouse/virtualization/tests/e2e/kubectl" ) -var _ = Describe("Complex test", ginkgoutil.CommonE2ETestDecorators(), func() { +var _ = Describe("ComplexTest", Serial, ginkgoutil.CommonE2ETestDecorators(), func() { var ( testCaseLabel = map[string]string{"testcase": "complex-test"} hasNoConsumerLabel = map[string]string{"hasNoConsumer": "complex-test"} diff --git a/tests/e2e/go.mod b/tests/e2e/go.mod index 2a08973f6d..82775c78f5 100644 --- a/tests/e2e/go.mod +++ b/tests/e2e/go.mod @@ -9,6 +9,7 @@ require ( github.com/kubernetes-csi/external-snapshotter/client/v8 v8.0.0 github.com/onsi/ginkgo/v2 v2.21.0 github.com/onsi/gomega v1.35.1 + golang.org/x/sync v0.8.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.32.3 k8s.io/apimachinery v0.32.3 @@ -64,7 +65,6 @@ require ( golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/net v0.30.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect - golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.26.0 // indirect golang.org/x/term v0.25.0 // indirect golang.org/x/text v0.19.0 // indirect diff --git a/tests/e2e/ipam_test.go b/tests/e2e/ipam_test.go index c262916057..7ef65566fa 100644 --- a/tests/e2e/ipam_test.go +++ b/tests/e2e/ipam_test.go @@ -59,7 +59,7 @@ var _ = Describe("IPAM", ginkgoutil.CommonE2ETestDecorators(), func() { }) BeforeEach(func() { - ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second) + ctx, cancel = context.WithTimeout(context.Background(), 50*time.Second) vmip = &virtv2.VirtualMachineIPAddress{ ObjectMeta: metav1.ObjectMeta{ diff --git a/tests/e2e/tests_suite_test.go b/tests/e2e/tests_suite_test.go index 98ccbf8e5b..ec5e1328fe 100644 --- a/tests/e2e/tests_suite_test.go +++ b/tests/e2e/tests_suite_test.go @@ -171,20 +171,15 @@ func TestTests(t *testing.T) { RegisterFailHandler(Fail) fmt.Fprintf(GinkgoWriter, "Starting test suite\n") RunSpecs(t, "Tests") - - if (ginkgoutil.FailureBehaviourEnvSwitcher{}).IsStopOnFailure() || !config.IsCleanUpNeeded() { - return - } } var _ = SynchronizedBeforeSuite(func() { - // TODO: get kustomization files from testdata directory when all tests will be refactored var kustomizationFiles []string v := reflect.ValueOf(conf.TestData) t := reflect.TypeOf(conf.TestData) if v.Kind() == reflect.Struct { - for i := 0; i < v.NumField(); i++ { + for i := range v.NumField() { field := v.Field(i) fieldType := t.Field(i) @@ -216,11 +211,11 @@ var _ = SynchronizedBeforeSuite(func() { } else { log.Println("Run test in REUSABLE mode") } -}, func() { - // StartV12nControllerLogStream(logStreamByV12nControllerPod) -}) -var _ = SynchronizedAfterSuite(func() { + StartV12nControllerLogStream(logStreamByV12nControllerPod) +}, func() {}) + +var _ = SynchronizedAfterSuite(func() {}, func() { errs := make([]error, 0) checkErrs := CheckV12nControllerRestarts(logStreamByV12nControllerPod) if len(checkErrs) != 0 { @@ -231,7 +226,11 @@ var _ = SynchronizedAfterSuite(func() { errs = append(errs, stopErrs...) } Expect(errs).Should(BeEmpty()) -}, func() { + + if (ginkgoutil.FailureBehaviourEnvSwitcher{}).IsStopOnFailure() || !config.IsCleanUpNeeded() { + return + } + err := Cleanup() if err != nil { log.Fatal(err) @@ -241,7 +240,11 @@ var _ = SynchronizedAfterSuite(func() { func Cleanup() error { var eg errgroup.Group - eg.Go(deleteProject) + err := deleteProject() + if err != nil { + return err + } + eg.Go(deleteNamespaces) eg.Go(deleteResources) diff --git a/tests/e2e/util_test.go b/tests/e2e/util_test.go index 37530772cf..da5a9fa831 100644 --- a/tests/e2e/util_test.go +++ b/tests/e2e/util_test.go @@ -551,6 +551,7 @@ func DeleteTestCaseResources(ns string, resources ResourcesToDelete) { res := kubectl.Delete(kc.DeleteOptions{ Filename: []string{resources.KustomizationDir}, FilenameOption: kc.Kustomize, + IgnoreNotFound: true, }) Expect(res.Error()).NotTo(HaveOccurred(), fmt.Sprintf("%s\nkustomizationDir: %s\ncmd: %s\nstderr: %s", errMessage, resources.KustomizationDir, res.GetCmd(), res.StdErr())) } diff --git a/tests/e2e/vd_snapshots_test.go b/tests/e2e/vd_snapshots_test.go index c582c12346..21484e91da 100644 --- a/tests/e2e/vd_snapshots_test.go +++ b/tests/e2e/vd_snapshots_test.go @@ -46,7 +46,7 @@ const ( var _ = Describe("VirtualDiskSnapshots", ginkgoutil.CommonE2ETestDecorators(), func() { var ( defaultVolumeSnapshotClassName string - testCaseLabel = map[string]string{"testcase": "vd-snapshots"} + testCaseLabel = map[string]string{"testcase": "vd-snapshots", "id": namePrefix} attachedVirtualDiskLabel = map[string]string{"attachedVirtualDisk": ""} hasNoConsumerLabel = map[string]string{"hasNoConsumer": "vd-snapshots"} vmAutomaticWithHotplug = map[string]string{"vm": "automatic-with-hotplug"} diff --git a/tests/e2e/vm_configuration_test.go b/tests/e2e/vm_configuration_test.go index c237569ec3..d047ca27cf 100644 --- a/tests/e2e/vm_configuration_test.go +++ b/tests/e2e/vm_configuration_test.go @@ -315,7 +315,7 @@ func CheckCPUCoresNumber(approvalMode, stage string, requiredValue int, vmNamesp } } -func CheckCPUCoresNumberFromVirtualMachine(requiredValue string, vmNamespace string, vmNames ...string) { +func CheckCPUCoresNumberFromVirtualMachine(requiredValue, vmNamespace string, vmNames ...string) { By("Checking the number of processor cores after changing from virtual machine") for _, vmName := range vmNames { cmd := "nproc --all" diff --git a/tests/e2e/vm_connectivity_test.go b/tests/e2e/vm_connectivity_test.go index 2000196021..09fc18ae9e 100644 --- a/tests/e2e/vm_connectivity_test.go +++ b/tests/e2e/vm_connectivity_test.go @@ -41,7 +41,7 @@ const ( nginxActiveStatus = "active" ) -var _ = Describe(fmt.Sprintf("VirtualMachineConnectivity %d", GinkgoParallelProcess()), ginkgoutil.CommonE2ETestDecorators(), func() { +var _ = Describe("VirtualMachineConnectivity", ginkgoutil.CommonE2ETestDecorators(), func() { var ( testCaseLabel = map[string]string{"testcase": "vm-connectivity"} aObjName = fmt.Sprintf("%s-vm-connectivity-a", namePrefix) @@ -314,7 +314,7 @@ func CheckCiliumAgents(kubectl kc.Kubectl, namespace string, vms ...string) { } } -func CheckExternalConnection(host, httpCode string, vmNamespace string, vmNames ...string) { +func CheckExternalConnection(host, httpCode, vmNamespace string, vmNames ...string) { GinkgoHelper() for _, vmName := range vmNames { By(fmt.Sprintf("Response code from %q should be %q for %q", host, httpCode, vmName)) diff --git a/tests/e2e/vm_version_test.go b/tests/e2e/vm_version_test.go index cdb488fd53..f3868e76a2 100644 --- a/tests/e2e/vm_version_test.go +++ b/tests/e2e/vm_version_test.go @@ -94,13 +94,13 @@ var _ = Describe("VirtualMachineVersions", ginkgoutil.CommonE2ETestDecorators(), }) Expect(err).NotTo(HaveOccurred()) - It("has qemu version is status", func() { + It("has qemu version in the status", func() { for _, vm := range vms.Items { Expect(vm.Status.Versions.Qemu).NotTo(BeEmpty()) } }) - It("has libvirt version is status", func() { + It("has libvirt version in the status", func() { for _, vm := range vms.Items { Expect(vm.Status.Versions.Libvirt).NotTo(BeEmpty()) } From 868f133c65a450848826f0c34dc1642f1ebe804d Mon Sep 17 00:00:00 2001 From: Roman Sysoev Date: Fri, 18 Jul 2025 12:41:15 +0300 Subject: [PATCH 3/3] chore(test): fix some problems - increase migration timeout - fix storage class for unattached disk Signed-off-by: Roman Sysoev --- tests/e2e/affinity_toleration_test.go | 4 ++-- tests/e2e/network/cilium_agents.go | 2 +- tests/e2e/vd_snapshots_test.go | 9 +++++++++ tests/e2e/vm_connectivity_test.go | 2 +- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/tests/e2e/affinity_toleration_test.go b/tests/e2e/affinity_toleration_test.go index 417ddc5e36..4f43de35a3 100644 --- a/tests/e2e/affinity_toleration_test.go +++ b/tests/e2e/affinity_toleration_test.go @@ -35,7 +35,7 @@ import ( kc "github.com/deckhouse/virtualization/tests/e2e/kubectl" ) -var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2ETestDecorators(), func() { +var _ = Describe("VirtualMachineAffinityAndToleration", ginkgoutil.CommonE2ETestDecorators(), func() { const ( nodeLabelKey = "kubernetes.io/hostname" masterLabelKey = "node.deckhouse.io/group" @@ -176,7 +176,7 @@ var _ = Describe("Virtual machine affinity and toleration", ginkgoutil.CommonE2E return fmt.Errorf("the `VirtualMachine` should be %s", virtv2.MachineMigrating) } return nil - }).WithTimeout(Timeout).WithPolling(migratingStatusPollingInterval).Should(Succeed()) + }).WithTimeout(LongWaitDuration).WithPolling(migratingStatusPollingInterval).Should(Succeed()) }() res := kubectl.PatchResource(kc.ResourceVM, vmObjC.Name, kc.PatchOptions{ JSONPatch: []*kc.JSONPatch{ diff --git a/tests/e2e/network/cilium_agents.go b/tests/e2e/network/cilium_agents.go index 60ed468bc9..759eb4eb7b 100644 --- a/tests/e2e/network/cilium_agents.go +++ b/tests/e2e/network/cilium_agents.go @@ -81,7 +81,7 @@ func CheckCilliumAgents(ctx context.Context, kubectl kc.Kubectl, vmName, vmNames } func getVMInfo(kubectl kc.Kubectl, vmName, vmNamespace string) (string, string, error) { - result := kubectl.GetResource(kc.ResourceVM, vmName, kc.GetOptions{Namespace: vmNamespace, Output: "json"}) + result := kubectl.GetResource(virtv2.VirtualMachineResource, vmName, kc.GetOptions{Namespace: vmNamespace, Output: "json"}) if result.Error() != nil { return "", "", fmt.Errorf("failed to get VM: %w", result.Error()) } diff --git a/tests/e2e/vd_snapshots_test.go b/tests/e2e/vd_snapshots_test.go index 21484e91da..525f8af42e 100644 --- a/tests/e2e/vd_snapshots_test.go +++ b/tests/e2e/vd_snapshots_test.go @@ -65,6 +65,15 @@ var _ = Describe("VirtualDiskSnapshots", ginkgoutil.CommonE2ETestDecorators(), f Expect(conf.StorageClass.ImmediateStorageClass).NotTo(BeNil(), "immediate storage class cannot be nil; please set up the immediate storage class in the cluster") + virtualDiskWithoutConsumer := virtv2.VirtualDisk{} + vdWithoutConsumerFilePath := fmt.Sprintf("%s/vd/vd-alpine-http.yaml", conf.TestData.VdSnapshots) + err = UnmarshalResource(vdWithoutConsumerFilePath, &virtualDiskWithoutConsumer) + Expect(err).NotTo(HaveOccurred(), "cannot get object from file: %s\nstderr: %s", vdWithoutConsumerFilePath, err) + + virtualDiskWithoutConsumer.Spec.PersistentVolumeClaim.StorageClass = &conf.StorageClass.ImmediateStorageClass.Name + err = WriteYamlObject(vdWithoutConsumerFilePath, &virtualDiskWithoutConsumer) + Expect(err).NotTo(HaveOccurred(), "cannot update virtual disk with custom storage class: %s\nstderr: %s", vdWithoutConsumerFilePath, err) + defaultVolumeSnapshotClassName, err = GetVolumeSnapshotClassName(conf.StorageClass.DefaultStorageClass) Expect(err).NotTo(HaveOccurred(), "cannot define default `VolumeSnapshotClass`\nstderr: %s", err) }) diff --git a/tests/e2e/vm_connectivity_test.go b/tests/e2e/vm_connectivity_test.go index 09fc18ae9e..373ba5d95c 100644 --- a/tests/e2e/vm_connectivity_test.go +++ b/tests/e2e/vm_connectivity_test.go @@ -173,7 +173,7 @@ var _ = Describe("VirtualMachineConnectivity", ginkgoutil.CommonE2ETestDecorator }) It("checks VMs connection to external network", func() { - CheckCiliumAgents(kubectl, vmA.Name, vmB.Name) + CheckCiliumAgents(kubectl, ns, vmA.Name, vmB.Name) CheckExternalConnection(externalHost, httpStatusOk, ns, vmA.Name, vmB.Name) })