validation_test.go 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649
  1. /*
  2. Copyright 2016 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 validation
  14. import (
  15. "fmt"
  16. "strings"
  17. "testing"
  18. "k8s.io/apimachinery/pkg/api/resource"
  19. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  20. utilfeature "k8s.io/apiserver/pkg/util/feature"
  21. featuregatetesting "k8s.io/component-base/featuregate/testing"
  22. api "k8s.io/kubernetes/pkg/apis/core"
  23. "k8s.io/kubernetes/pkg/apis/storage"
  24. "k8s.io/kubernetes/pkg/features"
  25. )
  26. var (
  27. deleteReclaimPolicy = api.PersistentVolumeReclaimDelete
  28. immediateMode1 = storage.VolumeBindingImmediate
  29. immediateMode2 = storage.VolumeBindingImmediate
  30. waitingMode = storage.VolumeBindingWaitForFirstConsumer
  31. invalidMode = storage.VolumeBindingMode("foo")
  32. inlineSpec = api.PersistentVolumeSpec{
  33. AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
  34. PersistentVolumeSource: api.PersistentVolumeSource{
  35. CSI: &api.CSIPersistentVolumeSource{
  36. Driver: "com.test.foo",
  37. VolumeHandle: "foobar",
  38. },
  39. },
  40. }
  41. )
  42. func TestValidateStorageClass(t *testing.T) {
  43. deleteReclaimPolicy := api.PersistentVolumeReclaimPolicy("Delete")
  44. retainReclaimPolicy := api.PersistentVolumeReclaimPolicy("Retain")
  45. recycleReclaimPolicy := api.PersistentVolumeReclaimPolicy("Recycle")
  46. successCases := []storage.StorageClass{
  47. {
  48. // empty parameters
  49. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  50. Provisioner: "kubernetes.io/foo-provisioner",
  51. Parameters: map[string]string{},
  52. ReclaimPolicy: &deleteReclaimPolicy,
  53. VolumeBindingMode: &immediateMode1,
  54. },
  55. {
  56. // nil parameters
  57. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  58. Provisioner: "kubernetes.io/foo-provisioner",
  59. ReclaimPolicy: &deleteReclaimPolicy,
  60. VolumeBindingMode: &immediateMode1,
  61. },
  62. {
  63. // some parameters
  64. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  65. Provisioner: "kubernetes.io/foo-provisioner",
  66. Parameters: map[string]string{
  67. "kubernetes.io/foo-parameter": "free/form/string",
  68. "foo-parameter": "free-form-string",
  69. "foo-parameter2": "{\"embedded\": \"json\", \"with\": {\"structures\":\"inside\"}}",
  70. },
  71. ReclaimPolicy: &deleteReclaimPolicy,
  72. VolumeBindingMode: &immediateMode1,
  73. },
  74. {
  75. // retain reclaimPolicy
  76. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  77. Provisioner: "kubernetes.io/foo-provisioner",
  78. ReclaimPolicy: &retainReclaimPolicy,
  79. VolumeBindingMode: &immediateMode1,
  80. },
  81. }
  82. // Success cases are expected to pass validation.
  83. for k, v := range successCases {
  84. if errs := ValidateStorageClass(&v); len(errs) != 0 {
  85. t.Errorf("Expected success for %d, got %v", k, errs)
  86. }
  87. }
  88. // generate a map longer than maxProvisionerParameterSize
  89. longParameters := make(map[string]string)
  90. totalSize := 0
  91. for totalSize < maxProvisionerParameterSize {
  92. k := fmt.Sprintf("param/%d", totalSize)
  93. v := fmt.Sprintf("value-%d", totalSize)
  94. longParameters[k] = v
  95. totalSize = totalSize + len(k) + len(v)
  96. }
  97. errorCases := map[string]storage.StorageClass{
  98. "namespace is present": {
  99. ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
  100. Provisioner: "kubernetes.io/foo-provisioner",
  101. ReclaimPolicy: &deleteReclaimPolicy,
  102. },
  103. "invalid provisioner": {
  104. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  105. Provisioner: "kubernetes.io/invalid/provisioner",
  106. ReclaimPolicy: &deleteReclaimPolicy,
  107. },
  108. "invalid empty parameter name": {
  109. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  110. Provisioner: "kubernetes.io/foo",
  111. Parameters: map[string]string{
  112. "": "value",
  113. },
  114. ReclaimPolicy: &deleteReclaimPolicy,
  115. },
  116. "provisioner: Required value": {
  117. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  118. Provisioner: "",
  119. ReclaimPolicy: &deleteReclaimPolicy,
  120. },
  121. "too long parameters": {
  122. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  123. Provisioner: "kubernetes.io/foo",
  124. Parameters: longParameters,
  125. ReclaimPolicy: &deleteReclaimPolicy,
  126. },
  127. "invalid reclaimpolicy": {
  128. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  129. Provisioner: "kubernetes.io/foo",
  130. ReclaimPolicy: &recycleReclaimPolicy,
  131. },
  132. }
  133. // Error cases are not expected to pass validation.
  134. for testName, storageClass := range errorCases {
  135. if errs := ValidateStorageClass(&storageClass); len(errs) == 0 {
  136. t.Errorf("Expected failure for test: %s", testName)
  137. }
  138. }
  139. }
  140. func TestVolumeAttachmentValidation(t *testing.T) {
  141. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIMigration, true)()
  142. volumeName := "pv-name"
  143. empty := ""
  144. migrationEnabledSuccessCases := []storage.VolumeAttachment{
  145. {
  146. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  147. Spec: storage.VolumeAttachmentSpec{
  148. Attacher: "myattacher",
  149. Source: storage.VolumeAttachmentSource{
  150. PersistentVolumeName: &volumeName,
  151. },
  152. NodeName: "mynode",
  153. },
  154. },
  155. {
  156. ObjectMeta: metav1.ObjectMeta{Name: "foo-with-inlinespec"},
  157. Spec: storage.VolumeAttachmentSpec{
  158. Attacher: "myattacher",
  159. Source: storage.VolumeAttachmentSource{
  160. InlineVolumeSpec: &inlineSpec,
  161. },
  162. NodeName: "mynode",
  163. },
  164. },
  165. {
  166. ObjectMeta: metav1.ObjectMeta{Name: "foo-with-status"},
  167. Spec: storage.VolumeAttachmentSpec{
  168. Attacher: "myattacher",
  169. Source: storage.VolumeAttachmentSource{
  170. PersistentVolumeName: &volumeName,
  171. },
  172. NodeName: "mynode",
  173. },
  174. Status: storage.VolumeAttachmentStatus{
  175. Attached: true,
  176. AttachmentMetadata: map[string]string{
  177. "foo": "bar",
  178. },
  179. AttachError: &storage.VolumeError{
  180. Time: metav1.Time{},
  181. Message: "hello world",
  182. },
  183. DetachError: &storage.VolumeError{
  184. Time: metav1.Time{},
  185. Message: "hello world",
  186. },
  187. },
  188. },
  189. {
  190. ObjectMeta: metav1.ObjectMeta{Name: "foo-with-inlinespec-and-status"},
  191. Spec: storage.VolumeAttachmentSpec{
  192. Attacher: "myattacher",
  193. Source: storage.VolumeAttachmentSource{
  194. InlineVolumeSpec: &inlineSpec,
  195. },
  196. NodeName: "mynode",
  197. },
  198. Status: storage.VolumeAttachmentStatus{
  199. Attached: true,
  200. AttachmentMetadata: map[string]string{
  201. "foo": "bar",
  202. },
  203. AttachError: &storage.VolumeError{
  204. Time: metav1.Time{},
  205. Message: "hello world",
  206. },
  207. DetachError: &storage.VolumeError{
  208. Time: metav1.Time{},
  209. Message: "hello world",
  210. },
  211. },
  212. },
  213. }
  214. for _, volumeAttachment := range migrationEnabledSuccessCases {
  215. if errs := ValidateVolumeAttachment(&volumeAttachment); len(errs) != 0 {
  216. t.Errorf("expected success: %v %v", volumeAttachment, errs)
  217. }
  218. }
  219. migrationEnabledErrorCases := []storage.VolumeAttachment{
  220. {
  221. // Empty attacher name
  222. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  223. Spec: storage.VolumeAttachmentSpec{
  224. Attacher: "",
  225. NodeName: "mynode",
  226. Source: storage.VolumeAttachmentSource{
  227. PersistentVolumeName: &volumeName,
  228. },
  229. },
  230. },
  231. {
  232. // Empty node name
  233. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  234. Spec: storage.VolumeAttachmentSpec{
  235. Attacher: "myattacher",
  236. NodeName: "",
  237. Source: storage.VolumeAttachmentSource{
  238. PersistentVolumeName: &volumeName,
  239. },
  240. },
  241. },
  242. {
  243. // No volume name
  244. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  245. Spec: storage.VolumeAttachmentSpec{
  246. Attacher: "myattacher",
  247. NodeName: "node",
  248. Source: storage.VolumeAttachmentSource{
  249. PersistentVolumeName: nil,
  250. },
  251. },
  252. },
  253. {
  254. // Empty volume name
  255. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  256. Spec: storage.VolumeAttachmentSpec{
  257. Attacher: "myattacher",
  258. NodeName: "node",
  259. Source: storage.VolumeAttachmentSource{
  260. PersistentVolumeName: &empty,
  261. },
  262. },
  263. },
  264. {
  265. // Too long error message
  266. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  267. Spec: storage.VolumeAttachmentSpec{
  268. Attacher: "myattacher",
  269. NodeName: "node",
  270. Source: storage.VolumeAttachmentSource{
  271. PersistentVolumeName: &volumeName,
  272. },
  273. },
  274. Status: storage.VolumeAttachmentStatus{
  275. Attached: true,
  276. AttachmentMetadata: map[string]string{
  277. "foo": "bar",
  278. },
  279. AttachError: &storage.VolumeError{
  280. Time: metav1.Time{},
  281. Message: "hello world",
  282. },
  283. DetachError: &storage.VolumeError{
  284. Time: metav1.Time{},
  285. Message: strings.Repeat("a", maxVolumeErrorMessageSize+1),
  286. },
  287. },
  288. },
  289. {
  290. // Too long metadata
  291. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  292. Spec: storage.VolumeAttachmentSpec{
  293. Attacher: "myattacher",
  294. NodeName: "node",
  295. Source: storage.VolumeAttachmentSource{
  296. PersistentVolumeName: &volumeName,
  297. },
  298. },
  299. Status: storage.VolumeAttachmentStatus{
  300. Attached: true,
  301. AttachmentMetadata: map[string]string{
  302. "foo": strings.Repeat("a", maxAttachedVolumeMetadataSize),
  303. },
  304. AttachError: &storage.VolumeError{
  305. Time: metav1.Time{},
  306. Message: "hello world",
  307. },
  308. DetachError: &storage.VolumeError{
  309. Time: metav1.Time{},
  310. Message: "hello world",
  311. },
  312. },
  313. },
  314. {
  315. // VolumeAttachmentSource with no PersistentVolumeName nor InlineSpec
  316. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  317. Spec: storage.VolumeAttachmentSpec{
  318. Attacher: "myattacher",
  319. NodeName: "node",
  320. Source: storage.VolumeAttachmentSource{},
  321. },
  322. },
  323. {
  324. // VolumeAttachmentSource with PersistentVolumeName and InlineSpec
  325. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  326. Spec: storage.VolumeAttachmentSpec{
  327. Attacher: "myattacher",
  328. NodeName: "node",
  329. Source: storage.VolumeAttachmentSource{
  330. PersistentVolumeName: &volumeName,
  331. InlineVolumeSpec: &inlineSpec,
  332. },
  333. },
  334. },
  335. {
  336. // VolumeAttachmentSource with InlineSpec without CSI PV Source
  337. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  338. Spec: storage.VolumeAttachmentSpec{
  339. Attacher: "myattacher",
  340. NodeName: "node",
  341. Source: storage.VolumeAttachmentSource{
  342. PersistentVolumeName: &volumeName,
  343. InlineVolumeSpec: &api.PersistentVolumeSpec{
  344. Capacity: api.ResourceList{
  345. api.ResourceName(api.ResourceStorage): resource.MustParse("10G"),
  346. },
  347. AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
  348. PersistentVolumeSource: api.PersistentVolumeSource{
  349. FlexVolume: &api.FlexPersistentVolumeSource{
  350. Driver: "kubernetes.io/blue",
  351. FSType: "ext4",
  352. },
  353. },
  354. StorageClassName: "test-storage-class",
  355. },
  356. },
  357. },
  358. },
  359. }
  360. for _, volumeAttachment := range migrationEnabledErrorCases {
  361. if errs := ValidateVolumeAttachment(&volumeAttachment); len(errs) == 0 {
  362. t.Errorf("expected failure for test: %v", volumeAttachment)
  363. }
  364. }
  365. // validate with CSIMigration disabled
  366. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIMigration, false)()
  367. migrationDisabledSuccessCases := []storage.VolumeAttachment{
  368. {
  369. // PVName specified with migration disabled
  370. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  371. Spec: storage.VolumeAttachmentSpec{
  372. Attacher: "myattacher",
  373. NodeName: "node",
  374. Source: storage.VolumeAttachmentSource{
  375. PersistentVolumeName: &volumeName,
  376. },
  377. },
  378. },
  379. {
  380. // InlineSpec specified with migration disabled
  381. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  382. Spec: storage.VolumeAttachmentSpec{
  383. Attacher: "myattacher",
  384. NodeName: "node",
  385. Source: storage.VolumeAttachmentSource{
  386. InlineVolumeSpec: &inlineSpec,
  387. },
  388. },
  389. },
  390. }
  391. for _, volumeAttachment := range migrationDisabledSuccessCases {
  392. if errs := ValidateVolumeAttachment(&volumeAttachment); len(errs) != 0 {
  393. t.Errorf("expected success: %v %v", volumeAttachment, errs)
  394. }
  395. }
  396. }
  397. func TestVolumeAttachmentUpdateValidation(t *testing.T) {
  398. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIMigration, true)()
  399. volumeName := "foo"
  400. newVolumeName := "bar"
  401. old := storage.VolumeAttachment{
  402. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  403. Spec: storage.VolumeAttachmentSpec{
  404. Attacher: "myattacher",
  405. Source: storage.VolumeAttachmentSource{},
  406. NodeName: "mynode",
  407. },
  408. }
  409. successCases := []storage.VolumeAttachment{
  410. {
  411. // no change
  412. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  413. Spec: storage.VolumeAttachmentSpec{
  414. Attacher: "myattacher",
  415. Source: storage.VolumeAttachmentSource{},
  416. NodeName: "mynode",
  417. },
  418. },
  419. {
  420. // modify status
  421. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  422. Spec: storage.VolumeAttachmentSpec{
  423. Attacher: "myattacher",
  424. Source: storage.VolumeAttachmentSource{},
  425. NodeName: "mynode",
  426. },
  427. Status: storage.VolumeAttachmentStatus{
  428. Attached: true,
  429. AttachmentMetadata: map[string]string{
  430. "foo": "bar",
  431. },
  432. AttachError: &storage.VolumeError{
  433. Time: metav1.Time{},
  434. Message: "hello world",
  435. },
  436. DetachError: &storage.VolumeError{
  437. Time: metav1.Time{},
  438. Message: "hello world",
  439. },
  440. },
  441. },
  442. }
  443. for _, volumeAttachment := range successCases {
  444. volumeAttachment.Spec.Source = storage.VolumeAttachmentSource{}
  445. old.Spec.Source = storage.VolumeAttachmentSource{}
  446. // test scenarios with PersistentVolumeName set
  447. volumeAttachment.Spec.Source.PersistentVolumeName = &volumeName
  448. old.Spec.Source.PersistentVolumeName = &volumeName
  449. if errs := ValidateVolumeAttachmentUpdate(&volumeAttachment, &old); len(errs) != 0 {
  450. t.Errorf("expected success: %+v", errs)
  451. }
  452. volumeAttachment.Spec.Source = storage.VolumeAttachmentSource{}
  453. old.Spec.Source = storage.VolumeAttachmentSource{}
  454. // test scenarios with InlineVolumeSpec set
  455. volumeAttachment.Spec.Source.InlineVolumeSpec = &inlineSpec
  456. old.Spec.Source.InlineVolumeSpec = &inlineSpec
  457. if errs := ValidateVolumeAttachmentUpdate(&volumeAttachment, &old); len(errs) != 0 {
  458. t.Errorf("expected success: %+v", errs)
  459. }
  460. }
  461. // reset old's source with volumeName in case it was left with something else by earlier tests
  462. old.Spec.Source = storage.VolumeAttachmentSource{}
  463. old.Spec.Source.PersistentVolumeName = &volumeName
  464. errorCases := []storage.VolumeAttachment{
  465. {
  466. // change attacher
  467. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  468. Spec: storage.VolumeAttachmentSpec{
  469. Attacher: "another-attacher",
  470. Source: storage.VolumeAttachmentSource{
  471. PersistentVolumeName: &volumeName,
  472. },
  473. NodeName: "mynode",
  474. },
  475. },
  476. {
  477. // change source volume name
  478. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  479. Spec: storage.VolumeAttachmentSpec{
  480. Attacher: "myattacher",
  481. Source: storage.VolumeAttachmentSource{
  482. PersistentVolumeName: &newVolumeName,
  483. },
  484. NodeName: "mynode",
  485. },
  486. },
  487. {
  488. // change node
  489. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  490. Spec: storage.VolumeAttachmentSpec{
  491. Attacher: "myattacher",
  492. Source: storage.VolumeAttachmentSource{
  493. PersistentVolumeName: &volumeName,
  494. },
  495. NodeName: "anothernode",
  496. },
  497. },
  498. {
  499. // change source
  500. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  501. Spec: storage.VolumeAttachmentSpec{
  502. Attacher: "myattacher",
  503. Source: storage.VolumeAttachmentSource{
  504. InlineVolumeSpec: &inlineSpec,
  505. },
  506. NodeName: "mynode",
  507. },
  508. },
  509. {
  510. // add invalid status
  511. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  512. Spec: storage.VolumeAttachmentSpec{
  513. Attacher: "myattacher",
  514. Source: storage.VolumeAttachmentSource{
  515. PersistentVolumeName: &volumeName,
  516. },
  517. NodeName: "mynode",
  518. },
  519. Status: storage.VolumeAttachmentStatus{
  520. Attached: true,
  521. AttachmentMetadata: map[string]string{
  522. "foo": "bar",
  523. },
  524. AttachError: &storage.VolumeError{
  525. Time: metav1.Time{},
  526. Message: strings.Repeat("a", maxAttachedVolumeMetadataSize),
  527. },
  528. DetachError: &storage.VolumeError{
  529. Time: metav1.Time{},
  530. Message: "hello world",
  531. },
  532. },
  533. },
  534. }
  535. for _, volumeAttachment := range errorCases {
  536. if errs := ValidateVolumeAttachmentUpdate(&volumeAttachment, &old); len(errs) == 0 {
  537. t.Errorf("Expected failure for test: %+v", volumeAttachment)
  538. }
  539. }
  540. }
  541. func TestVolumeAttachmentValidationV1(t *testing.T) {
  542. volumeName := "pv-name"
  543. invalidVolumeName := "-invalid-@#$%^&*()-"
  544. successCases := []storage.VolumeAttachment{
  545. {
  546. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  547. Spec: storage.VolumeAttachmentSpec{
  548. Attacher: "myattacher",
  549. Source: storage.VolumeAttachmentSource{
  550. PersistentVolumeName: &volumeName,
  551. },
  552. NodeName: "mynode",
  553. },
  554. },
  555. }
  556. for _, volumeAttachment := range successCases {
  557. if errs := ValidateVolumeAttachmentV1(&volumeAttachment); len(errs) != 0 {
  558. t.Errorf("expected success: %+v", errs)
  559. }
  560. }
  561. errorCases := []storage.VolumeAttachment{
  562. {
  563. // Invalid attacher name
  564. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  565. Spec: storage.VolumeAttachmentSpec{
  566. Attacher: "invalid-@#$%^&*()",
  567. NodeName: "mynode",
  568. Source: storage.VolumeAttachmentSource{
  569. PersistentVolumeName: &volumeName,
  570. },
  571. },
  572. },
  573. {
  574. // Invalid PV name
  575. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  576. Spec: storage.VolumeAttachmentSpec{
  577. Attacher: "myattacher",
  578. NodeName: "mynode",
  579. Source: storage.VolumeAttachmentSource{
  580. PersistentVolumeName: &invalidVolumeName,
  581. },
  582. },
  583. },
  584. }
  585. for _, volumeAttachment := range errorCases {
  586. if errs := ValidateVolumeAttachmentV1(&volumeAttachment); len(errs) == 0 {
  587. t.Errorf("Expected failure for test: %+v", volumeAttachment)
  588. }
  589. }
  590. }
  591. func makeClass(mode *storage.VolumeBindingMode, topologies []api.TopologySelectorTerm) *storage.StorageClass {
  592. return &storage.StorageClass{
  593. ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "foo"},
  594. Provisioner: "kubernetes.io/foo-provisioner",
  595. ReclaimPolicy: &deleteReclaimPolicy,
  596. VolumeBindingMode: mode,
  597. AllowedTopologies: topologies,
  598. }
  599. }
  600. type bindingTest struct {
  601. class *storage.StorageClass
  602. shouldSucceed bool
  603. }
  604. func TestValidateVolumeBindingMode(t *testing.T) {
  605. cases := map[string]bindingTest{
  606. "no mode": {
  607. class: makeClass(nil, nil),
  608. shouldSucceed: false,
  609. },
  610. "immediate mode": {
  611. class: makeClass(&immediateMode1, nil),
  612. shouldSucceed: true,
  613. },
  614. "waiting mode": {
  615. class: makeClass(&waitingMode, nil),
  616. shouldSucceed: true,
  617. },
  618. "invalid mode": {
  619. class: makeClass(&invalidMode, nil),
  620. shouldSucceed: false,
  621. },
  622. }
  623. for testName, testCase := range cases {
  624. errs := ValidateStorageClass(testCase.class)
  625. if testCase.shouldSucceed && len(errs) != 0 {
  626. t.Errorf("Expected success for test %q, got %v", testName, errs)
  627. }
  628. if !testCase.shouldSucceed && len(errs) == 0 {
  629. t.Errorf("Expected failure for test %q, got success", testName)
  630. }
  631. }
  632. }
  633. type updateTest struct {
  634. oldClass *storage.StorageClass
  635. newClass *storage.StorageClass
  636. shouldSucceed bool
  637. }
  638. func TestValidateUpdateVolumeBindingMode(t *testing.T) {
  639. noBinding := makeClass(nil, nil)
  640. immediateBinding1 := makeClass(&immediateMode1, nil)
  641. immediateBinding2 := makeClass(&immediateMode2, nil)
  642. waitBinding := makeClass(&waitingMode, nil)
  643. cases := map[string]updateTest{
  644. "old and new no mode": {
  645. oldClass: noBinding,
  646. newClass: noBinding,
  647. shouldSucceed: true,
  648. },
  649. "old and new same mode ptr": {
  650. oldClass: immediateBinding1,
  651. newClass: immediateBinding1,
  652. shouldSucceed: true,
  653. },
  654. "old and new same mode value": {
  655. oldClass: immediateBinding1,
  656. newClass: immediateBinding2,
  657. shouldSucceed: true,
  658. },
  659. "old no mode, new mode": {
  660. oldClass: noBinding,
  661. newClass: waitBinding,
  662. shouldSucceed: false,
  663. },
  664. "old mode, new no mode": {
  665. oldClass: waitBinding,
  666. newClass: noBinding,
  667. shouldSucceed: false,
  668. },
  669. "old and new different modes": {
  670. oldClass: waitBinding,
  671. newClass: immediateBinding1,
  672. shouldSucceed: false,
  673. },
  674. }
  675. for testName, testCase := range cases {
  676. errs := ValidateStorageClassUpdate(testCase.newClass, testCase.oldClass)
  677. if testCase.shouldSucceed && len(errs) != 0 {
  678. t.Errorf("Expected success for %v, got %v", testName, errs)
  679. }
  680. if !testCase.shouldSucceed && len(errs) == 0 {
  681. t.Errorf("Expected failure for %v, got success", testName)
  682. }
  683. }
  684. }
  685. func TestValidateAllowedTopologies(t *testing.T) {
  686. validTopology := []api.TopologySelectorTerm{
  687. {
  688. MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
  689. {
  690. Key: "failure-domain.beta.kubernetes.io/zone",
  691. Values: []string{"zone1"},
  692. },
  693. {
  694. Key: "kubernetes.io/hostname",
  695. Values: []string{"node1"},
  696. },
  697. },
  698. },
  699. {
  700. MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
  701. {
  702. Key: "failure-domain.beta.kubernetes.io/zone",
  703. Values: []string{"zone2"},
  704. },
  705. {
  706. Key: "kubernetes.io/hostname",
  707. Values: []string{"node2"},
  708. },
  709. },
  710. },
  711. }
  712. topologyInvalidKey := []api.TopologySelectorTerm{
  713. {
  714. MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
  715. {
  716. Key: "/invalidkey",
  717. Values: []string{"zone1"},
  718. },
  719. },
  720. },
  721. }
  722. topologyLackOfValues := []api.TopologySelectorTerm{
  723. {
  724. MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
  725. {
  726. Key: "kubernetes.io/hostname",
  727. Values: []string{},
  728. },
  729. },
  730. },
  731. }
  732. topologyDupValues := []api.TopologySelectorTerm{
  733. {
  734. MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
  735. {
  736. Key: "kubernetes.io/hostname",
  737. Values: []string{"node1", "node1"},
  738. },
  739. },
  740. },
  741. }
  742. topologyMultiValues := []api.TopologySelectorTerm{
  743. {
  744. MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
  745. {
  746. Key: "kubernetes.io/hostname",
  747. Values: []string{"node1", "node2"},
  748. },
  749. },
  750. },
  751. }
  752. topologyEmptyMatchLabelExpressions := []api.TopologySelectorTerm{
  753. {
  754. MatchLabelExpressions: nil,
  755. },
  756. }
  757. topologyDupKeys := []api.TopologySelectorTerm{
  758. {
  759. MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
  760. {
  761. Key: "kubernetes.io/hostname",
  762. Values: []string{"node1"},
  763. },
  764. {
  765. Key: "kubernetes.io/hostname",
  766. Values: []string{"node2"},
  767. },
  768. },
  769. },
  770. }
  771. topologyMultiTerm := []api.TopologySelectorTerm{
  772. {
  773. MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
  774. {
  775. Key: "kubernetes.io/hostname",
  776. Values: []string{"node1"},
  777. },
  778. },
  779. },
  780. {
  781. MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
  782. {
  783. Key: "kubernetes.io/hostname",
  784. Values: []string{"node2"},
  785. },
  786. },
  787. },
  788. }
  789. topologyDupTermsIdentical := []api.TopologySelectorTerm{
  790. {
  791. MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
  792. {
  793. Key: "failure-domain.beta.kubernetes.io/zone",
  794. Values: []string{"zone1"},
  795. },
  796. {
  797. Key: "kubernetes.io/hostname",
  798. Values: []string{"node1"},
  799. },
  800. },
  801. },
  802. {
  803. MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
  804. {
  805. Key: "failure-domain.beta.kubernetes.io/zone",
  806. Values: []string{"zone1"},
  807. },
  808. {
  809. Key: "kubernetes.io/hostname",
  810. Values: []string{"node1"},
  811. },
  812. },
  813. },
  814. }
  815. topologyExprsOneSameOneDiff := []api.TopologySelectorTerm{
  816. {
  817. MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
  818. {
  819. Key: "failure-domain.beta.kubernetes.io/zone",
  820. Values: []string{"zone1"},
  821. },
  822. {
  823. Key: "kubernetes.io/hostname",
  824. Values: []string{"node1"},
  825. },
  826. },
  827. },
  828. {
  829. MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
  830. {
  831. Key: "failure-domain.beta.kubernetes.io/zone",
  832. Values: []string{"zone1"},
  833. },
  834. {
  835. Key: "kubernetes.io/hostname",
  836. Values: []string{"node2"},
  837. },
  838. },
  839. },
  840. }
  841. topologyValuesOneSameOneDiff := []api.TopologySelectorTerm{
  842. {
  843. MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
  844. {
  845. Key: "kubernetes.io/hostname",
  846. Values: []string{"node1", "node2"},
  847. },
  848. },
  849. },
  850. {
  851. MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
  852. {
  853. Key: "kubernetes.io/hostname",
  854. Values: []string{"node1", "node3"},
  855. },
  856. },
  857. },
  858. }
  859. topologyDupTermsDiffExprOrder := []api.TopologySelectorTerm{
  860. {
  861. MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
  862. {
  863. Key: "kubernetes.io/hostname",
  864. Values: []string{"node1"},
  865. },
  866. {
  867. Key: "failure-domain.beta.kubernetes.io/zone",
  868. Values: []string{"zone1"},
  869. },
  870. },
  871. },
  872. {
  873. MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
  874. {
  875. Key: "failure-domain.beta.kubernetes.io/zone",
  876. Values: []string{"zone1"},
  877. },
  878. {
  879. Key: "kubernetes.io/hostname",
  880. Values: []string{"node1"},
  881. },
  882. },
  883. },
  884. }
  885. topologyDupTermsDiffValueOrder := []api.TopologySelectorTerm{
  886. {
  887. MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
  888. {
  889. Key: "failure-domain.beta.kubernetes.io/zone",
  890. Values: []string{"zone1", "zone2"},
  891. },
  892. },
  893. },
  894. {
  895. MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
  896. {
  897. Key: "failure-domain.beta.kubernetes.io/zone",
  898. Values: []string{"zone2", "zone1"},
  899. },
  900. },
  901. },
  902. }
  903. cases := map[string]bindingTest{
  904. "no topology": {
  905. class: makeClass(&waitingMode, nil),
  906. shouldSucceed: true,
  907. },
  908. "valid topology": {
  909. class: makeClass(&waitingMode, validTopology),
  910. shouldSucceed: true,
  911. },
  912. "topology invalid key": {
  913. class: makeClass(&waitingMode, topologyInvalidKey),
  914. shouldSucceed: false,
  915. },
  916. "topology lack of values": {
  917. class: makeClass(&waitingMode, topologyLackOfValues),
  918. shouldSucceed: false,
  919. },
  920. "duplicate TopologySelectorRequirement values": {
  921. class: makeClass(&waitingMode, topologyDupValues),
  922. shouldSucceed: false,
  923. },
  924. "multiple TopologySelectorRequirement values": {
  925. class: makeClass(&waitingMode, topologyMultiValues),
  926. shouldSucceed: true,
  927. },
  928. "empty MatchLabelExpressions": {
  929. class: makeClass(&waitingMode, topologyEmptyMatchLabelExpressions),
  930. shouldSucceed: false,
  931. },
  932. "duplicate MatchLabelExpression keys": {
  933. class: makeClass(&waitingMode, topologyDupKeys),
  934. shouldSucceed: false,
  935. },
  936. "duplicate MatchLabelExpression keys but across separate terms": {
  937. class: makeClass(&waitingMode, topologyMultiTerm),
  938. shouldSucceed: true,
  939. },
  940. "duplicate AllowedTopologies terms - identical": {
  941. class: makeClass(&waitingMode, topologyDupTermsIdentical),
  942. shouldSucceed: false,
  943. },
  944. "two AllowedTopologies terms, with a pair of the same MatchLabelExpressions and a pair of different ones": {
  945. class: makeClass(&waitingMode, topologyExprsOneSameOneDiff),
  946. shouldSucceed: true,
  947. },
  948. "two AllowedTopologies terms, with a pair of the same Values and a pair of different ones": {
  949. class: makeClass(&waitingMode, topologyValuesOneSameOneDiff),
  950. shouldSucceed: true,
  951. },
  952. "duplicate AllowedTopologies terms - different MatchLabelExpressions order": {
  953. class: makeClass(&waitingMode, topologyDupTermsDiffExprOrder),
  954. shouldSucceed: false,
  955. },
  956. "duplicate AllowedTopologies terms - different TopologySelectorRequirement values order": {
  957. class: makeClass(&waitingMode, topologyDupTermsDiffValueOrder),
  958. shouldSucceed: false,
  959. },
  960. }
  961. for testName, testCase := range cases {
  962. errs := ValidateStorageClass(testCase.class)
  963. if testCase.shouldSucceed && len(errs) != 0 {
  964. t.Errorf("Expected success for test %q, got %v", testName, errs)
  965. }
  966. if !testCase.shouldSucceed && len(errs) == 0 {
  967. t.Errorf("Expected failure for test %q, got success", testName)
  968. }
  969. }
  970. }
  971. func TestCSINodeValidation(t *testing.T) {
  972. driverName := "driver-name"
  973. driverName2 := "1io.kubernetes-storage-2-csi-driver3"
  974. longName := "my-a-b-c-d-c-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z-ABCDEFGHIJKLMNOPQRSTUVWXYZ-driver"
  975. nodeID := "nodeA"
  976. successCases := []storage.CSINode{
  977. {
  978. // driver name: dot only
  979. ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  980. Spec: storage.CSINodeSpec{
  981. Drivers: []storage.CSINodeDriver{
  982. {
  983. Name: "io.kubernetes.storage.csi.driver",
  984. NodeID: nodeID,
  985. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  986. },
  987. },
  988. },
  989. },
  990. {
  991. // driver name: dash only
  992. ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
  993. Spec: storage.CSINodeSpec{
  994. Drivers: []storage.CSINodeDriver{
  995. {
  996. Name: "io-kubernetes-storage-csi-driver",
  997. NodeID: nodeID,
  998. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  999. },
  1000. },
  1001. },
  1002. },
  1003. {
  1004. // driver name: numbers
  1005. ObjectMeta: metav1.ObjectMeta{Name: "foo3"},
  1006. Spec: storage.CSINodeSpec{
  1007. Drivers: []storage.CSINodeDriver{
  1008. {
  1009. Name: "1io-kubernetes-storage-2-csi-driver3",
  1010. NodeID: nodeID,
  1011. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1012. },
  1013. },
  1014. },
  1015. },
  1016. {
  1017. // driver name: dot, dash
  1018. ObjectMeta: metav1.ObjectMeta{Name: "foo4"},
  1019. Spec: storage.CSINodeSpec{
  1020. Drivers: []storage.CSINodeDriver{
  1021. {
  1022. Name: "io.kubernetes.storage-csi-driver",
  1023. NodeID: nodeID,
  1024. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1025. },
  1026. },
  1027. },
  1028. },
  1029. {
  1030. // driver name: dot, dash, and numbers
  1031. ObjectMeta: metav1.ObjectMeta{Name: "foo5"},
  1032. Spec: storage.CSINodeSpec{
  1033. Drivers: []storage.CSINodeDriver{
  1034. {
  1035. Name: driverName2,
  1036. NodeID: nodeID,
  1037. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1038. },
  1039. },
  1040. },
  1041. },
  1042. {
  1043. // Driver name length 1
  1044. ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
  1045. Spec: storage.CSINodeSpec{
  1046. Drivers: []storage.CSINodeDriver{
  1047. {
  1048. Name: "a",
  1049. NodeID: nodeID,
  1050. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1051. },
  1052. },
  1053. },
  1054. },
  1055. {
  1056. // multiple drivers with different node IDs, topology keys
  1057. ObjectMeta: metav1.ObjectMeta{Name: "foo6"},
  1058. Spec: storage.CSINodeSpec{
  1059. Drivers: []storage.CSINodeDriver{
  1060. {
  1061. Name: "driver1",
  1062. NodeID: "node1",
  1063. TopologyKeys: []string{"key1", "key2"},
  1064. },
  1065. {
  1066. Name: "driverB",
  1067. NodeID: "nodeA",
  1068. TopologyKeys: []string{"keyA", "keyB"},
  1069. },
  1070. },
  1071. },
  1072. },
  1073. {
  1074. // multiple drivers with same node IDs, topology keys
  1075. ObjectMeta: metav1.ObjectMeta{Name: "foo7"},
  1076. Spec: storage.CSINodeSpec{
  1077. Drivers: []storage.CSINodeDriver{
  1078. {
  1079. Name: "driver1",
  1080. NodeID: "node1",
  1081. TopologyKeys: []string{"key1"},
  1082. },
  1083. {
  1084. Name: "driver2",
  1085. NodeID: "node1",
  1086. TopologyKeys: []string{"key1"},
  1087. },
  1088. },
  1089. },
  1090. },
  1091. {
  1092. // topology key names with -, _, and dot .
  1093. ObjectMeta: metav1.ObjectMeta{Name: "foo8"},
  1094. Spec: storage.CSINodeSpec{
  1095. Drivers: []storage.CSINodeDriver{
  1096. {
  1097. Name: "driver1",
  1098. NodeID: "node1",
  1099. TopologyKeys: []string{"zone_1", "zone.2"},
  1100. },
  1101. {
  1102. Name: "driver2",
  1103. NodeID: "node1",
  1104. TopologyKeys: []string{"zone-3", "zone.4"},
  1105. },
  1106. },
  1107. },
  1108. },
  1109. {
  1110. // topology prefix with - and dot.
  1111. ObjectMeta: metav1.ObjectMeta{Name: "foo9"},
  1112. Spec: storage.CSINodeSpec{
  1113. Drivers: []storage.CSINodeDriver{
  1114. {
  1115. Name: "driver1",
  1116. NodeID: "node1",
  1117. TopologyKeys: []string{"company-com/zone1", "company.com/zone2"},
  1118. },
  1119. },
  1120. },
  1121. },
  1122. {
  1123. // No topology keys
  1124. ObjectMeta: metav1.ObjectMeta{Name: "foo10"},
  1125. Spec: storage.CSINodeSpec{
  1126. Drivers: []storage.CSINodeDriver{
  1127. {
  1128. Name: driverName,
  1129. NodeID: nodeID,
  1130. },
  1131. },
  1132. },
  1133. },
  1134. }
  1135. for _, csiNode := range successCases {
  1136. if errs := ValidateCSINode(&csiNode); len(errs) != 0 {
  1137. t.Errorf("expected success: %v", errs)
  1138. }
  1139. }
  1140. errorCases := []storage.CSINode{
  1141. {
  1142. // Empty driver name
  1143. ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  1144. Spec: storage.CSINodeSpec{
  1145. Drivers: []storage.CSINodeDriver{
  1146. {
  1147. Name: "",
  1148. NodeID: nodeID,
  1149. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1150. },
  1151. },
  1152. },
  1153. },
  1154. {
  1155. // Invalid start char in driver name
  1156. ObjectMeta: metav1.ObjectMeta{Name: "foo3"},
  1157. Spec: storage.CSINodeSpec{
  1158. Drivers: []storage.CSINodeDriver{
  1159. {
  1160. Name: "_io.kubernetes.storage.csi.driver",
  1161. NodeID: nodeID,
  1162. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1163. },
  1164. },
  1165. },
  1166. },
  1167. {
  1168. // Invalid end char in driver name
  1169. ObjectMeta: metav1.ObjectMeta{Name: "foo4"},
  1170. Spec: storage.CSINodeSpec{
  1171. Drivers: []storage.CSINodeDriver{
  1172. {
  1173. Name: "io.kubernetes.storage.csi.driver/",
  1174. NodeID: nodeID,
  1175. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1176. },
  1177. },
  1178. },
  1179. },
  1180. {
  1181. // Invalid separators in driver name
  1182. ObjectMeta: metav1.ObjectMeta{Name: "foo5"},
  1183. Spec: storage.CSINodeSpec{
  1184. Drivers: []storage.CSINodeDriver{
  1185. {
  1186. Name: "io/kubernetes/storage/csi~driver",
  1187. NodeID: nodeID,
  1188. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1189. },
  1190. },
  1191. },
  1192. },
  1193. {
  1194. // driver name: underscore only
  1195. ObjectMeta: metav1.ObjectMeta{Name: "foo6"},
  1196. Spec: storage.CSINodeSpec{
  1197. Drivers: []storage.CSINodeDriver{
  1198. {
  1199. Name: "io_kubernetes_storage_csi_driver",
  1200. NodeID: nodeID,
  1201. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1202. },
  1203. },
  1204. },
  1205. },
  1206. {
  1207. // Driver name length > 63
  1208. ObjectMeta: metav1.ObjectMeta{Name: "foo7"},
  1209. Spec: storage.CSINodeSpec{
  1210. Drivers: []storage.CSINodeDriver{
  1211. {
  1212. Name: longName,
  1213. NodeID: nodeID,
  1214. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1215. },
  1216. },
  1217. },
  1218. },
  1219. {
  1220. // No driver name
  1221. ObjectMeta: metav1.ObjectMeta{Name: "foo8"},
  1222. Spec: storage.CSINodeSpec{
  1223. Drivers: []storage.CSINodeDriver{
  1224. {
  1225. NodeID: nodeID,
  1226. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1227. },
  1228. },
  1229. },
  1230. },
  1231. {
  1232. // Empty individual topology key
  1233. ObjectMeta: metav1.ObjectMeta{Name: "foo9"},
  1234. Spec: storage.CSINodeSpec{
  1235. Drivers: []storage.CSINodeDriver{
  1236. {
  1237. Name: driverName,
  1238. NodeID: nodeID,
  1239. TopologyKeys: []string{"company.com/zone1", ""},
  1240. },
  1241. },
  1242. },
  1243. },
  1244. {
  1245. // duplicate drivers in driver specs
  1246. ObjectMeta: metav1.ObjectMeta{Name: "foo10"},
  1247. Spec: storage.CSINodeSpec{
  1248. Drivers: []storage.CSINodeDriver{
  1249. {
  1250. Name: "driver1",
  1251. NodeID: "node1",
  1252. TopologyKeys: []string{"key1", "key2"},
  1253. },
  1254. {
  1255. Name: "driver1",
  1256. NodeID: "nodeX",
  1257. TopologyKeys: []string{"keyA", "keyB"},
  1258. },
  1259. },
  1260. },
  1261. },
  1262. {
  1263. // single driver with duplicate topology keys in driver specs
  1264. ObjectMeta: metav1.ObjectMeta{Name: "foo11"},
  1265. Spec: storage.CSINodeSpec{
  1266. Drivers: []storage.CSINodeDriver{
  1267. {
  1268. Name: "driver1",
  1269. NodeID: "node1",
  1270. TopologyKeys: []string{"key1", "key1"},
  1271. },
  1272. },
  1273. },
  1274. },
  1275. {
  1276. // multiple drivers with one set of duplicate topology keys in driver specs
  1277. ObjectMeta: metav1.ObjectMeta{Name: "foo12"},
  1278. Spec: storage.CSINodeSpec{
  1279. Drivers: []storage.CSINodeDriver{
  1280. {
  1281. Name: "driver1",
  1282. NodeID: "node1",
  1283. TopologyKeys: []string{"key1"},
  1284. },
  1285. {
  1286. Name: "driver2",
  1287. NodeID: "nodeX",
  1288. TopologyKeys: []string{"keyA", "keyA"},
  1289. },
  1290. },
  1291. },
  1292. },
  1293. {
  1294. // Empty NodeID
  1295. ObjectMeta: metav1.ObjectMeta{Name: "foo13"},
  1296. Spec: storage.CSINodeSpec{
  1297. Drivers: []storage.CSINodeDriver{
  1298. {
  1299. Name: driverName,
  1300. NodeID: "",
  1301. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1302. },
  1303. },
  1304. },
  1305. },
  1306. {
  1307. // topology prefix should be lower case
  1308. ObjectMeta: metav1.ObjectMeta{Name: "foo14"},
  1309. Spec: storage.CSINodeSpec{
  1310. Drivers: []storage.CSINodeDriver{
  1311. {
  1312. Name: driverName,
  1313. NodeID: "node1",
  1314. TopologyKeys: []string{"Company.Com/zone1", "company.com/zone2"},
  1315. },
  1316. },
  1317. },
  1318. },
  1319. }
  1320. for _, csiNode := range errorCases {
  1321. if errs := ValidateCSINode(&csiNode); len(errs) == 0 {
  1322. t.Errorf("Expected failure for test: %v", csiNode)
  1323. }
  1324. }
  1325. }
  1326. func TestCSINodeUpdateValidation(t *testing.T) {
  1327. //driverName := "driver-name"
  1328. //driverName2 := "1io.kubernetes-storage-2-csi-driver3"
  1329. //longName := "my-a-b-c-d-c-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z-ABCDEFGHIJKLMNOPQRSTUVWXYZ-driver"
  1330. nodeID := "nodeA"
  1331. old := storage.CSINode{
  1332. ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  1333. Spec: storage.CSINodeSpec{
  1334. Drivers: []storage.CSINodeDriver{
  1335. {
  1336. Name: "io.kubernetes.storage.csi.driver-1",
  1337. NodeID: nodeID,
  1338. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1339. },
  1340. {
  1341. Name: "io.kubernetes.storage.csi.driver-2",
  1342. NodeID: nodeID,
  1343. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1344. },
  1345. },
  1346. },
  1347. }
  1348. successCases := []storage.CSINode{
  1349. {
  1350. // no change
  1351. ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  1352. Spec: storage.CSINodeSpec{
  1353. Drivers: []storage.CSINodeDriver{
  1354. {
  1355. Name: "io.kubernetes.storage.csi.driver-1",
  1356. NodeID: nodeID,
  1357. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1358. },
  1359. {
  1360. Name: "io.kubernetes.storage.csi.driver-2",
  1361. NodeID: nodeID,
  1362. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1363. },
  1364. },
  1365. },
  1366. },
  1367. {
  1368. // remove a driver
  1369. ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  1370. Spec: storage.CSINodeSpec{
  1371. Drivers: []storage.CSINodeDriver{
  1372. {
  1373. Name: "io.kubernetes.storage.csi.driver-1",
  1374. NodeID: nodeID,
  1375. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1376. },
  1377. },
  1378. },
  1379. },
  1380. {
  1381. // add a driver
  1382. ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  1383. Spec: storage.CSINodeSpec{
  1384. Drivers: []storage.CSINodeDriver{
  1385. {
  1386. Name: "io.kubernetes.storage.csi.driver-1",
  1387. NodeID: nodeID,
  1388. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1389. },
  1390. {
  1391. Name: "io.kubernetes.storage.csi.driver-2",
  1392. NodeID: nodeID,
  1393. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1394. },
  1395. {
  1396. Name: "io.kubernetes.storage.csi.driver-3",
  1397. NodeID: nodeID,
  1398. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1399. },
  1400. },
  1401. },
  1402. },
  1403. {
  1404. // remove a driver and add a driver
  1405. ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  1406. Spec: storage.CSINodeSpec{
  1407. Drivers: []storage.CSINodeDriver{
  1408. {
  1409. Name: "io.kubernetes.storage.csi.driver-1",
  1410. NodeID: nodeID,
  1411. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1412. },
  1413. {
  1414. Name: "io.kubernetes.storage.csi.new-driver",
  1415. NodeID: nodeID,
  1416. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1417. },
  1418. },
  1419. },
  1420. },
  1421. }
  1422. for _, csiNode := range successCases {
  1423. if errs := ValidateCSINodeUpdate(&csiNode, &old); len(errs) != 0 {
  1424. t.Errorf("expected success: %+v", errs)
  1425. }
  1426. }
  1427. errorCases := []storage.CSINode{
  1428. {
  1429. // invalid change node id
  1430. ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  1431. Spec: storage.CSINodeSpec{
  1432. Drivers: []storage.CSINodeDriver{
  1433. {
  1434. Name: "io.kubernetes.storage.csi.driver-1",
  1435. NodeID: "nodeB",
  1436. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1437. },
  1438. {
  1439. Name: "io.kubernetes.storage.csi.driver-2",
  1440. NodeID: nodeID,
  1441. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1442. },
  1443. },
  1444. },
  1445. },
  1446. {
  1447. // invalid change topology keys
  1448. ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  1449. Spec: storage.CSINodeSpec{
  1450. Drivers: []storage.CSINodeDriver{
  1451. {
  1452. Name: "io.kubernetes.storage.csi.driver-1",
  1453. NodeID: "nodeB",
  1454. TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1455. },
  1456. {
  1457. Name: "io.kubernetes.storage.csi.driver-2",
  1458. NodeID: nodeID,
  1459. TopologyKeys: []string{"company.com/zone2"},
  1460. },
  1461. },
  1462. },
  1463. },
  1464. }
  1465. for _, csiNode := range errorCases {
  1466. if errs := ValidateCSINodeUpdate(&csiNode, &old); len(errs) == 0 {
  1467. t.Errorf("Expected failure for test: %+v", csiNode)
  1468. }
  1469. }
  1470. }
  1471. func TestCSIDriverValidation(t *testing.T) {
  1472. driverName := "test-driver"
  1473. longName := "my-a-b-c-d-c-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z-ABCDEFGHIJKLMNOPQRSTUVWXYZ-driver"
  1474. invalidName := "-invalid-@#$%^&*()-"
  1475. attachRequired := true
  1476. attachNotRequired := false
  1477. podInfoOnMount := true
  1478. notPodInfoOnMount := false
  1479. successCases := []storage.CSIDriver{
  1480. {
  1481. ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1482. Spec: storage.CSIDriverSpec{
  1483. AttachRequired: &attachRequired,
  1484. PodInfoOnMount: &podInfoOnMount,
  1485. },
  1486. },
  1487. {
  1488. // driver name: dot only
  1489. ObjectMeta: metav1.ObjectMeta{Name: "io.kubernetes.storage.csi.driver"},
  1490. Spec: storage.CSIDriverSpec{
  1491. AttachRequired: &attachRequired,
  1492. PodInfoOnMount: &podInfoOnMount,
  1493. },
  1494. },
  1495. {
  1496. // driver name: dash only
  1497. ObjectMeta: metav1.ObjectMeta{Name: "io-kubernetes-storage-csi-driver"},
  1498. Spec: storage.CSIDriverSpec{
  1499. AttachRequired: &attachRequired,
  1500. PodInfoOnMount: &notPodInfoOnMount,
  1501. },
  1502. },
  1503. {
  1504. // driver name: numbers
  1505. ObjectMeta: metav1.ObjectMeta{Name: "1csi2driver3"},
  1506. Spec: storage.CSIDriverSpec{
  1507. AttachRequired: &attachRequired,
  1508. PodInfoOnMount: &podInfoOnMount,
  1509. },
  1510. },
  1511. {
  1512. // driver name: dot and dash
  1513. ObjectMeta: metav1.ObjectMeta{Name: "io.kubernetes.storage.csi-driver"},
  1514. Spec: storage.CSIDriverSpec{
  1515. AttachRequired: &attachRequired,
  1516. PodInfoOnMount: &podInfoOnMount,
  1517. },
  1518. },
  1519. {
  1520. ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1521. Spec: storage.CSIDriverSpec{
  1522. AttachRequired: &attachRequired,
  1523. PodInfoOnMount: &notPodInfoOnMount,
  1524. },
  1525. },
  1526. {
  1527. ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1528. Spec: storage.CSIDriverSpec{
  1529. AttachRequired: &attachRequired,
  1530. PodInfoOnMount: &podInfoOnMount,
  1531. },
  1532. },
  1533. {
  1534. ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1535. Spec: storage.CSIDriverSpec{
  1536. AttachRequired: &attachNotRequired,
  1537. PodInfoOnMount: &notPodInfoOnMount,
  1538. },
  1539. },
  1540. }
  1541. for _, csiDriver := range successCases {
  1542. if errs := ValidateCSIDriver(&csiDriver); len(errs) != 0 {
  1543. t.Errorf("expected success: %v", errs)
  1544. }
  1545. }
  1546. errorCases := []storage.CSIDriver{
  1547. {
  1548. ObjectMeta: metav1.ObjectMeta{Name: invalidName},
  1549. Spec: storage.CSIDriverSpec{
  1550. AttachRequired: &attachRequired,
  1551. PodInfoOnMount: &podInfoOnMount,
  1552. },
  1553. },
  1554. {
  1555. ObjectMeta: metav1.ObjectMeta{Name: longName},
  1556. Spec: storage.CSIDriverSpec{
  1557. AttachRequired: &attachNotRequired,
  1558. PodInfoOnMount: &notPodInfoOnMount,
  1559. },
  1560. },
  1561. {
  1562. // AttachRequired not set
  1563. ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1564. Spec: storage.CSIDriverSpec{
  1565. AttachRequired: nil,
  1566. PodInfoOnMount: &podInfoOnMount,
  1567. },
  1568. },
  1569. {
  1570. // AttachRequired not set
  1571. ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1572. Spec: storage.CSIDriverSpec{
  1573. AttachRequired: &attachNotRequired,
  1574. PodInfoOnMount: nil,
  1575. },
  1576. },
  1577. }
  1578. for _, csiDriver := range errorCases {
  1579. if errs := ValidateCSIDriver(&csiDriver); len(errs) == 0 {
  1580. t.Errorf("Expected failure for test: %v", csiDriver)
  1581. }
  1582. }
  1583. }