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