1
16
17 package storage
18
19 import (
20 "context"
21 "fmt"
22 "time"
23
24 "github.com/onsi/ginkgo/v2"
25 "github.com/onsi/gomega"
26
27 v1 "k8s.io/api/core/v1"
28 storagev1 "k8s.io/api/storage/v1"
29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30 "k8s.io/apimachinery/pkg/util/wait"
31 clientset "k8s.io/client-go/kubernetes"
32 storageutil "k8s.io/kubernetes/pkg/apis/storage/util"
33 "k8s.io/kubernetes/test/e2e/framework"
34 e2epv "k8s.io/kubernetes/test/e2e/framework/pv"
35 "k8s.io/kubernetes/test/e2e/storage/testsuites"
36 "k8s.io/kubernetes/test/e2e/storage/utils"
37 admissionapi "k8s.io/pod-security-admission/api"
38 )
39
40 var _ = utils.SIGDescribe("Persistent Volume Claim and StorageClass", func() {
41 f := framework.NewDefaultFramework("pvc-retroactive-storageclass")
42 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline
43
44 var (
45 client clientset.Interface
46 namespace string
47 prefixPVC string
48 prefixSC string
49 t testsuites.StorageClassTest
50 pvc *v1.PersistentVolumeClaim
51 err error
52 )
53
54 ginkgo.BeforeEach(func() {
55 client = f.ClientSet
56 namespace = f.Namespace.Name
57 prefixPVC = "retro-pvc-"
58 prefixSC = "retro"
59 t = testsuites.StorageClassTest{
60 Timeouts: f.Timeouts,
61 ClaimSize: "1Gi",
62 }
63 })
64
65 f.Describe("Retroactive StorageClass assignment", framework.WithSerial(), framework.WithDisruptive(), func() {
66 ginkgo.It("should assign default SC to PVCs that have no SC set", func(ctx context.Context) {
67
68
69 restoreClasses := temporarilyUnsetDefaultClasses(ctx, client)
70 defer restoreClasses()
71
72
73 pvcObj := e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{
74 NamePrefix: prefixPVC,
75 ClaimSize: t.ClaimSize,
76 VolumeMode: &t.VolumeMode,
77 }, namespace)
78 pvc, err = client.CoreV1().PersistentVolumeClaims(pvcObj.Namespace).Create(ctx, pvcObj, metav1.CreateOptions{})
79 framework.ExpectNoError(err, "Error creating PVC")
80 defer func(pvc *v1.PersistentVolumeClaim) {
81
82 err := client.CoreV1().PersistentVolumeClaims(pvc.Namespace).Delete(ctx, pvc.Name, metav1.DeleteOptions{})
83 framework.ExpectNoError(err, "Error cleaning up PVC")
84 }(pvc)
85
86
87 storageClass := testsuites.SetupStorageClass(ctx, client, makeStorageClass(prefixSC))
88
89
90 pvc, err = waitForPVCStorageClass(ctx, client, namespace, pvc.Name, storageClass.Name, f.Timeouts.ClaimBound)
91 framework.ExpectNoError(err, "Error updating PVC with the correct storage class")
92
93
94 pv := e2epv.MakePersistentVolume(e2epv.PersistentVolumeConfig{
95 NamePrefix: "pv-",
96 StorageClassName: storageClass.Name,
97 VolumeMode: pvc.Spec.VolumeMode,
98 PVSource: v1.PersistentVolumeSource{
99 HostPath: &v1.HostPathVolumeSource{
100 Path: "/tmp/test",
101 },
102 },
103 })
104 _, err = e2epv.CreatePV(ctx, client, f.Timeouts, pv)
105 framework.ExpectNoError(err, "Error creating pv %v", err)
106 ginkgo.DeferCleanup(e2epv.DeletePersistentVolume, client, pv.Name)
107
108
109 claimNames := []string{pvc.Name}
110 err = e2epv.WaitForPersistentVolumeClaimsPhase(ctx, v1.ClaimBound, client, namespace, claimNames, 2*time.Second , t.Timeouts.ClaimProvisionShort, false)
111 framework.ExpectNoError(err)
112 updatedPVC, err := client.CoreV1().PersistentVolumeClaims(namespace).Get(ctx, pvc.Name, metav1.GetOptions{})
113 framework.ExpectNoError(err)
114 gomega.Expect(*updatedPVC.Spec.StorageClassName).To(gomega.Equal(storageClass.Name), "Expected PVC %v to have StorageClass %v, but it has StorageClass %v instead", updatedPVC.Name, prefixSC, updatedPVC.Spec.StorageClassName)
115 framework.Logf("Success - PersistentVolumeClaim %s got updated retroactively with StorageClass %v", updatedPVC.Name, storageClass.Name)
116 })
117 })
118 })
119
120 func makeStorageClass(prefixSC string) *storagev1.StorageClass {
121 return &storagev1.StorageClass{
122 TypeMeta: metav1.TypeMeta{
123 Kind: "StorageClass",
124 },
125 ObjectMeta: metav1.ObjectMeta{
126 GenerateName: prefixSC,
127 Annotations: map[string]string{
128 storageutil.IsDefaultStorageClassAnnotation: "true",
129 },
130 },
131 Provisioner: "fake-1",
132 }
133 }
134
135 func temporarilyUnsetDefaultClasses(ctx context.Context, client clientset.Interface) func() {
136 classes, err := client.StorageV1().StorageClasses().List(ctx, metav1.ListOptions{})
137 framework.ExpectNoError(err)
138
139 changedClasses := make(map[string]bool)
140
141 for _, sc := range classes.Items {
142 if sc.Annotations[storageutil.IsDefaultStorageClassAnnotation] == "true" {
143 changedClasses[sc.GetName()] = true
144 sc.Annotations[storageutil.IsDefaultStorageClassAnnotation] = "false"
145 _, err := client.StorageV1().StorageClasses().Update(ctx, &sc, metav1.UpdateOptions{})
146 framework.ExpectNoError(err)
147 }
148 }
149
150 return func() {
151 classes, err = client.StorageV1().StorageClasses().List(ctx, metav1.ListOptions{})
152 framework.ExpectNoError(err)
153 for _, sc := range classes.Items {
154 if _, found := changedClasses[sc.GetName()]; found {
155 sc.Annotations[storageutil.IsDefaultStorageClassAnnotation] = "true"
156 _, err := client.StorageV1().StorageClasses().Update(ctx, &sc, metav1.UpdateOptions{})
157 framework.ExpectNoError(err)
158 }
159 }
160 }
161
162 }
163
164 func waitForPVCStorageClass(ctx context.Context, c clientset.Interface, namespace, pvcName, scName string, timeout time.Duration) (*v1.PersistentVolumeClaim, error) {
165 var watchedPVC *v1.PersistentVolumeClaim
166
167 err := wait.PollWithContext(ctx, 1*time.Second, timeout, func(ctx context.Context) (bool, error) {
168 var err error
169 watchedPVC, err = c.CoreV1().PersistentVolumeClaims(namespace).Get(ctx, pvcName, metav1.GetOptions{})
170 if err != nil {
171 return true, err
172 }
173
174 if watchedPVC.Spec.StorageClassName == nil {
175 return false, nil
176 }
177 if watchedPVC.Spec.StorageClassName != nil && *watchedPVC.Spec.StorageClassName != scName {
178 framework.Logf("PersistentVolumeClaim %s has unexpected StorageClass %v, expected StorageClass is: %v", watchedPVC.Name, *watchedPVC.Spec.StorageClassName, scName)
179 return false, nil
180 }
181 return true, nil
182 })
183
184 if err != nil {
185 return watchedPVC, fmt.Errorf("error waiting for claim %s to have StorageClass set to %s: %w", pvcName, scName, err)
186 }
187
188 return watchedPVC, nil
189 }
190
View as plain text