...

Source file src/k8s.io/kubernetes/test/e2e/storage/storageclass.go

Documentation: k8s.io/kubernetes/test/e2e/storage

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

View as plain text