k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e/storage/pvc_protection.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 storage 18 19 import ( 20 "context" 21 22 "github.com/onsi/ginkgo/v2" 23 "github.com/onsi/gomega" 24 25 "fmt" 26 "time" 27 28 v1 "k8s.io/api/core/v1" 29 apierrors "k8s.io/apimachinery/pkg/api/errors" 30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 clientset "k8s.io/client-go/kubernetes" 32 volumeutil "k8s.io/kubernetes/pkg/volume/util" 33 "k8s.io/kubernetes/test/e2e/framework" 34 e2enode "k8s.io/kubernetes/test/e2e/framework/node" 35 e2epod "k8s.io/kubernetes/test/e2e/framework/pod" 36 e2epv "k8s.io/kubernetes/test/e2e/framework/pv" 37 "k8s.io/kubernetes/test/e2e/storage/testsuites" 38 "k8s.io/kubernetes/test/e2e/storage/utils" 39 admissionapi "k8s.io/pod-security-admission/api" 40 ) 41 42 const ( 43 // claimDeletingTimeout is How long claims have to become deleted. 44 claimDeletingTimeout = 3 * time.Minute 45 ) 46 47 // waitForPersistentVolumeClaimDeleted waits for a PersistentVolumeClaim to be removed from the system until timeout occurs, whichever comes first. 48 func waitForPersistentVolumeClaimDeleted(ctx context.Context, c clientset.Interface, ns string, pvcName string, Poll, timeout time.Duration) error { 49 framework.Logf("Waiting up to %v for PersistentVolumeClaim %s to be removed", timeout, pvcName) 50 for start := time.Now(); time.Since(start) < timeout; time.Sleep(Poll) { 51 _, err := c.CoreV1().PersistentVolumeClaims(ns).Get(ctx, pvcName, metav1.GetOptions{}) 52 if err != nil { 53 if apierrors.IsNotFound(err) { 54 framework.Logf("Claim %q in namespace %q doesn't exist in the system", pvcName, ns) 55 return nil 56 } 57 framework.Logf("Failed to get claim %q in namespace %q, retrying in %v. Error: %v", pvcName, ns, Poll, err) 58 } 59 } 60 return fmt.Errorf("PersistentVolumeClaim %s is not removed from the system within %v", pvcName, timeout) 61 } 62 63 var _ = utils.SIGDescribe("PVC Protection", func() { 64 var ( 65 client clientset.Interface 66 nameSpace string 67 err error 68 pvc *v1.PersistentVolumeClaim 69 pvcCreatedAndNotDeleted bool 70 pod *v1.Pod 71 ) 72 73 f := framework.NewDefaultFramework("pvc-protection") 74 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline 75 ginkgo.BeforeEach(func(ctx context.Context) { 76 client = f.ClientSet 77 nameSpace = f.Namespace.Name 78 framework.ExpectNoError(e2enode.WaitForAllNodesSchedulable(ctx, client, f.Timeouts.NodeSchedulable)) 79 80 ginkgo.By("Creating a PVC") 81 prefix := "pvc-protection" 82 e2epv.SkipIfNoDefaultStorageClass(ctx, client) 83 t := testsuites.StorageClassTest{ 84 Timeouts: f.Timeouts, 85 ClaimSize: "1Gi", 86 } 87 pvc = e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{ 88 NamePrefix: prefix, 89 ClaimSize: t.ClaimSize, 90 VolumeMode: &t.VolumeMode, 91 }, nameSpace) 92 pvc, err = client.CoreV1().PersistentVolumeClaims(pvc.Namespace).Create(ctx, pvc, metav1.CreateOptions{}) 93 framework.ExpectNoError(err, "Error creating PVC") 94 pvcCreatedAndNotDeleted = true 95 96 ginkgo.By("Creating a Pod that becomes Running and therefore is actively using the PVC") 97 pvcClaims := []*v1.PersistentVolumeClaim{pvc} 98 pod, err = e2epod.CreatePod(ctx, client, nameSpace, nil, pvcClaims, f.NamespacePodSecurityLevel, "") 99 framework.ExpectNoError(err, "While creating pod that uses the PVC or waiting for the Pod to become Running") 100 101 ginkgo.By("Waiting for PVC to become Bound") 102 err = e2epv.WaitForPersistentVolumeClaimPhase(ctx, v1.ClaimBound, client, nameSpace, pvc.Name, framework.Poll, f.Timeouts.ClaimBound) 103 framework.ExpectNoError(err, "Failed waiting for PVC to be bound %v", err) 104 105 ginkgo.By("Checking that PVC Protection finalizer is set") 106 pvc, err = client.CoreV1().PersistentVolumeClaims(pvc.Namespace).Get(ctx, pvc.Name, metav1.GetOptions{}) 107 framework.ExpectNoError(err, "While getting PVC status") 108 gomega.Expect(pvc.ObjectMeta.Finalizers).Should(gomega.ContainElement(volumeutil.PVCProtectionFinalizer), "PVC Protection finalizer(%v) is not set in %v", volumeutil.PVCProtectionFinalizer, pvc.ObjectMeta.Finalizers) 109 }) 110 111 ginkgo.AfterEach(func(ctx context.Context) { 112 if pvcCreatedAndNotDeleted { 113 e2epv.DeletePersistentVolumeClaim(ctx, client, pvc.Name, nameSpace) 114 } 115 }) 116 117 ginkgo.It("Verify \"immediate\" deletion of a PVC that is not in active use by a pod", func(ctx context.Context) { 118 ginkgo.By("Deleting the pod using the PVC") 119 err = e2epod.DeletePodWithWait(ctx, client, pod) 120 framework.ExpectNoError(err, "Error terminating and deleting pod") 121 122 ginkgo.By("Deleting the PVC") 123 err = client.CoreV1().PersistentVolumeClaims(pvc.Namespace).Delete(ctx, pvc.Name, *metav1.NewDeleteOptions(0)) 124 framework.ExpectNoError(err, "Error deleting PVC") 125 waitForPersistentVolumeClaimDeleted(ctx, client, pvc.Namespace, pvc.Name, framework.Poll, claimDeletingTimeout) 126 pvcCreatedAndNotDeleted = false 127 }) 128 129 ginkgo.It("Verify that PVC in active use by a pod is not removed immediately", func(ctx context.Context) { 130 ginkgo.By("Deleting the PVC, however, the PVC must not be removed from the system as it's in active use by a pod") 131 err = client.CoreV1().PersistentVolumeClaims(pvc.Namespace).Delete(ctx, pvc.Name, *metav1.NewDeleteOptions(0)) 132 framework.ExpectNoError(err, "Error deleting PVC") 133 134 ginkgo.By("Checking that the PVC status is Terminating") 135 pvc, err = client.CoreV1().PersistentVolumeClaims(pvc.Namespace).Get(ctx, pvc.Name, metav1.GetOptions{}) 136 framework.ExpectNoError(err, "While checking PVC status") 137 gomega.Expect(pvc.ObjectMeta.DeletionTimestamp).ToNot(gomega.BeNil()) 138 139 ginkgo.By("Deleting the pod that uses the PVC") 140 err = e2epod.DeletePodWithWait(ctx, client, pod) 141 framework.ExpectNoError(err, "Error terminating and deleting pod") 142 143 ginkgo.By("Checking that the PVC is automatically removed from the system because it's no longer in active use by a pod") 144 waitForPersistentVolumeClaimDeleted(ctx, client, pvc.Namespace, pvc.Name, framework.Poll, claimDeletingTimeout) 145 pvcCreatedAndNotDeleted = false 146 }) 147 148 ginkgo.It("Verify that scheduling of a pod that uses PVC that is being deleted fails and the pod becomes Unschedulable", func(ctx context.Context) { 149 ginkgo.By("Deleting the PVC, however, the PVC must not be removed from the system as it's in active use by a pod") 150 err = client.CoreV1().PersistentVolumeClaims(pvc.Namespace).Delete(ctx, pvc.Name, *metav1.NewDeleteOptions(0)) 151 framework.ExpectNoError(err, "Error deleting PVC") 152 153 ginkgo.By("Checking that the PVC status is Terminating") 154 pvc, err = client.CoreV1().PersistentVolumeClaims(pvc.Namespace).Get(ctx, pvc.Name, metav1.GetOptions{}) 155 framework.ExpectNoError(err, "While checking PVC status") 156 gomega.Expect(pvc.ObjectMeta.DeletionTimestamp).ToNot(gomega.BeNil()) 157 158 ginkgo.By("Creating second Pod whose scheduling fails because it uses a PVC that is being deleted") 159 secondPod, err2 := e2epod.CreateUnschedulablePod(ctx, client, nameSpace, nil, []*v1.PersistentVolumeClaim{pvc}, f.NamespacePodSecurityLevel, "") 160 framework.ExpectNoError(err2, "While creating second pod that uses a PVC that is being deleted and that is Unschedulable") 161 162 ginkgo.By("Deleting the second pod that uses the PVC that is being deleted") 163 err = e2epod.DeletePodWithWait(ctx, client, secondPod) 164 framework.ExpectNoError(err, "Error terminating and deleting pod") 165 166 ginkgo.By("Checking again that the PVC status is Terminating") 167 pvc, err = client.CoreV1().PersistentVolumeClaims(pvc.Namespace).Get(ctx, pvc.Name, metav1.GetOptions{}) 168 framework.ExpectNoError(err, "While checking PVC status") 169 gomega.Expect(pvc.ObjectMeta.DeletionTimestamp).ToNot(gomega.BeNil()) 170 171 ginkgo.By("Deleting the first pod that uses the PVC") 172 err = e2epod.DeletePodWithWait(ctx, client, pod) 173 framework.ExpectNoError(err, "Error terminating and deleting pod") 174 175 ginkgo.By("Checking that the PVC is automatically removed from the system because it's no longer in active use by a pod") 176 waitForPersistentVolumeClaimDeleted(ctx, client, pvc.Namespace, pvc.Name, framework.Poll, claimDeletingTimeout) 177 pvcCreatedAndNotDeleted = false 178 }) 179 })