1
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
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
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,
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: "",
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
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
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
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,
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
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
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
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
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
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
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
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
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
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
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
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
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
1361 err := PluginHandler.ValidatePlugin(tc.pluginName, tc.endpoint, tc.versions)
1362
1363
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
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
1426 err = PluginHandler.ValidatePlugin(tc.pluginName2, tc.endpoint2, tc.versions2)
1427
1428
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