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

     1  /*
     2  Copyright 2018 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  /*
    18   * This file defines various in-tree volume test drivers for TestSuites.
    19   *
    20   * There are two ways, how to prepare test drivers:
    21   * 1) With containerized server (NFS, Ceph, iSCSI, ...)
    22   * It creates a server pod which defines one volume for the tests.
    23   * These tests work only when privileged containers are allowed, exporting
    24   * various filesystems (like NFS) usually needs some mounting or
    25   * other privileged magic in the server pod.
    26   *
    27   * Note that the server containers are for testing purposes only and should not
    28   * be used in production.
    29   *
    30   * 2) With server or cloud provider outside of Kubernetes (Cinder, GCE, AWS, Azure, ...)
    31   * Appropriate server or cloud provider must exist somewhere outside
    32   * the tested Kubernetes cluster. CreateVolume will create a new volume to be
    33   * used in the TestSuites for inlineVolume or DynamicPV tests.
    34   */
    35  
    36  package drivers
    37  
    38  import (
    39  	"context"
    40  	"fmt"
    41  	"strconv"
    42  	"strings"
    43  	"time"
    44  
    45  	"github.com/onsi/ginkgo/v2"
    46  	v1 "k8s.io/api/core/v1"
    47  	rbacv1 "k8s.io/api/rbac/v1"
    48  	storagev1 "k8s.io/api/storage/v1"
    49  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    50  	"k8s.io/apimachinery/pkg/runtime/schema"
    51  	"k8s.io/apimachinery/pkg/util/sets"
    52  	"k8s.io/apiserver/pkg/authentication/serviceaccount"
    53  	clientset "k8s.io/client-go/kubernetes"
    54  	"k8s.io/kubernetes/test/e2e/feature"
    55  	"k8s.io/kubernetes/test/e2e/framework"
    56  	e2eauth "k8s.io/kubernetes/test/e2e/framework/auth"
    57  	e2enode "k8s.io/kubernetes/test/e2e/framework/node"
    58  	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
    59  	e2epv "k8s.io/kubernetes/test/e2e/framework/pv"
    60  	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
    61  	e2evolume "k8s.io/kubernetes/test/e2e/framework/volume"
    62  	storageframework "k8s.io/kubernetes/test/e2e/storage/framework"
    63  	"k8s.io/kubernetes/test/e2e/storage/utils"
    64  	imageutils "k8s.io/kubernetes/test/utils/image"
    65  )
    66  
    67  const (
    68  	// Template for iSCSI IQN.
    69  	iSCSIIQNTemplate = "iqn.2003-01.io.k8s:e2e.%s"
    70  )
    71  
    72  // NFS
    73  type nfsDriver struct {
    74  	externalProvisionerPod *v1.Pod
    75  	externalPluginName     string
    76  
    77  	driverInfo storageframework.DriverInfo
    78  }
    79  
    80  type nfsVolume struct {
    81  	serverHost string
    82  	serverPod  *v1.Pod
    83  	f          *framework.Framework
    84  }
    85  
    86  var _ storageframework.TestDriver = &nfsDriver{}
    87  var _ storageframework.PreprovisionedVolumeTestDriver = &nfsDriver{}
    88  var _ storageframework.InlineVolumeTestDriver = &nfsDriver{}
    89  var _ storageframework.PreprovisionedPVTestDriver = &nfsDriver{}
    90  var _ storageframework.DynamicPVTestDriver = &nfsDriver{}
    91  
    92  // InitNFSDriver returns nfsDriver that implements TestDriver interface
    93  func InitNFSDriver() storageframework.TestDriver {
    94  	return &nfsDriver{
    95  		driverInfo: storageframework.DriverInfo{
    96  			Name:             "nfs",
    97  			InTreePluginName: "kubernetes.io/nfs",
    98  			MaxFileSize:      storageframework.FileSizeLarge,
    99  			SupportedSizeRange: e2evolume.SizeRange{
   100  				Min: "1Gi",
   101  			},
   102  			SupportedFsType: sets.NewString(
   103  				"", // Default fsType
   104  			),
   105  			SupportedMountOption: sets.NewString("relatime"),
   106  			RequiredMountOption:  sets.NewString("vers=4.0"),
   107  			Capabilities: map[storageframework.Capability]bool{
   108  				storageframework.CapPersistence:       true,
   109  				storageframework.CapExec:              true,
   110  				storageframework.CapRWX:               true,
   111  				storageframework.CapMultiPODs:         true,
   112  				storageframework.CapMultiplePVsSameID: true,
   113  			},
   114  		},
   115  	}
   116  }
   117  
   118  func (n *nfsDriver) GetDriverInfo() *storageframework.DriverInfo {
   119  	return &n.driverInfo
   120  }
   121  
   122  func (n *nfsDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) {
   123  }
   124  
   125  func (n *nfsDriver) GetVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) *v1.VolumeSource {
   126  	nv, ok := e2evolume.(*nfsVolume)
   127  	if !ok {
   128  		framework.Failf("Failed to cast test volume of type %T to the NFS test volume", e2evolume)
   129  	}
   130  	return &v1.VolumeSource{
   131  		NFS: &v1.NFSVolumeSource{
   132  			Server:   nv.serverHost,
   133  			Path:     "/",
   134  			ReadOnly: readOnly,
   135  		},
   136  	}
   137  }
   138  
   139  func (n *nfsDriver) GetPersistentVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) (*v1.PersistentVolumeSource, *v1.VolumeNodeAffinity) {
   140  	nv, ok := e2evolume.(*nfsVolume)
   141  	if !ok {
   142  		framework.Failf("Failed to cast test volume of type %T to the NFS test volume", e2evolume)
   143  	}
   144  	return &v1.PersistentVolumeSource{
   145  		NFS: &v1.NFSVolumeSource{
   146  			Server:   nv.serverHost,
   147  			Path:     "/",
   148  			ReadOnly: readOnly,
   149  		},
   150  	}, nil
   151  }
   152  
   153  func (n *nfsDriver) GetDynamicProvisionStorageClass(ctx context.Context, config *storageframework.PerTestConfig, fsType string) *storagev1.StorageClass {
   154  	provisioner := n.externalPluginName
   155  	parameters := map[string]string{"mountOptions": "vers=4.0"}
   156  	ns := config.Framework.Namespace.Name
   157  
   158  	return storageframework.GetStorageClass(provisioner, parameters, nil, ns)
   159  }
   160  
   161  func (n *nfsDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig {
   162  	cs := f.ClientSet
   163  	ns := f.Namespace
   164  	n.externalPluginName = fmt.Sprintf("example.com/nfs-%s", ns.Name)
   165  
   166  	// TODO(mkimuram): cluster-admin gives too much right but system:persistent-volume-provisioner
   167  	// is not enough. We should create new clusterrole for testing.
   168  	err := e2eauth.BindClusterRole(ctx, cs.RbacV1(), "cluster-admin", ns.Name,
   169  		rbacv1.Subject{Kind: rbacv1.ServiceAccountKind, Namespace: ns.Name, Name: "default"})
   170  	framework.ExpectNoError(err)
   171  	ginkgo.DeferCleanup(cs.RbacV1().ClusterRoleBindings().Delete, ns.Name+"--"+"cluster-admin", *metav1.NewDeleteOptions(0))
   172  
   173  	err = e2eauth.WaitForAuthorizationUpdate(ctx, cs.AuthorizationV1(),
   174  		serviceaccount.MakeUsername(ns.Name, "default"),
   175  		"", "get", schema.GroupResource{Group: "storage.k8s.io", Resource: "storageclasses"}, true)
   176  	framework.ExpectNoError(err, "Failed to update authorization: %v", err)
   177  
   178  	ginkgo.By("creating an external dynamic provisioner pod")
   179  	n.externalProvisionerPod = utils.StartExternalProvisioner(ctx, cs, ns.Name, n.externalPluginName)
   180  	ginkgo.DeferCleanup(e2epod.DeletePodWithWait, cs, n.externalProvisionerPod)
   181  
   182  	return &storageframework.PerTestConfig{
   183  		Driver:    n,
   184  		Prefix:    "nfs",
   185  		Framework: f,
   186  	}
   187  }
   188  
   189  func (n *nfsDriver) CreateVolume(ctx context.Context, config *storageframework.PerTestConfig, volType storageframework.TestVolType) storageframework.TestVolume {
   190  	f := config.Framework
   191  	cs := f.ClientSet
   192  	ns := f.Namespace
   193  
   194  	// NewNFSServer creates a pod for InlineVolume and PreprovisionedPV,
   195  	// and startExternalProvisioner creates a pod for DynamicPV.
   196  	// Therefore, we need a different PrepareTest logic for volType.
   197  	switch volType {
   198  	case storageframework.InlineVolume:
   199  		fallthrough
   200  	case storageframework.PreprovisionedPV:
   201  		c, serverPod, serverHost := e2evolume.NewNFSServer(ctx, cs, ns.Name, []string{})
   202  		config.ServerConfig = &c
   203  		return &nfsVolume{
   204  			serverHost: serverHost,
   205  			serverPod:  serverPod,
   206  			f:          f,
   207  		}
   208  	case storageframework.DynamicPV:
   209  		// Do nothing
   210  	default:
   211  		framework.Failf("Unsupported volType:%v is specified", volType)
   212  	}
   213  	return nil
   214  }
   215  
   216  func (v *nfsVolume) DeleteVolume(ctx context.Context) {
   217  	cleanUpVolumeServer(ctx, v.f, v.serverPod)
   218  }
   219  
   220  // iSCSI
   221  // The iscsiadm utility and iscsi target kernel modules must be installed on all nodes.
   222  type iSCSIDriver struct {
   223  	driverInfo storageframework.DriverInfo
   224  }
   225  type iSCSIVolume struct {
   226  	serverPod *v1.Pod
   227  	serverIP  string
   228  	f         *framework.Framework
   229  	iqn       string
   230  }
   231  
   232  var _ storageframework.TestDriver = &iSCSIDriver{}
   233  var _ storageframework.PreprovisionedVolumeTestDriver = &iSCSIDriver{}
   234  var _ storageframework.InlineVolumeTestDriver = &iSCSIDriver{}
   235  var _ storageframework.PreprovisionedPVTestDriver = &iSCSIDriver{}
   236  
   237  // InitISCSIDriver returns iSCSIDriver that implements TestDriver interface
   238  func InitISCSIDriver() storageframework.TestDriver {
   239  	return &iSCSIDriver{
   240  		driverInfo: storageframework.DriverInfo{
   241  			Name:             "iscsi",
   242  			InTreePluginName: "kubernetes.io/iscsi",
   243  			TestTags:         []interface{}{feature.Volumes},
   244  			MaxFileSize:      storageframework.FileSizeMedium,
   245  			SupportedFsType: sets.NewString(
   246  				"", // Default fsType
   247  				"ext4",
   248  			),
   249  			TopologyKeys: []string{v1.LabelHostname},
   250  			Capabilities: map[storageframework.Capability]bool{
   251  				storageframework.CapPersistence:       true,
   252  				storageframework.CapFsGroup:           true,
   253  				storageframework.CapBlock:             true,
   254  				storageframework.CapExec:              true,
   255  				storageframework.CapMultiPODs:         true,
   256  				storageframework.CapTopology:          true,
   257  				storageframework.CapMultiplePVsSameID: true,
   258  			},
   259  		},
   260  	}
   261  }
   262  
   263  func (i *iSCSIDriver) GetDriverInfo() *storageframework.DriverInfo {
   264  	return &i.driverInfo
   265  }
   266  
   267  func (i *iSCSIDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) {
   268  }
   269  
   270  func (i *iSCSIDriver) GetVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) *v1.VolumeSource {
   271  	iv, ok := e2evolume.(*iSCSIVolume)
   272  	if !ok {
   273  		framework.Failf("failed to cast test volume of type %T to the iSCSI test volume", e2evolume)
   274  	}
   275  
   276  	volSource := v1.VolumeSource{
   277  		ISCSI: &v1.ISCSIVolumeSource{
   278  			TargetPortal: "127.0.0.1:3260",
   279  			IQN:          iv.iqn,
   280  			Lun:          0,
   281  			ReadOnly:     readOnly,
   282  		},
   283  	}
   284  	if fsType != "" {
   285  		volSource.ISCSI.FSType = fsType
   286  	}
   287  	return &volSource
   288  }
   289  
   290  func (i *iSCSIDriver) GetPersistentVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) (*v1.PersistentVolumeSource, *v1.VolumeNodeAffinity) {
   291  	iv, ok := e2evolume.(*iSCSIVolume)
   292  	if !ok {
   293  		framework.Failf("failed to cast test volume of type %T to the iSCSI test volume", e2evolume)
   294  	}
   295  
   296  	pvSource := v1.PersistentVolumeSource{
   297  		ISCSI: &v1.ISCSIPersistentVolumeSource{
   298  			TargetPortal: "127.0.0.1:3260",
   299  			IQN:          iv.iqn,
   300  			Lun:          0,
   301  			ReadOnly:     readOnly,
   302  		},
   303  	}
   304  	if fsType != "" {
   305  		pvSource.ISCSI.FSType = fsType
   306  	}
   307  	return &pvSource, nil
   308  }
   309  
   310  func (i *iSCSIDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig {
   311  	return &storageframework.PerTestConfig{
   312  		Driver:    i,
   313  		Prefix:    "iscsi",
   314  		Framework: f,
   315  	}
   316  }
   317  
   318  func (i *iSCSIDriver) CreateVolume(ctx context.Context, config *storageframework.PerTestConfig, volType storageframework.TestVolType) storageframework.TestVolume {
   319  	f := config.Framework
   320  	cs := f.ClientSet
   321  	ns := f.Namespace
   322  
   323  	c, serverPod, serverIP, iqn := newISCSIServer(ctx, cs, ns.Name)
   324  	config.ServerConfig = &c
   325  	config.ClientNodeSelection = c.ClientNodeSelection
   326  	return &iSCSIVolume{
   327  		serverPod: serverPod,
   328  		serverIP:  serverIP,
   329  		iqn:       iqn,
   330  		f:         f,
   331  	}
   332  }
   333  
   334  // newISCSIServer is an iSCSI-specific wrapper for CreateStorageServer.
   335  func newISCSIServer(ctx context.Context, cs clientset.Interface, namespace string) (config e2evolume.TestConfig, pod *v1.Pod, ip, iqn string) {
   336  	// Generate cluster-wide unique IQN
   337  	iqn = fmt.Sprintf(iSCSIIQNTemplate, namespace)
   338  	config = e2evolume.TestConfig{
   339  		Namespace:   namespace,
   340  		Prefix:      "iscsi",
   341  		ServerImage: imageutils.GetE2EImage(imageutils.VolumeISCSIServer),
   342  		ServerArgs:  []string{iqn},
   343  		ServerVolumes: map[string]string{
   344  			// iSCSI container needs to insert modules from the host
   345  			"/lib/modules": "/lib/modules",
   346  			// iSCSI container needs to configure kernel
   347  			"/sys/kernel": "/sys/kernel",
   348  			// iSCSI source "block devices" must be available on the host
   349  			"/srv/iscsi": "/srv/iscsi",
   350  			// targetcli uses dbus
   351  			"/run/dbus": "/run/dbus",
   352  		},
   353  		ServerReadyMessage: "iscsi target started",
   354  		ServerHostNetwork:  true,
   355  	}
   356  	pod, ip = e2evolume.CreateStorageServer(ctx, cs, config)
   357  	// Make sure the client runs on the same node as server so we don't need to open any firewalls.
   358  	config.ClientNodeSelection = e2epod.NodeSelection{Name: pod.Spec.NodeName}
   359  	return config, pod, ip, iqn
   360  }
   361  
   362  func (v *iSCSIVolume) DeleteVolume(ctx context.Context) {
   363  	cleanUpVolumeServer(ctx, v.f, v.serverPod)
   364  }
   365  
   366  // Hostpath
   367  type hostPathDriver struct {
   368  	driverInfo storageframework.DriverInfo
   369  }
   370  
   371  var _ storageframework.TestDriver = &hostPathDriver{}
   372  var _ storageframework.PreprovisionedVolumeTestDriver = &hostPathDriver{}
   373  var _ storageframework.InlineVolumeTestDriver = &hostPathDriver{}
   374  
   375  // InitHostPathDriver returns hostPathDriver that implements TestDriver interface
   376  func InitHostPathDriver() storageframework.TestDriver {
   377  	return &hostPathDriver{
   378  		driverInfo: storageframework.DriverInfo{
   379  			Name:             "hostPath",
   380  			InTreePluginName: "kubernetes.io/host-path",
   381  			MaxFileSize:      storageframework.FileSizeMedium,
   382  			SupportedFsType: sets.NewString(
   383  				"", // Default fsType
   384  			),
   385  			TopologyKeys: []string{v1.LabelHostname},
   386  			Capabilities: map[storageframework.Capability]bool{
   387  				storageframework.CapPersistence:       true,
   388  				storageframework.CapMultiPODs:         true,
   389  				storageframework.CapSingleNodeVolume:  true,
   390  				storageframework.CapTopology:          true,
   391  				storageframework.CapMultiplePVsSameID: true,
   392  			},
   393  		},
   394  	}
   395  }
   396  
   397  func (h *hostPathDriver) GetDriverInfo() *storageframework.DriverInfo {
   398  	return &h.driverInfo
   399  }
   400  
   401  func (h *hostPathDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) {
   402  }
   403  
   404  func (h *hostPathDriver) GetVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) *v1.VolumeSource {
   405  	// hostPath doesn't support readOnly volume
   406  	if readOnly {
   407  		return nil
   408  	}
   409  	return &v1.VolumeSource{
   410  		HostPath: &v1.HostPathVolumeSource{
   411  			Path: "/tmp",
   412  		},
   413  	}
   414  }
   415  
   416  func (h *hostPathDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig {
   417  	return &storageframework.PerTestConfig{
   418  		Driver:    h,
   419  		Prefix:    "hostpath",
   420  		Framework: f,
   421  	}
   422  }
   423  
   424  func (h *hostPathDriver) CreateVolume(ctx context.Context, config *storageframework.PerTestConfig, volType storageframework.TestVolType) storageframework.TestVolume {
   425  	f := config.Framework
   426  	cs := f.ClientSet
   427  
   428  	// pods should be scheduled on the node
   429  	node, err := e2enode.GetRandomReadySchedulableNode(ctx, cs)
   430  	framework.ExpectNoError(err)
   431  	config.ClientNodeSelection = e2epod.NodeSelection{Name: node.Name}
   432  	return nil
   433  }
   434  
   435  // HostPathSymlink
   436  type hostPathSymlinkDriver struct {
   437  	driverInfo storageframework.DriverInfo
   438  }
   439  
   440  type hostPathSymlinkVolume struct {
   441  	targetPath string
   442  	sourcePath string
   443  	prepPod    *v1.Pod
   444  	f          *framework.Framework
   445  }
   446  
   447  var _ storageframework.TestDriver = &hostPathSymlinkDriver{}
   448  var _ storageframework.PreprovisionedVolumeTestDriver = &hostPathSymlinkDriver{}
   449  var _ storageframework.InlineVolumeTestDriver = &hostPathSymlinkDriver{}
   450  
   451  // InitHostPathSymlinkDriver returns hostPathSymlinkDriver that implements TestDriver interface
   452  func InitHostPathSymlinkDriver() storageframework.TestDriver {
   453  	return &hostPathSymlinkDriver{
   454  		driverInfo: storageframework.DriverInfo{
   455  			Name:             "hostPathSymlink",
   456  			InTreePluginName: "kubernetes.io/host-path",
   457  			MaxFileSize:      storageframework.FileSizeMedium,
   458  			SupportedFsType: sets.NewString(
   459  				"", // Default fsType
   460  			),
   461  			TopologyKeys: []string{v1.LabelHostname},
   462  			Capabilities: map[storageframework.Capability]bool{
   463  				storageframework.CapPersistence:       true,
   464  				storageframework.CapMultiPODs:         true,
   465  				storageframework.CapSingleNodeVolume:  true,
   466  				storageframework.CapTopology:          true,
   467  				storageframework.CapMultiplePVsSameID: true,
   468  			},
   469  		},
   470  	}
   471  }
   472  
   473  func (h *hostPathSymlinkDriver) GetDriverInfo() *storageframework.DriverInfo {
   474  	return &h.driverInfo
   475  }
   476  
   477  func (h *hostPathSymlinkDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) {
   478  }
   479  
   480  func (h *hostPathSymlinkDriver) GetVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) *v1.VolumeSource {
   481  	hv, ok := e2evolume.(*hostPathSymlinkVolume)
   482  	if !ok {
   483  		framework.Failf("Failed to cast test volume of type %T to the Hostpath Symlink test volume", e2evolume)
   484  	}
   485  
   486  	// hostPathSymlink doesn't support readOnly volume
   487  	if readOnly {
   488  		return nil
   489  	}
   490  	return &v1.VolumeSource{
   491  		HostPath: &v1.HostPathVolumeSource{
   492  			Path: hv.targetPath,
   493  		},
   494  	}
   495  }
   496  
   497  func (h *hostPathSymlinkDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig {
   498  	return &storageframework.PerTestConfig{
   499  		Driver:    h,
   500  		Prefix:    "hostpathsymlink",
   501  		Framework: f,
   502  	}
   503  }
   504  
   505  func (h *hostPathSymlinkDriver) CreateVolume(ctx context.Context, config *storageframework.PerTestConfig, volType storageframework.TestVolType) storageframework.TestVolume {
   506  	f := config.Framework
   507  	cs := f.ClientSet
   508  
   509  	sourcePath := fmt.Sprintf("/tmp/%v", f.Namespace.Name)
   510  	targetPath := fmt.Sprintf("/tmp/%v-link", f.Namespace.Name)
   511  	volumeName := "test-volume"
   512  
   513  	// pods should be scheduled on the node
   514  	node, err := e2enode.GetRandomReadySchedulableNode(ctx, cs)
   515  	framework.ExpectNoError(err)
   516  	config.ClientNodeSelection = e2epod.NodeSelection{Name: node.Name}
   517  
   518  	cmd := fmt.Sprintf("mkdir %v -m 777 && ln -s %v %v", sourcePath, sourcePath, targetPath)
   519  	privileged := true
   520  
   521  	// Launch pod to initialize hostPath directory and symlink
   522  	prepPod := &v1.Pod{
   523  		ObjectMeta: metav1.ObjectMeta{
   524  			Name: fmt.Sprintf("hostpath-symlink-prep-%s", f.Namespace.Name),
   525  		},
   526  		Spec: v1.PodSpec{
   527  			Containers: []v1.Container{
   528  				{
   529  					Name:    fmt.Sprintf("init-volume-%s", f.Namespace.Name),
   530  					Image:   imageutils.GetE2EImage(imageutils.BusyBox),
   531  					Command: []string{"/bin/sh", "-ec", cmd},
   532  					VolumeMounts: []v1.VolumeMount{
   533  						{
   534  							Name:      volumeName,
   535  							MountPath: "/tmp",
   536  						},
   537  					},
   538  					SecurityContext: &v1.SecurityContext{
   539  						Privileged: &privileged,
   540  					},
   541  				},
   542  			},
   543  			RestartPolicy: v1.RestartPolicyNever,
   544  			Volumes: []v1.Volume{
   545  				{
   546  					Name: volumeName,
   547  					VolumeSource: v1.VolumeSource{
   548  						HostPath: &v1.HostPathVolumeSource{
   549  							Path: "/tmp",
   550  						},
   551  					},
   552  				},
   553  			},
   554  			NodeName: node.Name,
   555  		},
   556  	}
   557  	// h.prepPod will be reused in cleanupDriver.
   558  	pod, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(ctx, prepPod, metav1.CreateOptions{})
   559  	framework.ExpectNoError(err, "while creating hostPath init pod")
   560  
   561  	err = e2epod.WaitForPodSuccessInNamespaceTimeout(ctx, f.ClientSet, pod.Name, pod.Namespace, f.Timeouts.PodStart)
   562  	framework.ExpectNoError(err, "while waiting for hostPath init pod to succeed")
   563  
   564  	err = e2epod.DeletePodWithWait(ctx, f.ClientSet, pod)
   565  	framework.ExpectNoError(err, "while deleting hostPath init pod")
   566  	return &hostPathSymlinkVolume{
   567  		sourcePath: sourcePath,
   568  		targetPath: targetPath,
   569  		prepPod:    prepPod,
   570  		f:          f,
   571  	}
   572  }
   573  
   574  func (v *hostPathSymlinkVolume) DeleteVolume(ctx context.Context) {
   575  	f := v.f
   576  
   577  	cmd := fmt.Sprintf("rm -rf %v&& rm -rf %v", v.targetPath, v.sourcePath)
   578  	v.prepPod.Spec.Containers[0].Command = []string{"/bin/sh", "-ec", cmd}
   579  
   580  	pod, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(ctx, v.prepPod, metav1.CreateOptions{})
   581  	framework.ExpectNoError(err, "while creating hostPath teardown pod")
   582  
   583  	err = e2epod.WaitForPodSuccessInNamespaceTimeout(ctx, f.ClientSet, pod.Name, pod.Namespace, f.Timeouts.PodStart)
   584  	framework.ExpectNoError(err, "while waiting for hostPath teardown pod to succeed")
   585  
   586  	err = e2epod.DeletePodWithWait(ctx, f.ClientSet, pod)
   587  	framework.ExpectNoError(err, "while deleting hostPath teardown pod")
   588  }
   589  
   590  // emptydir
   591  type emptydirDriver struct {
   592  	driverInfo storageframework.DriverInfo
   593  }
   594  
   595  var _ storageframework.TestDriver = &emptydirDriver{}
   596  var _ storageframework.PreprovisionedVolumeTestDriver = &emptydirDriver{}
   597  var _ storageframework.InlineVolumeTestDriver = &emptydirDriver{}
   598  
   599  // InitEmptydirDriver returns emptydirDriver that implements TestDriver interface
   600  func InitEmptydirDriver() storageframework.TestDriver {
   601  	return &emptydirDriver{
   602  		driverInfo: storageframework.DriverInfo{
   603  			Name:             "emptydir",
   604  			InTreePluginName: "kubernetes.io/empty-dir",
   605  			MaxFileSize:      storageframework.FileSizeMedium,
   606  			SupportedFsType: sets.NewString(
   607  				"", // Default fsType
   608  			),
   609  			Capabilities: map[storageframework.Capability]bool{
   610  				storageframework.CapExec:             true,
   611  				storageframework.CapSingleNodeVolume: true,
   612  			},
   613  		},
   614  	}
   615  }
   616  
   617  func (e *emptydirDriver) GetDriverInfo() *storageframework.DriverInfo {
   618  	return &e.driverInfo
   619  }
   620  
   621  func (e *emptydirDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) {
   622  }
   623  
   624  func (e *emptydirDriver) GetVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) *v1.VolumeSource {
   625  	// emptydir doesn't support readOnly volume
   626  	if readOnly {
   627  		return nil
   628  	}
   629  	return &v1.VolumeSource{
   630  		EmptyDir: &v1.EmptyDirVolumeSource{},
   631  	}
   632  }
   633  
   634  func (e *emptydirDriver) CreateVolume(ctx context.Context, config *storageframework.PerTestConfig, volType storageframework.TestVolType) storageframework.TestVolume {
   635  	return nil
   636  }
   637  
   638  func (e *emptydirDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig {
   639  	return &storageframework.PerTestConfig{
   640  		Driver:    e,
   641  		Prefix:    "emptydir",
   642  		Framework: f,
   643  	}
   644  }
   645  
   646  // Cinder
   647  // This tests only CSI migration with dynamically provisioned volumes.
   648  type cinderDriver struct {
   649  	driverInfo storageframework.DriverInfo
   650  }
   651  
   652  var _ storageframework.TestDriver = &cinderDriver{}
   653  var _ storageframework.DynamicPVTestDriver = &cinderDriver{}
   654  
   655  // InitCinderDriver returns cinderDriver that implements TestDriver interface
   656  func InitCinderDriver() storageframework.TestDriver {
   657  	return &cinderDriver{
   658  		driverInfo: storageframework.DriverInfo{
   659  			Name:             "cinder",
   660  			InTreePluginName: "kubernetes.io/cinder",
   661  			MaxFileSize:      storageframework.FileSizeMedium,
   662  			SupportedSizeRange: e2evolume.SizeRange{
   663  				Min: "1Gi",
   664  			},
   665  			SupportedFsType: sets.NewString(
   666  				"", // Default fsType
   667  			),
   668  			TopologyKeys: []string{v1.LabelFailureDomainBetaZone},
   669  			Capabilities: map[storageframework.Capability]bool{
   670  				storageframework.CapPersistence: true,
   671  				storageframework.CapFsGroup:     true,
   672  				storageframework.CapExec:        true,
   673  				storageframework.CapBlock:       true,
   674  				// Cinder supports volume limits, but the test creates large
   675  				// number of volumes and times out test suites.
   676  				storageframework.CapVolumeLimits: false,
   677  				storageframework.CapTopology:     true,
   678  			},
   679  		},
   680  	}
   681  }
   682  
   683  func (c *cinderDriver) GetDriverInfo() *storageframework.DriverInfo {
   684  	return &c.driverInfo
   685  }
   686  
   687  func (c *cinderDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) {
   688  	e2eskipper.SkipUnlessProviderIs("openstack")
   689  }
   690  
   691  func (c *cinderDriver) GetDynamicProvisionStorageClass(ctx context.Context, config *storageframework.PerTestConfig, fsType string) *storagev1.StorageClass {
   692  	provisioner := "kubernetes.io/cinder"
   693  	parameters := map[string]string{}
   694  	if fsType != "" {
   695  		parameters["fsType"] = fsType
   696  	}
   697  	ns := config.Framework.Namespace.Name
   698  
   699  	return storageframework.GetStorageClass(provisioner, parameters, nil, ns)
   700  }
   701  
   702  func (c *cinderDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig {
   703  	return &storageframework.PerTestConfig{
   704  		Driver:    c,
   705  		Prefix:    "cinder",
   706  		Framework: f,
   707  	}
   708  }
   709  
   710  // GCE
   711  type gcePdDriver struct {
   712  	driverInfo storageframework.DriverInfo
   713  }
   714  
   715  type gcePdVolume struct {
   716  	volumeName string
   717  }
   718  
   719  var _ storageframework.TestDriver = &gcePdDriver{}
   720  var _ storageframework.PreprovisionedVolumeTestDriver = &gcePdDriver{}
   721  var _ storageframework.InlineVolumeTestDriver = &gcePdDriver{}
   722  var _ storageframework.PreprovisionedPVTestDriver = &gcePdDriver{}
   723  var _ storageframework.DynamicPVTestDriver = &gcePdDriver{}
   724  
   725  // InitGcePdDriver returns gcePdDriver that implements TestDriver interface
   726  func InitGcePdDriver() storageframework.TestDriver {
   727  	supportedTypes := sets.NewString(
   728  		"", // Default fsType
   729  		"ext2",
   730  		"ext3",
   731  		"ext4",
   732  		"xfs",
   733  	)
   734  	return &gcePdDriver{
   735  		driverInfo: storageframework.DriverInfo{
   736  			Name:             "gcepd",
   737  			InTreePluginName: "kubernetes.io/gce-pd",
   738  			MaxFileSize:      storageframework.FileSizeMedium,
   739  			SupportedSizeRange: e2evolume.SizeRange{
   740  				Min: "1Gi",
   741  			},
   742  			SupportedFsType:      supportedTypes,
   743  			SupportedMountOption: sets.NewString("debug", "nouid32"),
   744  			TopologyKeys:         []string{v1.LabelTopologyZone},
   745  			Capabilities: map[storageframework.Capability]bool{
   746  				storageframework.CapPersistence:         true,
   747  				storageframework.CapFsGroup:             true,
   748  				storageframework.CapBlock:               true,
   749  				storageframework.CapExec:                true,
   750  				storageframework.CapMultiPODs:           true,
   751  				storageframework.CapControllerExpansion: true,
   752  				storageframework.CapOfflineExpansion:    true,
   753  				storageframework.CapOnlineExpansion:     true,
   754  				storageframework.CapNodeExpansion:       true,
   755  				// GCE supports volume limits, but the test creates large
   756  				// number of volumes and times out test suites.
   757  				storageframework.CapVolumeLimits:      false,
   758  				storageframework.CapTopology:          true,
   759  				storageframework.CapMultiplePVsSameID: true,
   760  			},
   761  		},
   762  	}
   763  }
   764  
   765  // InitWindowsGcePdDriver returns gcePdDriver running on Windows cluster that implements TestDriver interface
   766  // In current test structure, it first initialize the driver and then set up
   767  // the new framework, so we cannot get the correct OS here and select which file system is supported.
   768  // So here uses a separate Windows in-tree gce pd driver
   769  func InitWindowsGcePdDriver() storageframework.TestDriver {
   770  	supportedTypes := sets.NewString(
   771  		"ntfs",
   772  	)
   773  	return &gcePdDriver{
   774  		driverInfo: storageframework.DriverInfo{
   775  			Name:             "windows-gcepd",
   776  			InTreePluginName: "kubernetes.io/gce-pd",
   777  			MaxFileSize:      storageframework.FileSizeMedium,
   778  			SupportedSizeRange: e2evolume.SizeRange{
   779  				Min: "1Gi",
   780  			},
   781  			SupportedFsType: supportedTypes,
   782  			TopologyKeys:    []string{v1.LabelZoneFailureDomain},
   783  			Capabilities: map[storageframework.Capability]bool{
   784  				storageframework.CapControllerExpansion: false,
   785  				storageframework.CapPersistence:         true,
   786  				storageframework.CapExec:                true,
   787  				storageframework.CapMultiPODs:           true,
   788  				// GCE supports volume limits, but the test creates large
   789  				// number of volumes and times out test suites.
   790  				storageframework.CapVolumeLimits:      false,
   791  				storageframework.CapTopology:          true,
   792  				storageframework.CapMultiplePVsSameID: true,
   793  			},
   794  		},
   795  	}
   796  }
   797  
   798  func (g *gcePdDriver) GetDriverInfo() *storageframework.DriverInfo {
   799  	return &g.driverInfo
   800  }
   801  
   802  func (g *gcePdDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) {
   803  	e2eskipper.SkipUnlessProviderIs("gce", "gke")
   804  	for _, tag := range pattern.TestTags {
   805  		if tag == feature.Windows {
   806  			e2eskipper.SkipUnlessNodeOSDistroIs("windows")
   807  		}
   808  	}
   809  }
   810  
   811  func (g *gcePdDriver) GetVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) *v1.VolumeSource {
   812  	gv, ok := e2evolume.(*gcePdVolume)
   813  	if !ok {
   814  		framework.Failf("Failed to cast test volume of type %T to the GCE PD test volume", e2evolume)
   815  	}
   816  	volSource := v1.VolumeSource{
   817  		GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
   818  			PDName:   gv.volumeName,
   819  			ReadOnly: readOnly,
   820  		},
   821  	}
   822  	if fsType != "" {
   823  		volSource.GCEPersistentDisk.FSType = fsType
   824  	}
   825  	return &volSource
   826  }
   827  
   828  func (g *gcePdDriver) GetPersistentVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) (*v1.PersistentVolumeSource, *v1.VolumeNodeAffinity) {
   829  	gv, ok := e2evolume.(*gcePdVolume)
   830  	if !ok {
   831  		framework.Failf("Failed to cast test volume of type %T to the GCE PD test volume", e2evolume)
   832  	}
   833  	pvSource := v1.PersistentVolumeSource{
   834  		GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
   835  			PDName:   gv.volumeName,
   836  			ReadOnly: readOnly,
   837  		},
   838  	}
   839  	if fsType != "" {
   840  		pvSource.GCEPersistentDisk.FSType = fsType
   841  	}
   842  	return &pvSource, nil
   843  }
   844  
   845  func (g *gcePdDriver) GetDynamicProvisionStorageClass(ctx context.Context, config *storageframework.PerTestConfig, fsType string) *storagev1.StorageClass {
   846  	provisioner := "kubernetes.io/gce-pd"
   847  	parameters := map[string]string{}
   848  	if fsType != "" {
   849  		parameters["fsType"] = fsType
   850  	}
   851  	ns := config.Framework.Namespace.Name
   852  	delayedBinding := storagev1.VolumeBindingWaitForFirstConsumer
   853  
   854  	return storageframework.GetStorageClass(provisioner, parameters, &delayedBinding, ns)
   855  }
   856  
   857  func (g *gcePdDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig {
   858  	config := &storageframework.PerTestConfig{
   859  		Driver:    g,
   860  		Prefix:    "gcepd",
   861  		Framework: f,
   862  	}
   863  
   864  	if framework.NodeOSDistroIs("windows") {
   865  		config.ClientNodeSelection = e2epod.NodeSelection{
   866  			Selector: map[string]string{
   867  				"kubernetes.io/os": "windows",
   868  			},
   869  		}
   870  	}
   871  	return config
   872  
   873  }
   874  
   875  func (g *gcePdDriver) CreateVolume(ctx context.Context, config *storageframework.PerTestConfig, volType storageframework.TestVolType) storageframework.TestVolume {
   876  	zone := getInlineVolumeZone(ctx, config.Framework)
   877  	if volType == storageframework.InlineVolume {
   878  		// PD will be created in framework.TestContext.CloudConfig.Zone zone,
   879  		// so pods should be also scheduled there.
   880  		config.ClientNodeSelection = e2epod.NodeSelection{
   881  			Selector: map[string]string{
   882  				v1.LabelTopologyZone: zone,
   883  			},
   884  		}
   885  	}
   886  	ginkgo.By("creating a test gce pd volume")
   887  	vname, err := e2epv.CreatePDWithRetryAndZone(ctx, zone)
   888  	framework.ExpectNoError(err)
   889  	return &gcePdVolume{
   890  		volumeName: vname,
   891  	}
   892  }
   893  
   894  func (v *gcePdVolume) DeleteVolume(ctx context.Context) {
   895  	_ = e2epv.DeletePDWithRetry(ctx, v.volumeName)
   896  }
   897  
   898  // vSphere
   899  type vSphereDriver struct {
   900  	driverInfo storageframework.DriverInfo
   901  }
   902  
   903  var _ storageframework.TestDriver = &vSphereDriver{}
   904  var _ storageframework.DynamicPVTestDriver = &vSphereDriver{}
   905  
   906  // InitVSphereDriver returns vSphereDriver that implements TestDriver interface
   907  func InitVSphereDriver() storageframework.TestDriver {
   908  	return &vSphereDriver{
   909  		driverInfo: storageframework.DriverInfo{
   910  			Name:             "vsphere",
   911  			InTreePluginName: "kubernetes.io/vsphere-volume",
   912  			MaxFileSize:      storageframework.FileSizeMedium,
   913  			SupportedSizeRange: e2evolume.SizeRange{
   914  				Min: "1Gi",
   915  			},
   916  			SupportedFsType: sets.NewString(
   917  				"", // Default fsType
   918  				"ext4",
   919  				"ntfs",
   920  			),
   921  			TopologyKeys: []string{v1.LabelFailureDomainBetaZone},
   922  			Capabilities: map[storageframework.Capability]bool{
   923  				storageframework.CapPersistence:       true,
   924  				storageframework.CapFsGroup:           true,
   925  				storageframework.CapExec:              true,
   926  				storageframework.CapMultiPODs:         true,
   927  				storageframework.CapTopology:          true,
   928  				storageframework.CapBlock:             true,
   929  				storageframework.CapMultiplePVsSameID: false,
   930  			},
   931  		},
   932  	}
   933  }
   934  
   935  func (v *vSphereDriver) GetDriverInfo() *storageframework.DriverInfo {
   936  	return &v.driverInfo
   937  }
   938  
   939  func (v *vSphereDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) {
   940  	e2eskipper.SkipUnlessProviderIs("vsphere")
   941  }
   942  
   943  func (v *vSphereDriver) GetDynamicProvisionStorageClass(ctx context.Context, config *storageframework.PerTestConfig, fsType string) *storagev1.StorageClass {
   944  	provisioner := "kubernetes.io/vsphere-volume"
   945  	parameters := map[string]string{}
   946  	if fsType != "" {
   947  		parameters["fsType"] = fsType
   948  	}
   949  	ns := config.Framework.Namespace.Name
   950  
   951  	return storageframework.GetStorageClass(provisioner, parameters, nil, ns)
   952  }
   953  
   954  func (v *vSphereDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig {
   955  	return &storageframework.PerTestConfig{
   956  		Driver:    v,
   957  		Prefix:    "vsphere",
   958  		Framework: f,
   959  	}
   960  }
   961  
   962  // Azure Disk
   963  type azureDiskDriver struct {
   964  	driverInfo storageframework.DriverInfo
   965  }
   966  
   967  var _ storageframework.TestDriver = &azureDiskDriver{}
   968  var _ storageframework.DynamicPVTestDriver = &azureDiskDriver{}
   969  var _ storageframework.CustomTimeoutsTestDriver = &azureDiskDriver{}
   970  
   971  // InitAzureDiskDriver returns azureDiskDriver that implements TestDriver interface
   972  func InitAzureDiskDriver() storageframework.TestDriver {
   973  	return &azureDiskDriver{
   974  		driverInfo: storageframework.DriverInfo{
   975  			Name:             "azure-disk",
   976  			InTreePluginName: "kubernetes.io/azure-disk",
   977  			MaxFileSize:      storageframework.FileSizeMedium,
   978  			SupportedSizeRange: e2evolume.SizeRange{
   979  				Min: "1Gi",
   980  			},
   981  			SupportedFsType: sets.NewString(
   982  				"", // Default fsType
   983  				"ext4",
   984  				"xfs",
   985  			),
   986  			TopologyKeys: []string{v1.LabelFailureDomainBetaZone},
   987  			Capabilities: map[storageframework.Capability]bool{
   988  				storageframework.CapPersistence: true,
   989  				storageframework.CapFsGroup:     true,
   990  				storageframework.CapBlock:       true,
   991  				storageframework.CapExec:        true,
   992  				storageframework.CapMultiPODs:   true,
   993  				// Azure supports volume limits, but the test creates large
   994  				// number of volumes and times out test suites.
   995  				storageframework.CapVolumeLimits:      false,
   996  				storageframework.CapTopology:          true,
   997  				storageframework.CapMultiplePVsSameID: true,
   998  			},
   999  		},
  1000  	}
  1001  }
  1002  
  1003  func (a *azureDiskDriver) GetDriverInfo() *storageframework.DriverInfo {
  1004  	return &a.driverInfo
  1005  }
  1006  
  1007  func (a *azureDiskDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) {
  1008  	e2eskipper.SkipUnlessProviderIs("azure")
  1009  }
  1010  
  1011  func (a *azureDiskDriver) GetDynamicProvisionStorageClass(ctx context.Context, config *storageframework.PerTestConfig, fsType string) *storagev1.StorageClass {
  1012  	provisioner := "kubernetes.io/azure-disk"
  1013  	parameters := map[string]string{}
  1014  	if fsType != "" {
  1015  		parameters["fsType"] = fsType
  1016  	}
  1017  	ns := config.Framework.Namespace.Name
  1018  	delayedBinding := storagev1.VolumeBindingWaitForFirstConsumer
  1019  
  1020  	return storageframework.GetStorageClass(provisioner, parameters, &delayedBinding, ns)
  1021  }
  1022  
  1023  func (a *azureDiskDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig {
  1024  	return &storageframework.PerTestConfig{
  1025  		Driver:    a,
  1026  		Prefix:    "azure",
  1027  		Framework: f,
  1028  	}
  1029  }
  1030  
  1031  func (a *azureDiskDriver) GetTimeouts() *framework.TimeoutContext {
  1032  	timeouts := framework.NewTimeoutContext()
  1033  	timeouts.PodStart = time.Minute * 15
  1034  	timeouts.PodDelete = time.Minute * 15
  1035  	timeouts.PVDelete = time.Minute * 20
  1036  	return timeouts
  1037  }
  1038  
  1039  // AWS
  1040  type awsDriver struct {
  1041  	driverInfo storageframework.DriverInfo
  1042  }
  1043  
  1044  var _ storageframework.TestDriver = &awsDriver{}
  1045  var _ storageframework.DynamicPVTestDriver = &awsDriver{}
  1046  
  1047  // InitAwsDriver returns awsDriver that implements TestDriver interface
  1048  func InitAwsDriver() storageframework.TestDriver {
  1049  	return &awsDriver{
  1050  		driverInfo: storageframework.DriverInfo{
  1051  			Name:             "aws",
  1052  			InTreePluginName: "kubernetes.io/aws-ebs",
  1053  			MaxFileSize:      storageframework.FileSizeMedium,
  1054  			SupportedSizeRange: e2evolume.SizeRange{
  1055  				Min: "1Gi",
  1056  			},
  1057  			SupportedFsType: sets.NewString(
  1058  				"", // Default fsType
  1059  				"ext4",
  1060  				"xfs",
  1061  				"ntfs",
  1062  			),
  1063  			SupportedMountOption: sets.NewString("debug", "nouid32"),
  1064  			TopologyKeys:         []string{v1.LabelTopologyZone},
  1065  			Capabilities: map[storageframework.Capability]bool{
  1066  				storageframework.CapPersistence:         true,
  1067  				storageframework.CapFsGroup:             true,
  1068  				storageframework.CapBlock:               true,
  1069  				storageframework.CapExec:                true,
  1070  				storageframework.CapMultiPODs:           true,
  1071  				storageframework.CapControllerExpansion: true,
  1072  				storageframework.CapNodeExpansion:       true,
  1073  				storageframework.CapOfflineExpansion:    true,
  1074  				storageframework.CapOnlineExpansion:     true,
  1075  				// AWS supports volume limits, but the test creates large
  1076  				// number of volumes and times out test suites.
  1077  				storageframework.CapVolumeLimits:      false,
  1078  				storageframework.CapTopology:          true,
  1079  				storageframework.CapMultiplePVsSameID: true,
  1080  			},
  1081  		},
  1082  	}
  1083  }
  1084  
  1085  func (a *awsDriver) GetDriverInfo() *storageframework.DriverInfo {
  1086  	return &a.driverInfo
  1087  }
  1088  
  1089  func (a *awsDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) {
  1090  	e2eskipper.SkipUnlessProviderIs("aws")
  1091  }
  1092  
  1093  func (a *awsDriver) GetDynamicProvisionStorageClass(ctx context.Context, config *storageframework.PerTestConfig, fsType string) *storagev1.StorageClass {
  1094  	provisioner := "kubernetes.io/aws-ebs"
  1095  	parameters := map[string]string{}
  1096  	if fsType != "" {
  1097  		parameters["fsType"] = fsType
  1098  	}
  1099  	ns := config.Framework.Namespace.Name
  1100  	delayedBinding := storagev1.VolumeBindingWaitForFirstConsumer
  1101  
  1102  	return storageframework.GetStorageClass(provisioner, parameters, &delayedBinding, ns)
  1103  }
  1104  
  1105  func (a *awsDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig {
  1106  	config := &storageframework.PerTestConfig{
  1107  		Driver:    a,
  1108  		Prefix:    "aws",
  1109  		Framework: f,
  1110  	}
  1111  
  1112  	if framework.NodeOSDistroIs("windows") {
  1113  		config.ClientNodeSelection = e2epod.NodeSelection{
  1114  			Selector: map[string]string{
  1115  				"kubernetes.io/os": "windows",
  1116  			},
  1117  		}
  1118  	}
  1119  	return config
  1120  }
  1121  
  1122  // local
  1123  type localDriver struct {
  1124  	driverInfo storageframework.DriverInfo
  1125  	node       *v1.Node
  1126  	hostExec   utils.HostExec
  1127  	// volumeType represents local volume type we are testing, e.g.  tmpfs,
  1128  	// directory, block device.
  1129  	volumeType utils.LocalVolumeType
  1130  	ltrMgr     utils.LocalTestResourceManager
  1131  }
  1132  
  1133  type localVolume struct {
  1134  	ltrMgr utils.LocalTestResourceManager
  1135  	ltr    *utils.LocalTestResource
  1136  }
  1137  
  1138  var (
  1139  	// capabilities
  1140  	defaultLocalVolumeCapabilities = map[storageframework.Capability]bool{
  1141  		storageframework.CapPersistence:       true,
  1142  		storageframework.CapFsGroup:           true,
  1143  		storageframework.CapBlock:             false,
  1144  		storageframework.CapExec:              true,
  1145  		storageframework.CapMultiPODs:         true,
  1146  		storageframework.CapSingleNodeVolume:  true,
  1147  		storageframework.CapMultiplePVsSameID: true,
  1148  	}
  1149  	localVolumeCapabitilies = map[utils.LocalVolumeType]map[storageframework.Capability]bool{
  1150  		utils.LocalVolumeBlock: {
  1151  			storageframework.CapPersistence:       true,
  1152  			storageframework.CapFsGroup:           true,
  1153  			storageframework.CapBlock:             true,
  1154  			storageframework.CapExec:              true,
  1155  			storageframework.CapMultiPODs:         true,
  1156  			storageframework.CapSingleNodeVolume:  true,
  1157  			storageframework.CapMultiplePVsSameID: true,
  1158  		},
  1159  	}
  1160  	// fstype
  1161  	defaultLocalVolumeSupportedFsTypes = sets.NewString("")
  1162  	localVolumeSupportedFsTypes        = map[utils.LocalVolumeType]sets.String{
  1163  		utils.LocalVolumeBlock: sets.NewString(
  1164  			"", // Default fsType
  1165  			"ext4",
  1166  			//"xfs", disabled see issue https://github.com/kubernetes/kubernetes/issues/74095
  1167  		),
  1168  	}
  1169  	// max file size
  1170  	defaultLocalVolumeMaxFileSize = storageframework.FileSizeSmall
  1171  	localVolumeMaxFileSizes       = map[utils.LocalVolumeType]int64{}
  1172  )
  1173  
  1174  var _ storageframework.TestDriver = &localDriver{}
  1175  var _ storageframework.PreprovisionedVolumeTestDriver = &localDriver{}
  1176  var _ storageframework.PreprovisionedPVTestDriver = &localDriver{}
  1177  
  1178  // InitLocalDriverWithVolumeType initializes the local driver based on the volume type.
  1179  func InitLocalDriverWithVolumeType(volumeType utils.LocalVolumeType) func() storageframework.TestDriver {
  1180  	maxFileSize := defaultLocalVolumeMaxFileSize
  1181  	if maxFileSizeByVolType, ok := localVolumeMaxFileSizes[volumeType]; ok {
  1182  		maxFileSize = maxFileSizeByVolType
  1183  	}
  1184  	supportedFsTypes := defaultLocalVolumeSupportedFsTypes
  1185  	if supportedFsTypesByType, ok := localVolumeSupportedFsTypes[volumeType]; ok {
  1186  		supportedFsTypes = supportedFsTypesByType
  1187  	}
  1188  	capabilities := defaultLocalVolumeCapabilities
  1189  	if capabilitiesByType, ok := localVolumeCapabitilies[volumeType]; ok {
  1190  		capabilities = capabilitiesByType
  1191  	}
  1192  	return func() storageframework.TestDriver {
  1193  		// custom tag to distinguish from tests of other volume types
  1194  		testTags := []interface{}{fmt.Sprintf("[LocalVolumeType: %s]", volumeType)}
  1195  		// For GCE Local SSD volumes, we must run serially
  1196  		if volumeType == utils.LocalVolumeGCELocalSSD {
  1197  			testTags = append(testTags, framework.WithSerial())
  1198  		}
  1199  		return &localDriver{
  1200  			driverInfo: storageframework.DriverInfo{
  1201  				Name:             "local",
  1202  				InTreePluginName: "kubernetes.io/local-volume",
  1203  				TestTags:         testTags,
  1204  				MaxFileSize:      maxFileSize,
  1205  				SupportedFsType:  supportedFsTypes,
  1206  				Capabilities:     capabilities,
  1207  			},
  1208  			volumeType: volumeType,
  1209  		}
  1210  	}
  1211  }
  1212  
  1213  func (l *localDriver) GetDriverInfo() *storageframework.DriverInfo {
  1214  	return &l.driverInfo
  1215  }
  1216  
  1217  func (l *localDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) {
  1218  }
  1219  
  1220  func (l *localDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig {
  1221  	var err error
  1222  	l.node, err = e2enode.GetRandomReadySchedulableNode(ctx, f.ClientSet)
  1223  	framework.ExpectNoError(err)
  1224  
  1225  	l.hostExec = utils.NewHostExec(f)
  1226  	l.ltrMgr = utils.NewLocalResourceManager("local-driver", l.hostExec, "/tmp")
  1227  
  1228  	// This can't be done in SkipUnsupportedTest because the test framework is not initialized yet
  1229  	if l.volumeType == utils.LocalVolumeGCELocalSSD {
  1230  		ssdInterface := "scsi"
  1231  		filesystemType := "fs"
  1232  		ssdCmd := fmt.Sprintf("ls -1 /mnt/disks/by-uuid/google-local-ssds-%s-%s/ | wc -l", ssdInterface, filesystemType)
  1233  		res, err := l.hostExec.IssueCommandWithResult(ctx, ssdCmd, l.node)
  1234  		framework.ExpectNoError(err)
  1235  		num, err := strconv.Atoi(strings.TrimSpace(res))
  1236  		framework.ExpectNoError(err)
  1237  		if num < 1 {
  1238  			e2eskipper.Skipf("Requires at least 1 %s %s localSSD ", ssdInterface, filesystemType)
  1239  		}
  1240  	}
  1241  
  1242  	ginkgo.DeferCleanup(l.hostExec.Cleanup)
  1243  	return &storageframework.PerTestConfig{
  1244  		Driver:              l,
  1245  		Prefix:              "local",
  1246  		Framework:           f,
  1247  		ClientNodeSelection: e2epod.NodeSelection{Name: l.node.Name},
  1248  	}
  1249  }
  1250  
  1251  func (l *localDriver) CreateVolume(ctx context.Context, config *storageframework.PerTestConfig, volType storageframework.TestVolType) storageframework.TestVolume {
  1252  	switch volType {
  1253  	case storageframework.PreprovisionedPV:
  1254  		node := l.node
  1255  		// assign this to schedule pod on this node
  1256  		config.ClientNodeSelection = e2epod.NodeSelection{Name: node.Name}
  1257  		return &localVolume{
  1258  			ltrMgr: l.ltrMgr,
  1259  			ltr:    l.ltrMgr.Create(ctx, node, l.volumeType, nil),
  1260  		}
  1261  	default:
  1262  		framework.Failf("Unsupported volType: %v is specified", volType)
  1263  	}
  1264  	return nil
  1265  }
  1266  
  1267  func (v *localVolume) DeleteVolume(ctx context.Context) {
  1268  	v.ltrMgr.Remove(ctx, v.ltr)
  1269  }
  1270  
  1271  func (l *localDriver) nodeAffinityForNode(node *v1.Node) *v1.VolumeNodeAffinity {
  1272  	nodeKey := "kubernetes.io/hostname"
  1273  	if node.Labels == nil {
  1274  		framework.Failf("Node does not have labels")
  1275  	}
  1276  	nodeValue, found := node.Labels[nodeKey]
  1277  	if !found {
  1278  		framework.Failf("Node does not have required label %q", nodeKey)
  1279  	}
  1280  	return &v1.VolumeNodeAffinity{
  1281  		Required: &v1.NodeSelector{
  1282  			NodeSelectorTerms: []v1.NodeSelectorTerm{
  1283  				{
  1284  					MatchExpressions: []v1.NodeSelectorRequirement{
  1285  						{
  1286  							Key:      nodeKey,
  1287  							Operator: v1.NodeSelectorOpIn,
  1288  							Values:   []string{nodeValue},
  1289  						},
  1290  					},
  1291  				},
  1292  			},
  1293  		},
  1294  	}
  1295  }
  1296  
  1297  func (l *localDriver) GetPersistentVolumeSource(readOnly bool, fsType string, e2evolume storageframework.TestVolume) (*v1.PersistentVolumeSource, *v1.VolumeNodeAffinity) {
  1298  	lv, ok := e2evolume.(*localVolume)
  1299  	if !ok {
  1300  		framework.Failf("Failed to cast test volume of type %T to the local test volume", e2evolume)
  1301  	}
  1302  	return &v1.PersistentVolumeSource{
  1303  		Local: &v1.LocalVolumeSource{
  1304  			Path:   lv.ltr.Path,
  1305  			FSType: &fsType,
  1306  		},
  1307  	}, l.nodeAffinityForNode(lv.ltr.Node)
  1308  }
  1309  
  1310  // cleanUpVolumeServer is a wrapper of cleanup function for volume server without secret created by specific CreateStorageServer function.
  1311  func cleanUpVolumeServer(ctx context.Context, f *framework.Framework, serverPod *v1.Pod) {
  1312  	cleanUpVolumeServerWithSecret(ctx, f, serverPod, nil)
  1313  }
  1314  
  1315  func getInlineVolumeZone(ctx context.Context, f *framework.Framework) string {
  1316  	if framework.TestContext.CloudConfig.Zone != "" {
  1317  		return framework.TestContext.CloudConfig.Zone
  1318  	}
  1319  	// if zone is not specified we will randomly pick a zone from schedulable nodes for inline tests
  1320  	node, err := e2enode.GetRandomReadySchedulableNode(ctx, f.ClientSet)
  1321  	framework.ExpectNoError(err)
  1322  	zone, ok := node.Labels[v1.LabelFailureDomainBetaZone]
  1323  	if ok {
  1324  		return zone
  1325  	}
  1326  	topologyZone, ok := node.Labels[v1.LabelTopologyZone]
  1327  	if ok {
  1328  		return topologyZone
  1329  	}
  1330  	return ""
  1331  }
  1332  
  1333  // cleanUpVolumeServerWithSecret is a wrapper of cleanup function for volume server with secret created by specific CreateStorageServer function.
  1334  func cleanUpVolumeServerWithSecret(ctx context.Context, f *framework.Framework, serverPod *v1.Pod, secret *v1.Secret) {
  1335  	cs := f.ClientSet
  1336  	ns := f.Namespace
  1337  
  1338  	if secret != nil {
  1339  		framework.Logf("Deleting server secret %q...", secret.Name)
  1340  		err := cs.CoreV1().Secrets(ns.Name).Delete(ctx, secret.Name, metav1.DeleteOptions{})
  1341  		if err != nil {
  1342  			framework.Logf("Delete secret failed: %v", err)
  1343  		}
  1344  	}
  1345  
  1346  	framework.Logf("Deleting server pod %q...", serverPod.Name)
  1347  	err := e2epod.DeletePodWithWait(ctx, cs, serverPod)
  1348  	if err != nil {
  1349  		framework.Logf("Server pod delete failed: %v", err)
  1350  	}
  1351  }
  1352  
  1353  // Azure File
  1354  type azureFileDriver struct {
  1355  	driverInfo storageframework.DriverInfo
  1356  }
  1357  
  1358  var _ storageframework.TestDriver = &azureFileDriver{}
  1359  var _ storageframework.DynamicPVTestDriver = &azureFileDriver{}
  1360  
  1361  // InitAzureFileDriver returns azureFileDriver that implements TestDriver interface
  1362  func InitAzureFileDriver() storageframework.TestDriver {
  1363  	return &azureFileDriver{
  1364  		driverInfo: storageframework.DriverInfo{
  1365  			Name:             "azure-file",
  1366  			InTreePluginName: "kubernetes.io/azure-file",
  1367  			MaxFileSize:      storageframework.FileSizeMedium,
  1368  			SupportedSizeRange: e2evolume.SizeRange{
  1369  				Min: "1Gi",
  1370  			},
  1371  			SupportedFsType: sets.NewString(
  1372  				"", // Default fsType
  1373  			),
  1374  			Capabilities: map[storageframework.Capability]bool{
  1375  				storageframework.CapPersistence:         true,
  1376  				storageframework.CapExec:                true,
  1377  				storageframework.CapRWX:                 true,
  1378  				storageframework.CapMultiPODs:           true,
  1379  				storageframework.CapControllerExpansion: true,
  1380  				storageframework.CapNodeExpansion:       true,
  1381  				storageframework.CapMultiplePVsSameID:   true,
  1382  			},
  1383  		},
  1384  	}
  1385  }
  1386  
  1387  func (a *azureFileDriver) GetDriverInfo() *storageframework.DriverInfo {
  1388  	return &a.driverInfo
  1389  }
  1390  
  1391  func (a *azureFileDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) {
  1392  	e2eskipper.SkipUnlessProviderIs("azure")
  1393  }
  1394  
  1395  func (a *azureFileDriver) GetDynamicProvisionStorageClass(ctx context.Context, config *storageframework.PerTestConfig, fsType string) *storagev1.StorageClass {
  1396  	provisioner := "kubernetes.io/azure-file"
  1397  	parameters := map[string]string{}
  1398  	ns := config.Framework.Namespace.Name
  1399  	immediateBinding := storagev1.VolumeBindingImmediate
  1400  	return storageframework.GetStorageClass(provisioner, parameters, &immediateBinding, ns)
  1401  }
  1402  
  1403  func (a *azureFileDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig {
  1404  	return &storageframework.PerTestConfig{
  1405  		Driver:    a,
  1406  		Prefix:    "azure-file",
  1407  		Framework: f,
  1408  	}
  1409  }