validation_test.go 52 KB

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