k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e/storage/volume_provisioning.go (about)

     1  //go:build !providerless
     2  // +build !providerless
     3  
     4  /*
     5  Copyright 2016 The Kubernetes Authors.
     6  
     7  Licensed under the Apache License, Version 2.0 (the "License");
     8  you may not use this file except in compliance with the License.
     9  You may obtain a copy of the License at
    10  
    11      http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  Unless required by applicable law or agreed to in writing, software
    14  distributed under the License is distributed on an "AS IS" BASIS,
    15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  See the License for the specific language governing permissions and
    17  limitations under the License.
    18  */
    19  
    20  package storage
    21  
    22  import (
    23  	"context"
    24  	"fmt"
    25  	"strings"
    26  	"time"
    27  
    28  	"github.com/onsi/ginkgo/v2"
    29  	"github.com/onsi/gomega"
    30  
    31  	v1 "k8s.io/api/core/v1"
    32  	rbacv1 "k8s.io/api/rbac/v1"
    33  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    34  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    35  	"k8s.io/apimachinery/pkg/runtime/schema"
    36  	"k8s.io/apimachinery/pkg/types"
    37  	"k8s.io/apimachinery/pkg/util/rand"
    38  	"k8s.io/apimachinery/pkg/util/wait"
    39  	"k8s.io/apiserver/pkg/authentication/serviceaccount"
    40  	clientset "k8s.io/client-go/kubernetes"
    41  	storageutil "k8s.io/kubernetes/pkg/apis/storage/util"
    42  	"k8s.io/kubernetes/test/e2e/feature"
    43  	"k8s.io/kubernetes/test/e2e/framework"
    44  	e2eauth "k8s.io/kubernetes/test/e2e/framework/auth"
    45  	e2enode "k8s.io/kubernetes/test/e2e/framework/node"
    46  	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
    47  	e2epv "k8s.io/kubernetes/test/e2e/framework/pv"
    48  	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
    49  	"k8s.io/kubernetes/test/e2e/storage/testsuites"
    50  	"k8s.io/kubernetes/test/e2e/storage/utils"
    51  	admissionapi "k8s.io/pod-security-admission/api"
    52  )
    53  
    54  const (
    55  	// Plugin name of the external provisioner
    56  	externalPluginName = "example.com/nfs"
    57  )
    58  
    59  var _ = utils.SIGDescribe("Dynamic Provisioning", func() {
    60  	f := framework.NewDefaultFramework("volume-provisioning")
    61  	f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
    62  
    63  	// filled in BeforeEach
    64  	var c clientset.Interface
    65  	var timeouts *framework.TimeoutContext
    66  	var ns string
    67  
    68  	ginkgo.BeforeEach(func() {
    69  		c = f.ClientSet
    70  		ns = f.Namespace.Name
    71  		timeouts = f.Timeouts
    72  	})
    73  
    74  	f.Describe("DynamicProvisioner", framework.WithSlow(), feature.StorageProvider, func() {
    75  		ginkgo.It("should provision storage with different parameters", func(ctx context.Context) {
    76  
    77  			// This test checks that dynamic provisioning can provision a volume
    78  			// that can be used to persist data among pods.
    79  			tests := []testsuites.StorageClassTest{
    80  				// GCE/GKE
    81  				{
    82  					Name:           "SSD PD on GCE/GKE",
    83  					CloudProviders: []string{"gce", "gke"},
    84  					Timeouts:       f.Timeouts,
    85  					Provisioner:    "kubernetes.io/gce-pd",
    86  					Parameters: map[string]string{
    87  						"type": "pd-ssd",
    88  						"zone": getRandomClusterZone(ctx, c),
    89  					},
    90  					ClaimSize:    "1.5Gi",
    91  					ExpectedSize: "2Gi",
    92  					PvCheck: func(ctx context.Context, claim *v1.PersistentVolumeClaim) {
    93  						volume := testsuites.PVWriteReadSingleNodeCheck(ctx, c, f.Timeouts, claim, e2epod.NodeSelection{})
    94  						gomega.Expect(volume).NotTo(gomega.BeNil(), "get bound PV")
    95  					},
    96  				},
    97  				{
    98  					Name:           "HDD PD on GCE/GKE",
    99  					CloudProviders: []string{"gce", "gke"},
   100  					Timeouts:       f.Timeouts,
   101  					Provisioner:    "kubernetes.io/gce-pd",
   102  					Parameters: map[string]string{
   103  						"type": "pd-standard",
   104  					},
   105  					ClaimSize:    "1.5Gi",
   106  					ExpectedSize: "2Gi",
   107  					PvCheck: func(ctx context.Context, claim *v1.PersistentVolumeClaim) {
   108  						volume := testsuites.PVWriteReadSingleNodeCheck(ctx, c, f.Timeouts, claim, e2epod.NodeSelection{})
   109  						gomega.Expect(volume).NotTo(gomega.BeNil(), "get bound PV")
   110  					},
   111  				},
   112  				// AWS
   113  				{
   114  					Name:           "gp2 EBS on AWS",
   115  					CloudProviders: []string{"aws"},
   116  					Timeouts:       f.Timeouts,
   117  					Provisioner:    "kubernetes.io/aws-ebs",
   118  					Parameters: map[string]string{
   119  						"type": "gp2",
   120  						"zone": getRandomClusterZone(ctx, c),
   121  					},
   122  					ClaimSize:    "1.5Gi",
   123  					ExpectedSize: "2Gi",
   124  					PvCheck: func(ctx context.Context, claim *v1.PersistentVolumeClaim) {
   125  						volume := testsuites.PVWriteReadSingleNodeCheck(ctx, c, f.Timeouts, claim, e2epod.NodeSelection{})
   126  						gomega.Expect(volume).NotTo(gomega.BeNil(), "get bound PV")
   127  					},
   128  				},
   129  				{
   130  					Name:           "io1 EBS on AWS",
   131  					CloudProviders: []string{"aws"},
   132  					Timeouts:       f.Timeouts,
   133  					Provisioner:    "kubernetes.io/aws-ebs",
   134  					Parameters: map[string]string{
   135  						"type":      "io1",
   136  						"iopsPerGB": "50",
   137  					},
   138  					ClaimSize:    "3.5Gi",
   139  					ExpectedSize: "4Gi", // 4 GiB is minimum for io1
   140  					PvCheck: func(ctx context.Context, claim *v1.PersistentVolumeClaim) {
   141  						volume := testsuites.PVWriteReadSingleNodeCheck(ctx, c, f.Timeouts, claim, e2epod.NodeSelection{})
   142  						gomega.Expect(volume).NotTo(gomega.BeNil(), "get bound PV")
   143  					},
   144  				},
   145  				{
   146  					Name:           "sc1 EBS on AWS",
   147  					CloudProviders: []string{"aws"},
   148  					Timeouts:       f.Timeouts,
   149  					Provisioner:    "kubernetes.io/aws-ebs",
   150  					Parameters: map[string]string{
   151  						"type": "sc1",
   152  					},
   153  					ClaimSize:    "500Gi", // minimum for sc1
   154  					ExpectedSize: "500Gi",
   155  					PvCheck: func(ctx context.Context, claim *v1.PersistentVolumeClaim) {
   156  						volume := testsuites.PVWriteReadSingleNodeCheck(ctx, c, f.Timeouts, claim, e2epod.NodeSelection{})
   157  						gomega.Expect(volume).NotTo(gomega.BeNil(), "get bound PV")
   158  					},
   159  				},
   160  				{
   161  					Name:           "st1 EBS on AWS",
   162  					CloudProviders: []string{"aws"},
   163  					Timeouts:       f.Timeouts,
   164  					Provisioner:    "kubernetes.io/aws-ebs",
   165  					Parameters: map[string]string{
   166  						"type": "st1",
   167  					},
   168  					ClaimSize:    "500Gi", // minimum for st1
   169  					ExpectedSize: "500Gi",
   170  					PvCheck: func(ctx context.Context, claim *v1.PersistentVolumeClaim) {
   171  						volume := testsuites.PVWriteReadSingleNodeCheck(ctx, c, f.Timeouts, claim, e2epod.NodeSelection{})
   172  						gomega.Expect(volume).NotTo(gomega.BeNil(), "get bound PV")
   173  					},
   174  				},
   175  				{
   176  					Name:           "encrypted EBS on AWS",
   177  					CloudProviders: []string{"aws"},
   178  					Timeouts:       f.Timeouts,
   179  					Provisioner:    "kubernetes.io/aws-ebs",
   180  					Parameters: map[string]string{
   181  						"encrypted": "true",
   182  					},
   183  					ClaimSize:    "1Gi",
   184  					ExpectedSize: "1Gi",
   185  					PvCheck: func(ctx context.Context, claim *v1.PersistentVolumeClaim) {
   186  						volume := testsuites.PVWriteReadSingleNodeCheck(ctx, c, f.Timeouts, claim, e2epod.NodeSelection{})
   187  						gomega.Expect(volume).NotTo(gomega.BeNil(), "get bound PV")
   188  					},
   189  				},
   190  				// OpenStack generic tests (works on all OpenStack deployments)
   191  				{
   192  					Name:           "generic Cinder volume on OpenStack",
   193  					CloudProviders: []string{"openstack"},
   194  					Timeouts:       f.Timeouts,
   195  					Provisioner:    "kubernetes.io/cinder",
   196  					Parameters:     map[string]string{},
   197  					ClaimSize:      "1.5Gi",
   198  					ExpectedSize:   "2Gi",
   199  					PvCheck: func(ctx context.Context, claim *v1.PersistentVolumeClaim) {
   200  						testsuites.PVWriteReadSingleNodeCheck(ctx, c, f.Timeouts, claim, e2epod.NodeSelection{})
   201  					},
   202  				},
   203  				{
   204  					Name:           "Cinder volume with empty volume type and zone on OpenStack",
   205  					CloudProviders: []string{"openstack"},
   206  					Timeouts:       f.Timeouts,
   207  					Provisioner:    "kubernetes.io/cinder",
   208  					Parameters: map[string]string{
   209  						"type":         "",
   210  						"availability": "",
   211  					},
   212  					ClaimSize:    "1.5Gi",
   213  					ExpectedSize: "2Gi",
   214  					PvCheck: func(ctx context.Context, claim *v1.PersistentVolumeClaim) {
   215  						testsuites.PVWriteReadSingleNodeCheck(ctx, c, f.Timeouts, claim, e2epod.NodeSelection{})
   216  					},
   217  				},
   218  				// vSphere generic test
   219  				{
   220  					Name:           "generic vSphere volume",
   221  					CloudProviders: []string{"vsphere"},
   222  					Timeouts:       f.Timeouts,
   223  					Provisioner:    "kubernetes.io/vsphere-volume",
   224  					Parameters:     map[string]string{},
   225  					ClaimSize:      "1.5Gi",
   226  					ExpectedSize:   "1.5Gi",
   227  					PvCheck: func(ctx context.Context, claim *v1.PersistentVolumeClaim) {
   228  						testsuites.PVWriteReadSingleNodeCheck(ctx, c, f.Timeouts, claim, e2epod.NodeSelection{})
   229  					},
   230  				},
   231  				// Azure
   232  				{
   233  					Name:           "Azure disk volume with empty sku and location",
   234  					CloudProviders: []string{"azure"},
   235  					Timeouts:       f.Timeouts,
   236  					Provisioner:    "kubernetes.io/azure-disk",
   237  					Parameters:     map[string]string{},
   238  					ClaimSize:      "1Gi",
   239  					ExpectedSize:   "1Gi",
   240  					PvCheck: func(ctx context.Context, claim *v1.PersistentVolumeClaim) {
   241  						testsuites.PVWriteReadSingleNodeCheck(ctx, c, f.Timeouts, claim, e2epod.NodeSelection{})
   242  					},
   243  				},
   244  			}
   245  
   246  			for i, t := range tests {
   247  				// Beware of closure, use local variables instead of those from
   248  				// outer scope
   249  				test := t
   250  
   251  				if !framework.ProviderIs(test.CloudProviders...) {
   252  					framework.Logf("Skipping %q: cloud providers is not %v", test.Name, test.CloudProviders)
   253  					continue
   254  				}
   255  
   256  				if zone, ok := test.Parameters["zone"]; ok {
   257  					gomega.Expect(zone).ToNot(gomega.BeEmpty(), "expect at least one zone")
   258  				}
   259  
   260  				ginkgo.By("Testing " + test.Name)
   261  				suffix := fmt.Sprintf("%d", i)
   262  				test.Client = c
   263  
   264  				// overwrite StorageClass spec with provisioned StorageClass
   265  				storageClass := testsuites.SetupStorageClass(ctx, test.Client, newStorageClass(test, ns, suffix))
   266  
   267  				test.Class = storageClass
   268  				test.Claim = e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{
   269  					ClaimSize:        test.ClaimSize,
   270  					StorageClassName: &test.Class.Name,
   271  					VolumeMode:       &test.VolumeMode,
   272  				}, ns)
   273  
   274  				test.TestDynamicProvisioning(ctx)
   275  			}
   276  		})
   277  
   278  		ginkgo.It("should provision storage with non-default reclaim policy Retain", func(ctx context.Context) {
   279  			e2eskipper.SkipUnlessProviderIs("gce", "gke")
   280  
   281  			test := testsuites.StorageClassTest{
   282  				Client:         c,
   283  				Name:           "HDD PD on GCE/GKE",
   284  				CloudProviders: []string{"gce", "gke"},
   285  				Provisioner:    "kubernetes.io/gce-pd",
   286  				Timeouts:       f.Timeouts,
   287  				Parameters: map[string]string{
   288  					"type": "pd-standard",
   289  				},
   290  				ClaimSize:    "1Gi",
   291  				ExpectedSize: "1Gi",
   292  				PvCheck: func(ctx context.Context, claim *v1.PersistentVolumeClaim) {
   293  					volume := testsuites.PVWriteReadSingleNodeCheck(ctx, c, f.Timeouts, claim, e2epod.NodeSelection{})
   294  					gomega.Expect(volume).NotTo(gomega.BeNil(), "get bound PV")
   295  				},
   296  			}
   297  			test.Class = newStorageClass(test, ns, "reclaimpolicy")
   298  			retain := v1.PersistentVolumeReclaimRetain
   299  			test.Class.ReclaimPolicy = &retain
   300  			storageClass := testsuites.SetupStorageClass(ctx, test.Client, test.Class)
   301  			test.Class = storageClass
   302  
   303  			test.Claim = e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{
   304  				ClaimSize:        test.ClaimSize,
   305  				StorageClassName: &test.Class.Name,
   306  				VolumeMode:       &test.VolumeMode,
   307  			}, ns)
   308  
   309  			pv := test.TestDynamicProvisioning(ctx)
   310  
   311  			ginkgo.By(fmt.Sprintf("waiting for the provisioned PV %q to enter phase %s", pv.Name, v1.VolumeReleased))
   312  			framework.ExpectNoError(e2epv.WaitForPersistentVolumePhase(ctx, v1.VolumeReleased, c, pv.Name, 1*time.Second, 30*time.Second))
   313  
   314  			ginkgo.By(fmt.Sprintf("deleting the storage asset backing the PV %q", pv.Name))
   315  			framework.ExpectNoError(e2epv.DeletePDWithRetry(ctx, pv.Spec.GCEPersistentDisk.PDName))
   316  
   317  			ginkgo.By(fmt.Sprintf("deleting the PV %q", pv.Name))
   318  			framework.ExpectNoError(e2epv.DeletePersistentVolume(ctx, c, pv.Name), "Failed to delete PV ", pv.Name)
   319  			framework.ExpectNoError(e2epv.WaitForPersistentVolumeDeleted(ctx, c, pv.Name, 1*time.Second, 30*time.Second))
   320  		})
   321  
   322  		ginkgo.It("should test that deleting a claim before the volume is provisioned deletes the volume.", func(ctx context.Context) {
   323  			// This case tests for the regressions of a bug fixed by PR #21268
   324  			// REGRESSION: Deleting the PVC before the PV is provisioned can result in the PV
   325  			// not being deleted.
   326  			// NOTE:  Polls until no PVs are detected, times out at 5 minutes.
   327  
   328  			e2eskipper.SkipUnlessProviderIs("openstack", "gce", "aws", "gke", "vsphere", "azure")
   329  
   330  			const raceAttempts int = 100
   331  			var residualPVs []*v1.PersistentVolume
   332  			ginkgo.By(fmt.Sprintf("Creating and deleting PersistentVolumeClaims %d times", raceAttempts))
   333  			test := testsuites.StorageClassTest{
   334  				Name:        "deletion race",
   335  				Provisioner: "", // Use a native one based on current cloud provider
   336  				Timeouts:    f.Timeouts,
   337  				ClaimSize:   "1Gi",
   338  			}
   339  
   340  			class := newStorageClass(test, ns, "race")
   341  			class, err := c.StorageV1().StorageClasses().Create(ctx, class, metav1.CreateOptions{})
   342  			framework.ExpectNoError(err)
   343  			ginkgo.DeferCleanup(deleteStorageClass, c, class.Name)
   344  
   345  			// To increase chance of detection, attempt multiple iterations
   346  			for i := 0; i < raceAttempts; i++ {
   347  				prefix := fmt.Sprintf("race-%d", i)
   348  				claim := e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{
   349  					NamePrefix:       prefix,
   350  					ClaimSize:        test.ClaimSize,
   351  					StorageClassName: &class.Name,
   352  					VolumeMode:       &test.VolumeMode,
   353  				}, ns)
   354  				tmpClaim, err := e2epv.CreatePVC(ctx, c, ns, claim)
   355  				framework.ExpectNoError(err)
   356  				framework.ExpectNoError(e2epv.DeletePersistentVolumeClaim(ctx, c, tmpClaim.Name, ns))
   357  			}
   358  
   359  			ginkgo.By(fmt.Sprintf("Checking for residual PersistentVolumes associated with StorageClass %s", class.Name))
   360  			residualPVs, err = waitForProvisionedVolumesDeleted(ctx, c, class.Name)
   361  			// Cleanup the test resources before breaking
   362  			ginkgo.DeferCleanup(deleteProvisionedVolumesAndDisks, c, residualPVs)
   363  			framework.ExpectNoError(err, "PersistentVolumes were not deleted as expected. %d remain", len(residualPVs))
   364  
   365  			framework.Logf("0 PersistentVolumes remain.")
   366  		})
   367  
   368  		ginkgo.It("deletion should be idempotent", func(ctx context.Context) {
   369  			// This test ensures that deletion of a volume is idempotent.
   370  			// It creates a PV with Retain policy, deletes underlying AWS / GCE
   371  			// volume and changes the reclaim policy to Delete.
   372  			// PV controller should delete the PV even though the underlying volume
   373  			// is already deleted.
   374  			e2eskipper.SkipUnlessProviderIs("gce", "gke", "aws")
   375  			ginkgo.By("creating PD")
   376  			diskName, err := e2epv.CreatePDWithRetry(ctx)
   377  			framework.ExpectNoError(err)
   378  
   379  			ginkgo.By("creating PV")
   380  			pv := e2epv.MakePersistentVolume(e2epv.PersistentVolumeConfig{
   381  				NamePrefix: "volume-idempotent-delete-",
   382  				// Use Retain to keep the PV, the test will change it to Delete
   383  				// when the time comes.
   384  				ReclaimPolicy: v1.PersistentVolumeReclaimRetain,
   385  				AccessModes: []v1.PersistentVolumeAccessMode{
   386  					v1.ReadWriteOnce,
   387  				},
   388  				Capacity: "1Gi",
   389  				// PV is bound to non-existing PVC, so it's reclaim policy is
   390  				// executed immediately
   391  				Prebind: &v1.PersistentVolumeClaim{
   392  					ObjectMeta: metav1.ObjectMeta{
   393  						Name:      "dummy-claim-name",
   394  						Namespace: ns,
   395  						UID:       types.UID("01234567890"),
   396  					},
   397  				},
   398  			})
   399  			switch framework.TestContext.Provider {
   400  			case "aws":
   401  				pv.Spec.PersistentVolumeSource = v1.PersistentVolumeSource{
   402  					AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{
   403  						VolumeID: diskName,
   404  					},
   405  				}
   406  			case "gce", "gke":
   407  				pv.Spec.PersistentVolumeSource = v1.PersistentVolumeSource{
   408  					GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
   409  						PDName: diskName,
   410  					},
   411  				}
   412  			}
   413  			pv, err = c.CoreV1().PersistentVolumes().Create(ctx, pv, metav1.CreateOptions{})
   414  			framework.ExpectNoError(err)
   415  
   416  			ginkgo.By("waiting for the PV to get Released")
   417  			err = e2epv.WaitForPersistentVolumePhase(ctx, v1.VolumeReleased, c, pv.Name, 2*time.Second, timeouts.PVReclaim)
   418  			framework.ExpectNoError(err)
   419  
   420  			ginkgo.By("deleting the PD")
   421  			err = e2epv.DeletePVSource(ctx, &pv.Spec.PersistentVolumeSource)
   422  			framework.ExpectNoError(err)
   423  
   424  			ginkgo.By("changing the PV reclaim policy")
   425  			pv, err = c.CoreV1().PersistentVolumes().Get(ctx, pv.Name, metav1.GetOptions{})
   426  			framework.ExpectNoError(err)
   427  			pv.Spec.PersistentVolumeReclaimPolicy = v1.PersistentVolumeReclaimDelete
   428  			pv, err = c.CoreV1().PersistentVolumes().Update(ctx, pv, metav1.UpdateOptions{})
   429  			framework.ExpectNoError(err)
   430  
   431  			ginkgo.By("waiting for the PV to get deleted")
   432  			err = e2epv.WaitForPersistentVolumeDeleted(ctx, c, pv.Name, 5*time.Second, timeouts.PVDelete)
   433  			framework.ExpectNoError(err)
   434  		})
   435  	})
   436  
   437  	ginkgo.Describe("DynamicProvisioner External", func() {
   438  		f.It("should let an external dynamic provisioner create and delete persistent volumes", f.WithSlow(), func(ctx context.Context) {
   439  			// external dynamic provisioner pods need additional permissions provided by the
   440  			// persistent-volume-provisioner clusterrole and a leader-locking role
   441  			serviceAccountName := "default"
   442  			subject := rbacv1.Subject{
   443  				Kind:      rbacv1.ServiceAccountKind,
   444  				Namespace: ns,
   445  				Name:      serviceAccountName,
   446  			}
   447  
   448  			err := e2eauth.BindClusterRole(ctx, c.RbacV1(), "system:persistent-volume-provisioner", ns, subject)
   449  			framework.ExpectNoError(err)
   450  
   451  			roleName := "leader-locking-nfs-provisioner"
   452  			_, err = f.ClientSet.RbacV1().Roles(ns).Create(ctx, &rbacv1.Role{
   453  				ObjectMeta: metav1.ObjectMeta{
   454  					Name: roleName,
   455  				},
   456  				Rules: []rbacv1.PolicyRule{{
   457  					APIGroups: []string{""},
   458  					Resources: []string{"endpoints"},
   459  					Verbs:     []string{"get", "list", "watch", "create", "update", "patch"},
   460  				}},
   461  			}, metav1.CreateOptions{})
   462  			framework.ExpectNoError(err, "Failed to create leader-locking role")
   463  
   464  			err = e2eauth.BindRoleInNamespace(ctx, c.RbacV1(), roleName, ns, subject)
   465  			framework.ExpectNoError(err)
   466  
   467  			err = e2eauth.WaitForAuthorizationUpdate(ctx, c.AuthorizationV1(),
   468  				serviceaccount.MakeUsername(ns, serviceAccountName),
   469  				"", "get", schema.GroupResource{Group: "storage.k8s.io", Resource: "storageclasses"}, true)
   470  			framework.ExpectNoError(err, "Failed to update authorization")
   471  
   472  			ginkgo.By("creating an external dynamic provisioner pod")
   473  			pod := utils.StartExternalProvisioner(ctx, c, ns, externalPluginName)
   474  			ginkgo.DeferCleanup(e2epod.DeletePodOrFail, c, ns, pod.Name)
   475  
   476  			ginkgo.By("creating a StorageClass")
   477  			test := testsuites.StorageClassTest{
   478  				Client:       c,
   479  				Name:         "external provisioner test",
   480  				Provisioner:  externalPluginName,
   481  				Timeouts:     f.Timeouts,
   482  				ClaimSize:    "1500Mi",
   483  				ExpectedSize: "1500Mi",
   484  			}
   485  
   486  			storageClass := testsuites.SetupStorageClass(ctx, test.Client, newStorageClass(test, ns, "external"))
   487  			test.Class = storageClass
   488  
   489  			test.Claim = e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{
   490  				ClaimSize:        test.ClaimSize,
   491  				StorageClassName: &test.Class.Name,
   492  				VolumeMode:       &test.VolumeMode,
   493  			}, ns)
   494  
   495  			ginkgo.By("creating a claim with a external provisioning annotation")
   496  
   497  			test.TestDynamicProvisioning(ctx)
   498  		})
   499  	})
   500  
   501  	ginkgo.Describe("DynamicProvisioner Default", func() {
   502  		f.It("should create and delete default persistent volumes", f.WithSlow(), func(ctx context.Context) {
   503  			e2eskipper.SkipUnlessProviderIs("openstack", "gce", "aws", "gke", "vsphere", "azure")
   504  			e2epv.SkipIfNoDefaultStorageClass(ctx, c)
   505  
   506  			ginkgo.By("creating a claim with no annotation")
   507  			test := testsuites.StorageClassTest{
   508  				Client:       c,
   509  				Name:         "default",
   510  				Timeouts:     f.Timeouts,
   511  				ClaimSize:    "2Gi",
   512  				ExpectedSize: "2Gi",
   513  			}
   514  
   515  			test.Claim = e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{
   516  				ClaimSize:  test.ClaimSize,
   517  				VolumeMode: &test.VolumeMode,
   518  			}, ns)
   519  			// NOTE: this test assumes that there's a default storageclass
   520  			test.Class = testsuites.SetupStorageClass(ctx, test.Client, nil)
   521  
   522  			test.TestDynamicProvisioning(ctx)
   523  		})
   524  
   525  		// Modifying the default storage class can be disruptive to other tests that depend on it
   526  		f.It("should be disabled by changing the default annotation", f.WithSerial(), f.WithDisruptive(), func(ctx context.Context) {
   527  			e2eskipper.SkipUnlessProviderIs("openstack", "gce", "aws", "gke", "vsphere", "azure")
   528  			e2epv.SkipIfNoDefaultStorageClass(ctx, c)
   529  
   530  			scName, scErr := e2epv.GetDefaultStorageClassName(ctx, c)
   531  			framework.ExpectNoError(scErr)
   532  
   533  			test := testsuites.StorageClassTest{
   534  				Name:      "default",
   535  				Timeouts:  f.Timeouts,
   536  				ClaimSize: "2Gi",
   537  			}
   538  
   539  			ginkgo.By("setting the is-default StorageClass annotation to false")
   540  			verifyDefaultStorageClass(ctx, c, scName, true)
   541  			ginkgo.DeferCleanup(updateDefaultStorageClass, c, scName, "true")
   542  			updateDefaultStorageClass(ctx, c, scName, "false")
   543  
   544  			ginkgo.By("creating a claim with default storageclass and expecting it to timeout")
   545  			claim := e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{
   546  				ClaimSize:  test.ClaimSize,
   547  				VolumeMode: &test.VolumeMode,
   548  			}, ns)
   549  			claim, err := c.CoreV1().PersistentVolumeClaims(ns).Create(ctx, claim, metav1.CreateOptions{})
   550  			framework.ExpectNoError(err)
   551  			ginkgo.DeferCleanup(e2epv.DeletePersistentVolumeClaim, c, claim.Name, ns)
   552  
   553  			// The claim should timeout phase:Pending
   554  			err = e2epv.WaitForPersistentVolumeClaimPhase(ctx, v1.ClaimBound, c, ns, claim.Name, 2*time.Second, framework.ClaimProvisionShortTimeout)
   555  			gomega.Expect(err).To(gomega.MatchError(gomega.ContainSubstring("not all in phase Bound")))
   556  			framework.Logf(err.Error())
   557  			claim, err = c.CoreV1().PersistentVolumeClaims(ns).Get(ctx, claim.Name, metav1.GetOptions{})
   558  			framework.ExpectNoError(err)
   559  			gomega.Expect(claim.Status.Phase).To(gomega.Equal(v1.ClaimPending))
   560  		})
   561  
   562  		// Modifying the default storage class can be disruptive to other tests that depend on it
   563  		f.It("should be disabled by removing the default annotation", f.WithSerial(), f.WithDisruptive(), func(ctx context.Context) {
   564  			e2eskipper.SkipUnlessProviderIs("openstack", "gce", "aws", "gke", "vsphere", "azure")
   565  			e2epv.SkipIfNoDefaultStorageClass(ctx, c)
   566  
   567  			scName, scErr := e2epv.GetDefaultStorageClassName(ctx, c)
   568  			framework.ExpectNoError(scErr)
   569  
   570  			test := testsuites.StorageClassTest{
   571  				Name:      "default",
   572  				Timeouts:  f.Timeouts,
   573  				ClaimSize: "2Gi",
   574  			}
   575  
   576  			ginkgo.By("removing the is-default StorageClass annotation")
   577  			verifyDefaultStorageClass(ctx, c, scName, true)
   578  			ginkgo.DeferCleanup(updateDefaultStorageClass, c, scName, "true")
   579  			updateDefaultStorageClass(ctx, c, scName, "")
   580  
   581  			ginkgo.By("creating a claim with default storageclass and expecting it to timeout")
   582  			claim := e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{
   583  				ClaimSize:  test.ClaimSize,
   584  				VolumeMode: &test.VolumeMode,
   585  			}, ns)
   586  			claim, err := c.CoreV1().PersistentVolumeClaims(ns).Create(ctx, claim, metav1.CreateOptions{})
   587  			framework.ExpectNoError(err)
   588  			defer func() {
   589  				framework.ExpectNoError(e2epv.DeletePersistentVolumeClaim(ctx, c, claim.Name, ns))
   590  			}()
   591  
   592  			// The claim should timeout phase:Pending
   593  			err = e2epv.WaitForPersistentVolumeClaimPhase(ctx, v1.ClaimBound, c, ns, claim.Name, 2*time.Second, framework.ClaimProvisionShortTimeout)
   594  			gomega.Expect(err).To(gomega.MatchError(gomega.ContainSubstring("not all in phase Bound")))
   595  			framework.Logf(err.Error())
   596  			claim, err = c.CoreV1().PersistentVolumeClaims(ns).Get(ctx, claim.Name, metav1.GetOptions{})
   597  			framework.ExpectNoError(err)
   598  			gomega.Expect(claim.Status.Phase).To(gomega.Equal(v1.ClaimPending))
   599  		})
   600  	})
   601  
   602  	ginkgo.Describe("Invalid AWS KMS key", func() {
   603  		ginkgo.It("should report an error and create no PV", func(ctx context.Context) {
   604  			e2eskipper.SkipUnlessProviderIs("aws")
   605  			test := testsuites.StorageClassTest{
   606  				Client:      c,
   607  				Name:        "AWS EBS with invalid KMS key",
   608  				Provisioner: "kubernetes.io/aws-ebs",
   609  				Timeouts:    f.Timeouts,
   610  				ClaimSize:   "2Gi",
   611  				Parameters:  map[string]string{"kmsKeyId": "arn:aws:kms:us-east-1:123456789012:key/55555555-5555-5555-5555-555555555555"},
   612  			}
   613  
   614  			ginkgo.By("creating a StorageClass")
   615  			test.Class = testsuites.SetupStorageClass(ctx, test.Client, newStorageClass(test, ns, "invalid-aws"))
   616  
   617  			ginkgo.By("creating a claim object")
   618  			claim := e2epv.MakePersistentVolumeClaim(e2epv.PersistentVolumeClaimConfig{
   619  				ClaimSize:        test.ClaimSize,
   620  				StorageClassName: &test.Class.Name,
   621  				VolumeMode:       &test.VolumeMode,
   622  			}, ns)
   623  			claim, err := c.CoreV1().PersistentVolumeClaims(claim.Namespace).Create(ctx, claim, metav1.CreateOptions{})
   624  			framework.ExpectNoError(err)
   625  			defer func() {
   626  				framework.Logf("deleting claim %q/%q", claim.Namespace, claim.Name)
   627  				err = c.CoreV1().PersistentVolumeClaims(claim.Namespace).Delete(ctx, claim.Name, metav1.DeleteOptions{})
   628  				if err != nil && !apierrors.IsNotFound(err) {
   629  					framework.Failf("Error deleting claim %q. Error: %v", claim.Name, err)
   630  				}
   631  			}()
   632  
   633  			// Watch events until the message about invalid key appears.
   634  			// Event delivery is not reliable and it's used only as a quick way how to check if volume with wrong KMS
   635  			// key was not provisioned. If the event is not delivered, we check that the volume is not Bound for whole
   636  			// ClaimProvisionTimeout in the very same loop.
   637  			err = wait.Poll(time.Second, framework.ClaimProvisionTimeout, func() (bool, error) {
   638  				events, err := c.CoreV1().Events(claim.Namespace).List(ctx, metav1.ListOptions{})
   639  				if err != nil {
   640  					return false, fmt.Errorf("could not list PVC events in %s: %w", claim.Namespace, err)
   641  				}
   642  				for _, event := range events.Items {
   643  					if strings.Contains(event.Message, "failed to create encrypted volume: the volume disappeared after creation, most likely due to inaccessible KMS encryption key") {
   644  						return true, nil
   645  					}
   646  				}
   647  
   648  				pvc, err := c.CoreV1().PersistentVolumeClaims(claim.Namespace).Get(ctx, claim.Name, metav1.GetOptions{})
   649  				if err != nil {
   650  					return true, err
   651  				}
   652  				if pvc.Status.Phase != v1.ClaimPending {
   653  					// The PVC was bound to something, i.e. PV was created for wrong KMS key. That's bad!
   654  					return true, fmt.Errorf("PVC got unexpectedly %s (to PV %q)", pvc.Status.Phase, pvc.Spec.VolumeName)
   655  				}
   656  
   657  				return false, nil
   658  			})
   659  			if wait.Interrupted(err) {
   660  				framework.Logf("The test missed event about failed provisioning, but checked that no volume was provisioned for %v", framework.ClaimProvisionTimeout)
   661  				err = nil
   662  			}
   663  			framework.ExpectNoError(err, "Error waiting for PVC to fail provisioning: %v", err)
   664  		})
   665  	})
   666  })
   667  
   668  func verifyDefaultStorageClass(ctx context.Context, c clientset.Interface, scName string, expectedDefault bool) {
   669  	sc, err := c.StorageV1().StorageClasses().Get(ctx, scName, metav1.GetOptions{})
   670  	framework.ExpectNoError(err)
   671  	gomega.Expect(storageutil.IsDefaultAnnotation(sc.ObjectMeta)).To(gomega.Equal(expectedDefault))
   672  }
   673  
   674  func updateDefaultStorageClass(ctx context.Context, c clientset.Interface, scName string, defaultStr string) {
   675  	sc, err := c.StorageV1().StorageClasses().Get(ctx, scName, metav1.GetOptions{})
   676  	framework.ExpectNoError(err)
   677  
   678  	if defaultStr == "" {
   679  		delete(sc.Annotations, storageutil.BetaIsDefaultStorageClassAnnotation)
   680  		delete(sc.Annotations, storageutil.IsDefaultStorageClassAnnotation)
   681  	} else {
   682  		if sc.Annotations == nil {
   683  			sc.Annotations = make(map[string]string)
   684  		}
   685  		sc.Annotations[storageutil.BetaIsDefaultStorageClassAnnotation] = defaultStr
   686  		sc.Annotations[storageutil.IsDefaultStorageClassAnnotation] = defaultStr
   687  	}
   688  
   689  	_, err = c.StorageV1().StorageClasses().Update(ctx, sc, metav1.UpdateOptions{})
   690  	framework.ExpectNoError(err)
   691  
   692  	expectedDefault := false
   693  	if defaultStr == "true" {
   694  		expectedDefault = true
   695  	}
   696  	verifyDefaultStorageClass(ctx, c, scName, expectedDefault)
   697  }
   698  
   699  // waitForProvisionedVolumesDelete is a polling wrapper to scan all PersistentVolumes for any associated to the test's
   700  // StorageClass.  Returns either an error and nil values or the remaining PVs and their count.
   701  func waitForProvisionedVolumesDeleted(ctx context.Context, c clientset.Interface, scName string) ([]*v1.PersistentVolume, error) {
   702  	var remainingPVs []*v1.PersistentVolume
   703  
   704  	err := wait.Poll(10*time.Second, 300*time.Second, func() (bool, error) {
   705  		remainingPVs = []*v1.PersistentVolume{}
   706  
   707  		allPVs, err := c.CoreV1().PersistentVolumes().List(ctx, metav1.ListOptions{})
   708  		if err != nil {
   709  			return true, err
   710  		}
   711  		for _, pv := range allPVs.Items {
   712  			if pv.Spec.StorageClassName == scName {
   713  				pv := pv
   714  				remainingPVs = append(remainingPVs, &pv)
   715  			}
   716  		}
   717  		if len(remainingPVs) > 0 {
   718  			return false, nil // Poll until no PVs remain
   719  		}
   720  		return true, nil // No PVs remain
   721  	})
   722  	if err != nil {
   723  		return remainingPVs, fmt.Errorf("error waiting for PVs to be deleted: %w", err)
   724  	}
   725  	return nil, nil
   726  }
   727  
   728  // deleteStorageClass deletes the passed in StorageClass and catches errors other than "Not Found"
   729  func deleteStorageClass(ctx context.Context, c clientset.Interface, className string) {
   730  	err := c.StorageV1().StorageClasses().Delete(ctx, className, metav1.DeleteOptions{})
   731  	if err != nil && !apierrors.IsNotFound(err) {
   732  		framework.ExpectNoError(err)
   733  	}
   734  }
   735  
   736  // deleteProvisionedVolumes [gce||gke only]  iteratively deletes persistent volumes and attached GCE PDs.
   737  func deleteProvisionedVolumesAndDisks(ctx context.Context, c clientset.Interface, pvs []*v1.PersistentVolume) {
   738  	framework.Logf("Remaining PersistentVolumes:")
   739  	for i, pv := range pvs {
   740  		framework.Logf("\t%d) %s", i+1, pv.Name)
   741  	}
   742  	for _, pv := range pvs {
   743  		framework.ExpectNoError(e2epv.DeletePDWithRetry(ctx, pv.Spec.PersistentVolumeSource.GCEPersistentDisk.PDName))
   744  		framework.ExpectNoError(e2epv.DeletePersistentVolume(ctx, c, pv.Name))
   745  	}
   746  }
   747  
   748  func getRandomClusterZone(ctx context.Context, c clientset.Interface) string {
   749  	zones, err := e2enode.GetClusterZones(ctx, c)
   750  	zone := ""
   751  	framework.ExpectNoError(err)
   752  	if len(zones) != 0 {
   753  		zonesList := zones.UnsortedList()
   754  		zone = zonesList[rand.Intn(zones.Len())]
   755  	}
   756  	return zone
   757  }