csi_test.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. /*
  2. Copyright 2019 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package csi
  14. import (
  15. "fmt"
  16. "math/rand"
  17. "os"
  18. "path/filepath"
  19. "testing"
  20. "time"
  21. api "k8s.io/api/core/v1"
  22. v1 "k8s.io/api/core/v1"
  23. storage "k8s.io/api/storage/v1"
  24. storagebeta1 "k8s.io/api/storage/v1beta1"
  25. meta "k8s.io/apimachinery/pkg/apis/meta/v1"
  26. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  27. "k8s.io/apimachinery/pkg/runtime"
  28. "k8s.io/apimachinery/pkg/types"
  29. "k8s.io/apimachinery/pkg/util/wait"
  30. "k8s.io/apimachinery/pkg/watch"
  31. utilfeature "k8s.io/apiserver/pkg/util/feature"
  32. "k8s.io/client-go/informers"
  33. fakeclient "k8s.io/client-go/kubernetes/fake"
  34. utiltesting "k8s.io/client-go/util/testing"
  35. featuregatetesting "k8s.io/component-base/featuregate/testing"
  36. "k8s.io/kubernetes/pkg/features"
  37. "k8s.io/kubernetes/pkg/volume"
  38. volumetest "k8s.io/kubernetes/pkg/volume/testing"
  39. )
  40. // TestCSI_VolumeAll runs a close approximation of volume workflow
  41. // based on operations from the volume manager/reconciler/operation executor
  42. func TestCSI_VolumeAll(t *testing.T) {
  43. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIInlineVolume, true)()
  44. tests := []struct {
  45. name string
  46. specName string
  47. driver string
  48. volName string
  49. specFunc func(specName, driver, volName string) *volume.Spec
  50. podFunc func() *api.Pod
  51. isInline bool
  52. shouldFail bool
  53. driverSpec *storagebeta1.CSIDriverSpec
  54. }{
  55. {
  56. name: "PersistentVolume",
  57. specName: "pv2",
  58. driver: "simple-driver",
  59. volName: "vol2",
  60. specFunc: func(specName, driver, volName string) *volume.Spec {
  61. return volume.NewSpecFromPersistentVolume(makeTestPV(specName, 20, driver, volName), false)
  62. },
  63. podFunc: func() *api.Pod {
  64. podUID := types.UID(fmt.Sprintf("%08X", rand.Uint64()))
  65. return &api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}}
  66. },
  67. },
  68. {
  69. name: "PersistentVolume with driver info",
  70. specName: "pv2",
  71. driver: "simple-driver",
  72. volName: "vol2",
  73. specFunc: func(specName, driver, volName string) *volume.Spec {
  74. return volume.NewSpecFromPersistentVolume(makeTestPV(specName, 20, driver, volName), false)
  75. },
  76. podFunc: func() *api.Pod {
  77. podUID := types.UID(fmt.Sprintf("%08X", rand.Uint64()))
  78. return &api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}}
  79. },
  80. driverSpec: &storagebeta1.CSIDriverSpec{
  81. // Required for the driver to be accepted for the persistent volume.
  82. VolumeLifecycleModes: []storagebeta1.VolumeLifecycleMode{storagebeta1.VolumeLifecyclePersistent},
  83. },
  84. },
  85. {
  86. name: "PersistentVolume with wrong mode in driver info",
  87. specName: "pv2",
  88. driver: "simple-driver",
  89. volName: "vol2",
  90. specFunc: func(specName, driver, volName string) *volume.Spec {
  91. return volume.NewSpecFromPersistentVolume(makeTestPV(specName, 20, driver, volName), false)
  92. },
  93. podFunc: func() *api.Pod {
  94. podUID := types.UID(fmt.Sprintf("%08X", rand.Uint64()))
  95. return &api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}}
  96. },
  97. driverSpec: &storagebeta1.CSIDriverSpec{
  98. // This will cause the volume to be rejected.
  99. VolumeLifecycleModes: []storagebeta1.VolumeLifecycleMode{storagebeta1.VolumeLifecycleEphemeral},
  100. },
  101. shouldFail: true,
  102. },
  103. {
  104. name: "ephemeral inline supported",
  105. driver: "inline-driver-1",
  106. volName: "test.vol2",
  107. specFunc: func(specName, driver, volName string) *volume.Spec {
  108. return volume.NewSpecFromVolume(makeTestVol(specName, driver))
  109. },
  110. podFunc: func() *api.Pod {
  111. podUID := types.UID(fmt.Sprintf("%08X", rand.Uint64()))
  112. return &api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}}
  113. },
  114. isInline: true,
  115. driverSpec: &storagebeta1.CSIDriverSpec{
  116. // Required for the driver to be accepted for the inline volume.
  117. VolumeLifecycleModes: []storagebeta1.VolumeLifecycleMode{storagebeta1.VolumeLifecycleEphemeral},
  118. },
  119. },
  120. {
  121. name: "ephemeral inline also supported",
  122. driver: "inline-driver-1",
  123. volName: "test.vol2",
  124. specFunc: func(specName, driver, volName string) *volume.Spec {
  125. return volume.NewSpecFromVolume(makeTestVol(specName, driver))
  126. },
  127. podFunc: func() *api.Pod {
  128. podUID := types.UID(fmt.Sprintf("%08X", rand.Uint64()))
  129. return &api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}}
  130. },
  131. isInline: true,
  132. driverSpec: &storagebeta1.CSIDriverSpec{
  133. // Required for the driver to be accepted for the inline volume.
  134. VolumeLifecycleModes: []storagebeta1.VolumeLifecycleMode{storagebeta1.VolumeLifecyclePersistent, storagebeta1.VolumeLifecycleEphemeral},
  135. },
  136. },
  137. {
  138. name: "ephemeral inline without CSIDriver info",
  139. driver: "inline-driver-2",
  140. volName: "test.vol3",
  141. specFunc: func(specName, driver, volName string) *volume.Spec {
  142. return volume.NewSpecFromVolume(makeTestVol(specName, driver))
  143. },
  144. podFunc: func() *api.Pod {
  145. podUID := types.UID(fmt.Sprintf("%08X", rand.Uint64()))
  146. return &api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}}
  147. },
  148. isInline: true,
  149. },
  150. {
  151. name: "ephemeral inline with driver that has no mode",
  152. driver: "inline-driver-3",
  153. volName: "test.vol4",
  154. specFunc: func(specName, driver, volName string) *volume.Spec {
  155. return volume.NewSpecFromVolume(makeTestVol(specName, driver))
  156. },
  157. podFunc: func() *api.Pod {
  158. podUID := types.UID(fmt.Sprintf("%08X", rand.Uint64()))
  159. return &api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}}
  160. },
  161. isInline: true,
  162. driverSpec: &storagebeta1.CSIDriverSpec{
  163. // This means the driver *cannot* handle the inline volume because
  164. // the default is "persistent".
  165. VolumeLifecycleModes: nil,
  166. },
  167. },
  168. {
  169. name: "ephemeral inline with driver that has wrong mode",
  170. driver: "inline-driver-3",
  171. volName: "test.vol4",
  172. specFunc: func(specName, driver, volName string) *volume.Spec {
  173. return volume.NewSpecFromVolume(makeTestVol(specName, driver))
  174. },
  175. podFunc: func() *api.Pod {
  176. podUID := types.UID(fmt.Sprintf("%08X", rand.Uint64()))
  177. return &api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}}
  178. },
  179. isInline: true,
  180. driverSpec: &storagebeta1.CSIDriverSpec{
  181. // This means the driver *cannot* handle the inline volume.
  182. VolumeLifecycleModes: []storagebeta1.VolumeLifecycleMode{storagebeta1.VolumeLifecyclePersistent},
  183. },
  184. },
  185. {
  186. name: "missing spec",
  187. specName: "pv2",
  188. driver: "simple-driver",
  189. volName: "vol2",
  190. specFunc: func(specName, driver, volName string) *volume.Spec {
  191. return nil
  192. },
  193. podFunc: func() *api.Pod {
  194. podUID := types.UID(fmt.Sprintf("%08X", rand.Uint64()))
  195. return &api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}}
  196. },
  197. shouldFail: true,
  198. },
  199. {
  200. name: "incompete spec",
  201. specName: "pv2",
  202. driver: "simple-driver",
  203. volName: "vol2",
  204. specFunc: func(specName, driver, volName string) *volume.Spec {
  205. return &volume.Spec{ReadOnly: true}
  206. },
  207. podFunc: func() *api.Pod {
  208. podUID := types.UID(fmt.Sprintf("%08X", rand.Uint64()))
  209. return &api.Pod{ObjectMeta: meta.ObjectMeta{UID: podUID, Namespace: testns}}
  210. },
  211. shouldFail: true,
  212. },
  213. }
  214. for _, test := range tests {
  215. t.Run(test.name, func(t *testing.T) {
  216. tmpDir, err := utiltesting.MkTmpdir("csi-test")
  217. if err != nil {
  218. t.Fatalf("can't create temp dir: %v", err)
  219. }
  220. defer os.RemoveAll(tmpDir)
  221. var driverInfo *storagebeta1.CSIDriver
  222. objs := []runtime.Object{}
  223. if test.driverSpec != nil {
  224. driverInfo = &storagebeta1.CSIDriver{
  225. ObjectMeta: metav1.ObjectMeta{
  226. Name: test.driver,
  227. },
  228. Spec: *test.driverSpec,
  229. }
  230. objs = append(objs, driverInfo)
  231. }
  232. objs = append(objs, &v1.Node{
  233. ObjectMeta: metav1.ObjectMeta{
  234. Name: "fakeNode",
  235. },
  236. Spec: v1.NodeSpec{},
  237. })
  238. client := fakeclient.NewSimpleClientset(objs...)
  239. fakeWatcher := watch.NewRaceFreeFake()
  240. factory := informers.NewSharedInformerFactory(client, time.Hour /* disable resync */)
  241. csiDriverInformer := factory.Storage().V1beta1().CSIDrivers()
  242. if driverInfo != nil {
  243. csiDriverInformer.Informer().GetStore().Add(driverInfo)
  244. }
  245. factory.Start(wait.NeverStop)
  246. host := volumetest.NewFakeVolumeHostWithCSINodeName(t,
  247. tmpDir,
  248. client,
  249. ProbeVolumePlugins(),
  250. "fakeNode",
  251. csiDriverInformer.Lister(),
  252. )
  253. plugMgr := host.GetPluginMgr()
  254. csiClient := setupClient(t, true)
  255. volSpec := test.specFunc(test.specName, test.driver, test.volName)
  256. pod := test.podFunc()
  257. attachName := getAttachmentName(test.volName, test.driver, string(host.GetNodeName()))
  258. t.Log("csiTest.VolumeAll starting...")
  259. // *************** Attach/Mount volume resources ****************//
  260. // attach volume
  261. t.Log("csiTest.VolumeAll Attaching volume...")
  262. attachPlug, err := plugMgr.FindAttachablePluginBySpec(volSpec)
  263. if err != nil {
  264. if !test.shouldFail {
  265. t.Fatalf("csiTest.VolumeAll PluginManager.FindAttachablePluginBySpec failed: %v", err)
  266. } else {
  267. t.Log("csiTest.VolumeAll failed: ", err)
  268. return
  269. }
  270. }
  271. if test.isInline && attachPlug != nil {
  272. t.Fatal("csiTest.VolumeAll AttachablePlugin found with ephemeral volume")
  273. }
  274. if !test.isInline && attachPlug == nil {
  275. t.Fatal("csiTest.VolumeAll AttachablePlugin not found with PV")
  276. }
  277. var devicePath string
  278. if attachPlug != nil {
  279. t.Log("csiTest.VolumeAll attacher.Attach starting")
  280. var volAttacher volume.Attacher
  281. volAttacher, err := attachPlug.NewAttacher()
  282. if err != nil {
  283. t.Fatal("csiTest.VolumeAll failed to create new attacher: ", err)
  284. }
  285. // creates VolumeAttachment and blocks until it is marked attached (done by external attacher)
  286. go func(spec *volume.Spec, nodeName types.NodeName) {
  287. attachID, err := volAttacher.Attach(spec, nodeName)
  288. if err != nil {
  289. t.Fatalf("csiTest.VolumeAll attacher.Attach failed: %s", err)
  290. }
  291. t.Logf("csiTest.VolumeAll got attachID %s", attachID)
  292. }(volSpec, host.GetNodeName())
  293. // Simulates external-attacher and marks VolumeAttachment.Status.Attached = true
  294. markVolumeAttached(t, host.GetKubeClient(), fakeWatcher, attachName, storage.VolumeAttachmentStatus{Attached: true})
  295. devicePath, err = volAttacher.WaitForAttach(volSpec, "", pod, 500*time.Millisecond)
  296. if err != nil {
  297. t.Fatal("csiTest.VolumeAll attacher.WaitForAttach failed:", err)
  298. }
  299. if devicePath != attachName {
  300. t.Fatalf("csiTest.VolumeAll attacher.WaitForAttach got unexpected value %s", devicePath)
  301. }
  302. t.Log("csiTest.VolumeAll attacher.WaitForAttach succeeded OK, attachment ID:", devicePath)
  303. } else {
  304. t.Log("csiTest.VolumeAll volume attacher not found, skipping attachment")
  305. }
  306. // Mount Device
  307. t.Log("csiTest.VolumeAll Mouting device...")
  308. devicePlug, err := plugMgr.FindDeviceMountablePluginBySpec(volSpec)
  309. if err != nil {
  310. t.Fatalf("csiTest.VolumeAll PluginManager.FindDeviceMountablePluginBySpec failed: %v", err)
  311. }
  312. if test.isInline && devicePlug != nil {
  313. t.Fatal("csiTest.VolumeAll DeviceMountablePlugin found with ephemeral volume")
  314. }
  315. if !test.isInline && devicePlug == nil {
  316. t.Fatal("csiTest.VolumeAll DeviceMountablePlugin not found with PV")
  317. }
  318. var devMounter volume.DeviceMounter
  319. if devicePlug != nil {
  320. devMounter, err = devicePlug.NewDeviceMounter()
  321. if err != nil {
  322. t.Fatal("csiTest.VolumeAll failed to create new device mounter: ", err)
  323. }
  324. }
  325. if devMounter != nil {
  326. csiDevMounter := devMounter.(*csiAttacher)
  327. csiDevMounter.csiClient = csiClient
  328. devMountPath, err := csiDevMounter.GetDeviceMountPath(volSpec)
  329. if err != nil {
  330. t.Fatalf("csiTest.VolumeAll deviceMounter.GetdeviceMountPath failed %s", err)
  331. }
  332. if err := csiDevMounter.MountDevice(volSpec, devicePath, devMountPath); err != nil {
  333. t.Fatalf("csiTest.VolumeAll deviceMounter.MountDevice failed: %v", err)
  334. }
  335. t.Log("csiTest.VolumeAll device mounted at path:", devMountPath)
  336. } else {
  337. t.Log("csiTest.VolumeAll DeviceMountablePlugin not found, skipping deviceMounter.MountDevice")
  338. }
  339. // mount volume
  340. t.Log("csiTest.VolumeAll Mouting volume...")
  341. volPlug, err := plugMgr.FindPluginBySpec(volSpec)
  342. if err != nil || volPlug == nil {
  343. t.Fatalf("csiTest.VolumeAll PluginMgr.FindPluginBySpec failed: %v", err)
  344. }
  345. if volPlug == nil {
  346. t.Fatalf("csiTest.VolumeAll volumePlugin is nil")
  347. }
  348. if !volPlug.CanSupport(volSpec) {
  349. t.Fatal("csiTest.VolumeAll volumePlugin.CanSupport returned false")
  350. }
  351. mounter, err := volPlug.NewMounter(volSpec, pod, volume.VolumeOptions{})
  352. if test.isInline && (test.driverSpec == nil || !containsVolumeMode(test.driverSpec.VolumeLifecycleModes, storagebeta1.VolumeLifecycleEphemeral)) {
  353. // This *must* fail because a CSIDriver.Spec.VolumeLifecycleModes entry "ephemeral"
  354. // is required.
  355. if err == nil || mounter != nil {
  356. 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)
  357. }
  358. return
  359. }
  360. if !test.isInline && test.driverSpec != nil && !containsVolumeMode(test.driverSpec.VolumeLifecycleModes, storagebeta1.VolumeLifecyclePersistent) {
  361. // This *must* fail because a CSIDriver.Spec.VolumeLifecycleModes entry "persistent"
  362. // is required when a driver object is available.
  363. if err == nil || mounter != nil {
  364. 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)
  365. }
  366. return
  367. }
  368. if err != nil || mounter == nil {
  369. t.Fatalf("csiTest.VolumeAll volPlugin.NewMounter is nil or error: %s", err)
  370. }
  371. if err := mounter.CanMount(); err != nil {
  372. t.Fatal("csiTest.VolumeAll mounter.CanMount failed, skipping mount")
  373. }
  374. var fsGroup *int64
  375. if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.FSGroup != nil {
  376. fsGroup = pod.Spec.SecurityContext.FSGroup
  377. }
  378. csiMounter := mounter.(*csiMountMgr)
  379. csiMounter.csiClient = csiClient
  380. var mounterArgs volume.MounterArgs
  381. mounterArgs.FsGroup = fsGroup
  382. if err := csiMounter.SetUp(mounterArgs); err != nil {
  383. t.Fatalf("csiTest.VolumeAll mounter.Setup(fsGroup) failed: %s", err)
  384. }
  385. t.Log("csiTest.VolumeAll mounter.Setup(fsGroup) done OK")
  386. dataFile := filepath.Join(filepath.Dir(mounter.GetPath()), volDataFileName)
  387. if _, err := os.Stat(dataFile); err != nil {
  388. t.Fatalf("csiTest.VolumeAll meatadata JSON file not found: %s", dataFile)
  389. }
  390. t.Log("csiTest.VolumeAll JSON datafile generated OK:", dataFile)
  391. // ******** Volume Reconstruction ************* //
  392. volPath := filepath.Dir(csiMounter.GetPath())
  393. t.Log("csiTest.VolumeAll entering plugin.ConstructVolumeSpec for path", volPath)
  394. spec, err := volPlug.ConstructVolumeSpec(test.volName, volPath)
  395. if err != nil {
  396. t.Fatalf("csiTest.VolumeAll plugin.ConstructVolumeSpec failed: %s", err)
  397. } else {
  398. if spec == nil {
  399. t.Fatalf("csiTest.VolumeAll plugin.ConstructVolumeSpec returned nil spec")
  400. } else {
  401. volSpec = spec
  402. if test.isInline {
  403. if volSpec.Volume == nil || volSpec.Volume.CSI == nil {
  404. t.Fatal("csiTest.VolumeAll reconstruction of ephemeral volumeSpec missing CSI Volume source")
  405. }
  406. if volSpec.Volume.CSI.Driver == "" {
  407. t.Fatal("csiTest.VolumeAll reconstruction ephemral volume missing driver name")
  408. }
  409. } else {
  410. if volSpec.PersistentVolume == nil || volSpec.PersistentVolume.Spec.CSI == nil {
  411. t.Fatal("csiTest.VolumeAll reconstruction of volumeSpec missing CSI PersistentVolume source")
  412. }
  413. csi := volSpec.PersistentVolume.Spec.CSI
  414. if csi.Driver == "" {
  415. t.Fatal("csiTest.VolumeAll reconstruction of PV missing driver name")
  416. }
  417. if csi.VolumeHandle == "" {
  418. t.Fatal("csiTest.VolumeAll reconstruction of PV missing volume handle")
  419. }
  420. }
  421. }
  422. }
  423. // ************* Teardown everything **************** //
  424. t.Log("csiTest.VolumeAll Tearing down...")
  425. // unmount volume
  426. t.Log("csiTest.VolumeAll Unmouting volume...")
  427. volPlug, err = plugMgr.FindPluginBySpec(volSpec)
  428. if err != nil || volPlug == nil {
  429. t.Fatalf("csiTest.VolumeAll PluginMgr.FindPluginBySpec failed: %v", err)
  430. }
  431. if volPlug == nil {
  432. t.Fatalf("csiTest.VolumeAll volumePlugin is nil")
  433. }
  434. mounter, err = volPlug.NewMounter(volSpec, pod, volume.VolumeOptions{})
  435. if err != nil || mounter == nil {
  436. t.Fatalf("csiTest.VolumeAll volPlugin.NewMounter is nil or error: %s", err)
  437. }
  438. unmounter, err := volPlug.NewUnmounter(test.specName, pod.GetUID())
  439. if err != nil {
  440. t.Fatal("csiTest.VolumeAll volumePlugin.NewUnmounter failed:", err)
  441. }
  442. csiUnmounter := unmounter.(*csiMountMgr)
  443. csiUnmounter.csiClient = csiClient
  444. if err := csiUnmounter.TearDownAt(mounter.GetPath()); err != nil {
  445. t.Fatal("csiTest.VolumeAll unmounter.TearDownAt failed:", err)
  446. }
  447. t.Log("csiTest.VolumeAll unmounter.TearDownAt done OK for dir:", mounter.GetPath())
  448. // unmount device
  449. t.Log("csiTest.VolumeAll Unmouting device...")
  450. devicePlug, err = plugMgr.FindDeviceMountablePluginBySpec(volSpec)
  451. if err != nil {
  452. t.Fatalf("csiTest.VolumeAll failed to create mountable device plugin: %s", err)
  453. }
  454. if test.isInline && devicePlug != nil {
  455. t.Fatal("csiTest.VolumeAll DeviceMountablePlugin found with ephemeral volume")
  456. }
  457. if !test.isInline && devicePlug == nil {
  458. t.Fatal("csiTest.VolumeAll DeviceMountablePlugin not found with PV")
  459. }
  460. var devUnmounter volume.DeviceUnmounter
  461. if devicePlug != nil {
  462. t.Log("csiTest.VolumeAll found DeviceMountablePlugin, entering device unmouting ...")
  463. devMounter, err = devicePlug.NewDeviceMounter()
  464. if err != nil {
  465. t.Fatal("csiTest.VolumeAll failed to create new device mounter: ", err)
  466. }
  467. devUnmounter, err = devicePlug.NewDeviceUnmounter()
  468. if err != nil {
  469. t.Fatal("csiTest.VolumeAll failed to create new device unmounter: ", err)
  470. }
  471. if devMounter != nil && devUnmounter != nil {
  472. csiDevMounter := devMounter.(*csiAttacher)
  473. csiDevUnmounter := devUnmounter.(*csiAttacher)
  474. csiDevUnmounter.csiClient = csiClient
  475. devMountPath, err := csiDevMounter.GetDeviceMountPath(volSpec)
  476. if err != nil {
  477. t.Fatalf("csiTest.VolumeAll deviceMounter.GetdeviceMountPath failed %s", err)
  478. }
  479. if err := csiDevUnmounter.UnmountDevice(devMountPath); err != nil {
  480. t.Fatalf("csiTest.VolumeAll deviceMounter.UnmountDevice failed: %s", err)
  481. }
  482. t.Log("csiTest.VolumeAll deviceUmounter.UnmountDevice done OK for path", devMountPath)
  483. }
  484. } else {
  485. t.Log("csiTest.VolumeAll DeviceMountablePluginBySpec did not find a plugin, skipping unmounting.")
  486. }
  487. // detach volume
  488. t.Log("csiTest.VolumeAll Detaching volume...")
  489. attachPlug, err = plugMgr.FindAttachablePluginBySpec(volSpec)
  490. if err != nil {
  491. t.Fatalf("csiTest.VolumeAll PluginManager.FindAttachablePluginBySpec failed: %v", err)
  492. }
  493. if test.isInline && attachPlug != nil {
  494. t.Fatal("csiTest.VolumeAll AttachablePlugin found with ephemeral volume")
  495. }
  496. if !test.isInline && attachPlug == nil {
  497. t.Fatal("csiTest.VolumeAll AttachablePlugin not found with PV")
  498. }
  499. if attachPlug != nil {
  500. volDetacher, err := attachPlug.NewDetacher()
  501. if err != nil {
  502. t.Fatal("csiTest.VolumeAll failed to create new detacher: ", err)
  503. }
  504. t.Log("csiTest.VolumeAll preparing detacher.Detach...")
  505. volName, err := volPlug.GetVolumeName(volSpec)
  506. if err != nil {
  507. t.Fatal("csiTest.VolumeAll volumePlugin.GetVolumeName failed:", err)
  508. }
  509. csiDetacher := volDetacher.(*csiAttacher)
  510. csiDetacher.csiClient = csiClient
  511. if err := csiDetacher.Detach(volName, host.GetNodeName()); err != nil {
  512. t.Fatal("csiTest.VolumeAll detacher.Detach failed:", err)
  513. }
  514. t.Log("csiTest.VolumeAll detacher.Detach succeeded for volume", volName)
  515. } else {
  516. t.Log("csiTest.VolumeAll attachable plugin not found for plugin.Detach call, skipping")
  517. }
  518. })
  519. }
  520. }