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  })