k8s.io/kubernetes@v1.29.3/test/e2e/storage/vsphere/pv_reclaimpolicy.go (about) 1 /* 2 Copyright 2017 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 vsphere 18 19 import ( 20 "context" 21 "strconv" 22 "time" 23 24 "github.com/onsi/ginkgo/v2" 25 v1 "k8s.io/api/core/v1" 26 apierrors "k8s.io/apimachinery/pkg/api/errors" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 clientset "k8s.io/client-go/kubernetes" 29 "k8s.io/kubernetes/test/e2e/feature" 30 "k8s.io/kubernetes/test/e2e/framework" 31 e2enode "k8s.io/kubernetes/test/e2e/framework/node" 32 e2epod "k8s.io/kubernetes/test/e2e/framework/pod" 33 e2epv "k8s.io/kubernetes/test/e2e/framework/pv" 34 e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper" 35 "k8s.io/kubernetes/test/e2e/storage/utils" 36 admissionapi "k8s.io/pod-security-admission/api" 37 ) 38 39 var _ = utils.SIGDescribe("PersistentVolumes", feature.Vsphere, feature.ReclaimPolicy, func() { 40 f := framework.NewDefaultFramework("persistentvolumereclaim") 41 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged 42 var ( 43 c clientset.Interface 44 ns string 45 volumePath string 46 pv *v1.PersistentVolume 47 pvc *v1.PersistentVolumeClaim 48 nodeInfo *NodeInfo 49 ) 50 51 ginkgo.BeforeEach(func(ctx context.Context) { 52 c = f.ClientSet 53 ns = f.Namespace.Name 54 framework.ExpectNoError(e2enode.WaitForAllNodesSchedulable(ctx, c, f.Timeouts.NodeSchedulable)) 55 }) 56 57 f.Describe("persistentvolumereclaim:vsphere", feature.Vsphere, func() { 58 ginkgo.BeforeEach(func(ctx context.Context) { 59 e2eskipper.SkipUnlessProviderIs("vsphere") 60 ginkgo.DeferCleanup(testCleanupVSpherePersistentVolumeReclaim, c, nodeInfo, ns, volumePath, pv, pvc) 61 Bootstrap(f) 62 nodeInfo = GetReadySchedulableRandomNodeInfo(ctx, c) 63 pv = nil 64 pvc = nil 65 volumePath = "" 66 }) 67 68 /* 69 This test verifies persistent volume should be deleted when reclaimPolicy on the PV is set to delete and 70 associated claim is deleted 71 72 Test Steps: 73 1. Create vmdk 74 2. Create PV Spec with volume path set to VMDK file created in Step-1, and PersistentVolumeReclaimPolicy is set to Delete 75 3. Create PVC with the storage request set to PV's storage capacity. 76 4. Wait for PV and PVC to bound. 77 5. Delete PVC 78 6. Verify PV is deleted automatically. 79 */ 80 ginkgo.It("should delete persistent volume when reclaimPolicy set to delete and associated claim is deleted", func(ctx context.Context) { 81 var err error 82 volumePath, pv, pvc, err = testSetupVSpherePersistentVolumeReclaim(ctx, c, nodeInfo, ns, v1.PersistentVolumeReclaimDelete) 83 framework.ExpectNoError(err) 84 85 deletePVCAfterBind(ctx, c, ns, pvc, pv, f.Timeouts) 86 pvc = nil 87 88 ginkgo.By("verify pv is deleted") 89 err = e2epv.WaitForPersistentVolumeDeleted(ctx, c, pv.Name, 3*time.Second, 300*time.Second) 90 framework.ExpectNoError(err) 91 92 pv = nil 93 volumePath = "" 94 }) 95 96 /* 97 Test Steps: 98 1. Create vmdk 99 2. Create PV Spec with volume path set to VMDK file created in Step-1, and PersistentVolumeReclaimPolicy is set to Delete 100 3. Create PVC with the storage request set to PV's storage capacity. 101 4. Wait for PV and PVC to bound. 102 5. Delete PVC. 103 6. Verify volume is attached to the node and volume is accessible in the pod. 104 7. Verify PV status should be failed. 105 8. Delete the pod. 106 9. Verify PV should be detached from the node and automatically deleted. 107 */ 108 ginkgo.It("should not detach and unmount PV when associated pvc with delete as reclaimPolicy is deleted when it is in use by the pod", func(ctx context.Context) { 109 var err error 110 111 volumePath, pv, pvc, err = testSetupVSpherePersistentVolumeReclaim(ctx, c, nodeInfo, ns, v1.PersistentVolumeReclaimDelete) 112 framework.ExpectNoError(err) 113 // Wait for PV and PVC to Bind 114 framework.ExpectNoError(e2epv.WaitOnPVandPVC(ctx, c, f.Timeouts, ns, pv, pvc)) 115 116 ginkgo.By("Creating the Pod") 117 pod, err := e2epod.CreateClientPod(ctx, c, ns, pvc) 118 framework.ExpectNoError(err) 119 120 ginkgo.By("Deleting the Claim") 121 framework.ExpectNoError(e2epv.DeletePersistentVolumeClaim(ctx, c, pvc.Name, ns), "Failed to delete PVC ", pvc.Name) 122 pvc = nil 123 124 // Verify PV is Present, after PVC is deleted and PV status should be Failed. 125 pv, err := c.CoreV1().PersistentVolumes().Get(ctx, pv.Name, metav1.GetOptions{}) 126 framework.ExpectNoError(err) 127 err = e2epv.WaitForPersistentVolumePhase(ctx, v1.VolumeFailed, c, pv.Name, 1*time.Second, 60*time.Second) 128 framework.ExpectNoError(err) 129 130 ginkgo.By("Verify the volume is attached to the node") 131 isVolumeAttached, verifyDiskAttachedError := diskIsAttached(ctx, pv.Spec.VsphereVolume.VolumePath, pod.Spec.NodeName) 132 framework.ExpectNoError(verifyDiskAttachedError) 133 if !isVolumeAttached { 134 framework.Failf("Disk %s is not attached with the node %s", pv.Spec.VsphereVolume.VolumePath, pod.Spec.NodeName) 135 } 136 137 ginkgo.By("Verify the volume is accessible and available in the pod") 138 verifyVSphereVolumesAccessible(ctx, c, pod, []*v1.PersistentVolume{pv}) 139 framework.Logf("Verified that Volume is accessible in the POD after deleting PV claim") 140 141 ginkgo.By("Deleting the Pod") 142 framework.ExpectNoError(e2epod.DeletePodWithWait(ctx, c, pod), "Failed to delete pod ", pod.Name) 143 144 ginkgo.By("Verify PV is detached from the node after Pod is deleted") 145 err = waitForVSphereDiskToDetach(ctx, pv.Spec.VsphereVolume.VolumePath, pod.Spec.NodeName) 146 framework.ExpectNoError(err) 147 148 ginkgo.By("Verify PV should be deleted automatically") 149 framework.ExpectNoError(e2epv.WaitForPersistentVolumeDeleted(ctx, c, pv.Name, 1*time.Second, 30*time.Second)) 150 pv = nil 151 volumePath = "" 152 }) 153 154 /* 155 This test Verify persistent volume should be retained when reclaimPolicy on the PV is set to retain 156 and associated claim is deleted 157 158 Test Steps: 159 1. Create vmdk 160 2. Create PV Spec with volume path set to VMDK file created in Step-1, and PersistentVolumeReclaimPolicy is set to Retain 161 3. Create PVC with the storage request set to PV's storage capacity. 162 4. Wait for PV and PVC to bound. 163 5. Write some content in the volume. 164 6. Delete PVC 165 7. Verify PV is retained. 166 8. Delete retained PV. 167 9. Create PV Spec with the same volume path used in step 2. 168 10. Create PVC with the storage request set to PV's storage capacity. 169 11. Created POD using PVC created in Step 10 and verify volume content is matching. 170 */ 171 172 ginkgo.It("should retain persistent volume when reclaimPolicy set to retain when associated claim is deleted", func(ctx context.Context) { 173 var err error 174 var volumeFileContent = "hello from vsphere cloud provider, Random Content is :" + strconv.FormatInt(time.Now().UnixNano(), 10) 175 176 volumePath, pv, pvc, err = testSetupVSpherePersistentVolumeReclaim(ctx, c, nodeInfo, ns, v1.PersistentVolumeReclaimRetain) 177 framework.ExpectNoError(err) 178 179 writeContentToVSpherePV(ctx, c, f.Timeouts, pvc, volumeFileContent) 180 181 ginkgo.By("Delete PVC") 182 framework.ExpectNoError(e2epv.DeletePersistentVolumeClaim(ctx, c, pvc.Name, ns), "Failed to delete PVC ", pvc.Name) 183 pvc = nil 184 185 ginkgo.By("Verify PV is retained") 186 framework.Logf("Waiting for PV %v to become Released", pv.Name) 187 err = e2epv.WaitForPersistentVolumePhase(ctx, v1.VolumeReleased, c, pv.Name, 3*time.Second, 300*time.Second) 188 framework.ExpectNoError(err) 189 framework.ExpectNoError(e2epv.DeletePersistentVolume(ctx, c, pv.Name), "Failed to delete PV ", pv.Name) 190 191 ginkgo.By("Creating the PV for same volume path") 192 pv = getVSpherePersistentVolumeSpec(volumePath, v1.PersistentVolumeReclaimRetain, nil) 193 pv, err = c.CoreV1().PersistentVolumes().Create(ctx, pv, metav1.CreateOptions{}) 194 framework.ExpectNoError(err) 195 196 ginkgo.By("creating the pvc") 197 pvc = getVSpherePersistentVolumeClaimSpec(ns, nil) 198 pvc, err = c.CoreV1().PersistentVolumeClaims(ns).Create(ctx, pvc, metav1.CreateOptions{}) 199 framework.ExpectNoError(err) 200 201 ginkgo.By("wait for the pv and pvc to bind") 202 framework.ExpectNoError(e2epv.WaitOnPVandPVC(ctx, c, f.Timeouts, ns, pv, pvc)) 203 verifyContentOfVSpherePV(ctx, c, f.Timeouts, pvc, volumeFileContent) 204 205 }) 206 }) 207 }) 208 209 // Test Setup for persistentvolumereclaim tests for vSphere Provider 210 func testSetupVSpherePersistentVolumeReclaim(ctx context.Context, c clientset.Interface, nodeInfo *NodeInfo, ns string, persistentVolumeReclaimPolicy v1.PersistentVolumeReclaimPolicy) (volumePath string, pv *v1.PersistentVolume, pvc *v1.PersistentVolumeClaim, err error) { 211 ginkgo.By("running testSetupVSpherePersistentVolumeReclaim") 212 ginkgo.By("creating vmdk") 213 volumePath, err = nodeInfo.VSphere.CreateVolume(&VolumeOptions{}, nodeInfo.DataCenterRef) 214 if err != nil { 215 return 216 } 217 ginkgo.By("creating the pv") 218 pv = getVSpherePersistentVolumeSpec(volumePath, persistentVolumeReclaimPolicy, nil) 219 pv, err = c.CoreV1().PersistentVolumes().Create(ctx, pv, metav1.CreateOptions{}) 220 if err != nil { 221 return 222 } 223 ginkgo.By("creating the pvc") 224 pvc = getVSpherePersistentVolumeClaimSpec(ns, nil) 225 pvc, err = c.CoreV1().PersistentVolumeClaims(ns).Create(ctx, pvc, metav1.CreateOptions{}) 226 return 227 } 228 229 // Test Cleanup for persistentvolumereclaim tests for vSphere Provider 230 func testCleanupVSpherePersistentVolumeReclaim(ctx context.Context, c clientset.Interface, nodeInfo *NodeInfo, ns string, volumePath string, pv *v1.PersistentVolume, pvc *v1.PersistentVolumeClaim) { 231 ginkgo.By("running testCleanupVSpherePersistentVolumeReclaim") 232 if len(volumePath) > 0 { 233 err := nodeInfo.VSphere.DeleteVolume(volumePath, nodeInfo.DataCenterRef) 234 framework.ExpectNoError(err) 235 } 236 if pv != nil { 237 framework.ExpectNoError(e2epv.DeletePersistentVolume(ctx, c, pv.Name), "Failed to delete PV ", pv.Name) 238 } 239 if pvc != nil { 240 framework.ExpectNoError(e2epv.DeletePersistentVolumeClaim(ctx, c, pvc.Name, ns), "Failed to delete PVC ", pvc.Name) 241 } 242 } 243 244 // func to wait until PV and PVC bind and once bind completes, delete the PVC 245 func deletePVCAfterBind(ctx context.Context, c clientset.Interface, ns string, pvc *v1.PersistentVolumeClaim, pv *v1.PersistentVolume, timeouts *framework.TimeoutContext) { 246 var err error 247 248 ginkgo.By("wait for the pv and pvc to bind") 249 framework.ExpectNoError(e2epv.WaitOnPVandPVC(ctx, c, timeouts, ns, pv, pvc)) 250 251 ginkgo.By("delete pvc") 252 framework.ExpectNoError(e2epv.DeletePersistentVolumeClaim(ctx, c, pvc.Name, ns), "Failed to delete PVC ", pvc.Name) 253 _, err = c.CoreV1().PersistentVolumeClaims(ns).Get(ctx, pvc.Name, metav1.GetOptions{}) 254 if !apierrors.IsNotFound(err) { 255 framework.ExpectNoError(err) 256 } 257 }