...

Source file src/k8s.io/kubernetes/pkg/volume/csi/csi_plugin_test.go

Documentation: k8s.io/kubernetes/pkg/volume/csi

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package csi
    18  
    19  import (
    20  	"fmt"
    21  	"math/rand"
    22  	"os"
    23  	"path/filepath"
    24  	"testing"
    25  	"time"
    26  
    27  	api "k8s.io/api/core/v1"
    28  	storage "k8s.io/api/storage/v1"
    29  	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/types"
    31  	utilversion "k8s.io/apimachinery/pkg/util/version"
    32  	"k8s.io/apimachinery/pkg/util/wait"
    33  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    34  	"k8s.io/client-go/informers"
    35  	fakeclient "k8s.io/client-go/kubernetes/fake"
    36  	utiltesting "k8s.io/client-go/util/testing"
    37  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    38  	"k8s.io/kubernetes/pkg/features"
    39  	"k8s.io/kubernetes/pkg/volume"
    40  	volumetest "k8s.io/kubernetes/pkg/volume/testing"
    41  )
    42  
    43  const (
    44  	volumeHostType int = iota
    45  	kubeletVolumeHostType
    46  	attachDetachVolumeHostType
    47  )
    48  
    49  func newTestPlugin(t *testing.T, client *fakeclient.Clientset) (*csiPlugin, string) {
    50  	return newTestPluginWithVolumeHost(t, client, kubeletVolumeHostType)
    51  }
    52  
    53  func newTestPluginWithAttachDetachVolumeHost(t *testing.T, client *fakeclient.Clientset) (*csiPlugin, string) {
    54  	return newTestPluginWithVolumeHost(t, client, attachDetachVolumeHostType)
    55  }
    56  
    57  // create a plugin mgr to load plugins and setup a fake client
    58  func newTestPluginWithVolumeHost(t *testing.T, client *fakeclient.Clientset, hostType int) (*csiPlugin, string) {
    59  	tmpDir, err := utiltesting.MkTmpdir("csi-test")
    60  	if err != nil {
    61  		t.Fatalf("can't create temp dir: %v", err)
    62  	}
    63  
    64  	if client == nil {
    65  		client = fakeclient.NewSimpleClientset()
    66  	}
    67  
    68  	client.Tracker().Add(&api.Node{
    69  		ObjectMeta: meta.ObjectMeta{
    70  			Name: "fakeNode",
    71  		},
    72  		Spec: api.NodeSpec{},
    73  	})
    74  
    75  	// Start informer for CSIDrivers.
    76  	factory := informers.NewSharedInformerFactory(client, CsiResyncPeriod)
    77  	csiDriverInformer := factory.Storage().V1().CSIDrivers()
    78  	csiDriverLister := csiDriverInformer.Lister()
    79  	volumeAttachmentInformer := factory.Storage().V1().VolumeAttachments()
    80  	volumeAttachmentLister := volumeAttachmentInformer.Lister()
    81  
    82  	factory.Start(wait.NeverStop)
    83  	syncedTypes := factory.WaitForCacheSync(wait.NeverStop)
    84  	if len(syncedTypes) != 2 {
    85  		t.Fatalf("informers are not synced")
    86  	}
    87  	for ty, ok := range syncedTypes {
    88  		if !ok {
    89  			t.Fatalf("failed to sync: %#v", ty)
    90  		}
    91  	}
    92  
    93  	var host volume.VolumeHost
    94  	switch hostType {
    95  	case volumeHostType:
    96  		host = volumetest.NewFakeVolumeHostWithCSINodeName(t,
    97  			tmpDir,
    98  			client,
    99  			ProbeVolumePlugins(),
   100  			"fakeNode",
   101  			csiDriverLister,
   102  			nil,
   103  		)
   104  	case kubeletVolumeHostType:
   105  		host = volumetest.NewFakeKubeletVolumeHostWithCSINodeName(t,
   106  			tmpDir,
   107  			client,
   108  			ProbeVolumePlugins(),
   109  			"fakeNode",
   110  			csiDriverLister,
   111  			volumeAttachmentLister,
   112  		)
   113  	case attachDetachVolumeHostType:
   114  		host = volumetest.NewFakeAttachDetachVolumeHostWithCSINodeName(t,
   115  			tmpDir,
   116  			client,
   117  			ProbeVolumePlugins(),
   118  			"fakeNode",
   119  			csiDriverLister,
   120  			volumeAttachmentLister,
   121  		)
   122  	default:
   123  		t.Fatalf("Unsupported volume host type")
   124  	}
   125  
   126  	fakeHost, ok := host.(volumetest.FakeVolumeHost)
   127  	if !ok {
   128  		t.Fatalf("Unsupported volume host type")
   129  	}
   130  
   131  	pluginMgr := fakeHost.GetPluginMgr()
   132  	plug, err := pluginMgr.FindPluginByName(CSIPluginName)
   133  	if err != nil {
   134  		t.Fatalf("can't find plugin %v", CSIPluginName)
   135  	}
   136  
   137  	csiPlug, ok := plug.(*csiPlugin)
   138  	if !ok {
   139  		t.Fatalf("cannot assert plugin to be type csiPlugin")
   140  	}
   141  
   142  	return csiPlug, tmpDir
   143  }
   144  
   145  func registerFakePlugin(pluginName, endpoint string, versions []string, t *testing.T) {
   146  	highestSupportedVersions, err := utilversion.HighestSupportedVersion(versions)
   147  	if err != nil {
   148  		t.Fatalf("unexpected error parsing versions (%v) for pluginName %q endpoint %q: %#v", versions, pluginName, endpoint, err)
   149  	}
   150  
   151  	csiDrivers.Clear()
   152  	csiDrivers.Set(pluginName, Driver{
   153  		endpoint:                endpoint,
   154  		highestSupportedVersion: highestSupportedVersions,
   155  	})
   156  }
   157  
   158  func TestPluginGetPluginName(t *testing.T) {
   159  	plug, tmpDir := newTestPlugin(t, nil)
   160  	defer os.RemoveAll(tmpDir)
   161  	if plug.GetPluginName() != "kubernetes.io/csi" {
   162  		t.Errorf("unexpected plugin name %v", plug.GetPluginName())
   163  	}
   164  }
   165  
   166  func TestPluginGetVolumeName(t *testing.T) {
   167  	plug, tmpDir := newTestPlugin(t, nil)
   168  	defer os.RemoveAll(tmpDir)
   169  	testCases := []struct {
   170  		name       string
   171  		driverName string
   172  		volName    string
   173  		spec       *volume.Spec
   174  		shouldFail bool
   175  	}{
   176  		{
   177  			name:       "alphanum names",
   178  			driverName: "testdr",
   179  			volName:    "testvol",
   180  			spec:       volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 10, "testdr", "testvol"), false),
   181  		},
   182  		{
   183  			name:       "mixchar driver",
   184  			driverName: "test.dr.cc",
   185  			volName:    "testvol",
   186  			spec:       volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 10, "test.dr.cc", "testvol"), false),
   187  		},
   188  		{
   189  			name:       "mixchar volume",
   190  			driverName: "testdr",
   191  			volName:    "test-vol-name",
   192  			spec:       volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 10, "testdr", "test-vol-name"), false),
   193  		},
   194  		{
   195  			name:       "mixchars all",
   196  			driverName: "test-driver",
   197  			volName:    "test.vol.name",
   198  			spec:       volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 10, "test-driver", "test.vol.name"), false),
   199  		},
   200  		{
   201  			name:       "volume source with mixchars all",
   202  			driverName: "test-driver",
   203  			volName:    "test.vol.name",
   204  			spec:       volume.NewSpecFromVolume(makeTestVol("test-pv", "test-driver")),
   205  			shouldFail: true, // csi inline feature off
   206  		},
   207  		{
   208  			name:       "missing spec",
   209  			shouldFail: true,
   210  		},
   211  	}
   212  
   213  	for _, tc := range testCases {
   214  		t.Logf("testing: %s", tc.name)
   215  		registerFakePlugin(tc.driverName, "endpoint", []string{"1.3.0"}, t)
   216  		name, err := plug.GetVolumeName(tc.spec)
   217  		if tc.shouldFail != (err != nil) {
   218  			t.Fatal("shouldFail does match expected error")
   219  		}
   220  		if tc.shouldFail && err != nil {
   221  			t.Log(err)
   222  			continue
   223  		}
   224  		if name != fmt.Sprintf("%s%s%s", tc.driverName, volNameSep, tc.volName) {
   225  			t.Errorf("unexpected volume name %s", name)
   226  		}
   227  	}
   228  }
   229  
   230  func TestPluginGetVolumeNameWithInline(t *testing.T) {
   231  	modes := []storage.VolumeLifecycleMode{
   232  		storage.VolumeLifecyclePersistent,
   233  	}
   234  	driver := getTestCSIDriver(testDriver, nil, nil, modes)
   235  	client := fakeclient.NewSimpleClientset(driver)
   236  	plug, tmpDir := newTestPlugin(t, client)
   237  	defer os.RemoveAll(tmpDir)
   238  	testCases := []struct {
   239  		name       string
   240  		driverName string
   241  		volName    string
   242  		shouldFail bool
   243  		spec       *volume.Spec
   244  	}{
   245  		{
   246  			name:       "missing spec",
   247  			shouldFail: true,
   248  		},
   249  		{
   250  			name:       "alphanum names for pv",
   251  			driverName: "testdr",
   252  			volName:    "testvol",
   253  			spec:       volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 10, "testdr", "testvol"), false),
   254  		},
   255  		{
   256  			name:       "alphanum names for vol source",
   257  			driverName: "testdr",
   258  			volName:    "testvol",
   259  			spec:       volume.NewSpecFromVolume(makeTestVol("test-pv", "testdr")),
   260  			shouldFail: true,
   261  		},
   262  	}
   263  
   264  	for _, tc := range testCases {
   265  		t.Logf("testing: %s", tc.name)
   266  		registerFakePlugin(tc.driverName, "endpoint", []string{"1.3.0"}, t)
   267  		name, err := plug.GetVolumeName(tc.spec)
   268  		if tc.shouldFail != (err != nil) {
   269  			t.Fatal("shouldFail does match expected error")
   270  		}
   271  		if tc.shouldFail && err != nil {
   272  			t.Log(err)
   273  			continue
   274  		}
   275  		if name != fmt.Sprintf("%s%s%s", tc.driverName, volNameSep, tc.volName) {
   276  			t.Errorf("unexpected volume name %s", name)
   277  		}
   278  	}
   279  }
   280  
   281  func TestPluginCanSupport(t *testing.T) {
   282  	tests := []struct {
   283  		name       string
   284  		spec       *volume.Spec
   285  		canSupport bool
   286  	}{
   287  		{
   288  			name:       "no spec provided",
   289  			canSupport: false,
   290  		},
   291  		{
   292  			name:       "can support volume source",
   293  			spec:       volume.NewSpecFromVolume(makeTestVol("test-vol", testDriver)),
   294  			canSupport: true,
   295  		},
   296  		{
   297  			name:       "can support persistent volume source",
   298  			spec:       volume.NewSpecFromPersistentVolume(makeTestPV("test-pv", 20, testDriver, testVol), true),
   299  			canSupport: true,
   300  		},
   301  	}
   302  
   303  	plug, tmpDir := newTestPlugin(t, nil)
   304  	defer os.RemoveAll(tmpDir)
   305  	registerFakePlugin(testDriver, "endpoint", []string{"1.0.0"}, t)
   306  
   307  	for _, tc := range tests {
   308  		t.Run(tc.name, func(t *testing.T) {
   309  
   310  			actual := plug.CanSupport(tc.spec)
   311  			if tc.canSupport != actual {
   312  				t.Errorf("expecting canSupport %t, got %t", tc.canSupport, actual)
   313  			}
   314  		})
   315  	}
   316  }
   317  
   318  func TestPluginConstructVolumeSpec(t *testing.T) {
   319  	plug, tmpDir := newTestPlugin(t, nil)
   320  	defer os.RemoveAll(tmpDir)
   321  
   322  	testCases := []struct {
   323  		name                      string
   324  		seLinuxMountEnabled       bool
   325  		originSpec                *volume.Spec
   326  		originSELinuxMountContext string
   327  		specVolID                 string
   328  		volHandle                 string
   329  		expectedSELinuxContext    string
   330  		podUID                    types.UID
   331  	}{
   332  		{
   333  			name:      "construct spec1 from original persistent spec",
   334  			specVolID: "test.vol.id",
   335  			volHandle: "testvol-handle1",
   336  
   337  			originSpec: volume.NewSpecFromPersistentVolume(makeTestPV("test.vol.id", 20, testDriver, "testvol-handle1"), true),
   338  			podUID:     types.UID(fmt.Sprintf("%08X", rand.Uint64())),
   339  		},
   340  		{
   341  			name:       "construct spec2 from original persistent spec",
   342  			specVolID:  "spec2",
   343  			volHandle:  "handle2",
   344  			originSpec: volume.NewSpecFromPersistentVolume(makeTestPV("spec2", 20, testDriver, "handle2"), true),
   345  			podUID:     types.UID(fmt.Sprintf("%08X", rand.Uint64())),
   346  		},
   347  		{
   348  			name:                      "construct SELinux context from original persistent spec when the feature is enabled",
   349  			seLinuxMountEnabled:       true,
   350  			specVolID:                 "spec3",
   351  			volHandle:                 "handle3",
   352  			originSELinuxMountContext: "system_u:object_r:container_file_t:s0:c314,c894",
   353  			originSpec:                volume.NewSpecFromPersistentVolume(makeTestPV("spec3", 20, testDriver, "handle3"), true),
   354  			expectedSELinuxContext:    "system_u:object_r:container_file_t:s0:c314,c894",
   355  			podUID:                    types.UID(fmt.Sprintf("%08X", rand.Uint64())),
   356  		},
   357  		{
   358  			name:                      "construct empty SELinux from original persistent spec when the feature is disabled",
   359  			seLinuxMountEnabled:       false,
   360  			specVolID:                 "spec4",
   361  			volHandle:                 "handle4",
   362  			originSELinuxMountContext: "system_u:object_r:container_file_t:s0:c314,c894",
   363  			originSpec:                volume.NewSpecFromPersistentVolume(makeTestPV("spec4", 20, testDriver, "handle4"), true),
   364  			expectedSELinuxContext:    "", // The context is cleared when the feature gate is off
   365  			podUID:                    types.UID(fmt.Sprintf("%08X", rand.Uint64())),
   366  		},
   367  	}
   368  
   369  	registerFakePlugin(testDriver, "endpoint", []string{"1.0.0"}, t)
   370  
   371  	for _, tc := range testCases {
   372  		t.Run(tc.name, func(t *testing.T) {
   373  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, tc.seLinuxMountEnabled)()
   374  
   375  			mounter, err := plug.NewMounter(
   376  				tc.originSpec,
   377  				&api.Pod{ObjectMeta: meta.ObjectMeta{UID: tc.podUID, Namespace: testns}},
   378  				volume.VolumeOptions{},
   379  			)
   380  			if err != nil {
   381  				t.Fatal(err)
   382  			}
   383  			if mounter == nil {
   384  				t.Fatal("failed to create CSI mounter")
   385  			}
   386  			csiMounter := mounter.(*csiMountMgr)
   387  
   388  			mountPath := filepath.Dir(csiMounter.GetPath())
   389  			err = prepareVolumeInfoFile(mountPath, plug, tc.originSpec.Name(), csiMounter.volumeID, testDriver, string(csiMounter.volumeLifecycleMode), tc.originSELinuxMountContext)
   390  			if err != nil {
   391  				t.Fatalf("failed to save fake volume info file: %s", err)
   392  			}
   393  
   394  			// rebuild spec
   395  			rec, err := plug.ConstructVolumeSpec("test-pv", filepath.Dir(csiMounter.GetPath()))
   396  			if err != nil {
   397  				t.Fatal(err)
   398  			}
   399  			if rec.Spec == nil {
   400  				t.Fatal("nil volume.Spec constructed")
   401  			}
   402  
   403  			// inspect spec
   404  			if rec.Spec.PersistentVolume == nil || rec.Spec.PersistentVolume.Spec.CSI == nil {
   405  				t.Fatal("CSIPersistentVolume not found in constructed spec ")
   406  			}
   407  
   408  			volHandle := rec.Spec.PersistentVolume.Spec.CSI.VolumeHandle
   409  			if volHandle != tc.originSpec.PersistentVolume.Spec.CSI.VolumeHandle {
   410  				t.Error("unexpected volumeHandle constructed:", volHandle)
   411  			}
   412  			driverName := rec.Spec.PersistentVolume.Spec.CSI.Driver
   413  			if driverName != tc.originSpec.PersistentVolume.Spec.CSI.Driver {
   414  				t.Error("unexpected driverName constructed:", driverName)
   415  			}
   416  
   417  			if rec.Spec.PersistentVolume.Spec.VolumeMode == nil {
   418  				t.Fatalf("Volume mode has not been set.")
   419  			}
   420  
   421  			if *rec.Spec.PersistentVolume.Spec.VolumeMode != api.PersistentVolumeFilesystem {
   422  				t.Errorf("Unexpected volume mode %q", *rec.Spec.PersistentVolume.Spec.VolumeMode)
   423  			}
   424  
   425  			if rec.Spec.Name() != tc.specVolID {
   426  				t.Errorf("Unexpected spec name constructed %s", rec.Spec.Name())
   427  			}
   428  
   429  			if rec.SELinuxMountContext != tc.expectedSELinuxContext {
   430  				t.Errorf("Expected SELinux context %q, got %q", tc.expectedSELinuxContext, rec.SELinuxMountContext)
   431  			}
   432  		})
   433  	}
   434  }
   435  
   436  func TestPluginConstructVolumeSpecWithInline(t *testing.T) {
   437  	testCases := []struct {
   438  		name       string
   439  		originSpec *volume.Spec
   440  		specVolID  string
   441  		volHandle  string
   442  		podUID     types.UID
   443  		shouldFail bool
   444  		modes      []storage.VolumeLifecycleMode
   445  	}{
   446  		{
   447  			name:       "construct spec1 from persistent spec",
   448  			specVolID:  "test.vol.id",
   449  			volHandle:  "testvol-handle1",
   450  			originSpec: volume.NewSpecFromPersistentVolume(makeTestPV("test.vol.id", 20, testDriver, "testvol-handle1"), true),
   451  			podUID:     types.UID(fmt.Sprintf("%08X", rand.Uint64())),
   452  			modes:      []storage.VolumeLifecycleMode{storage.VolumeLifecyclePersistent},
   453  		},
   454  		{
   455  			name:       "construct spec2 from persistent spec",
   456  			specVolID:  "spec2",
   457  			volHandle:  "handle2",
   458  			originSpec: volume.NewSpecFromPersistentVolume(makeTestPV("spec2", 20, testDriver, "handle2"), true),
   459  			podUID:     types.UID(fmt.Sprintf("%08X", rand.Uint64())),
   460  			modes:      []storage.VolumeLifecycleMode{storage.VolumeLifecyclePersistent},
   461  		},
   462  		{
   463  			name:       "construct spec2 from persistent spec, missing mode",
   464  			specVolID:  "spec2",
   465  			volHandle:  "handle2",
   466  			originSpec: volume.NewSpecFromPersistentVolume(makeTestPV("spec2", 20, testDriver, "handle2"), true),
   467  			podUID:     types.UID(fmt.Sprintf("%08X", rand.Uint64())),
   468  			modes:      []storage.VolumeLifecycleMode{},
   469  			shouldFail: true,
   470  		},
   471  		{
   472  			name:       "construct spec from volume spec",
   473  			specVolID:  "volspec",
   474  			originSpec: volume.NewSpecFromVolume(makeTestVol("volspec", testDriver)),
   475  			podUID:     types.UID(fmt.Sprintf("%08X", rand.Uint64())),
   476  			modes:      []storage.VolumeLifecycleMode{storage.VolumeLifecycleEphemeral},
   477  		},
   478  		{
   479  			name:       "construct spec from volume spec2",
   480  			specVolID:  "volspec2",
   481  			originSpec: volume.NewSpecFromVolume(makeTestVol("volspec2", testDriver)),
   482  			podUID:     types.UID(fmt.Sprintf("%08X", rand.Uint64())),
   483  			modes:      []storage.VolumeLifecycleMode{storage.VolumeLifecycleEphemeral},
   484  		},
   485  		{
   486  			name:       "construct spec from volume spec2, missing mode",
   487  			specVolID:  "volspec2",
   488  			originSpec: volume.NewSpecFromVolume(makeTestVol("volspec2", testDriver)),
   489  			podUID:     types.UID(fmt.Sprintf("%08X", rand.Uint64())),
   490  			modes:      []storage.VolumeLifecycleMode{},
   491  			shouldFail: true,
   492  		},
   493  		{
   494  			name:       "missing spec",
   495  			podUID:     types.UID(fmt.Sprintf("%08X", rand.Uint64())),
   496  			shouldFail: true,
   497  		},
   498  	}
   499  
   500  	registerFakePlugin(testDriver, "endpoint", []string{"1.0.0"}, t)
   501  
   502  	for _, tc := range testCases {
   503  		t.Run(tc.name, func(t *testing.T) {
   504  			driver := getTestCSIDriver(testDriver, nil, nil, tc.modes)
   505  			client := fakeclient.NewSimpleClientset(driver)
   506  			plug, tmpDir := newTestPlugin(t, client)
   507  			defer os.RemoveAll(tmpDir)
   508  
   509  			mounter, err := plug.NewMounter(
   510  				tc.originSpec,
   511  				&api.Pod{ObjectMeta: meta.ObjectMeta{UID: tc.podUID, Namespace: testns}},
   512  				volume.VolumeOptions{},
   513  			)
   514  			if tc.shouldFail && err != nil {
   515  				t.Log(err)
   516  				return
   517  			}
   518  			if !tc.shouldFail && err != nil {
   519  				t.Fatal(err)
   520  			}
   521  			if mounter == nil {
   522  				t.Fatal("failed to create CSI mounter")
   523  			}
   524  			csiMounter := mounter.(*csiMountMgr)
   525  
   526  			mountPath := filepath.Dir(csiMounter.GetPath())
   527  			err = prepareVolumeInfoFile(mountPath, plug, tc.originSpec.Name(), csiMounter.volumeID, testDriver, string(csiMounter.volumeLifecycleMode), "")
   528  			if err != nil {
   529  				t.Fatalf("failed to save fake volume info file: %s", err)
   530  			}
   531  
   532  			// rebuild spec
   533  			rec, err := plug.ConstructVolumeSpec("test-pv", filepath.Dir(csiMounter.GetPath()))
   534  			if err != nil {
   535  				t.Fatal(err)
   536  			}
   537  			if rec.Spec == nil {
   538  				t.Fatal("nil volume.Spec constructed")
   539  			}
   540  
   541  			if rec.Spec.Name() != tc.specVolID {
   542  				t.Errorf("unexpected spec name constructed volume.Spec: %s", rec.Spec.Name())
   543  			}
   544  
   545  			switch {
   546  			case rec.Spec.Volume != nil:
   547  				if rec.Spec.Volume.CSI == nil {
   548  					t.Error("missing CSIVolumeSource in constructed volume.Spec")
   549  				}
   550  				if rec.Spec.Volume.CSI.Driver != tc.originSpec.Volume.CSI.Driver {
   551  					t.Error("unexpected driver in constructed volume source:", rec.Spec.Volume.CSI.Driver)
   552  				}
   553  
   554  			case rec.Spec.PersistentVolume != nil:
   555  				if rec.Spec.PersistentVolume.Spec.CSI == nil {
   556  					t.Fatal("missing CSIPersistentVolumeSource in constructed volume.spec")
   557  				}
   558  				volHandle := rec.Spec.PersistentVolume.Spec.CSI.VolumeHandle
   559  				if volHandle != tc.originSpec.PersistentVolume.Spec.CSI.VolumeHandle {
   560  					t.Error("unexpected volumeHandle constructed in persistent volume source:", volHandle)
   561  				}
   562  				driverName := rec.Spec.PersistentVolume.Spec.CSI.Driver
   563  				if driverName != tc.originSpec.PersistentVolume.Spec.CSI.Driver {
   564  					t.Error("unexpected driverName constructed in persistent volume source:", driverName)
   565  				}
   566  				if rec.Spec.PersistentVolume.Spec.VolumeMode == nil {
   567  					t.Fatalf("Volume mode has not been set.")
   568  				}
   569  				if *rec.Spec.PersistentVolume.Spec.VolumeMode != api.PersistentVolumeFilesystem {
   570  					t.Errorf("Unexpected volume mode %q", *rec.Spec.PersistentVolume.Spec.VolumeMode)
   571  				}
   572  			default:
   573  				t.Fatal("invalid volume.Spec constructed")
   574  			}
   575  
   576  		})
   577  	}
   578  }
   579  
   580  func TestPluginNewMounter(t *testing.T) {
   581  	tests := []struct {
   582  		name                string
   583  		spec                *volume.Spec
   584  		podUID              types.UID
   585  		namespace           string
   586  		volumeLifecycleMode storage.VolumeLifecycleMode
   587  		shouldFail          bool
   588  	}{
   589  		{
   590  			name:                "mounter from persistent volume source",
   591  			spec:                volume.NewSpecFromPersistentVolume(makeTestPV("test-pv1", 20, testDriver, testVol), true),
   592  			podUID:              types.UID(fmt.Sprintf("%08X", rand.Uint64())),
   593  			namespace:           "test-ns1",
   594  			volumeLifecycleMode: storage.VolumeLifecyclePersistent,
   595  		},
   596  		{
   597  			name:                "mounter from volume source",
   598  			spec:                volume.NewSpecFromVolume(makeTestVol("test-vol1", testDriver)),
   599  			podUID:              types.UID(fmt.Sprintf("%08X", rand.Uint64())),
   600  			namespace:           "test-ns2",
   601  			volumeLifecycleMode: storage.VolumeLifecycleEphemeral,
   602  			shouldFail:          false, // NewMounter works with disabled inline volumes
   603  		},
   604  		{
   605  			name:       "mounter from no spec provided",
   606  			shouldFail: true,
   607  		},
   608  	}
   609  
   610  	for _, test := range tests {
   611  		t.Run(test.name, func(t *testing.T) {
   612  			plug, tmpDir := newTestPlugin(t, nil)
   613  			defer os.RemoveAll(tmpDir)
   614  
   615  			registerFakePlugin(testDriver, "endpoint", []string{"1.2.0"}, t)
   616  			mounter, err := plug.NewMounter(
   617  				test.spec,
   618  				&api.Pod{ObjectMeta: meta.ObjectMeta{UID: test.podUID, Namespace: test.namespace}},
   619  				volume.VolumeOptions{},
   620  			)
   621  			if test.shouldFail != (err != nil) {
   622  				t.Fatal("Unexpected error:", err)
   623  			}
   624  			if test.shouldFail && err != nil {
   625  				t.Log(err)
   626  				return
   627  			}
   628  
   629  			if mounter == nil {
   630  				t.Fatal("failed to create CSI mounter")
   631  			}
   632  			csiMounter := mounter.(*csiMountMgr)
   633  
   634  			// validate mounter fields
   635  			if string(csiMounter.driverName) != testDriver {
   636  				t.Error("mounter driver name not set")
   637  			}
   638  			if csiMounter.volumeID == "" {
   639  				t.Error("mounter volume id not set")
   640  			}
   641  			if csiMounter.pod == nil {
   642  				t.Error("mounter pod not set")
   643  			}
   644  			if string(csiMounter.podUID) != string(test.podUID) {
   645  				t.Error("mounter podUID not set")
   646  			}
   647  			csiClient, err := csiMounter.csiClientGetter.Get()
   648  			if csiClient == nil {
   649  				t.Errorf("mounter csiClient is nil: %v", err)
   650  			}
   651  			if err != nil {
   652  				t.Fatal(err)
   653  			}
   654  			if csiMounter.volumeLifecycleMode != test.volumeLifecycleMode {
   655  				t.Error("unexpected driver mode:", csiMounter.volumeLifecycleMode)
   656  			}
   657  		})
   658  	}
   659  }
   660  
   661  func TestPluginNewMounterWithInline(t *testing.T) {
   662  	bothModes := []storage.VolumeLifecycleMode{
   663  		storage.VolumeLifecycleEphemeral,
   664  		storage.VolumeLifecyclePersistent,
   665  	}
   666  	persistentMode := []storage.VolumeLifecycleMode{
   667  		storage.VolumeLifecyclePersistent,
   668  	}
   669  	ephemeralMode := []storage.VolumeLifecycleMode{
   670  		storage.VolumeLifecycleEphemeral,
   671  	}
   672  	tests := []struct {
   673  		name                string
   674  		spec                *volume.Spec
   675  		podUID              types.UID
   676  		namespace           string
   677  		volumeLifecycleMode storage.VolumeLifecycleMode
   678  		shouldFail          bool
   679  	}{
   680  		{
   681  			name:       "mounter with missing spec",
   682  			shouldFail: true,
   683  		},
   684  		{
   685  			name: "mounter with spec with both volSrc and pvSrc",
   686  			spec: &volume.Spec{
   687  				Volume:           makeTestVol("test-vol1", testDriver),
   688  				PersistentVolume: makeTestPV("test-pv1", 20, testDriver, testVol),
   689  				ReadOnly:         true,
   690  			},
   691  			shouldFail: true,
   692  		},
   693  		{
   694  			name:                "mounter with persistent volume source",
   695  			spec:                volume.NewSpecFromPersistentVolume(makeTestPV("test-pv1", 20, testDriver, testVol), true),
   696  			podUID:              types.UID(fmt.Sprintf("%08X", rand.Uint64())),
   697  			namespace:           "test-ns1",
   698  			volumeLifecycleMode: storage.VolumeLifecyclePersistent,
   699  		},
   700  		{
   701  			name:                "mounter with volume source",
   702  			spec:                volume.NewSpecFromVolume(makeTestVol("test-vol1", testDriver)),
   703  			podUID:              types.UID(fmt.Sprintf("%08X", rand.Uint64())),
   704  			namespace:           "test-ns2",
   705  			volumeLifecycleMode: storage.VolumeLifecycleEphemeral,
   706  		},
   707  	}
   708  
   709  	runAll := func(t *testing.T, supported []storage.VolumeLifecycleMode) {
   710  		for _, test := range tests {
   711  			t.Run(test.name, func(t *testing.T) {
   712  				driver := getTestCSIDriver(testDriver, nil, nil, supported)
   713  				fakeClient := fakeclient.NewSimpleClientset(driver)
   714  				plug, tmpDir := newTestPlugin(t, fakeClient)
   715  				defer os.RemoveAll(tmpDir)
   716  
   717  				registerFakePlugin(testDriver, "endpoint", []string{"1.2.0"}, t)
   718  
   719  				mounter, err := plug.NewMounter(
   720  					test.spec,
   721  					&api.Pod{ObjectMeta: meta.ObjectMeta{UID: test.podUID, Namespace: test.namespace}},
   722  					volume.VolumeOptions{},
   723  				)
   724  
   725  				// Some test cases are meant to fail because their input data is broken.
   726  				shouldFail := test.shouldFail
   727  				if shouldFail != (err != nil) {
   728  					t.Fatal("Unexpected error:", err)
   729  				}
   730  				if shouldFail && err != nil {
   731  					t.Log(err)
   732  					return
   733  				}
   734  
   735  				if mounter == nil {
   736  					t.Fatal("failed to create CSI mounter")
   737  				}
   738  				csiMounter := mounter.(*csiMountMgr)
   739  
   740  				// validate mounter fields
   741  				if string(csiMounter.driverName) != testDriver {
   742  					t.Error("mounter driver name not set")
   743  				}
   744  				if csiMounter.volumeID == "" {
   745  					t.Error("mounter volume id not set")
   746  				}
   747  				if csiMounter.pod == nil {
   748  					t.Error("mounter pod not set")
   749  				}
   750  				if string(csiMounter.podUID) != string(test.podUID) {
   751  					t.Error("mounter podUID not set")
   752  				}
   753  				csiClient, err := csiMounter.csiClientGetter.Get()
   754  				if csiClient == nil {
   755  					t.Errorf("mounter csiClient is nil: %v", err)
   756  				}
   757  				if csiMounter.volumeLifecycleMode != test.volumeLifecycleMode {
   758  					t.Error("unexpected driver mode:", csiMounter.volumeLifecycleMode)
   759  				}
   760  			})
   761  		}
   762  	}
   763  
   764  	t.Run("both supported", func(t *testing.T) {
   765  		runAll(t, bothModes)
   766  	})
   767  	t.Run("persistent supported", func(t *testing.T) {
   768  		runAll(t, persistentMode)
   769  	})
   770  	t.Run("ephemeral supported", func(t *testing.T) {
   771  		runAll(t, ephemeralMode)
   772  	})
   773  }
   774  
   775  func TestPluginNewUnmounter(t *testing.T) {
   776  	plug, tmpDir := newTestPlugin(t, nil)
   777  	defer os.RemoveAll(tmpDir)
   778  
   779  	registerFakePlugin(testDriver, "endpoint", []string{"1.0.0"}, t)
   780  	pv := makeTestPV("test-pv", 10, testDriver, testVol)
   781  
   782  	// save the data file to re-create client
   783  	dir := filepath.Join(getTargetPath(testPodUID, pv.ObjectMeta.Name, plug.host), "/mount")
   784  	if err := os.MkdirAll(dir, 0755); err != nil && !os.IsNotExist(err) {
   785  		t.Errorf("failed to create dir [%s]: %v", dir, err)
   786  	}
   787  
   788  	if err := saveVolumeData(
   789  		filepath.Dir(dir),
   790  		volDataFileName,
   791  		map[string]string{
   792  			volDataKey.specVolID:  pv.ObjectMeta.Name,
   793  			volDataKey.driverName: testDriver,
   794  			volDataKey.volHandle:  testVol,
   795  		},
   796  	); err != nil {
   797  		t.Fatalf("failed to save volume data: %v", err)
   798  	}
   799  
   800  	// test unmounter
   801  	unmounter, err := plug.NewUnmounter(pv.ObjectMeta.Name, testPodUID)
   802  	csiUnmounter := unmounter.(*csiMountMgr)
   803  
   804  	if err != nil {
   805  		t.Fatalf("Failed to make a new Unmounter: %v", err)
   806  	}
   807  
   808  	if csiUnmounter == nil {
   809  		t.Fatal("failed to create CSI Unmounter")
   810  	}
   811  
   812  	if csiUnmounter.podUID != testPodUID {
   813  		t.Error("podUID not set")
   814  	}
   815  
   816  	csiClient, err := csiUnmounter.csiClientGetter.Get()
   817  	if csiClient == nil {
   818  		t.Errorf("mounter csiClient is nil: %v", err)
   819  	}
   820  }
   821  
   822  func TestPluginNewAttacher(t *testing.T) {
   823  	plug, tmpDir := newTestPlugin(t, nil)
   824  	defer os.RemoveAll(tmpDir)
   825  
   826  	attacher, err := plug.NewAttacher()
   827  	if err != nil {
   828  		t.Fatalf("failed to create new attacher: %v", err)
   829  	}
   830  
   831  	csiAttacher := getCsiAttacherFromVolumeAttacher(attacher, testWatchTimeout)
   832  	if csiAttacher.plugin == nil {
   833  		t.Error("plugin not set for attacher")
   834  	}
   835  	if csiAttacher.k8s == nil {
   836  		t.Error("Kubernetes client not set for attacher")
   837  	}
   838  	if csiAttacher.watchTimeout == time.Duration(0) {
   839  		t.Error("watch timeout not set for attacher")
   840  	}
   841  }
   842  
   843  func TestPluginNewDetacher(t *testing.T) {
   844  	plug, tmpDir := newTestPlugin(t, nil)
   845  	defer os.RemoveAll(tmpDir)
   846  
   847  	detacher, err := plug.NewDetacher()
   848  	if err != nil {
   849  		t.Fatalf("failed to create new detacher: %v", err)
   850  	}
   851  
   852  	csiDetacher := getCsiAttacherFromVolumeDetacher(detacher, testWatchTimeout)
   853  	if csiDetacher.plugin == nil {
   854  		t.Error("plugin not set for detacher")
   855  	}
   856  	if csiDetacher.k8s == nil {
   857  		t.Error("Kubernetes client not set for detacher")
   858  	}
   859  	if csiDetacher.watchTimeout == time.Duration(0) {
   860  		t.Error("watch timeout not set for detacher")
   861  	}
   862  }
   863  
   864  func TestPluginCanAttach(t *testing.T) {
   865  	tests := []struct {
   866  		name       string
   867  		driverName string
   868  		spec       *volume.Spec
   869  		canAttach  bool
   870  		shouldFail bool
   871  	}{
   872  		{
   873  			name:       "non-attachable inline",
   874  			driverName: "attachable-inline",
   875  			spec:       volume.NewSpecFromVolume(makeTestVol("test-vol", "attachable-inline")),
   876  			canAttach:  false,
   877  		},
   878  		{
   879  			name:       "attachable PV",
   880  			driverName: "attachable-pv",
   881  			spec:       volume.NewSpecFromPersistentVolume(makeTestPV("test-vol", 20, "attachable-pv", testVol), true),
   882  			canAttach:  true,
   883  		},
   884  		{
   885  			name:       "incomplete spec",
   886  			driverName: "attachable-pv",
   887  			spec:       &volume.Spec{ReadOnly: true},
   888  			canAttach:  false,
   889  			shouldFail: true,
   890  		},
   891  		{
   892  			name:       "nil spec",
   893  			driverName: "attachable-pv",
   894  			canAttach:  false,
   895  			shouldFail: true,
   896  		},
   897  	}
   898  
   899  	for _, test := range tests {
   900  		t.Run(test.name, func(t *testing.T) {
   901  			csiDriver := getTestCSIDriver(test.driverName, nil, &test.canAttach, nil)
   902  			fakeCSIClient := fakeclient.NewSimpleClientset(csiDriver)
   903  			plug, tmpDir := newTestPlugin(t, fakeCSIClient)
   904  			defer os.RemoveAll(tmpDir)
   905  
   906  			pluginCanAttach, err := plug.CanAttach(test.spec)
   907  			if err != nil && !test.shouldFail {
   908  				t.Fatalf("unexpected plugin.CanAttach error: %s", err)
   909  			}
   910  			if pluginCanAttach != test.canAttach {
   911  				t.Fatalf("expecting plugin.CanAttach %t got %t", test.canAttach, pluginCanAttach)
   912  			}
   913  		})
   914  	}
   915  }
   916  
   917  func TestPluginFindAttachablePlugin(t *testing.T) {
   918  	tests := []struct {
   919  		name       string
   920  		driverName string
   921  		spec       *volume.Spec
   922  		canAttach  bool
   923  		shouldFail bool
   924  	}{
   925  		{
   926  			name:       "non-attachable inline",
   927  			driverName: "attachable-inline",
   928  			spec:       volume.NewSpecFromVolume(makeTestVol("test-vol", "attachable-inline")),
   929  			canAttach:  false,
   930  		},
   931  		{
   932  			name:       "attachable PV",
   933  			driverName: "attachable-pv",
   934  			spec:       volume.NewSpecFromPersistentVolume(makeTestPV("test-vol", 20, "attachable-pv", testVol), true),
   935  			canAttach:  true,
   936  		},
   937  		{
   938  			name:       "incomplete spec",
   939  			driverName: "attachable-pv",
   940  			spec:       &volume.Spec{ReadOnly: true},
   941  			canAttach:  false,
   942  			shouldFail: true,
   943  		},
   944  		{
   945  			name:       "nil spec",
   946  			driverName: "attachable-pv",
   947  			canAttach:  false,
   948  			shouldFail: true,
   949  		},
   950  	}
   951  
   952  	for _, test := range tests {
   953  		t.Run(test.name, func(t *testing.T) {
   954  			tmpDir, err := utiltesting.MkTmpdir("csi-test")
   955  			if err != nil {
   956  				t.Fatalf("can't create temp dir: %v", err)
   957  			}
   958  			defer os.RemoveAll(tmpDir)
   959  
   960  			client := fakeclient.NewSimpleClientset(
   961  				getTestCSIDriver(test.driverName, nil, &test.canAttach, nil),
   962  				&api.Node{
   963  					ObjectMeta: meta.ObjectMeta{
   964  						Name: "fakeNode",
   965  					},
   966  					Spec: api.NodeSpec{},
   967  				},
   968  			)
   969  			factory := informers.NewSharedInformerFactory(client, CsiResyncPeriod)
   970  			host := volumetest.NewFakeKubeletVolumeHostWithCSINodeName(t,
   971  				tmpDir,
   972  				client,
   973  				ProbeVolumePlugins(),
   974  				"fakeNode",
   975  				factory.Storage().V1().CSIDrivers().Lister(),
   976  				factory.Storage().V1().VolumeAttachments().Lister(),
   977  			)
   978  
   979  			plugMgr := host.GetPluginMgr()
   980  
   981  			plugin, err := plugMgr.FindAttachablePluginBySpec(test.spec)
   982  			if err != nil && !test.shouldFail {
   983  				t.Fatalf("unexpected error calling pluginMgr.FindAttachablePluginBySpec: %s", err)
   984  			}
   985  			if (plugin != nil) != test.canAttach {
   986  				t.Fatal("expecting attachable plugin, but got nil")
   987  			}
   988  		})
   989  	}
   990  }
   991  
   992  func TestPluginCanDeviceMount(t *testing.T) {
   993  	tests := []struct {
   994  		name           string
   995  		driverName     string
   996  		spec           *volume.Spec
   997  		canDeviceMount bool
   998  		shouldFail     bool
   999  	}{
  1000  		{
  1001  			name:           "non device mountable inline",
  1002  			driverName:     "inline-driver",
  1003  			spec:           volume.NewSpecFromVolume(makeTestVol("test-vol", "inline-driver")),
  1004  			canDeviceMount: false,
  1005  		},
  1006  		{
  1007  			name:           "device mountable PV",
  1008  			driverName:     "device-mountable-pv",
  1009  			spec:           volume.NewSpecFromPersistentVolume(makeTestPV("test-vol", 20, "device-mountable-pv", testVol), true),
  1010  			canDeviceMount: true,
  1011  		},
  1012  		{
  1013  			name:           "incomplete spec",
  1014  			driverName:     "device-unmountable",
  1015  			spec:           &volume.Spec{ReadOnly: true},
  1016  			canDeviceMount: false,
  1017  			shouldFail:     true,
  1018  		},
  1019  		{
  1020  			name:           "missing spec",
  1021  			driverName:     "device-unmountable",
  1022  			canDeviceMount: false,
  1023  			shouldFail:     true,
  1024  		},
  1025  	}
  1026  
  1027  	for _, test := range tests {
  1028  		t.Run(test.name, func(t *testing.T) {
  1029  			plug, tmpDir := newTestPlugin(t, nil)
  1030  			defer os.RemoveAll(tmpDir)
  1031  
  1032  			pluginCanDeviceMount, err := plug.CanDeviceMount(test.spec)
  1033  			if err != nil && !test.shouldFail {
  1034  				t.Fatalf("unexpected error in plug.CanDeviceMount: %s", err)
  1035  			}
  1036  			if pluginCanDeviceMount != test.canDeviceMount {
  1037  				t.Fatalf("expecting plugin.CanAttach %t got %t", test.canDeviceMount, pluginCanDeviceMount)
  1038  			}
  1039  		})
  1040  	}
  1041  }
  1042  
  1043  func TestPluginFindDeviceMountablePluginBySpec(t *testing.T) {
  1044  	tests := []struct {
  1045  		name           string
  1046  		driverName     string
  1047  		spec           *volume.Spec
  1048  		canDeviceMount bool
  1049  		shouldFail     bool
  1050  	}{
  1051  		{
  1052  			name:           "non device mountable inline",
  1053  			driverName:     "inline-driver",
  1054  			spec:           volume.NewSpecFromVolume(makeTestVol("test-vol", "inline-driver")),
  1055  			canDeviceMount: false,
  1056  		},
  1057  		{
  1058  			name:           "device mountable PV",
  1059  			driverName:     "device-mountable-pv",
  1060  			spec:           volume.NewSpecFromPersistentVolume(makeTestPV("test-vol", 20, "device-mountable-pv", testVol), true),
  1061  			canDeviceMount: true,
  1062  		},
  1063  		{
  1064  			name:           "incomplete spec",
  1065  			driverName:     "device-unmountable",
  1066  			spec:           &volume.Spec{ReadOnly: true},
  1067  			canDeviceMount: false,
  1068  			shouldFail:     true,
  1069  		},
  1070  		{
  1071  			name:           "missing spec",
  1072  			driverName:     "device-unmountable",
  1073  			canDeviceMount: false,
  1074  			shouldFail:     true,
  1075  		},
  1076  	}
  1077  
  1078  	for _, test := range tests {
  1079  		t.Run(test.name, func(t *testing.T) {
  1080  			tmpDir, err := utiltesting.MkTmpdir("csi-test")
  1081  			if err != nil {
  1082  				t.Fatalf("can't create temp dir: %v", err)
  1083  			}
  1084  			defer os.RemoveAll(tmpDir)
  1085  
  1086  			client := fakeclient.NewSimpleClientset(
  1087  				&api.Node{
  1088  					ObjectMeta: meta.ObjectMeta{
  1089  						Name: "fakeNode",
  1090  					},
  1091  					Spec: api.NodeSpec{},
  1092  				},
  1093  			)
  1094  			host := volumetest.NewFakeVolumeHostWithCSINodeName(t, tmpDir, client, ProbeVolumePlugins(), "fakeNode", nil, nil)
  1095  			plugMgr := host.GetPluginMgr()
  1096  			plug, err := plugMgr.FindDeviceMountablePluginBySpec(test.spec)
  1097  			if err != nil && !test.shouldFail {
  1098  				t.Fatalf("unexpected error in plugMgr.FindDeviceMountablePluginBySpec: %s", err)
  1099  			}
  1100  			if (plug != nil) != test.canDeviceMount {
  1101  				t.Fatalf("expecting deviceMountablePlugin, but got nil")
  1102  			}
  1103  		})
  1104  	}
  1105  }
  1106  
  1107  func TestPluginNewBlockMapper(t *testing.T) {
  1108  	plug, tmpDir := newTestPlugin(t, nil)
  1109  	defer os.RemoveAll(tmpDir)
  1110  
  1111  	registerFakePlugin(testDriver, "endpoint", []string{"1.0.0"}, t)
  1112  	pv := makeTestPV("test-block-pv", 10, testDriver, testVol)
  1113  	mounter, err := plug.NewBlockVolumeMapper(
  1114  		volume.NewSpecFromPersistentVolume(pv, pv.Spec.PersistentVolumeSource.CSI.ReadOnly),
  1115  		&api.Pod{ObjectMeta: meta.ObjectMeta{UID: testPodUID, Namespace: testns}},
  1116  		volume.VolumeOptions{},
  1117  	)
  1118  	if err != nil {
  1119  		t.Fatalf("Failed to make a new BlockMapper: %v", err)
  1120  	}
  1121  
  1122  	if mounter == nil {
  1123  		t.Fatal("failed to create CSI BlockMapper, mapper is nill")
  1124  	}
  1125  	csiMapper := mounter.(*csiBlockMapper)
  1126  
  1127  	// validate mounter fields
  1128  	if string(csiMapper.driverName) != testDriver {
  1129  		t.Error("CSI block mapper missing driver name")
  1130  	}
  1131  	if csiMapper.volumeID != testVol {
  1132  		t.Error("CSI block mapper missing volumeID")
  1133  	}
  1134  
  1135  	if csiMapper.podUID == types.UID("") {
  1136  		t.Error("CSI block mapper missing pod.UID")
  1137  	}
  1138  	csiClient, err := csiMapper.csiClientGetter.Get()
  1139  	if csiClient == nil {
  1140  		t.Errorf("mapper csiClient is nil: %v", err)
  1141  	}
  1142  
  1143  	// ensure data file is created
  1144  	dataFile := getVolumeDeviceDataDir(csiMapper.spec.Name(), plug.host)
  1145  	if _, err := os.Stat(dataFile); err != nil {
  1146  		if os.IsNotExist(err) {
  1147  			t.Errorf("data file not created %s", dataFile)
  1148  		} else {
  1149  			t.Fatal(err)
  1150  		}
  1151  	}
  1152  }
  1153  
  1154  func TestPluginNewUnmapper(t *testing.T) {
  1155  	plug, tmpDir := newTestPlugin(t, nil)
  1156  	defer os.RemoveAll(tmpDir)
  1157  
  1158  	registerFakePlugin(testDriver, "endpoint", []string{"1.0.0"}, t)
  1159  	pv := makeTestPV("test-pv", 10, testDriver, testVol)
  1160  
  1161  	// save the data file to re-create client
  1162  	dir := getVolumeDeviceDataDir(pv.ObjectMeta.Name, plug.host)
  1163  	if err := os.MkdirAll(dir, 0755); err != nil && !os.IsNotExist(err) {
  1164  		t.Errorf("failed to create dir [%s]: %v", dir, err)
  1165  	}
  1166  
  1167  	if err := saveVolumeData(
  1168  		dir,
  1169  		volDataFileName,
  1170  		map[string]string{
  1171  			volDataKey.specVolID:  pv.ObjectMeta.Name,
  1172  			volDataKey.driverName: testDriver,
  1173  			volDataKey.volHandle:  testVol,
  1174  		},
  1175  	); err != nil {
  1176  		t.Fatalf("failed to save volume data: %v", err)
  1177  	}
  1178  
  1179  	// test unmounter
  1180  	unmapper, err := plug.NewBlockVolumeUnmapper(pv.ObjectMeta.Name, testPodUID)
  1181  	csiUnmapper := unmapper.(*csiBlockMapper)
  1182  
  1183  	if err != nil {
  1184  		t.Fatalf("Failed to make a new Unmounter: %v", err)
  1185  	}
  1186  
  1187  	if csiUnmapper == nil {
  1188  		t.Fatal("failed to create CSI Unmounter")
  1189  	}
  1190  
  1191  	if csiUnmapper.podUID != testPodUID {
  1192  		t.Error("podUID not set")
  1193  	}
  1194  
  1195  	if csiUnmapper.specName != pv.ObjectMeta.Name {
  1196  		t.Error("specName not set")
  1197  	}
  1198  
  1199  	csiClient, err := csiUnmapper.csiClientGetter.Get()
  1200  	if csiClient == nil {
  1201  		t.Errorf("unmapper csiClient is nil: %v", err)
  1202  	}
  1203  
  1204  	// test loaded vol data
  1205  	if string(csiUnmapper.driverName) != testDriver {
  1206  		t.Error("unmapper driverName not set")
  1207  	}
  1208  	if csiUnmapper.volumeID != testVol {
  1209  		t.Error("unmapper volumeHandle not set")
  1210  	}
  1211  }
  1212  
  1213  func TestPluginConstructBlockVolumeSpec(t *testing.T) {
  1214  	plug, tmpDir := newTestPlugin(t, nil)
  1215  	defer os.RemoveAll(tmpDir)
  1216  
  1217  	testCases := []struct {
  1218  		name       string
  1219  		specVolID  string
  1220  		data       map[string]string
  1221  		shouldFail bool
  1222  	}{
  1223  		{
  1224  			name:      "valid spec name",
  1225  			specVolID: "test.vol.id",
  1226  			data:      map[string]string{volDataKey.specVolID: "test.vol.id", volDataKey.volHandle: "test-vol0", volDataKey.driverName: "test-driver0"},
  1227  		},
  1228  	}
  1229  
  1230  	for _, tc := range testCases {
  1231  		t.Logf("test case: %s", tc.name)
  1232  		deviceDataDir := getVolumeDeviceDataDir(tc.specVolID, plug.host)
  1233  
  1234  		// create data file in csi plugin dir
  1235  		if tc.data != nil {
  1236  			if err := os.MkdirAll(deviceDataDir, 0755); err != nil && !os.IsNotExist(err) {
  1237  				t.Errorf("failed to create dir [%s]: %v", deviceDataDir, err)
  1238  			}
  1239  			if err := saveVolumeData(deviceDataDir, volDataFileName, tc.data); err != nil {
  1240  				t.Fatal(err)
  1241  			}
  1242  		}
  1243  
  1244  		// rebuild spec
  1245  		spec, err := plug.ConstructBlockVolumeSpec("test-podUID", tc.specVolID, getVolumeDevicePluginDir(tc.specVolID, plug.host))
  1246  		if tc.shouldFail {
  1247  			if err == nil {
  1248  				t.Fatal("expecting ConstructVolumeSpec to fail, but got nil error")
  1249  			}
  1250  			continue
  1251  		}
  1252  
  1253  		if spec.PersistentVolume.Spec.VolumeMode == nil {
  1254  			t.Fatalf("Volume mode has not been set.")
  1255  		}
  1256  
  1257  		if *spec.PersistentVolume.Spec.VolumeMode != api.PersistentVolumeBlock {
  1258  			t.Errorf("Unexpected volume mode %q", *spec.PersistentVolume.Spec.VolumeMode)
  1259  		}
  1260  
  1261  		volHandle := spec.PersistentVolume.Spec.CSI.VolumeHandle
  1262  		if volHandle != tc.data[volDataKey.volHandle] {
  1263  			t.Errorf("expected volID %s, got volID %s", tc.data[volDataKey.volHandle], volHandle)
  1264  		}
  1265  
  1266  		if spec.Name() != tc.specVolID {
  1267  			t.Errorf("Unexpected spec name %s", spec.Name())
  1268  		}
  1269  	}
  1270  }
  1271  
  1272  func TestValidatePlugin(t *testing.T) {
  1273  	testCases := []struct {
  1274  		pluginName string
  1275  		endpoint   string
  1276  		versions   []string
  1277  		shouldFail bool
  1278  	}{
  1279  		{
  1280  			pluginName: "test.plugin",
  1281  			endpoint:   "/var/log/kubelet/plugins_registry/myplugin/csi.sock",
  1282  			versions:   []string{"v1.0.0"},
  1283  			shouldFail: false,
  1284  		},
  1285  		{
  1286  			pluginName: "test.plugin",
  1287  			endpoint:   "/var/log/kubelet/plugins_registry/myplugin/csi.sock",
  1288  			versions:   []string{"0.3.0"},
  1289  			shouldFail: true,
  1290  		},
  1291  		{
  1292  			pluginName: "test.plugin",
  1293  			endpoint:   "/var/log/kubelet/plugins_registry/myplugin/csi.sock",
  1294  			versions:   []string{"0.2.0"},
  1295  			shouldFail: true,
  1296  		},
  1297  		{
  1298  			pluginName: "test.plugin",
  1299  			endpoint:   "/var/log/kubelet/plugins_registry/myplugin/csi.sock",
  1300  			versions:   []string{"0.2.0", "v0.3.0"},
  1301  			shouldFail: true,
  1302  		},
  1303  		{
  1304  			pluginName: "test.plugin",
  1305  			endpoint:   "/var/log/kubelet/plugins_registry/myplugin/csi.sock",
  1306  			versions:   []string{"0.2.0", "v1.0.0"},
  1307  			shouldFail: false,
  1308  		},
  1309  		{
  1310  			pluginName: "test.plugin",
  1311  			endpoint:   "/var/log/kubelet/plugins_registry/myplugin/csi.sock",
  1312  			versions:   []string{"0.2.0", "v1.2.3"},
  1313  			shouldFail: false,
  1314  		},
  1315  		{
  1316  			pluginName: "test.plugin",
  1317  			endpoint:   "/var/log/kubelet/plugins_registry/myplugin/csi.sock",
  1318  			versions:   []string{"v1.2.3", "v0.3.0"},
  1319  			shouldFail: false,
  1320  		},
  1321  		{
  1322  			pluginName: "test.plugin",
  1323  			endpoint:   "/var/log/kubelet/plugins_registry/myplugin/csi.sock",
  1324  			versions:   []string{"v1.2.3", "v0.3.0", "2.0.1"},
  1325  			shouldFail: false,
  1326  		},
  1327  		{
  1328  			pluginName: "test.plugin",
  1329  			endpoint:   "/var/log/kubelet/plugins_registry/myplugin/csi.sock",
  1330  			versions:   []string{"v1.2.3", "4.9.12", "v0.3.0", "2.0.1"},
  1331  			shouldFail: false,
  1332  		},
  1333  		{
  1334  			pluginName: "test.plugin",
  1335  			endpoint:   "/var/log/kubelet/plugins_registry/myplugin/csi.sock",
  1336  			versions:   []string{"v1.2.3", "boo", "v0.3.0", "2.0.1"},
  1337  			shouldFail: false,
  1338  		},
  1339  		{
  1340  			pluginName: "test.plugin",
  1341  			endpoint:   "/var/log/kubelet/plugins_registry/myplugin/csi.sock",
  1342  			versions:   []string{"4.9.12", "2.0.1"},
  1343  			shouldFail: true,
  1344  		},
  1345  		{
  1346  			pluginName: "test.plugin",
  1347  			endpoint:   "/var/log/kubelet/plugins_registry/myplugin/csi.sock",
  1348  			versions:   []string{},
  1349  			shouldFail: true,
  1350  		},
  1351  		{
  1352  			pluginName: "test.plugin",
  1353  			endpoint:   "/var/log/kubelet/plugins_registry/myplugin/csi.sock",
  1354  			versions:   []string{"var", "boo", "foo"},
  1355  			shouldFail: true,
  1356  		},
  1357  	}
  1358  
  1359  	for _, tc := range testCases {
  1360  		// Arrange & Act
  1361  		err := PluginHandler.ValidatePlugin(tc.pluginName, tc.endpoint, tc.versions)
  1362  
  1363  		// Assert
  1364  		if tc.shouldFail && err == nil {
  1365  			t.Fatalf("expecting ValidatePlugin to fail, but got nil error for testcase: %#v", tc)
  1366  		}
  1367  		if !tc.shouldFail && err != nil {
  1368  			t.Fatalf("unexpected error during ValidatePlugin for testcase: %#v\r\n err:%v", tc, err)
  1369  		}
  1370  	}
  1371  }
  1372  
  1373  func TestValidatePluginExistingDriver(t *testing.T) {
  1374  	testCases := []struct {
  1375  		pluginName1 string
  1376  		endpoint1   string
  1377  		versions1   []string
  1378  		pluginName2 string
  1379  		endpoint2   string
  1380  		versions2   []string
  1381  		shouldFail  bool
  1382  	}{
  1383  		{
  1384  			pluginName1: "test.plugin",
  1385  			endpoint1:   "/var/log/kubelet/plugins_registry/myplugin/csi.sock",
  1386  			versions1:   []string{"v1.0.0"},
  1387  			pluginName2: "test.plugin2",
  1388  			endpoint2:   "/var/log/kubelet/plugins_registry/myplugin/csi.sock",
  1389  			versions2:   []string{"v1.0.0"},
  1390  			shouldFail:  false,
  1391  		},
  1392  		{
  1393  			pluginName1: "test.plugin",
  1394  			endpoint1:   "/var/log/kubelet/plugins_registry/myplugin/csi.sock",
  1395  			versions1:   []string{"v1.0.0"},
  1396  			pluginName2: "test.plugin",
  1397  			endpoint2:   "/var/log/kubelet/plugins_registry/myplugin/csi.sock",
  1398  			versions2:   []string{"v1.0.0"},
  1399  			shouldFail:  true,
  1400  		},
  1401  		{
  1402  			pluginName1: "test.plugin",
  1403  			endpoint1:   "/var/log/kubelet/plugins/myplugin/csi.sock",
  1404  			versions1:   []string{"v0.3.0", "v0.2.0", "v1.0.0"},
  1405  			pluginName2: "test.plugin",
  1406  			endpoint2:   "/var/log/kubelet/plugins_registry/myplugin/csi.sock",
  1407  			versions2:   []string{"v1.0.1"},
  1408  			shouldFail:  false,
  1409  		},
  1410  	}
  1411  
  1412  	for _, tc := range testCases {
  1413  		// Arrange & Act
  1414  		highestSupportedVersions1, err := utilversion.HighestSupportedVersion(tc.versions1)
  1415  		if err != nil {
  1416  			t.Fatalf("unexpected error parsing version for testcase: %#v: %v", tc, err)
  1417  		}
  1418  
  1419  		csiDrivers.Clear()
  1420  		csiDrivers.Set(tc.pluginName1, Driver{
  1421  			endpoint:                tc.endpoint1,
  1422  			highestSupportedVersion: highestSupportedVersions1,
  1423  		})
  1424  
  1425  		// Arrange & Act
  1426  		err = PluginHandler.ValidatePlugin(tc.pluginName2, tc.endpoint2, tc.versions2)
  1427  
  1428  		// Assert
  1429  		if tc.shouldFail && err == nil {
  1430  			t.Fatalf("expecting ValidatePlugin to fail, but got nil error for testcase: %#v", tc)
  1431  		}
  1432  		if !tc.shouldFail && err != nil {
  1433  			t.Fatalf("unexpected error during ValidatePlugin for testcase: %#v\r\n err:%v", tc, err)
  1434  		}
  1435  	}
  1436  }
  1437  

View as plain text