k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e/storage/storageclass.go (about) 1 /* 2 Copyright 2023 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 "fmt" 22 "time" 23 24 storagev1 "k8s.io/api/storage/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/labels" 27 types "k8s.io/apimachinery/pkg/types" 28 "k8s.io/client-go/util/retry" 29 "k8s.io/kubernetes/test/e2e/framework" 30 "k8s.io/kubernetes/test/e2e/storage/utils" 31 admissionapi "k8s.io/pod-security-admission/api" 32 33 "github.com/onsi/ginkgo/v2" 34 "github.com/onsi/gomega" 35 ) 36 37 var _ = utils.SIGDescribe("StorageClasses", func() { 38 39 f := framework.NewDefaultFramework("csi-storageclass") 40 f.NamespacePodSecurityLevel = admissionapi.LevelBaseline 41 42 ginkgo.Describe("CSI Conformance", func() { 43 44 /* 45 Release: v1.29 46 Testname: StorageClass, lifecycle 47 Description: Creating a StorageClass MUST succeed. Reading the StorageClass MUST 48 succeed. Patching the StorageClass MUST succeed with its new label found. Deleting 49 the StorageClass MUST succeed and it MUST be confirmed. Replacement StorageClass 50 MUST be created. Updating the StorageClass MUST succeed with its new label found. 51 Deleting the StorageClass via deleteCollection MUST succeed and it MUST be confirmed. 52 */ 53 framework.ConformanceIt("should run through the lifecycle of a StorageClass", func(ctx context.Context) { 54 55 scClient := f.ClientSet.StorageV1().StorageClasses() 56 var initialSC, replacementSC *storagev1.StorageClass 57 58 initialSC = &storagev1.StorageClass{ 59 TypeMeta: metav1.TypeMeta{ 60 Kind: "StorageClass", 61 }, 62 ObjectMeta: metav1.ObjectMeta{ 63 GenerateName: "e2e-", 64 }, 65 Provisioner: "e2e-fake-provisioner", 66 } 67 68 ginkgo.By("Creating a StorageClass") 69 createdStorageClass, err := scClient.Create(ctx, initialSC, metav1.CreateOptions{}) 70 framework.ExpectNoError(err, "failed to create the requested StorageClass") 71 72 ginkgo.By(fmt.Sprintf("Get StorageClass %q", createdStorageClass.Name)) 73 retrievedStorageClass, err := scClient.Get(ctx, createdStorageClass.Name, metav1.GetOptions{}) 74 framework.ExpectNoError(err, "failed to get StorageClass %q", createdStorageClass.Name) 75 76 ginkgo.By(fmt.Sprintf("Patching the StorageClass %q", retrievedStorageClass.Name)) 77 payload := "{\"metadata\":{\"labels\":{\"" + retrievedStorageClass.Name + "\":\"patched\"}}}" 78 patchedStorageClass, err := scClient.Patch(ctx, retrievedStorageClass.Name, types.StrategicMergePatchType, []byte(payload), metav1.PatchOptions{}) 79 framework.ExpectNoError(err, "failed to patch StorageClass %q", retrievedStorageClass.Name) 80 gomega.Expect(patchedStorageClass.Labels).To(gomega.HaveKeyWithValue(patchedStorageClass.Name, "patched"), "checking that patched label has been applied") 81 82 ginkgo.By(fmt.Sprintf("Delete StorageClass %q", patchedStorageClass.Name)) 83 err = scClient.Delete(ctx, patchedStorageClass.Name, metav1.DeleteOptions{}) 84 framework.ExpectNoError(err, "failed to delete StorageClass %q", patchedStorageClass.Name) 85 86 ginkgo.By(fmt.Sprintf("Confirm deletion of StorageClass %q", patchedStorageClass.Name)) 87 88 scSelector := labels.Set{patchedStorageClass.Name: "patched"}.AsSelector().String() 89 type state struct { 90 StorageClasses []storagev1.StorageClass 91 } 92 93 err = framework.Gomega().Eventually(ctx, framework.HandleRetry(func(ctx context.Context) (*state, error) { 94 scList, err := scClient.List(ctx, metav1.ListOptions{LabelSelector: scSelector}) 95 if err != nil { 96 return nil, fmt.Errorf("failed to list StorageClass: %w", err) 97 } 98 return &state{ 99 StorageClasses: scList.Items, 100 }, nil 101 })).WithTimeout(30 * time.Second).Should(framework.MakeMatcher(func(s *state) (func() string, error) { 102 if len(s.StorageClasses) == 0 { 103 return nil, nil 104 } 105 return func() string { 106 return fmt.Sprintf("expected StorageClass to be deleted, found %q", s.StorageClasses[0].Name) 107 }, nil 108 })) 109 framework.ExpectNoError(err, "timeout while waiting to confirm StorageClass %q deletion", patchedStorageClass.Name) 110 111 ginkgo.By("Create a replacement StorageClass") 112 113 replacementSC = &storagev1.StorageClass{ 114 TypeMeta: metav1.TypeMeta{ 115 Kind: "StorageClass", 116 }, 117 ObjectMeta: metav1.ObjectMeta{ 118 GenerateName: "e2e-v2-", 119 }, 120 Provisioner: "e2e-fake-provisioner", 121 } 122 123 replacementStorageClass, err := scClient.Create(ctx, replacementSC, metav1.CreateOptions{}) 124 framework.ExpectNoError(err, "failed to create replacement StorageClass") 125 126 ginkgo.By(fmt.Sprintf("Updating StorageClass %q", replacementStorageClass.Name)) 127 var updatedStorageClass *storagev1.StorageClass 128 129 err = retry.RetryOnConflict(retry.DefaultRetry, func() error { 130 sc, err := scClient.Get(ctx, replacementStorageClass.Name, metav1.GetOptions{}) 131 framework.ExpectNoError(err, "unable to get Storage %q", replacementStorageClass.Name) 132 sc.Labels = map[string]string{replacementStorageClass.Name: "updated"} 133 updatedStorageClass, err = scClient.Update(ctx, sc, metav1.UpdateOptions{}) 134 135 return err 136 }) 137 framework.ExpectNoError(err, "failed to update StorageClass %q", replacementStorageClass.Name) 138 gomega.Expect(updatedStorageClass.Labels).To(gomega.HaveKeyWithValue(replacementStorageClass.Name, "updated"), "checking that updated label has been applied") 139 140 scSelector = labels.Set{replacementStorageClass.Name: "updated"}.AsSelector().String() 141 ginkgo.By(fmt.Sprintf("Listing all StorageClass with the labelSelector: %q", scSelector)) 142 scList, err := scClient.List(ctx, metav1.ListOptions{LabelSelector: scSelector}) 143 framework.ExpectNoError(err, "failed to list StorageClasses with the labelSelector: %q", scSelector) 144 gomega.Expect(scList.Items).To(gomega.HaveLen(1)) 145 146 ginkgo.By(fmt.Sprintf("Deleting StorageClass %q via DeleteCollection", updatedStorageClass.Name)) 147 err = scClient.DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: scSelector}) 148 framework.ExpectNoError(err, "failed to delete StorageClass %q", updatedStorageClass.Name) 149 150 ginkgo.By(fmt.Sprintf("Confirm deletion of StorageClass %q", updatedStorageClass.Name)) 151 152 err = framework.Gomega().Eventually(ctx, framework.HandleRetry(func(ctx context.Context) (*state, error) { 153 scList, err := scClient.List(ctx, metav1.ListOptions{LabelSelector: scSelector}) 154 if err != nil { 155 return nil, fmt.Errorf("failed to list StorageClass: %w", err) 156 } 157 return &state{ 158 StorageClasses: scList.Items, 159 }, nil 160 })).WithTimeout(30 * time.Second).Should(framework.MakeMatcher(func(s *state) (func() string, error) { 161 if len(s.StorageClasses) == 0 { 162 return nil, nil 163 } 164 return func() string { 165 return fmt.Sprintf("expected StorageClass to be deleted, found %q", s.StorageClasses[0].Name) 166 }, nil 167 })) 168 framework.ExpectNoError(err, "timeout while waiting to confirm StorageClass %q deletion", updatedStorageClass.Name) 169 }) 170 }) 171 })