k8s.io/kubernetes@v1.29.3/test/e2e/storage/csi_inline.go (about) 1 /* 2 Copyright 2022 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 23 v1 "k8s.io/api/core/v1" 24 storagev1 "k8s.io/api/storage/v1" 25 apierrors "k8s.io/apimachinery/pkg/api/errors" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/labels" 28 types "k8s.io/apimachinery/pkg/types" 29 "k8s.io/apimachinery/pkg/util/uuid" 30 "k8s.io/client-go/util/retry" 31 "k8s.io/kubernetes/test/e2e/framework" 32 "k8s.io/kubernetes/test/e2e/storage/utils" 33 imageutils "k8s.io/kubernetes/test/utils/image" 34 admissionapi "k8s.io/pod-security-admission/api" 35 36 "github.com/onsi/ginkgo/v2" 37 "github.com/onsi/gomega" 38 ) 39 40 var _ = utils.SIGDescribe("CSIInlineVolumes", func() { 41 f := framework.NewDefaultFramework("csiinlinevolumes") 42 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged 43 44 /* 45 Release: v1.26 46 Testname: CSIInlineVolumes should support Pods with inline volumes 47 Description: Pod resources with CSIVolumeSource should support 48 create, get, list, patch, and delete operations. 49 */ 50 framework.ConformanceIt("should support CSIVolumeSource in Pod API", func(ctx context.Context) { 51 // Create client 52 client := f.ClientSet.CoreV1().Pods(f.Namespace.Name) 53 54 // Fake driver name for this API test 55 driverName := "e2e.example.com" 56 57 podName := "pod-csi-inline-volumes" 58 vol1name := "csi-inline-vol1" 59 vol2name := "csi-inline-vol2" 60 vol2fstype := "ext4" 61 vol2readonly := true 62 63 // Create a simple pod object with 2 CSI inline volumes 64 pod := &v1.Pod{ 65 ObjectMeta: metav1.ObjectMeta{ 66 Name: podName, 67 }, 68 Spec: v1.PodSpec{ 69 Volumes: []v1.Volume{ 70 { 71 Name: vol1name, 72 VolumeSource: v1.VolumeSource{ 73 CSI: &v1.CSIVolumeSource{ 74 Driver: driverName, 75 }, 76 }, 77 }, 78 { 79 Name: vol2name, 80 VolumeSource: v1.VolumeSource{ 81 CSI: &v1.CSIVolumeSource{ 82 Driver: driverName, 83 FSType: &vol2fstype, 84 ReadOnly: &vol2readonly, 85 }, 86 }, 87 }, 88 }, 89 Containers: []v1.Container{ 90 { 91 Name: podName, 92 Image: imageutils.GetE2EImage(imageutils.BusyBox), 93 Command: []string{"sh", "-c", "ls /mnt/*"}, 94 VolumeMounts: []v1.VolumeMount{ 95 { 96 Name: vol1name, 97 MountPath: "/mnt/vol1", 98 }, 99 { 100 Name: vol2name, 101 MountPath: "/mnt/vol2", 102 }, 103 }, 104 }, 105 }, 106 }, 107 } 108 109 ginkgo.By("creating") 110 createdPod, err := client.Create(ctx, pod, metav1.CreateOptions{}) 111 framework.ExpectNoError(err) 112 _, err = client.Create(ctx, pod, metav1.CreateOptions{}) 113 if !apierrors.IsAlreadyExists(err) { 114 framework.Failf("expected 409, got %#v", err) 115 } 116 117 ginkgo.By("getting") 118 retrievedPod, err := client.Get(ctx, podName, metav1.GetOptions{}) 119 framework.ExpectNoError(err) 120 gomega.Expect(retrievedPod.UID).To(gomega.Equal(createdPod.UID)) 121 122 ginkgo.By("listing in namespace") 123 podList, err := client.List(ctx, metav1.ListOptions{}) 124 framework.ExpectNoError(err) 125 gomega.Expect(podList.Items).To(gomega.HaveLen(1), "list should have 1 items, got: %s", podList) 126 127 ginkgo.By("patching") 128 patchedPod, err := client.Patch(ctx, createdPod.Name, types.MergePatchType, []byte(`{"metadata":{"annotations":{"patched":"true"}}}`), metav1.PatchOptions{}) 129 framework.ExpectNoError(err) 130 gomega.Expect(patchedPod.Annotations).To(gomega.HaveKeyWithValue("patched", "true"), "patched object should have the applied annotation") 131 132 ginkgo.By("deleting") 133 err = client.Delete(ctx, createdPod.Name, metav1.DeleteOptions{}) 134 framework.ExpectNoError(err) 135 retrievedPod, err = client.Get(ctx, createdPod.Name, metav1.GetOptions{}) 136 switch { 137 case apierrors.IsNotFound(err): 138 // Okay, normal case. 139 case err != nil: 140 framework.Failf("expected 404, got %#v", err) 141 case retrievedPod.DeletionTimestamp != nil: 142 // Okay, normal case. 143 default: 144 framework.Failf("Pod should have been deleted or have DeletionTimestamp, but instead got: %s", retrievedPod) 145 } 146 }) 147 148 /* 149 Release: v1.28 150 Testname: CSIDriver, lifecycle 151 Description: Creating two CSIDrivers MUST succeed. Patching a CSIDriver MUST 152 succeed with its new label found. Updating a CSIDriver MUST succeed with its 153 new label found. Two CSIDrivers MUST be found when listed. Deleting the first 154 CSIDriver MUST succeed. Deleting the second CSIDriver via deleteCollection 155 MUST succeed. 156 */ 157 framework.ConformanceIt("should run through the lifecycle of a CSIDriver", func(ctx context.Context) { 158 // Create client 159 client := f.ClientSet.StorageV1().CSIDrivers() 160 defaultFSGroupPolicy := storagev1.ReadWriteOnceWithFSTypeFSGroupPolicy 161 csiDriverLabel := map[string]string{"e2e-test": f.UniqueName} 162 csiDriverLabelSelector := labels.SelectorFromSet(csiDriverLabel).String() 163 164 // Driver that supports only Ephemeral 165 driver1 := &storagev1.CSIDriver{ 166 ObjectMeta: metav1.ObjectMeta{ 167 Name: "inline-driver-" + string(uuid.NewUUID()), 168 Labels: csiDriverLabel, 169 }, 170 171 Spec: storagev1.CSIDriverSpec{ 172 VolumeLifecycleModes: []storagev1.VolumeLifecycleMode{storagev1.VolumeLifecycleEphemeral}, 173 FSGroupPolicy: &defaultFSGroupPolicy, 174 }, 175 } 176 177 // Driver that supports both Ephemeral and Persistent 178 driver2 := &storagev1.CSIDriver{ 179 ObjectMeta: metav1.ObjectMeta{ 180 Name: "inline-driver-" + string(uuid.NewUUID()), 181 Labels: csiDriverLabel, 182 }, 183 184 Spec: storagev1.CSIDriverSpec{ 185 VolumeLifecycleModes: []storagev1.VolumeLifecycleMode{ 186 storagev1.VolumeLifecyclePersistent, 187 storagev1.VolumeLifecycleEphemeral, 188 }, 189 FSGroupPolicy: &defaultFSGroupPolicy, 190 }, 191 } 192 193 ginkgo.By("Creating two CSIDrivers") 194 createdDriver1, err := client.Create(ctx, driver1, metav1.CreateOptions{}) 195 framework.ExpectNoError(err, "Failed to create first CSIDriver") 196 createdDriver2, err := client.Create(ctx, driver2, metav1.CreateOptions{}) 197 framework.ExpectNoError(err, "Failed to create second CSIDriver") 198 _, err = client.Create(ctx, driver1, metav1.CreateOptions{}) 199 if !apierrors.IsAlreadyExists(err) { 200 framework.Failf("expected 409, got %#v", err) 201 } 202 203 ginkgo.By(fmt.Sprintf("Getting %q & %q", createdDriver1.Name, createdDriver2.Name)) 204 retrievedDriver1, err := client.Get(ctx, createdDriver1.Name, metav1.GetOptions{}) 205 framework.ExpectNoError(err, "Failed to get CSIDriver %q", createdDriver1.Name) 206 gomega.Expect(retrievedDriver1.UID).To(gomega.Equal(createdDriver1.UID)) 207 208 retrievedDriver2, err := client.Get(ctx, createdDriver2.Name, metav1.GetOptions{}) 209 framework.ExpectNoError(err, "Failed to get CSIDriver %q", createdDriver2.Name) 210 gomega.Expect(retrievedDriver2.UID).To(gomega.Equal(createdDriver2.UID)) 211 212 ginkgo.By(fmt.Sprintf("Patching the CSIDriver %q", createdDriver2.Name)) 213 payload := "{\"metadata\":{\"labels\":{\"" + createdDriver2.Name + "\":\"patched\"}}}" 214 patchedCSIDriver, err := client.Patch(ctx, createdDriver2.Name, types.StrategicMergePatchType, []byte(payload), metav1.PatchOptions{}) 215 framework.ExpectNoError(err, "failed to patch CSIDriver %q", createdDriver2.Name) 216 gomega.Expect(patchedCSIDriver.Labels[createdDriver2.Name]).To(gomega.ContainSubstring("patched"), "Checking that patched label has been applied") 217 218 ginkgo.By(fmt.Sprintf("Updating the CSIDriver %q", createdDriver2.Name)) 219 var updatedCSIDriver *storagev1.CSIDriver 220 221 err = retry.RetryOnConflict(retry.DefaultRetry, func() error { 222 csiDriver, err := client.Get(ctx, createdDriver2.Name, metav1.GetOptions{}) 223 framework.ExpectNoError(err, "Unable to get CSIDriver %q", createdDriver2.Name) 224 csiDriver.Labels[retrievedDriver2.Name] = "updated" 225 updatedCSIDriver, err = client.Update(ctx, csiDriver, metav1.UpdateOptions{}) 226 227 return err 228 }) 229 framework.ExpectNoError(err, "failed to update CSIDriver %q", createdDriver2.Name) 230 gomega.Expect(updatedCSIDriver.Labels[createdDriver2.Name]).To(gomega.ContainSubstring("updated"), "Checking that updated label has been applied") 231 232 ginkgo.By(fmt.Sprintf("Listing all CSIDrivers with the labelSelector: %q", csiDriverLabelSelector)) 233 driverList, err := client.List(ctx, metav1.ListOptions{LabelSelector: csiDriverLabelSelector}) 234 framework.ExpectNoError(err, "Failed to list all CSIDrivers with the labelSelector %q", csiDriverLabelSelector) 235 gomega.Expect(driverList.Items).To(gomega.HaveLen(2), "filtered list should have 2 items, got: %s", driverList) 236 237 ginkgo.By(fmt.Sprintf("Deleting CSIDriver %q", createdDriver1.Name)) 238 err = client.Delete(ctx, createdDriver1.Name, metav1.DeleteOptions{}) 239 framework.ExpectNoError(err, "Failed to delete CSIDriver %q", createdDriver1.Name) 240 241 ginkgo.By(fmt.Sprintf("Confirm deletion of CSIDriver %q", createdDriver1.Name)) 242 retrievedDriver, err := client.Get(ctx, createdDriver1.Name, metav1.GetOptions{}) 243 switch { 244 case apierrors.IsNotFound(err): 245 // Okay, normal case. 246 case err != nil: 247 framework.Failf("expected 404, got %#v", err) 248 case retrievedDriver.DeletionTimestamp != nil: 249 // Okay, normal case. 250 default: 251 framework.Failf("CSIDriver should have been deleted or have DeletionTimestamp, but instead got: %s", retrievedDriver) 252 } 253 254 ginkgo.By(fmt.Sprintf("Deleting CSIDriver %q via DeleteCollection", createdDriver2.Name)) 255 err = client.DeleteCollection(ctx, metav1.DeleteOptions{}, metav1.ListOptions{LabelSelector: createdDriver2.Name + "=updated"}) 256 framework.ExpectNoError(err, "Failed to delete CSIDriver %q", createdDriver2.Name) 257 258 ginkgo.By(fmt.Sprintf("Confirm deletion of CSIDriver %q", createdDriver2.Name)) 259 retrievedDriver, err = client.Get(ctx, createdDriver2.Name, metav1.GetOptions{}) 260 switch { 261 case apierrors.IsNotFound(err): 262 // Okay, normal case. 263 case err != nil: 264 framework.Failf("expected 404, got %#v", err) 265 case retrievedDriver.DeletionTimestamp != nil: 266 // Okay, normal case. 267 default: 268 framework.Failf("CSIDriver should have been deleted or have DeletionTimestamp, but instead got: %s", retrievedDriver) 269 } 270 }) 271 })