1
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
44 claimDeletingTimeout = 3 * time.Minute
45 )
46
47
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 })
180
View as plain text