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/runtime"
31 "k8s.io/apimachinery/pkg/types"
32 "k8s.io/apimachinery/pkg/util/wait"
33 "k8s.io/client-go/informers"
34 fakeclient "k8s.io/client-go/kubernetes/fake"
35 utiltesting "k8s.io/client-go/util/testing"
36 "k8s.io/kubernetes/pkg/volume"
37 volumetest "k8s.io/kubernetes/pkg/volume/testing"
38 )
39
40
41
42 func TestCSI_VolumeAll(t *testing.T) {
43 defaultFSGroupPolicy := storage.ReadWriteOnceWithFSTypeFSGroupPolicy
44
45 tests := []struct {
46 name string
47 specName string
48 driver string
49 volName string
50 specFunc func(specName, driver, volName string) *volume.Spec
51 podFunc func() *api.Pod
52 isInline bool
53 findPluginShouldFail bool
54 driverSpec *storage.CSIDriverSpec
55 watchTimeout time.Duration
56 }{
57 {
58 name: "PersistentVolume",
59 specName: "pv2",
60 driver: "simple-driver",
61 volName: "vol2",
62 specFunc: func(specName, driver, volName string) *volume.Spec {
63 return volume.NewSpecFromPersistentVolume(makeTestPV(specName, 20, driver, volName), false)
64 },
65 podFunc: func() *api.Pod {
66 podUID := types.UID(fmt.Sprintf("%08X", rand.Uint64()))
67 return &api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}}
68 },
69 },
70 {
71 name: "PersistentVolume with driver info",
72 specName: "pv2",
73 driver: "simple-driver",
74 volName: "vol2",
75 specFunc: func(specName, driver, volName string) *volume.Spec {
76 return volume.NewSpecFromPersistentVolume(makeTestPV(specName, 20, driver, volName), false)
77 },
78 podFunc: func() *api.Pod {
79 podUID := types.UID(fmt.Sprintf("%08X", rand.Uint64()))
80 return &api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}}
81 },
82 driverSpec: &storage.CSIDriverSpec{
83
84 VolumeLifecycleModes: []storage.VolumeLifecycleMode{storage.VolumeLifecyclePersistent},
85 FSGroupPolicy: &defaultFSGroupPolicy,
86 },
87 },
88 {
89 name: "PersistentVolume with wrong mode in driver info",
90 specName: "pv2",
91 driver: "simple-driver",
92 volName: "vol2",
93 specFunc: func(specName, driver, volName string) *volume.Spec {
94 return volume.NewSpecFromPersistentVolume(makeTestPV(specName, 20, driver, volName), false)
95 },
96 podFunc: func() *api.Pod {
97 podUID := types.UID(fmt.Sprintf("%08X", rand.Uint64()))
98 return &api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}}
99 },
100 driverSpec: &storage.CSIDriverSpec{
101
102 VolumeLifecycleModes: []storage.VolumeLifecycleMode{storage.VolumeLifecycleEphemeral},
103 FSGroupPolicy: &defaultFSGroupPolicy,
104 },
105 },
106 {
107 name: "ephemeral inline supported",
108 driver: "inline-driver-1",
109 volName: "test.vol2",
110 specFunc: func(specName, driver, volName string) *volume.Spec {
111 return volume.NewSpecFromVolume(makeTestVol(specName, driver))
112 },
113 podFunc: func() *api.Pod {
114 podUID := types.UID(fmt.Sprintf("%08X", rand.Uint64()))
115 return &api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}}
116 },
117 isInline: true,
118 driverSpec: &storage.CSIDriverSpec{
119
120 VolumeLifecycleModes: []storage.VolumeLifecycleMode{storage.VolumeLifecycleEphemeral},
121 FSGroupPolicy: &defaultFSGroupPolicy,
122 },
123 },
124 {
125 name: "ephemeral inline also supported",
126 driver: "inline-driver-1",
127 volName: "test.vol2",
128 specFunc: func(specName, driver, volName string) *volume.Spec {
129 return volume.NewSpecFromVolume(makeTestVol(specName, driver))
130 },
131 podFunc: func() *api.Pod {
132 podUID := types.UID(fmt.Sprintf("%08X", rand.Uint64()))
133 return &api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}}
134 },
135 isInline: true,
136 driverSpec: &storage.CSIDriverSpec{
137
138 VolumeLifecycleModes: []storage.VolumeLifecycleMode{storage.VolumeLifecyclePersistent, storage.VolumeLifecycleEphemeral},
139 FSGroupPolicy: &defaultFSGroupPolicy,
140 },
141 },
142 {
143 name: "ephemeral inline without CSIDriver info",
144 driver: "inline-driver-2",
145 volName: "test.vol3",
146 specFunc: func(specName, driver, volName string) *volume.Spec {
147 return volume.NewSpecFromVolume(makeTestVol(specName, driver))
148 },
149 podFunc: func() *api.Pod {
150 podUID := types.UID(fmt.Sprintf("%08X", rand.Uint64()))
151 return &api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}}
152 },
153 isInline: true,
154 },
155 {
156 name: "ephemeral inline with driver that has no mode",
157 driver: "inline-driver-3",
158 volName: "test.vol4",
159 specFunc: func(specName, driver, volName string) *volume.Spec {
160 return volume.NewSpecFromVolume(makeTestVol(specName, driver))
161 },
162 podFunc: func() *api.Pod {
163 podUID := types.UID(fmt.Sprintf("%08X", rand.Uint64()))
164 return &api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}}
165 },
166 isInline: true,
167 driverSpec: &storage.CSIDriverSpec{
168
169
170 VolumeLifecycleModes: nil,
171 FSGroupPolicy: &defaultFSGroupPolicy,
172 },
173 },
174 {
175 name: "ephemeral inline with driver that has wrong mode",
176 driver: "inline-driver-3",
177 volName: "test.vol4",
178 specFunc: func(specName, driver, volName string) *volume.Spec {
179 return volume.NewSpecFromVolume(makeTestVol(specName, driver))
180 },
181 podFunc: func() *api.Pod {
182 podUID := types.UID(fmt.Sprintf("%08X", rand.Uint64()))
183 return &api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}}
184 },
185 isInline: true,
186 driverSpec: &storage.CSIDriverSpec{
187
188 VolumeLifecycleModes: []storage.VolumeLifecycleMode{storage.VolumeLifecyclePersistent},
189 FSGroupPolicy: &defaultFSGroupPolicy,
190 },
191 },
192 {
193 name: "missing spec",
194 specName: "pv2",
195 driver: "simple-driver",
196 volName: "vol2",
197 specFunc: func(specName, driver, volName string) *volume.Spec {
198 return nil
199 },
200 podFunc: func() *api.Pod {
201 podUID := types.UID(fmt.Sprintf("%08X", rand.Uint64()))
202 return &api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}}
203 },
204 findPluginShouldFail: true,
205 },
206 {
207 name: "incomplete spec",
208 specName: "pv2",
209 driver: "simple-driver",
210 volName: "vol2",
211 specFunc: func(specName, driver, volName string) *volume.Spec {
212 return &volume.Spec{ReadOnly: true}
213 },
214 podFunc: func() *api.Pod {
215 podUID := types.UID(fmt.Sprintf("%08X", rand.Uint64()))
216 return &api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}}
217 },
218 findPluginShouldFail: true,
219 },
220 }
221
222 for _, test := range tests {
223 t.Run(test.name, func(t *testing.T) {
224 tmpDir, err := utiltesting.MkTmpdir("csi-test")
225 if err != nil {
226 t.Fatalf("can't create temp dir: %v", err)
227 }
228 defer os.RemoveAll(tmpDir)
229
230 var driverInfo *storage.CSIDriver
231 objs := []runtime.Object{}
232 if test.driverSpec != nil {
233 driverInfo = &storage.CSIDriver{
234 ObjectMeta: meta.ObjectMeta{
235 Name: test.driver,
236 },
237 Spec: *test.driverSpec,
238 }
239 objs = append(objs, driverInfo)
240 }
241 objs = append(objs, &api.Node{
242 ObjectMeta: meta.ObjectMeta{
243 Name: "fakeNode",
244 },
245 Spec: api.NodeSpec{},
246 })
247
248 client := fakeclient.NewSimpleClientset(objs...)
249
250 factory := informers.NewSharedInformerFactory(client, time.Hour )
251 csiDriverInformer := factory.Storage().V1().CSIDrivers()
252 volumeAttachmentInformer := factory.Storage().V1().VolumeAttachments()
253 if driverInfo != nil {
254 csiDriverInformer.Informer().GetStore().Add(driverInfo)
255 }
256
257 factory.Start(wait.NeverStop)
258 factory.WaitForCacheSync(wait.NeverStop)
259
260 attachDetachVolumeHost := volumetest.NewFakeAttachDetachVolumeHostWithCSINodeName(t,
261 tmpDir,
262 client,
263 ProbeVolumePlugins(),
264 "fakeNode",
265 csiDriverInformer.Lister(),
266 volumeAttachmentInformer.Lister(),
267 )
268 attachDetachPlugMgr := attachDetachVolumeHost.GetPluginMgr()
269 csiClient := setupClient(t, true)
270
271 volSpec := test.specFunc(test.specName, test.driver, test.volName)
272 pod := test.podFunc()
273 attachName := getAttachmentName(test.volName, test.driver, string(attachDetachVolumeHost.GetNodeName()))
274 t.Log("csiTest.VolumeAll starting...")
275
276
277
278 t.Log("csiTest.VolumeAll Attaching volume...")
279 attachPlug, err := attachDetachPlugMgr.FindAttachablePluginBySpec(volSpec)
280 if err != nil {
281 if !test.findPluginShouldFail {
282 t.Fatalf("csiTest.VolumeAll PluginManager.FindAttachablePluginBySpec failed: %v", err)
283 } else {
284 t.Log("csiTest.VolumeAll failed: ", err)
285 return
286 }
287 }
288
289 if test.isInline && attachPlug != nil {
290 t.Fatal("csiTest.VolumeAll AttachablePlugin found with ephemeral volume")
291 }
292 if !test.isInline && attachPlug == nil {
293 t.Fatal("csiTest.VolumeAll AttachablePlugin not found with PV")
294 }
295
296 var devicePath string
297 if attachPlug != nil {
298 t.Log("csiTest.VolumeAll attacher.Attach starting")
299
300 var volAttacher volume.Attacher
301
302 volAttacher, err := attachPlug.NewAttacher()
303 if err != nil {
304 t.Fatal("csiTest.VolumeAll failed to create new attacher: ", err)
305 }
306
307
308 go func() {
309 attachID, err := volAttacher.Attach(volSpec, attachDetachVolumeHost.GetNodeName())
310 if err != nil {
311 t.Errorf("csiTest.VolumeAll attacher.Attach failed: %s", err)
312 return
313 }
314 t.Logf("csiTest.VolumeAll got attachID %s", attachID)
315 }()
316
317
318 markVolumeAttached(t, attachDetachVolumeHost.GetKubeClient(), nil, attachName, storage.VolumeAttachmentStatus{Attached: true})
319
320
321 devicePath, err = volAttacher.WaitForAttach(volSpec, "", pod, 500*time.Millisecond)
322 if err != nil {
323 t.Fatal("csiTest.VolumeAll attacher.WaitForAttach failed:", err)
324 }
325
326 if devicePath != attachName {
327 t.Fatalf("csiTest.VolumeAll attacher.WaitForAttach got unexpected value %s", devicePath)
328 }
329
330 t.Log("csiTest.VolumeAll attacher.WaitForAttach succeeded OK, attachment ID:", devicePath)
331
332 } else {
333 t.Log("csiTest.VolumeAll volume attacher not found, skipping attachment")
334 }
335
336
337
338
339 kubeletVolumeHost := volumetest.NewFakeKubeletVolumeHostWithCSINodeName(t,
340 tmpDir,
341 client,
342 ProbeVolumePlugins(),
343 "fakeNode",
344 csiDriverInformer.Lister(),
345 volumeAttachmentInformer.Lister(),
346 )
347 kubeletPlugMgr := kubeletVolumeHost.GetPluginMgr()
348
349
350 t.Log("csiTest.VolumeAll Mouting device...")
351 devicePlug, err := kubeletPlugMgr.FindDeviceMountablePluginBySpec(volSpec)
352 if err != nil {
353 t.Fatalf("csiTest.VolumeAll PluginManager.FindDeviceMountablePluginBySpec failed: %v", err)
354 }
355
356 if test.isInline && devicePlug != nil {
357 t.Fatal("csiTest.VolumeAll DeviceMountablePlugin found with ephemeral volume")
358 }
359 if !test.isInline && devicePlug == nil {
360 t.Fatal("csiTest.VolumeAll DeviceMountablePlugin not found with PV")
361 }
362
363 var devMounter volume.DeviceMounter
364 if devicePlug != nil {
365 devMounter, err = devicePlug.NewDeviceMounter()
366 if err != nil {
367 t.Fatal("csiTest.VolumeAll failed to create new device mounter: ", err)
368 }
369 }
370
371 if devMounter != nil {
372 csiDevMounter := getCsiAttacherFromDeviceMounter(devMounter, test.watchTimeout)
373 csiDevMounter.csiClient = csiClient
374 devMountPath, err := csiDevMounter.GetDeviceMountPath(volSpec)
375 if err != nil {
376 t.Fatalf("csiTest.VolumeAll deviceMounter.GetdeviceMountPath failed %s", err)
377 }
378 if err := csiDevMounter.MountDevice(volSpec, devicePath, devMountPath, volume.DeviceMounterArgs{}); err != nil {
379 t.Fatalf("csiTest.VolumeAll deviceMounter.MountDevice failed: %v", err)
380 }
381 t.Log("csiTest.VolumeAll device mounted at path:", devMountPath)
382 } else {
383 t.Log("csiTest.VolumeAll DeviceMountablePlugin not found, skipping deviceMounter.MountDevice")
384 }
385
386
387 t.Log("csiTest.VolumeAll Mouting volume...")
388 volPlug, err := kubeletPlugMgr.FindPluginBySpec(volSpec)
389 if err != nil || volPlug == nil {
390 t.Fatalf("csiTest.VolumeAll PluginMgr.FindPluginBySpec failed: %v", err)
391 }
392
393 if volPlug == nil {
394 t.Fatalf("csiTest.VolumeAll volumePlugin is nil")
395 }
396
397 if !volPlug.CanSupport(volSpec) {
398 t.Fatal("csiTest.VolumeAll volumePlugin.CanSupport returned false")
399 }
400
401 mounter, err := volPlug.NewMounter(volSpec, pod, volume.VolumeOptions{})
402 if err != nil || mounter == nil {
403 t.Fatalf("csiTest.VolumeAll volPlugin.NewMounter is nil or error: %s", err)
404 }
405
406 var fsGroup *int64
407 if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.FSGroup != nil {
408 fsGroup = pod.Spec.SecurityContext.FSGroup
409 }
410
411 csiMounter := mounter.(*csiMountMgr)
412 csiMounter.csiClient = csiClient
413 var mounterArgs volume.MounterArgs
414 mounterArgs.FsGroup = fsGroup
415 err = csiMounter.SetUp(mounterArgs)
416 if test.isInline && (test.driverSpec == nil || !containsVolumeMode(test.driverSpec.VolumeLifecycleModes, storage.VolumeLifecycleEphemeral)) {
417
418
419 if err == nil {
420 t.Fatalf("csiTest.VolumeAll volPlugin.NewMounter should have failed for inline volume due to lack of support for inline volumes, got: %+v, %s", mounter, err)
421 }
422 return
423 }
424 if !test.isInline && test.driverSpec != nil && !containsVolumeMode(test.driverSpec.VolumeLifecycleModes, storage.VolumeLifecyclePersistent) {
425
426
427 if err == nil {
428 t.Fatalf("csiTest.VolumeAll volPlugin.NewMounter should have failed for persistent volume due to lack of support for persistent volumes, got: %+v, %s", mounter, err)
429 }
430 return
431 }
432 if err != nil {
433 t.Fatalf("csiTest.VolumeAll mounter.Setup(fsGroup) failed: %s", err)
434 }
435 t.Log("csiTest.VolumeAll mounter.Setup(fsGroup) done OK")
436
437 dataFile := filepath.Join(filepath.Dir(mounter.GetPath()), volDataFileName)
438 if _, err := os.Stat(dataFile); err != nil {
439 t.Fatalf("csiTest.VolumeAll metadata JSON file not found: %s", dataFile)
440 }
441 t.Log("csiTest.VolumeAll JSON datafile generated OK:", dataFile)
442
443
444 volPath := filepath.Dir(csiMounter.GetPath())
445 t.Log("csiTest.VolumeAll entering plugin.ConstructVolumeSpec for path", volPath)
446 rec, err := volPlug.ConstructVolumeSpec(test.volName, volPath)
447 if err != nil {
448 t.Fatalf("csiTest.VolumeAll plugin.ConstructVolumeSpec failed: %s", err)
449 } else {
450 if rec.Spec == nil {
451 t.Fatalf("csiTest.VolumeAll plugin.ConstructVolumeSpec returned nil spec")
452 } else {
453 volSpec = rec.Spec
454
455 if test.isInline {
456 if volSpec.Volume == nil || volSpec.Volume.CSI == nil {
457 t.Fatal("csiTest.VolumeAll reconstruction of ephemeral volumeSpec missing CSI Volume source")
458 }
459 if volSpec.Volume.CSI.Driver == "" {
460 t.Fatal("csiTest.VolumeAll reconstruction ephemral volume missing driver name")
461 }
462 } else {
463 if volSpec.PersistentVolume == nil || volSpec.PersistentVolume.Spec.CSI == nil {
464 t.Fatal("csiTest.VolumeAll reconstruction of volumeSpec missing CSI PersistentVolume source")
465 }
466 csi := volSpec.PersistentVolume.Spec.CSI
467 if csi.Driver == "" {
468 t.Fatal("csiTest.VolumeAll reconstruction of PV missing driver name")
469 }
470 if csi.VolumeHandle == "" {
471 t.Fatal("csiTest.VolumeAll reconstruction of PV missing volume handle")
472 }
473 }
474 }
475 }
476
477
478 t.Log("csiTest.VolumeAll Tearing down...")
479
480 t.Log("csiTest.VolumeAll Unmouting volume...")
481 volPlug, err = kubeletPlugMgr.FindPluginBySpec(volSpec)
482 if err != nil || volPlug == nil {
483 t.Fatalf("csiTest.VolumeAll PluginMgr.FindPluginBySpec failed: %v", err)
484 }
485 if volPlug == nil {
486 t.Fatalf("csiTest.VolumeAll volumePlugin is nil")
487 }
488 mounter, err = volPlug.NewMounter(volSpec, pod, volume.VolumeOptions{})
489 if err != nil || mounter == nil {
490 t.Fatalf("csiTest.VolumeAll volPlugin.NewMounter is nil or error: %s", err)
491 }
492
493 unmounter, err := volPlug.NewUnmounter(test.specName, pod.GetUID())
494 if err != nil {
495 t.Fatal("csiTest.VolumeAll volumePlugin.NewUnmounter failed:", err)
496 }
497 csiUnmounter := unmounter.(*csiMountMgr)
498 csiUnmounter.csiClient = csiClient
499
500 if err := csiUnmounter.TearDownAt(mounter.GetPath()); err != nil {
501 t.Fatal("csiTest.VolumeAll unmounter.TearDownAt failed:", err)
502 }
503 t.Log("csiTest.VolumeAll unmounter.TearDownAt done OK for dir:", mounter.GetPath())
504
505
506 t.Log("csiTest.VolumeAll Unmouting device...")
507 devicePlug, err = kubeletPlugMgr.FindDeviceMountablePluginBySpec(volSpec)
508 if err != nil {
509 t.Fatalf("csiTest.VolumeAll failed to create mountable device plugin: %s", err)
510 }
511
512 if test.isInline && devicePlug != nil {
513 t.Fatal("csiTest.VolumeAll DeviceMountablePlugin found with ephemeral volume")
514 }
515 if !test.isInline && devicePlug == nil {
516 t.Fatal("csiTest.VolumeAll DeviceMountablePlugin not found with PV")
517 }
518
519 var devUnmounter volume.DeviceUnmounter
520 if devicePlug != nil {
521 t.Log("csiTest.VolumeAll found DeviceMountablePlugin, entering device unmouting ...")
522 devMounter, err = devicePlug.NewDeviceMounter()
523 if err != nil {
524 t.Fatal("csiTest.VolumeAll failed to create new device mounter: ", err)
525 }
526 devUnmounter, err = devicePlug.NewDeviceUnmounter()
527 if err != nil {
528 t.Fatal("csiTest.VolumeAll failed to create new device unmounter: ", err)
529 }
530
531 if devMounter != nil && devUnmounter != nil {
532 csiDevMounter := getCsiAttacherFromDeviceMounter(devMounter, test.watchTimeout)
533 csiDevUnmounter := getCsiAttacherFromDeviceUnmounter(devUnmounter, test.watchTimeout)
534 csiDevUnmounter.csiClient = csiClient
535
536 devMountPath, err := csiDevMounter.GetDeviceMountPath(volSpec)
537 if err != nil {
538 t.Fatalf("csiTest.VolumeAll deviceMounter.GetdeviceMountPath failed %s", err)
539 }
540 if err := csiDevUnmounter.UnmountDevice(devMountPath); err != nil {
541 t.Fatalf("csiTest.VolumeAll deviceMounter.UnmountDevice failed: %s", err)
542 }
543 t.Log("csiTest.VolumeAll deviceUnmounter.UnmountDevice done OK for path", devMountPath)
544 }
545 } else {
546 t.Log("csiTest.VolumeAll DeviceMountablePluginBySpec did not find a plugin, skipping unmounting.")
547 }
548
549
550 t.Log("csiTest.VolumeAll Detaching volume...")
551 attachPlug, err = attachDetachPlugMgr.FindAttachablePluginBySpec(volSpec)
552 if err != nil {
553 t.Fatalf("csiTest.VolumeAll PluginManager.FindAttachablePluginBySpec failed: %v", err)
554 }
555
556 if test.isInline && attachPlug != nil {
557 t.Fatal("csiTest.VolumeAll AttachablePlugin found with ephemeral volume")
558 }
559 if !test.isInline && attachPlug == nil {
560 t.Fatal("csiTest.VolumeAll AttachablePlugin not found with PV")
561 }
562
563 if attachPlug != nil {
564 volDetacher, err := attachPlug.NewDetacher()
565 if err != nil {
566 t.Fatal("csiTest.VolumeAll failed to create new detacher: ", err)
567 }
568
569 t.Log("csiTest.VolumeAll preparing detacher.Detach...")
570 volName, err := volPlug.GetVolumeName(volSpec)
571 if err != nil {
572 t.Fatal("csiTest.VolumeAll volumePlugin.GetVolumeName failed:", err)
573 }
574 csiDetacher := getCsiAttacherFromVolumeDetacher(volDetacher, test.watchTimeout)
575 csiDetacher.csiClient = csiClient
576 if err := csiDetacher.Detach(volName, attachDetachVolumeHost.GetNodeName()); err != nil {
577 t.Fatal("csiTest.VolumeAll detacher.Detach failed:", err)
578 }
579 t.Log("csiTest.VolumeAll detacher.Detach succeeded for volume", volName)
580
581 } else {
582 t.Log("csiTest.VolumeAll attachable plugin not found for plugin.Detach call, skipping")
583 }
584 })
585 }
586 }
587
View as plain text