csi_plugin_test.go 51 KB

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