From 5ac9267c1e27f7b1453222fcd2365bd0a3eb4eb7 Mon Sep 17 00:00:00 2001 From: radeore Date: Wed, 15 Jul 2026 16:09:41 -0400 Subject: [PATCH] Add LocalVolume tolerations e2e test for provisioning and consumption on tainted nodes --- test/e2e/gomega_util.go | 5 +++ test/e2e/localvolume_test.go | 86 +++++++++++++++++++++++++++++++++--- 2 files changed, 84 insertions(+), 7 deletions(-) diff --git a/test/e2e/gomega_util.go b/test/e2e/gomega_util.go index 057cb4195..d1df244a5 100644 --- a/test/e2e/gomega_util.go +++ b/test/e2e/gomega_util.go @@ -172,6 +172,10 @@ func eventuallyFindAvailablePVs(f *framework.Framework, storageClassName string, } func consumePV(namespace string, pv corev1.PersistentVolume) (*corev1.PersistentVolumeClaim, *batchv1.Job, *corev1.Pod) { + return consumePVWithTolerations(namespace, pv, nil) +} + +func consumePVWithTolerations(namespace string, pv corev1.PersistentVolume, tolerations []corev1.Toleration) (*corev1.PersistentVolumeClaim, *batchv1.Job, *corev1.Pod) { f := framework.Global f.Logf("consuming PV: %q", pv.Name) name := fmt.Sprintf("%s-consumer", pv.ObjectMeta.Name) @@ -211,6 +215,7 @@ func consumePV(namespace string, pv corev1.PersistentVolume) (*corev1.Persistent }, Spec: corev1.PodSpec{ RestartPolicy: corev1.RestartPolicyNever, + Tolerations: tolerations, Containers: []corev1.Container{ { Name: "busybox", diff --git a/test/e2e/localvolume_test.go b/test/e2e/localvolume_test.go index eed409ac5..e917611f2 100644 --- a/test/e2e/localvolume_test.go +++ b/test/e2e/localvolume_test.go @@ -43,6 +43,7 @@ const ( diskIndexXfsFsType = 2 diskIndexBlockMode = 3 diskIndexLVDL = 4 + diskIndexTolerations = 0 ) type fsTypeTestCase struct { @@ -292,6 +293,65 @@ var _ = Describe("LocalVolume", Label("LocalVolume"), Ordered, func() { } }) + Context("localvolume with tolerations", func() { + It("schedules and consumes PV on tainted node", func() { + selectedNode := nodeEnv[1].node + testDisk := nodeEnv[1].disks[diskIndexTolerations] + Expect(testDisk.path).ShouldNot(BeZero(), "device path should not be empty for tolerations test") + + originalNodeTaints := append([]corev1.Taint(nil), selectedNode.Spec.Taints...) + selectedNode.Spec.Taints = []corev1.Taint{localVolumeNodeTaint()} + var err error + selectedNode, err = waitForNodeTaintUpdate(f.KubeClient, selectedNode, retryInterval, hourTimeout) + Expect(err).NotTo(HaveOccurred(), "tainting node for tolerations test") + DeferCleanup(func() error { + selectedNode.Spec.Taints = originalNodeTaints + _, restoreErr := waitForNodeTaintUpdate(f.KubeClient, selectedNode, retryInterval, hourTimeout) + if restoreErr != nil { + f.Logf("error restoring original taints on node: %v", restoreErr) + } + return restoreErr + }) + + localVolume := getLocalVolumeWithFSType( + selectedNode, + testDisk.path, + namespace, + lvStorageClassName, + "ext4", + localv1.PersistentVolumeFilesystem, + "test-local-disk-tolerations", + localVolumeTolerations(), + ) + DeferCleanup(func() { cleanupLVResources(f, localVolume) }) + + Eventually(func(ctx context.Context) error { + f.Logf("creating localvolume for tolerations test") + return f.Client.Create(ctx, localVolume, nil) + }, time.Minute, time.Second*2).WithContext(context.Background()).ShouldNot(HaveOccurred(), "creating localvolume for tolerations test") + + err = waitForDaemonSet(f.KubeClient, namespace, nodedaemon.DiskMakerName, retryInterval, hourTimeout) + Expect(err).NotTo(HaveOccurred(), "waiting for diskmaker daemonset for tolerations test") + + err = verifyLocalVolume(localVolume, f.Client) + Expect(err).NotTo(HaveOccurred(), "verifying localvolume cr for tolerations test") + + err = checkLocalVolumeStatus(localVolume) + Expect(err).NotTo(HaveOccurred(), "checking localvolume condition for tolerations test") + + pvs := eventuallyFindPVs(f, lvStorageClassName, 1) + expectedPath := testDisk.name + if testDisk.id != "" { + expectedPath = testDisk.id + } + Expect(filepath.Base(pvs[0].Spec.Local.Path)).To(Equal(expectedPath)) + + f.Logf("consuming PV with tolerations") + pvc, job, pod := consumePVWithTolerations(namespace, pvs[0], localVolumeTolerations()) + eventuallyDelete(job, pvc, pod) + }) + }) + Context("standard LocalVolume lifecycle", Ordered, func() { var tc *testContext @@ -792,16 +852,28 @@ func getLocalVolume(selectedNode corev1.Node, selectedDisk, namespace string) *l "", localv1.PersistentVolumeFilesystem, "test-local-disk", - []corev1.Toleration{ - { - Key: "localstorage", - Value: "testvalue", - Operator: "Equal", - }, - }, + localVolumeTolerations(), ) } +func localVolumeTolerations() []corev1.Toleration { + return []corev1.Toleration{ + { + Key: "localstorage", + Value: "testvalue", + Operator: corev1.TolerationOpEqual, + }, + } +} + +func localVolumeNodeTaint() corev1.Taint { + return corev1.Taint{ + Key: "localstorage", + Value: "testvalue", + Effect: corev1.TaintEffectNoSchedule, + } +} + // getLocalVolumeWithFSType creates a LocalVolume CR with specific FSType and VolumeMode // Parameters: // - selectedNode: the node to deploy on