k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e/storage/csimock/csi_honor_pv_reclaim_policy.go (about) 1 /* 2 Copyright 2024 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package csimock 18 19 import ( 20 "context" 21 "fmt" 22 "time" 23 24 "github.com/onsi/ginkgo/v2" 25 "github.com/onsi/gomega" 26 v1 "k8s.io/api/core/v1" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 storagehelpers "k8s.io/component-helpers/storage/volume" 29 "k8s.io/kubernetes/pkg/features" 30 "k8s.io/kubernetes/test/e2e/feature" 31 "k8s.io/kubernetes/test/e2e/framework" 32 e2epv "k8s.io/kubernetes/test/e2e/framework/pv" 33 "k8s.io/kubernetes/test/e2e/storage/utils" 34 admissionapi "k8s.io/pod-security-admission/api" 35 "k8s.io/utils/ptr" 36 ) 37 38 var _ = utils.SIGDescribe("CSI Mock honor pv reclaim policy", feature.HonorPVReclaimPolicy, framework.WithFeatureGate(features.HonorPVReclaimPolicy), func() { 39 f := framework.NewDefaultFramework("csi-mock-honor-pv-reclaim-policy") 40 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged 41 m := newMockDriverSetup(f) 42 43 ginkgo.Context("CSI honor pv reclaim policy using mock driver", func() { 44 ginkgo.It("Dynamic provisioning should honor pv delete reclaim policy", func(ctx context.Context) { 45 m.init(ctx, testParameters{ 46 registerDriver: true, 47 enableHonorPVReclaimPolicy: true, 48 reclaimPolicy: ptr.To(v1.PersistentVolumeReclaimDelete), 49 }) 50 ginkgo.DeferCleanup(m.cleanup) 51 52 _, pvc := m.createPVC(ctx) 53 54 ginkgo.By(fmt.Sprintf("Waiting for PVC %s to be bound", pvc.Name)) 55 pvs, err := e2epv.WaitForPVClaimBoundPhase(ctx, f.ClientSet, []*v1.PersistentVolumeClaim{pvc}, framework.ClaimProvisionTimeout) 56 framework.ExpectNoError(err, "failed to wait for PVC to be bound") 57 gomega.Expect(pvs).To(gomega.HaveLen(1), "expected 1 PV to be bound to PVC, got %d", len(pvs)) 58 59 pv := pvs[0] 60 ginkgo.By(fmt.Sprintf("PVC %s is bound to PV %s", pvc.Name, pv.Name)) 61 gomega.Expect(pv.Spec.PersistentVolumeReclaimPolicy).To(gomega.Equal(v1.PersistentVolumeReclaimDelete), 62 "expected PV %s to have reclaim policy %s, got %s", pv.Name, v1.PersistentVolumeReclaimDelete, pv.Spec.PersistentVolumeReclaimPolicy) 63 // For dynamic provisioning, the PV should be created with the deletion protection finalizer. 64 gomega.Expect(pv.Finalizers).To(gomega.ContainElement(storagehelpers.PVDeletionProtectionFinalizer), 65 "expected PV %s to have finalizer %s", pv.Name, storagehelpers.PVDeletionProtectionFinalizer) 66 67 ginkgo.By(fmt.Sprintf("Deleting PV %s", pv.Name)) 68 err = f.ClientSet.CoreV1().PersistentVolumes().Delete(ctx, pv.Name, metav1.DeleteOptions{}) 69 framework.ExpectNoError(err, "failed to delete PV %s", pv.Name) 70 71 ginkgo.By(fmt.Sprintf("Deleting PVC %s", pvc.Name)) 72 err = f.ClientSet.CoreV1().PersistentVolumeClaims(pvc.Namespace).Delete(ctx, pvc.Name, metav1.DeleteOptions{}) 73 framework.ExpectNoError(err, "failed to delete PVC %s", pvc.Name) 74 75 ginkgo.By(fmt.Sprintf("Waiting for PV %s to be deleted", pv.Name)) 76 err = e2epv.WaitForPersistentVolumeDeleted(ctx, f.ClientSet, pv.Name, framework.Poll, 2*time.Minute) 77 framework.ExpectNoError(err, "failed to wait for PV to be deleted") 78 79 ginkgo.By(fmt.Sprintf("Verifying that the driver received DeleteVolume call for PV %s", pv.Name)) 80 gomega.Expect(m.driver.GetCalls(ctx)).To(gomega.ContainElement(gomega.HaveField("Method", gomega.Equal("DeleteVolume")))) 81 }) 82 83 ginkgo.It("Dynamic provisioning should honor pv retain reclaim policy", func(ctx context.Context) { 84 m.init(ctx, testParameters{ 85 registerDriver: true, 86 enableHonorPVReclaimPolicy: true, 87 reclaimPolicy: ptr.To(v1.PersistentVolumeReclaimRetain), 88 }) 89 ginkgo.DeferCleanup(m.cleanup) 90 91 _, pvc := m.createPVC(ctx) 92 93 ginkgo.By(fmt.Sprintf("Waiting for PVC %s to be bound", pvc.Name)) 94 pvs, err := e2epv.WaitForPVClaimBoundPhase(ctx, f.ClientSet, []*v1.PersistentVolumeClaim{pvc}, framework.ClaimProvisionTimeout) 95 framework.ExpectNoError(err, "failed to wait for PVC to be bound") 96 gomega.Expect(pvs).To(gomega.HaveLen(1), "expected 1 PV to be bound to PVC, got %d", len(pvs)) 97 98 pv := pvs[0] 99 ginkgo.By(fmt.Sprintf("PVC %s is bound to PV %s", pvc.Name, pv.Name)) 100 gomega.Expect(pv.Spec.PersistentVolumeReclaimPolicy).To(gomega.Equal(v1.PersistentVolumeReclaimRetain), 101 "expected PV %s to have reclaim policy %s, got %s", pv.Name, v1.PersistentVolumeReclaimRetain, pv.Spec.PersistentVolumeReclaimPolicy) 102 103 ginkgo.By(fmt.Sprintf("Verifying that the PV %s does not have finalizer %s after creation", pv.Name, storagehelpers.PVDeletionProtectionFinalizer)) 104 gomega.Consistently(ctx, framework.GetObject(f.ClientSet.CoreV1().PersistentVolumes().Get, pv.Name, metav1.GetOptions{})). 105 WithPolling(framework.Poll).WithTimeout(framework.ClaimProvisionTimeout).ShouldNot(gomega.HaveField("Finalizers", 106 gomega.ContainElement(storagehelpers.PVDeletionProtectionFinalizer)), "pv unexpectedly has the finalizer %s", storagehelpers.PVDeletionProtectionFinalizer) 107 108 ginkgo.By(fmt.Sprintf("Deleting PV %s", pv.Name)) 109 err = f.ClientSet.CoreV1().PersistentVolumes().Delete(ctx, pv.Name, metav1.DeleteOptions{}) 110 framework.ExpectNoError(err, "failed to delete PV %s", pv.Name) 111 112 ginkgo.By(fmt.Sprintf("Deleting PVC %s", pvc.Name)) 113 err = f.ClientSet.CoreV1().PersistentVolumeClaims(pvc.Namespace).Delete(ctx, pvc.Name, metav1.DeleteOptions{}) 114 framework.ExpectNoError(err, "failed to delete PVC %s", pvc.Name) 115 116 ginkgo.By(fmt.Sprintf("Waiting for PV %s to be deleted", pv.Name)) 117 err = e2epv.WaitForPersistentVolumeDeleted(ctx, f.ClientSet, pv.Name, framework.Poll, 2*time.Minute) 118 framework.ExpectNoError(err, "failed to wait for PV to be deleted") 119 120 ginkgo.By(fmt.Sprintf("Verifying that the driver did not receive DeleteVolume call for PV %s", pv.Name)) 121 gomega.Expect(m.driver.GetCalls(ctx)).NotTo(gomega.ContainElement(gomega.HaveField("Method", gomega.Equal("DeleteVolume")))) 122 }) 123 124 ginkgo.It("Static provisioning should honor pv delete reclaim policy", func(ctx context.Context) { 125 m.init(ctx, testParameters{ 126 registerDriver: true, 127 enableHonorPVReclaimPolicy: true, 128 reclaimPolicy: ptr.To(v1.PersistentVolumeReclaimDelete), 129 }) 130 ginkgo.DeferCleanup(m.cleanup) 131 132 sc, pv, pvc := m.createPVPVC(ctx) 133 gomega.Expect(pv.Spec.PersistentVolumeReclaimPolicy).To(gomega.Equal(v1.PersistentVolumeReclaimDelete), 134 "expected PV %s to have reclaim policy %s, got %s", pv.Name, v1.PersistentVolumeReclaimDelete, pv.Spec.PersistentVolumeReclaimPolicy) 135 gomega.Expect(pv.Annotations).NotTo(gomega.HaveKeyWithValue(storagehelpers.AnnDynamicallyProvisioned, sc.Provisioner), "expected PV %s to not have annotation %s", pv.Name, storagehelpers.AnnDynamicallyProvisioned) 136 137 ginkgo.By(fmt.Sprintf("Verifying that the PV %s has finalizer %s after creation", pv.Name, storagehelpers.PVDeletionProtectionFinalizer)) 138 gomega.Eventually(ctx, framework.GetObject(f.ClientSet.CoreV1().PersistentVolumes().Get, pv.Name, metav1.GetOptions{})). 139 WithPolling(framework.Poll).WithTimeout(framework.ClaimProvisionTimeout).Should(gomega.HaveField("Finalizers", 140 gomega.ContainElement(storagehelpers.PVDeletionProtectionFinalizer)), "failed to wait for PV to have finalizer %s", storagehelpers.PVDeletionProtectionFinalizer) 141 142 ginkgo.By(fmt.Sprintf("Deleting PV %s", pv.Name)) 143 err := f.ClientSet.CoreV1().PersistentVolumes().Delete(ctx, pv.Name, metav1.DeleteOptions{}) 144 framework.ExpectNoError(err, "failed to delete PV %s", pv.Name) 145 146 ginkgo.By(fmt.Sprintf("Deleting PVC %s", pvc.Name)) 147 err = f.ClientSet.CoreV1().PersistentVolumeClaims(pvc.Namespace).Delete(ctx, pvc.Name, metav1.DeleteOptions{}) 148 framework.ExpectNoError(err, "failed to delete PVC %s", pvc.Name) 149 150 ginkgo.By(fmt.Sprintf("Waiting for PV %s to be deleted", pv.Name)) 151 err = e2epv.WaitForPersistentVolumeDeleted(ctx, f.ClientSet, pv.Name, framework.Poll, 2*time.Minute) 152 framework.ExpectNoError(err, "failed to wait for PV to be deleted") 153 154 ginkgo.By(fmt.Sprintf("Verifying that the driver received DeleteVolume call for PV %s", pv.Name)) 155 gomega.Expect(m.driver.GetCalls(ctx)).To(gomega.ContainElement(gomega.HaveField("Method", gomega.Equal("DeleteVolume")))) 156 }) 157 158 ginkgo.It("Static provisioning should honor pv retain reclaim policy", func(ctx context.Context) { 159 m.init(ctx, testParameters{ 160 registerDriver: true, 161 enableHonorPVReclaimPolicy: true, 162 reclaimPolicy: ptr.To(v1.PersistentVolumeReclaimRetain), 163 }) 164 ginkgo.DeferCleanup(m.cleanup) 165 166 sc, pv, pvc := m.createPVPVC(ctx) 167 gomega.Expect(pv.Spec.PersistentVolumeReclaimPolicy).To(gomega.Equal(v1.PersistentVolumeReclaimRetain), 168 "expected PV %s to have reclaim policy %s, got %s", pv.Name, v1.PersistentVolumeReclaimRetain, pv.Spec.PersistentVolumeReclaimPolicy) 169 gomega.Expect(pv.Annotations).NotTo(gomega.HaveKeyWithValue(storagehelpers.AnnDynamicallyProvisioned, sc.Provisioner), "expected PV %s to not have annotation %s", pv.Name, storagehelpers.AnnDynamicallyProvisioned) 170 171 ginkgo.By(fmt.Sprintf("Verifying that the PV %s does not have finalizer %s after creation", pv.Name, storagehelpers.PVDeletionProtectionFinalizer)) 172 gomega.Consistently(ctx, framework.GetObject(f.ClientSet.CoreV1().PersistentVolumes().Get, pv.Name, metav1.GetOptions{})). 173 WithPolling(framework.Poll).WithTimeout(framework.ClaimProvisionTimeout).ShouldNot(gomega.HaveField("Finalizers", 174 gomega.ContainElement(storagehelpers.PVDeletionProtectionFinalizer)), "pv unexpectedly has the finalizer %s", storagehelpers.PVDeletionProtectionFinalizer) 175 176 ginkgo.By(fmt.Sprintf("Deleting PV %s", pv.Name)) 177 err := f.ClientSet.CoreV1().PersistentVolumes().Delete(ctx, pv.Name, metav1.DeleteOptions{}) 178 framework.ExpectNoError(err, "failed to delete PV %s", pv.Name) 179 180 ginkgo.By(fmt.Sprintf("Deleting PVC %s", pvc.Name)) 181 err = f.ClientSet.CoreV1().PersistentVolumeClaims(pvc.Namespace).Delete(ctx, pvc.Name, metav1.DeleteOptions{}) 182 framework.ExpectNoError(err, "failed to delete PVC %s", pvc.Name) 183 184 ginkgo.By(fmt.Sprintf("Waiting for PV %s to be deleted", pv.Name)) 185 err = e2epv.WaitForPersistentVolumeDeleted(ctx, f.ClientSet, pv.Name, framework.Poll, 2*time.Minute) 186 framework.ExpectNoError(err, "failed to wait for PV to be deleted") 187 188 ginkgo.By(fmt.Sprintf("Verifying that the driver did not receive DeleteVolume call for PV %s", pv.Name)) 189 gomega.Expect(m.driver.GetCalls(ctx)).NotTo(gomega.ContainElement(gomega.HaveField("Method", gomega.Equal("DeleteVolume")))) 190 }) 191 }) 192 })