k8s.io/kubernetes@v1.29.3/test/e2e/storage/drivers/csi.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 csi 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 (ex: 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  	"encoding/json"
    41  	"errors"
    42  	"fmt"
    43  	"strconv"
    44  	"strings"
    45  	"sync"
    46  	"time"
    47  
    48  	"github.com/onsi/ginkgo/v2"
    49  	spb "google.golang.org/genproto/googleapis/rpc/status"
    50  	"google.golang.org/grpc/codes"
    51  	grpcstatus "google.golang.org/grpc/status"
    52  
    53  	appsv1 "k8s.io/api/apps/v1"
    54  	v1 "k8s.io/api/core/v1"
    55  	rbacv1 "k8s.io/api/rbac/v1"
    56  	storagev1 "k8s.io/api/storage/v1"
    57  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    58  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    59  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    60  	"k8s.io/apimachinery/pkg/util/sets"
    61  	"k8s.io/apimachinery/pkg/util/wait"
    62  	clientset "k8s.io/client-go/kubernetes"
    63  	"k8s.io/klog/v2"
    64  	"k8s.io/kubernetes/test/e2e/feature"
    65  	"k8s.io/kubernetes/test/e2e/framework"
    66  	e2enode "k8s.io/kubernetes/test/e2e/framework/node"
    67  	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
    68  	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
    69  	e2evolume "k8s.io/kubernetes/test/e2e/framework/volume"
    70  	mockdriver "k8s.io/kubernetes/test/e2e/storage/drivers/csi-test/driver"
    71  	mockservice "k8s.io/kubernetes/test/e2e/storage/drivers/csi-test/mock/service"
    72  	"k8s.io/kubernetes/test/e2e/storage/drivers/proxy"
    73  	storageframework "k8s.io/kubernetes/test/e2e/storage/framework"
    74  	"k8s.io/kubernetes/test/e2e/storage/utils"
    75  
    76  	"google.golang.org/grpc"
    77  )
    78  
    79  const (
    80  	// GCEPDCSIDriverName is the name of GCE Persistent Disk CSI driver
    81  	GCEPDCSIDriverName = "pd.csi.storage.gke.io"
    82  	// GCEPDCSIZoneTopologyKey is the key of GCE Persistent Disk CSI zone topology
    83  	GCEPDCSIZoneTopologyKey = "topology.gke.io/zone"
    84  
    85  	// Prefix of the mock driver grpc log
    86  	grpcCallPrefix = "gRPCCall:"
    87  )
    88  
    89  // hostpathCSI
    90  type hostpathCSIDriver struct {
    91  	driverInfo       storageframework.DriverInfo
    92  	manifests        []string
    93  	volumeAttributes []map[string]string
    94  }
    95  
    96  func initHostPathCSIDriver(name string, capabilities map[storageframework.Capability]bool, volumeAttributes []map[string]string, manifests ...string) storageframework.TestDriver {
    97  	return &hostpathCSIDriver{
    98  		driverInfo: storageframework.DriverInfo{
    99  			Name:        name,
   100  			MaxFileSize: storageframework.FileSizeMedium,
   101  			SupportedFsType: sets.NewString(
   102  				"", // Default fsType
   103  			),
   104  			SupportedSizeRange: e2evolume.SizeRange{
   105  				Min: "1Mi",
   106  			},
   107  			Capabilities: capabilities,
   108  			StressTestOptions: &storageframework.StressTestOptions{
   109  				NumPods:     10,
   110  				NumRestarts: 10,
   111  			},
   112  			VolumeSnapshotStressTestOptions: &storageframework.VolumeSnapshotStressTestOptions{
   113  				NumPods:      10,
   114  				NumSnapshots: 10,
   115  			},
   116  			PerformanceTestOptions: &storageframework.PerformanceTestOptions{
   117  				ProvisioningOptions: &storageframework.PerformanceTestProvisioningOptions{
   118  					VolumeSize: "1Mi",
   119  					Count:      300,
   120  					// Volume provisioning metrics are compared to a high baseline.
   121  					// Failure to pass would suggest a performance regression.
   122  					ExpectedMetrics: &storageframework.Metrics{
   123  						AvgLatency: 2 * time.Minute,
   124  						Throughput: 0.5,
   125  					},
   126  				},
   127  			},
   128  		},
   129  		manifests:        manifests,
   130  		volumeAttributes: volumeAttributes,
   131  	}
   132  }
   133  
   134  var _ storageframework.TestDriver = &hostpathCSIDriver{}
   135  var _ storageframework.DynamicPVTestDriver = &hostpathCSIDriver{}
   136  var _ storageframework.SnapshottableTestDriver = &hostpathCSIDriver{}
   137  var _ storageframework.EphemeralTestDriver = &hostpathCSIDriver{}
   138  
   139  // InitHostPathCSIDriver returns hostpathCSIDriver that implements TestDriver interface
   140  func InitHostPathCSIDriver() storageframework.TestDriver {
   141  	capabilities := map[storageframework.Capability]bool{
   142  		storageframework.CapPersistence:                    true,
   143  		storageframework.CapSnapshotDataSource:             true,
   144  		storageframework.CapMultiPODs:                      true,
   145  		storageframework.CapBlock:                          true,
   146  		storageframework.CapPVCDataSource:                  true,
   147  		storageframework.CapControllerExpansion:            true,
   148  		storageframework.CapOfflineExpansion:               true,
   149  		storageframework.CapOnlineExpansion:                true,
   150  		storageframework.CapSingleNodeVolume:               true,
   151  		storageframework.CapReadWriteOncePod:               true,
   152  		storageframework.CapMultiplePVsSameID:              true,
   153  		storageframework.CapFSResizeFromSourceNotSupported: true,
   154  
   155  		// This is needed for the
   156  		// testsuites/volumelimits.go `should support volume limits`
   157  		// test. --maxvolumespernode=10 gets
   158  		// added when patching the deployment.
   159  		storageframework.CapVolumeLimits: true,
   160  	}
   161  	return initHostPathCSIDriver("csi-hostpath",
   162  		capabilities,
   163  		// Volume attributes don't matter, but we have to provide at least one map.
   164  		[]map[string]string{
   165  			{"foo": "bar"},
   166  		},
   167  		"test/e2e/testing-manifests/storage-csi/external-attacher/rbac.yaml",
   168  		"test/e2e/testing-manifests/storage-csi/external-provisioner/rbac.yaml",
   169  		"test/e2e/testing-manifests/storage-csi/external-snapshotter/csi-snapshotter/rbac-csi-snapshotter.yaml",
   170  		"test/e2e/testing-manifests/storage-csi/external-health-monitor/external-health-monitor-controller/rbac.yaml",
   171  		"test/e2e/testing-manifests/storage-csi/external-resizer/rbac.yaml",
   172  		"test/e2e/testing-manifests/storage-csi/hostpath/hostpath/csi-hostpath-driverinfo.yaml",
   173  		"test/e2e/testing-manifests/storage-csi/hostpath/hostpath/csi-hostpath-plugin.yaml",
   174  		"test/e2e/testing-manifests/storage-csi/hostpath/hostpath/e2e-test-rbac.yaml",
   175  	)
   176  }
   177  
   178  func (h *hostpathCSIDriver) GetDriverInfo() *storageframework.DriverInfo {
   179  	return &h.driverInfo
   180  }
   181  
   182  func (h *hostpathCSIDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) {
   183  	if pattern.VolType == storageframework.CSIInlineVolume && len(h.volumeAttributes) == 0 {
   184  		e2eskipper.Skipf("%s has no volume attributes defined, doesn't support ephemeral inline volumes", h.driverInfo.Name)
   185  	}
   186  }
   187  
   188  func (h *hostpathCSIDriver) GetDynamicProvisionStorageClass(ctx context.Context, config *storageframework.PerTestConfig, fsType string) *storagev1.StorageClass {
   189  	provisioner := config.GetUniqueDriverName()
   190  	parameters := map[string]string{}
   191  	ns := config.Framework.Namespace.Name
   192  
   193  	return storageframework.GetStorageClass(provisioner, parameters, nil, ns)
   194  }
   195  
   196  func (h *hostpathCSIDriver) GetVolume(config *storageframework.PerTestConfig, volumeNumber int) (map[string]string, bool, bool) {
   197  	return h.volumeAttributes[volumeNumber%len(h.volumeAttributes)], false /* not shared */, false /* read-write */
   198  }
   199  
   200  func (h *hostpathCSIDriver) GetCSIDriverName(config *storageframework.PerTestConfig) string {
   201  	return config.GetUniqueDriverName()
   202  }
   203  
   204  func (h *hostpathCSIDriver) GetSnapshotClass(ctx context.Context, config *storageframework.PerTestConfig, parameters map[string]string) *unstructured.Unstructured {
   205  	snapshotter := config.GetUniqueDriverName()
   206  	ns := config.Framework.Namespace.Name
   207  
   208  	return utils.GenerateSnapshotClassSpec(snapshotter, parameters, ns)
   209  }
   210  
   211  func (h *hostpathCSIDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig {
   212  	// Create secondary namespace which will be used for creating driver
   213  	driverNamespace := utils.CreateDriverNamespace(ctx, f)
   214  	driverns := driverNamespace.Name
   215  	testns := f.Namespace.Name
   216  
   217  	ginkgo.By(fmt.Sprintf("deploying %s driver", h.driverInfo.Name))
   218  	cancelLogging := utils.StartPodLogs(ctx, f, driverNamespace)
   219  	cs := f.ClientSet
   220  
   221  	// The hostpath CSI driver only works when everything runs on the same node.
   222  	node, err := e2enode.GetRandomReadySchedulableNode(ctx, cs)
   223  	framework.ExpectNoError(err)
   224  	config := &storageframework.PerTestConfig{
   225  		Driver:              h,
   226  		Prefix:              "hostpath",
   227  		Framework:           f,
   228  		ClientNodeSelection: e2epod.NodeSelection{Name: node.Name},
   229  		DriverNamespace:     driverNamespace,
   230  	}
   231  
   232  	o := utils.PatchCSIOptions{
   233  		OldDriverName:       h.driverInfo.Name,
   234  		NewDriverName:       config.GetUniqueDriverName(),
   235  		DriverContainerName: "hostpath",
   236  		DriverContainerArguments: []string{"--drivername=" + config.GetUniqueDriverName(),
   237  			// This is needed for the
   238  			// testsuites/volumelimits.go `should support volume limits`
   239  			// test.
   240  			"--maxvolumespernode=10",
   241  			// Enable volume lifecycle checks, to report failure if
   242  			// the volume is not unpublished / unstaged correctly.
   243  			"--check-volume-lifecycle=true",
   244  		},
   245  		ProvisionerContainerName: "csi-provisioner",
   246  		SnapshotterContainerName: "csi-snapshotter",
   247  		NodeName:                 node.Name,
   248  	}
   249  
   250  	err = utils.CreateFromManifests(ctx, config.Framework, driverNamespace, func(item interface{}) error {
   251  		if err := utils.PatchCSIDeployment(config.Framework, o, item); err != nil {
   252  			return err
   253  		}
   254  
   255  		// Remove csi-external-health-monitor-agent and
   256  		// csi-external-health-monitor-controller
   257  		// containers. The agent is obsolete.
   258  		// The controller is not needed for any of the
   259  		// tests and is causing too much overhead when
   260  		// running in a large cluster (see
   261  		// https://github.com/kubernetes/kubernetes/issues/102452#issuecomment-856991009).
   262  		switch item := item.(type) {
   263  		case *appsv1.StatefulSet:
   264  			var containers []v1.Container
   265  			for _, container := range item.Spec.Template.Spec.Containers {
   266  				switch container.Name {
   267  				case "csi-external-health-monitor-agent", "csi-external-health-monitor-controller":
   268  					// Remove these containers.
   269  				default:
   270  					// Keep the others.
   271  					containers = append(containers, container)
   272  				}
   273  			}
   274  			item.Spec.Template.Spec.Containers = containers
   275  		}
   276  		return nil
   277  	}, h.manifests...)
   278  
   279  	if err != nil {
   280  		framework.Failf("deploying %s driver: %v", h.driverInfo.Name, err)
   281  	}
   282  
   283  	cleanupFunc := generateDriverCleanupFunc(
   284  		f,
   285  		h.driverInfo.Name,
   286  		testns,
   287  		driverns,
   288  		cancelLogging)
   289  	ginkgo.DeferCleanup(cleanupFunc)
   290  
   291  	return config
   292  }
   293  
   294  // mockCSI
   295  type mockCSIDriver struct {
   296  	driverInfo                    storageframework.DriverInfo
   297  	manifests                     []string
   298  	podInfo                       *bool
   299  	storageCapacity               *bool
   300  	attachable                    bool
   301  	attachLimit                   int
   302  	enableTopology                bool
   303  	enableNodeExpansion           bool
   304  	hooks                         Hooks
   305  	tokenRequests                 []storagev1.TokenRequest
   306  	requiresRepublish             *bool
   307  	fsGroupPolicy                 *storagev1.FSGroupPolicy
   308  	enableVolumeMountGroup        bool
   309  	embedded                      bool
   310  	calls                         MockCSICalls
   311  	embeddedCSIDriver             *mockdriver.CSIDriver
   312  	enableSELinuxMount            *bool
   313  	enableRecoverExpansionFailure bool
   314  
   315  	// Additional values set during PrepareTest
   316  	clientSet       clientset.Interface
   317  	driverNamespace *v1.Namespace
   318  }
   319  
   320  // Hooks to be run to execute while handling gRPC calls.
   321  //
   322  // At the moment, only generic pre- and post-function call
   323  // hooks are implemented. Those hooks can cast the request and
   324  // response values if needed. More hooks inside specific
   325  // functions could be added if needed.
   326  type Hooks struct {
   327  	// Pre is called before invoking the mock driver's implementation of a method.
   328  	// If either a non-nil reply or error are returned, then those are returned to the caller.
   329  	Pre func(ctx context.Context, method string, request interface{}) (reply interface{}, err error)
   330  
   331  	// Post is called after invoking the mock driver's implementation of a method.
   332  	// What it returns is used as actual result.
   333  	Post func(ctx context.Context, method string, request, reply interface{}, err error) (finalReply interface{}, finalErr error)
   334  }
   335  
   336  // MockCSITestDriver provides additional functions specific to the CSI mock driver.
   337  type MockCSITestDriver interface {
   338  	storageframework.DynamicPVTestDriver
   339  
   340  	// GetCalls returns all currently observed gRPC calls. Only valid
   341  	// after PrepareTest.
   342  	GetCalls(ctx context.Context) ([]MockCSICall, error)
   343  }
   344  
   345  // CSIMockDriverOpts defines options used for csi driver
   346  type CSIMockDriverOpts struct {
   347  	RegisterDriver                bool
   348  	DisableAttach                 bool
   349  	PodInfo                       *bool
   350  	StorageCapacity               *bool
   351  	AttachLimit                   int
   352  	EnableTopology                bool
   353  	EnableResizing                bool
   354  	EnableNodeExpansion           bool
   355  	EnableSnapshot                bool
   356  	EnableVolumeMountGroup        bool
   357  	TokenRequests                 []storagev1.TokenRequest
   358  	RequiresRepublish             *bool
   359  	FSGroupPolicy                 *storagev1.FSGroupPolicy
   360  	EnableSELinuxMount            *bool
   361  	EnableRecoverExpansionFailure bool
   362  
   363  	// Embedded defines whether the CSI mock driver runs
   364  	// inside the cluster (false, the default) or just a proxy
   365  	// runs inside the cluster and all gRPC calls are handled
   366  	// inside the e2e.test binary.
   367  	Embedded bool
   368  
   369  	// Hooks that will be called if (and only if!) the embedded
   370  	// mock driver is used. Beware that hooks are invoked
   371  	// asynchronously in different goroutines.
   372  	Hooks Hooks
   373  }
   374  
   375  // Dummy structure that parses just volume_attributes and error code out of logged CSI call
   376  type MockCSICall struct {
   377  	json string // full log entry
   378  
   379  	Method  string
   380  	Request struct {
   381  		VolumeContext map[string]string `json:"volume_context"`
   382  		Secrets       map[string]string `json:"secrets"`
   383  	}
   384  	FullError struct {
   385  		Code    codes.Code `json:"code"`
   386  		Message string     `json:"message"`
   387  	}
   388  	Error string
   389  }
   390  
   391  // MockCSICalls is a Thread-safe storage for MockCSICall instances.
   392  type MockCSICalls struct {
   393  	calls []MockCSICall
   394  	mutex sync.Mutex
   395  }
   396  
   397  // Get returns all currently recorded calls.
   398  func (c *MockCSICalls) Get() []MockCSICall {
   399  	c.mutex.Lock()
   400  	defer c.mutex.Unlock()
   401  
   402  	return c.calls[:]
   403  }
   404  
   405  // Add appends one new call at the end.
   406  func (c *MockCSICalls) Add(call MockCSICall) {
   407  	c.mutex.Lock()
   408  	defer c.mutex.Unlock()
   409  
   410  	c.calls = append(c.calls, call)
   411  }
   412  
   413  // LogGRPC takes individual parameters from the mock CSI driver and adds them.
   414  func (c *MockCSICalls) LogGRPC(method string, request, reply interface{}, err error) {
   415  	// Encoding to JSON and decoding mirrors the traditional way of capturing calls.
   416  	// Probably could be simplified now...
   417  	logMessage := struct {
   418  		Method   string
   419  		Request  interface{}
   420  		Response interface{}
   421  		// Error as string, for backward compatibility.
   422  		// "" on no error.
   423  		Error string
   424  		// Full error dump, to be able to parse out full gRPC error code and message separately in a test.
   425  		FullError *spb.Status
   426  	}{
   427  		Method:   method,
   428  		Request:  request,
   429  		Response: reply,
   430  	}
   431  
   432  	if err != nil {
   433  		logMessage.Error = err.Error()
   434  		logMessage.FullError = grpcstatus.Convert(err).Proto()
   435  	}
   436  
   437  	msg, _ := json.Marshal(logMessage)
   438  	call := MockCSICall{
   439  		json: string(msg),
   440  	}
   441  	json.Unmarshal(msg, &call)
   442  
   443  	klog.Infof("%s %s", grpcCallPrefix, string(msg))
   444  
   445  	// Trim gRPC service name, i.e. "/csi.v1.Identity/Probe" -> "Probe"
   446  	methodParts := strings.Split(call.Method, "/")
   447  	call.Method = methodParts[len(methodParts)-1]
   448  
   449  	c.Add(call)
   450  }
   451  
   452  var _ storageframework.TestDriver = &mockCSIDriver{}
   453  var _ storageframework.DynamicPVTestDriver = &mockCSIDriver{}
   454  var _ storageframework.SnapshottableTestDriver = &mockCSIDriver{}
   455  
   456  // InitMockCSIDriver returns a mockCSIDriver that implements TestDriver interface
   457  func InitMockCSIDriver(driverOpts CSIMockDriverOpts) MockCSITestDriver {
   458  	driverManifests := []string{
   459  		"test/e2e/testing-manifests/storage-csi/external-attacher/rbac.yaml",
   460  		"test/e2e/testing-manifests/storage-csi/external-provisioner/rbac.yaml",
   461  		"test/e2e/testing-manifests/storage-csi/external-resizer/rbac.yaml",
   462  		"test/e2e/testing-manifests/storage-csi/external-snapshotter/csi-snapshotter/rbac-csi-snapshotter.yaml",
   463  		"test/e2e/testing-manifests/storage-csi/mock/csi-mock-rbac.yaml",
   464  		"test/e2e/testing-manifests/storage-csi/mock/csi-storageclass.yaml",
   465  	}
   466  	if driverOpts.Embedded {
   467  		driverManifests = append(driverManifests, "test/e2e/testing-manifests/storage-csi/mock/csi-mock-proxy.yaml")
   468  	} else {
   469  		driverManifests = append(driverManifests, "test/e2e/testing-manifests/storage-csi/mock/csi-mock-driver.yaml")
   470  	}
   471  
   472  	if driverOpts.RegisterDriver {
   473  		driverManifests = append(driverManifests, "test/e2e/testing-manifests/storage-csi/mock/csi-mock-driverinfo.yaml")
   474  	}
   475  
   476  	if !driverOpts.DisableAttach {
   477  		driverManifests = append(driverManifests, "test/e2e/testing-manifests/storage-csi/mock/csi-mock-driver-attacher.yaml")
   478  	}
   479  
   480  	if driverOpts.EnableResizing {
   481  		driverManifests = append(driverManifests, "test/e2e/testing-manifests/storage-csi/mock/csi-mock-driver-resizer.yaml")
   482  	}
   483  
   484  	if driverOpts.EnableSnapshot {
   485  		driverManifests = append(driverManifests, "test/e2e/testing-manifests/storage-csi/mock/csi-mock-driver-snapshotter.yaml")
   486  	}
   487  
   488  	return &mockCSIDriver{
   489  		driverInfo: storageframework.DriverInfo{
   490  			Name:        "csi-mock",
   491  			MaxFileSize: storageframework.FileSizeMedium,
   492  			SupportedFsType: sets.NewString(
   493  				"", // Default fsType
   494  			),
   495  			Capabilities: map[storageframework.Capability]bool{
   496  				storageframework.CapPersistence:       false,
   497  				storageframework.CapFsGroup:           false,
   498  				storageframework.CapExec:              false,
   499  				storageframework.CapVolumeLimits:      true,
   500  				storageframework.CapMultiplePVsSameID: true,
   501  			},
   502  		},
   503  		manifests:                     driverManifests,
   504  		podInfo:                       driverOpts.PodInfo,
   505  		storageCapacity:               driverOpts.StorageCapacity,
   506  		enableTopology:                driverOpts.EnableTopology,
   507  		attachable:                    !driverOpts.DisableAttach,
   508  		attachLimit:                   driverOpts.AttachLimit,
   509  		enableNodeExpansion:           driverOpts.EnableNodeExpansion,
   510  		tokenRequests:                 driverOpts.TokenRequests,
   511  		requiresRepublish:             driverOpts.RequiresRepublish,
   512  		fsGroupPolicy:                 driverOpts.FSGroupPolicy,
   513  		enableVolumeMountGroup:        driverOpts.EnableVolumeMountGroup,
   514  		enableSELinuxMount:            driverOpts.EnableSELinuxMount,
   515  		enableRecoverExpansionFailure: driverOpts.EnableRecoverExpansionFailure,
   516  		embedded:                      driverOpts.Embedded,
   517  		hooks:                         driverOpts.Hooks,
   518  	}
   519  }
   520  
   521  func (m *mockCSIDriver) GetDriverInfo() *storageframework.DriverInfo {
   522  	return &m.driverInfo
   523  }
   524  
   525  func (m *mockCSIDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) {
   526  }
   527  
   528  func (m *mockCSIDriver) GetDynamicProvisionStorageClass(ctx context.Context, config *storageframework.PerTestConfig, fsType string) *storagev1.StorageClass {
   529  	provisioner := config.GetUniqueDriverName()
   530  	parameters := map[string]string{}
   531  	ns := config.Framework.Namespace.Name
   532  
   533  	return storageframework.GetStorageClass(provisioner, parameters, nil, ns)
   534  }
   535  
   536  func (m *mockCSIDriver) GetSnapshotClass(ctx context.Context, config *storageframework.PerTestConfig, parameters map[string]string) *unstructured.Unstructured {
   537  	snapshotter := m.driverInfo.Name + "-" + config.Framework.UniqueName
   538  	ns := config.Framework.Namespace.Name
   539  
   540  	return utils.GenerateSnapshotClassSpec(snapshotter, parameters, ns)
   541  }
   542  
   543  func (m *mockCSIDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig {
   544  	m.clientSet = f.ClientSet
   545  
   546  	// Create secondary namespace which will be used for creating driver
   547  	m.driverNamespace = utils.CreateDriverNamespace(ctx, f)
   548  	driverns := m.driverNamespace.Name
   549  	testns := f.Namespace.Name
   550  
   551  	if m.embedded {
   552  		ginkgo.By("deploying csi mock proxy")
   553  	} else {
   554  		ginkgo.By("deploying csi mock driver")
   555  	}
   556  	cancelLogging := utils.StartPodLogs(ctx, f, m.driverNamespace)
   557  	cs := f.ClientSet
   558  
   559  	// pods should be scheduled on the node
   560  	node, err := e2enode.GetRandomReadySchedulableNode(ctx, cs)
   561  	framework.ExpectNoError(err)
   562  
   563  	embeddedCleanup := func() {}
   564  	containerArgs := []string{}
   565  	if m.embedded {
   566  		// Run embedded CSI driver.
   567  		//
   568  		// For now we start exactly one instance which implements controller,
   569  		// node and identity services. It matches with the one pod that we run
   570  		// inside the cluster. The name and namespace of that one is deterministic,
   571  		// so we know what to connect to.
   572  		//
   573  		// Long-term we could also deploy one central controller and multiple
   574  		// node instances, with knowledge about provisioned volumes shared in
   575  		// this process.
   576  		podname := "csi-mockplugin-0"
   577  		containername := "mock"
   578  
   579  		// Must keep running even after the test context is cancelled
   580  		// for cleanup callbacks.
   581  		ctx, cancel := context.WithCancel(context.Background())
   582  		serviceConfig := mockservice.Config{
   583  			DisableAttach:            !m.attachable,
   584  			DriverName:               "csi-mock-" + f.UniqueName,
   585  			AttachLimit:              int64(m.attachLimit),
   586  			NodeExpansionRequired:    m.enableNodeExpansion,
   587  			VolumeMountGroupRequired: m.enableVolumeMountGroup,
   588  			EnableTopology:           m.enableTopology,
   589  			IO: proxy.PodDirIO{
   590  				F:             f,
   591  				Namespace:     m.driverNamespace.Name,
   592  				PodName:       podname,
   593  				ContainerName: "busybox",
   594  			},
   595  		}
   596  		s := mockservice.New(serviceConfig)
   597  		servers := &mockdriver.CSIDriverServers{
   598  			Controller: s,
   599  			Identity:   s,
   600  			Node:       s,
   601  		}
   602  		m.embeddedCSIDriver = mockdriver.NewCSIDriver(servers)
   603  		l, err := proxy.Listen(ctx, f.ClientSet, f.ClientConfig(),
   604  			proxy.Addr{
   605  				Namespace:     m.driverNamespace.Name,
   606  				PodName:       podname,
   607  				ContainerName: containername,
   608  				Port:          9000,
   609  			},
   610  		)
   611  
   612  		framework.ExpectNoError(err, "start connecting to proxy pod")
   613  		err = m.embeddedCSIDriver.Start(l, m.interceptGRPC)
   614  		framework.ExpectNoError(err, "start mock driver")
   615  
   616  		embeddedCleanup = func() {
   617  			// Kill all goroutines and delete resources of the mock driver.
   618  			m.embeddedCSIDriver.Stop()
   619  			l.Close()
   620  			cancel()
   621  		}
   622  	} else {
   623  		// When using the mock driver inside the cluster it has to be reconfigured
   624  		// via command line parameters.
   625  		containerArgs = append(containerArgs, "--drivername=csi-mock-"+f.UniqueName)
   626  
   627  		if m.attachable {
   628  			containerArgs = append(containerArgs, "--enable-attach")
   629  		}
   630  
   631  		if m.enableTopology {
   632  			containerArgs = append(containerArgs, "--enable-topology")
   633  		}
   634  
   635  		if m.attachLimit > 0 {
   636  			containerArgs = append(containerArgs, "--attach-limit", strconv.Itoa(m.attachLimit))
   637  		}
   638  
   639  		if m.enableNodeExpansion {
   640  			containerArgs = append(containerArgs, "--node-expand-required=true")
   641  		}
   642  	}
   643  
   644  	config := &storageframework.PerTestConfig{
   645  		Driver:              m,
   646  		Prefix:              "mock",
   647  		Framework:           f,
   648  		ClientNodeSelection: e2epod.NodeSelection{Name: node.Name},
   649  		DriverNamespace:     m.driverNamespace,
   650  	}
   651  
   652  	o := utils.PatchCSIOptions{
   653  		OldDriverName:            "csi-mock",
   654  		NewDriverName:            "csi-mock-" + f.UniqueName,
   655  		DriverContainerName:      "mock",
   656  		DriverContainerArguments: containerArgs,
   657  		ProvisionerContainerName: "csi-provisioner",
   658  		NodeName:                 node.Name,
   659  		PodInfo:                  m.podInfo,
   660  		StorageCapacity:          m.storageCapacity,
   661  		CanAttach:                &m.attachable,
   662  		VolumeLifecycleModes: &[]storagev1.VolumeLifecycleMode{
   663  			storagev1.VolumeLifecyclePersistent,
   664  			storagev1.VolumeLifecycleEphemeral,
   665  		},
   666  		TokenRequests:     m.tokenRequests,
   667  		RequiresRepublish: m.requiresRepublish,
   668  		FSGroupPolicy:     m.fsGroupPolicy,
   669  		SELinuxMount:      m.enableSELinuxMount,
   670  		Features:          map[string][]string{},
   671  	}
   672  
   673  	if m.enableRecoverExpansionFailure {
   674  		o.Features["csi-resizer"] = []string{"RecoverVolumeExpansionFailure=true"}
   675  	}
   676  	err = utils.CreateFromManifests(ctx, f, m.driverNamespace, func(item interface{}) error {
   677  		if err := utils.PatchCSIDeployment(config.Framework, o, item); err != nil {
   678  			return err
   679  		}
   680  
   681  		switch item := item.(type) {
   682  		case *rbacv1.ClusterRole:
   683  			if strings.HasPrefix(item.Name, "external-snapshotter-runner") {
   684  				// Re-enable access to secrets for the snapshotter sidecar for
   685  				// https://github.com/kubernetes/kubernetes/blob/6ede5ca95f78478fa627ecfea8136e0dff34436b/test/e2e/storage/csi_mock_volume.go#L1539-L1548
   686  				// It was disabled in https://github.com/kubernetes-csi/external-snapshotter/blob/501cc505846c03ee665355132f2da0ce7d5d747d/deploy/kubernetes/csi-snapshotter/rbac-csi-snapshotter.yaml#L26-L32
   687  				item.Rules = append(item.Rules, rbacv1.PolicyRule{
   688  					APIGroups: []string{""},
   689  					Resources: []string{"secrets"},
   690  					Verbs:     []string{"get", "list"},
   691  				})
   692  			}
   693  		}
   694  
   695  		return nil
   696  	}, m.manifests...)
   697  
   698  	if err != nil {
   699  		framework.Failf("deploying csi mock driver: %v", err)
   700  	}
   701  
   702  	driverCleanupFunc := generateDriverCleanupFunc(
   703  		f,
   704  		"mock",
   705  		testns,
   706  		driverns,
   707  		cancelLogging)
   708  
   709  	ginkgo.DeferCleanup(func(ctx context.Context) {
   710  		embeddedCleanup()
   711  		driverCleanupFunc(ctx)
   712  	})
   713  
   714  	return config
   715  }
   716  
   717  func (m *mockCSIDriver) interceptGRPC(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
   718  	defer func() {
   719  		// Always log the call and its final result,
   720  		// regardless whether the result was from the real
   721  		// implementation or a hook.
   722  		m.calls.LogGRPC(info.FullMethod, req, resp, err)
   723  	}()
   724  
   725  	if m.hooks.Pre != nil {
   726  		resp, err = m.hooks.Pre(ctx, info.FullMethod, req)
   727  		if resp != nil || err != nil {
   728  			return
   729  		}
   730  	}
   731  	resp, err = handler(ctx, req)
   732  	if m.hooks.Post != nil {
   733  		resp, err = m.hooks.Post(ctx, info.FullMethod, req, resp, err)
   734  	}
   735  	return
   736  }
   737  
   738  func (m *mockCSIDriver) GetCalls(ctx context.Context) ([]MockCSICall, error) {
   739  	if m.embedded {
   740  		return m.calls.Get(), nil
   741  	}
   742  
   743  	if m.driverNamespace == nil {
   744  		return nil, errors.New("PrepareTest not called yet")
   745  	}
   746  
   747  	// Name of CSI driver pod name (it's in a StatefulSet with a stable name)
   748  	driverPodName := "csi-mockplugin-0"
   749  	// Name of CSI driver container name
   750  	driverContainerName := "mock"
   751  
   752  	// Load logs of driver pod
   753  	log, err := e2epod.GetPodLogs(ctx, m.clientSet, m.driverNamespace.Name, driverPodName, driverContainerName)
   754  	if err != nil {
   755  		return nil, fmt.Errorf("could not load CSI driver logs: %w", err)
   756  	}
   757  
   758  	logLines := strings.Split(log, "\n")
   759  	var calls []MockCSICall
   760  	for _, line := range logLines {
   761  		index := strings.Index(line, grpcCallPrefix)
   762  		if index == -1 {
   763  			continue
   764  		}
   765  		line = line[index+len(grpcCallPrefix):]
   766  		call := MockCSICall{
   767  			json: string(line),
   768  		}
   769  		err := json.Unmarshal([]byte(line), &call)
   770  		if err != nil {
   771  			framework.Logf("Could not parse CSI driver log line %q: %s", line, err)
   772  			continue
   773  		}
   774  
   775  		// Trim gRPC service name, i.e. "/csi.v1.Identity/Probe" -> "Probe"
   776  		methodParts := strings.Split(call.Method, "/")
   777  		call.Method = methodParts[len(methodParts)-1]
   778  
   779  		calls = append(calls, call)
   780  	}
   781  	return calls, nil
   782  }
   783  
   784  // gce-pd
   785  type gcePDCSIDriver struct {
   786  	driverInfo storageframework.DriverInfo
   787  }
   788  
   789  var _ storageframework.TestDriver = &gcePDCSIDriver{}
   790  var _ storageframework.DynamicPVTestDriver = &gcePDCSIDriver{}
   791  var _ storageframework.SnapshottableTestDriver = &gcePDCSIDriver{}
   792  
   793  // InitGcePDCSIDriver returns gcePDCSIDriver that implements TestDriver interface
   794  func InitGcePDCSIDriver() storageframework.TestDriver {
   795  	return &gcePDCSIDriver{
   796  		driverInfo: storageframework.DriverInfo{
   797  			Name:        GCEPDCSIDriverName,
   798  			TestTags:    []interface{}{framework.WithSerial()},
   799  			MaxFileSize: storageframework.FileSizeMedium,
   800  			SupportedSizeRange: e2evolume.SizeRange{
   801  				Min: "5Gi",
   802  			},
   803  			SupportedFsType: sets.NewString(
   804  				"", // Default fsType
   805  				"ext2",
   806  				"ext3",
   807  				"ext4",
   808  				"xfs",
   809  			),
   810  			SupportedMountOption: sets.NewString("debug", "nouid32"),
   811  			Capabilities: map[storageframework.Capability]bool{
   812  				storageframework.CapPersistence: true,
   813  				storageframework.CapBlock:       true,
   814  				storageframework.CapFsGroup:     true,
   815  				storageframework.CapExec:        true,
   816  				storageframework.CapMultiPODs:   true,
   817  				// GCE supports volume limits, but the test creates large
   818  				// number of volumes and times out test suites.
   819  				storageframework.CapVolumeLimits:                   false,
   820  				storageframework.CapTopology:                       true,
   821  				storageframework.CapControllerExpansion:            true,
   822  				storageframework.CapOfflineExpansion:               true,
   823  				storageframework.CapOnlineExpansion:                true,
   824  				storageframework.CapNodeExpansion:                  true,
   825  				storageframework.CapSnapshotDataSource:             true,
   826  				storageframework.CapReadWriteOncePod:               true,
   827  				storageframework.CapMultiplePVsSameID:              true,
   828  				storageframework.CapFSResizeFromSourceNotSupported: true, //TODO: remove when CI tests use the fixed driver with: https://github.com/kubernetes-sigs/gcp-compute-persistent-disk-csi-driver/pull/972
   829  			},
   830  			RequiredAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
   831  			TopologyKeys:        []string{GCEPDCSIZoneTopologyKey},
   832  			StressTestOptions: &storageframework.StressTestOptions{
   833  				NumPods:     10,
   834  				NumRestarts: 10,
   835  			},
   836  			VolumeSnapshotStressTestOptions: &storageframework.VolumeSnapshotStressTestOptions{
   837  				// GCE only allows for one snapshot per volume to be created at a time,
   838  				// which can cause test timeouts. We reduce the likelihood of test timeouts
   839  				// by increasing the number of pods (and volumes) and reducing the number
   840  				// of snapshots per volume.
   841  				NumPods:      20,
   842  				NumSnapshots: 2,
   843  			},
   844  		},
   845  	}
   846  }
   847  
   848  func (g *gcePDCSIDriver) GetDriverInfo() *storageframework.DriverInfo {
   849  	return &g.driverInfo
   850  }
   851  
   852  func (g *gcePDCSIDriver) SkipUnsupportedTest(pattern storageframework.TestPattern) {
   853  	e2eskipper.SkipUnlessProviderIs("gce", "gke")
   854  	if pattern.FsType == "xfs" {
   855  		e2eskipper.SkipUnlessNodeOSDistroIs("ubuntu", "custom")
   856  	}
   857  	for _, tag := range pattern.TestTags {
   858  		if framework.TagsEqual(tag, feature.Windows) {
   859  			e2eskipper.Skipf("Skipping tests for windows since CSI does not support it yet")
   860  		}
   861  	}
   862  }
   863  
   864  func (g *gcePDCSIDriver) GetDynamicProvisionStorageClass(ctx context.Context, config *storageframework.PerTestConfig, fsType string) *storagev1.StorageClass {
   865  	ns := config.Framework.Namespace.Name
   866  	provisioner := g.driverInfo.Name
   867  
   868  	parameters := map[string]string{"type": "pd-standard"}
   869  	if fsType != "" {
   870  		parameters["csi.storage.k8s.io/fstype"] = fsType
   871  	}
   872  	delayedBinding := storagev1.VolumeBindingWaitForFirstConsumer
   873  
   874  	return storageframework.GetStorageClass(provisioner, parameters, &delayedBinding, ns)
   875  }
   876  
   877  func (g *gcePDCSIDriver) GetSnapshotClass(ctx context.Context, config *storageframework.PerTestConfig, parameters map[string]string) *unstructured.Unstructured {
   878  	snapshotter := g.driverInfo.Name
   879  	ns := config.Framework.Namespace.Name
   880  
   881  	return utils.GenerateSnapshotClassSpec(snapshotter, parameters, ns)
   882  }
   883  
   884  func (g *gcePDCSIDriver) PrepareTest(ctx context.Context, f *framework.Framework) *storageframework.PerTestConfig {
   885  	testns := f.Namespace.Name
   886  	cfg := &storageframework.PerTestConfig{
   887  		Driver:    g,
   888  		Prefix:    "gcepd",
   889  		Framework: f,
   890  	}
   891  
   892  	if framework.ProviderIs("gke") {
   893  		framework.Logf("The csi gce-pd driver is automatically installed in GKE. Skipping driver installation.")
   894  		return cfg
   895  	}
   896  
   897  	ginkgo.By("deploying csi gce-pd driver")
   898  	// Create secondary namespace which will be used for creating driver
   899  	driverNamespace := utils.CreateDriverNamespace(ctx, f)
   900  	driverns := driverNamespace.Name
   901  
   902  	cancelLogging := utils.StartPodLogs(ctx, f, driverNamespace)
   903  	// It would be safer to rename the gcePD driver, but that
   904  	// hasn't been done before either and attempts to do so now led to
   905  	// errors during driver registration, therefore it is disabled
   906  	// by passing a nil function below.
   907  	//
   908  	// These are the options which would have to be used:
   909  	// o := utils.PatchCSIOptions{
   910  	// 	OldDriverName:            g.driverInfo.Name,
   911  	// 	NewDriverName:            storageframework.GetUniqueDriverName(g),
   912  	// 	DriverContainerName:      "gce-driver",
   913  	// 	ProvisionerContainerName: "csi-external-provisioner",
   914  	// }
   915  	createGCESecrets(f.ClientSet, driverns)
   916  
   917  	manifests := []string{
   918  		"test/e2e/testing-manifests/storage-csi/external-attacher/rbac.yaml",
   919  		"test/e2e/testing-manifests/storage-csi/external-provisioner/rbac.yaml",
   920  		"test/e2e/testing-manifests/storage-csi/gce-pd/csi-controller-rbac.yaml",
   921  		"test/e2e/testing-manifests/storage-csi/gce-pd/node_ds.yaml",
   922  		"test/e2e/testing-manifests/storage-csi/gce-pd/controller_ss.yaml",
   923  	}
   924  
   925  	err := utils.CreateFromManifests(ctx, f, driverNamespace, nil, manifests...)
   926  	if err != nil {
   927  		framework.Failf("deploying csi gce-pd driver: %v", err)
   928  	}
   929  
   930  	if err = WaitForCSIDriverRegistrationOnAllNodes(ctx, GCEPDCSIDriverName, f.ClientSet); err != nil {
   931  		framework.Failf("waiting for csi driver node registration on: %v", err)
   932  	}
   933  
   934  	cleanupFunc := generateDriverCleanupFunc(
   935  		f,
   936  		"gce-pd",
   937  		testns,
   938  		driverns,
   939  		cancelLogging)
   940  	ginkgo.DeferCleanup(cleanupFunc)
   941  
   942  	return &storageframework.PerTestConfig{
   943  		Driver:          g,
   944  		Prefix:          "gcepd",
   945  		Framework:       f,
   946  		DriverNamespace: driverNamespace,
   947  	}
   948  }
   949  
   950  // WaitForCSIDriverRegistrationOnAllNodes waits for the CSINode object to be updated
   951  // with the given driver on all schedulable nodes.
   952  func WaitForCSIDriverRegistrationOnAllNodes(ctx context.Context, driverName string, cs clientset.Interface) error {
   953  	nodes, err := e2enode.GetReadySchedulableNodes(ctx, cs)
   954  	if err != nil {
   955  		return err
   956  	}
   957  	for _, node := range nodes.Items {
   958  		if err := WaitForCSIDriverRegistrationOnNode(ctx, node.Name, driverName, cs); err != nil {
   959  			return err
   960  		}
   961  	}
   962  	return nil
   963  }
   964  
   965  // WaitForCSIDriverRegistrationOnNode waits for the CSINode object generated by the node-registrar on a certain node
   966  func WaitForCSIDriverRegistrationOnNode(ctx context.Context, nodeName string, driverName string, cs clientset.Interface) error {
   967  	framework.Logf("waiting for CSIDriver %v to register on node %v", driverName, nodeName)
   968  
   969  	// About 8.6 minutes timeout
   970  	backoff := wait.Backoff{
   971  		Duration: 2 * time.Second,
   972  		Factor:   1.5,
   973  		Steps:    12,
   974  	}
   975  
   976  	waitErr := wait.ExponentialBackoff(backoff, func() (bool, error) {
   977  		csiNode, err := cs.StorageV1().CSINodes().Get(ctx, nodeName, metav1.GetOptions{})
   978  		if err != nil && !apierrors.IsNotFound(err) {
   979  			return false, err
   980  		}
   981  		for _, driver := range csiNode.Spec.Drivers {
   982  			if driver.Name == driverName {
   983  				return true, nil
   984  			}
   985  		}
   986  		return false, nil
   987  	})
   988  	if waitErr != nil {
   989  		return fmt.Errorf("error waiting for CSI driver %s registration on node %s: %v", driverName, nodeName, waitErr)
   990  	}
   991  	return nil
   992  }
   993  
   994  func tryFunc(f func()) error {
   995  	var err error
   996  	if f == nil {
   997  		return nil
   998  	}
   999  	defer func() {
  1000  		if recoverError := recover(); recoverError != nil {
  1001  			err = fmt.Errorf("%v", recoverError)
  1002  		}
  1003  	}()
  1004  	f()
  1005  	return err
  1006  }
  1007  
  1008  func generateDriverCleanupFunc(
  1009  	f *framework.Framework,
  1010  	driverName, testns, driverns string,
  1011  	cancelLogging func()) func(ctx context.Context) {
  1012  
  1013  	// Cleanup CSI driver and namespaces. This function needs to be idempotent and can be
  1014  	// concurrently called from defer (or AfterEach) and AfterSuite action hooks.
  1015  	cleanupFunc := func(ctx context.Context) {
  1016  		ginkgo.By(fmt.Sprintf("deleting the test namespace: %s", testns))
  1017  		// Delete the primary namespace but it's okay to fail here because this namespace will
  1018  		// also be deleted by framework.Aftereach hook
  1019  		_ = tryFunc(func() { f.DeleteNamespace(ctx, testns) })
  1020  
  1021  		ginkgo.By(fmt.Sprintf("uninstalling csi %s driver", driverName))
  1022  		_ = tryFunc(cancelLogging)
  1023  
  1024  		ginkgo.By(fmt.Sprintf("deleting the driver namespace: %s", driverns))
  1025  		_ = tryFunc(func() { f.DeleteNamespace(ctx, driverns) })
  1026  	}
  1027  
  1028  	return cleanupFunc
  1029  }