1
16
17 package validation
18
19 import (
20 "fmt"
21 "strings"
22 "testing"
23
24 "k8s.io/apimachinery/pkg/api/resource"
25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 utilfeature "k8s.io/apiserver/pkg/util/feature"
27 featuregatetesting "k8s.io/component-base/featuregate/testing"
28 api "k8s.io/kubernetes/pkg/apis/core"
29 "k8s.io/kubernetes/pkg/apis/storage"
30 "k8s.io/kubernetes/pkg/features"
31 utilpointer "k8s.io/utils/pointer"
32 )
33
34 var (
35 deleteReclaimPolicy = api.PersistentVolumeReclaimDelete
36 immediateMode1 = storage.VolumeBindingImmediate
37 immediateMode2 = storage.VolumeBindingImmediate
38 waitingMode = storage.VolumeBindingWaitForFirstConsumer
39 invalidMode = storage.VolumeBindingMode("foo")
40 inlineSpec = api.PersistentVolumeSpec{
41 AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
42 PersistentVolumeSource: api.PersistentVolumeSource{
43 CSI: &api.CSIPersistentVolumeSource{
44 Driver: "com.test.foo",
45 VolumeHandle: "foobar",
46 },
47 },
48 }
49 longerIDValidateOption = CSINodeValidationOptions{
50 AllowLongNodeID: true,
51 }
52 shorterIDValidationOption = CSINodeValidationOptions{
53 AllowLongNodeID: false,
54 }
55 )
56
57 func TestValidateStorageClass(t *testing.T) {
58 deleteReclaimPolicy := api.PersistentVolumeReclaimPolicy("Delete")
59 retainReclaimPolicy := api.PersistentVolumeReclaimPolicy("Retain")
60 recycleReclaimPolicy := api.PersistentVolumeReclaimPolicy("Recycle")
61 successCases := []storage.StorageClass{{
62
63 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
64 Provisioner: "kubernetes.io/foo-provisioner",
65 Parameters: map[string]string{},
66 ReclaimPolicy: &deleteReclaimPolicy,
67 VolumeBindingMode: &immediateMode1,
68 }, {
69
70 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
71 Provisioner: "kubernetes.io/foo-provisioner",
72 ReclaimPolicy: &deleteReclaimPolicy,
73 VolumeBindingMode: &immediateMode1,
74 }, {
75
76 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
77 Provisioner: "kubernetes.io/foo-provisioner",
78 Parameters: map[string]string{
79 "kubernetes.io/foo-parameter": "free/form/string",
80 "foo-parameter": "free-form-string",
81 "foo-parameter2": "{\"embedded\": \"json\", \"with\": {\"structures\":\"inside\"}}",
82 },
83 ReclaimPolicy: &deleteReclaimPolicy,
84 VolumeBindingMode: &immediateMode1,
85 }, {
86
87 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
88 Provisioner: "kubernetes.io/foo-provisioner",
89 ReclaimPolicy: &retainReclaimPolicy,
90 VolumeBindingMode: &immediateMode1,
91 }}
92
93
94 for k, v := range successCases {
95 if errs := ValidateStorageClass(&v); len(errs) != 0 {
96 t.Errorf("Expected success for %d, got %v", k, errs)
97 }
98 }
99
100
101 longParameters := make(map[string]string)
102 totalSize := 0
103 for totalSize < maxProvisionerParameterSize {
104 k := fmt.Sprintf("param/%d", totalSize)
105 v := fmt.Sprintf("value-%d", totalSize)
106 longParameters[k] = v
107 totalSize = totalSize + len(k) + len(v)
108 }
109
110 errorCases := map[string]storage.StorageClass{
111 "namespace is present": {
112 ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
113 Provisioner: "kubernetes.io/foo-provisioner",
114 ReclaimPolicy: &deleteReclaimPolicy,
115 },
116 "invalid provisioner": {
117 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
118 Provisioner: "kubernetes.io/invalid/provisioner",
119 ReclaimPolicy: &deleteReclaimPolicy,
120 },
121 "invalid empty parameter name": {
122 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
123 Provisioner: "kubernetes.io/foo",
124 Parameters: map[string]string{
125 "": "value",
126 },
127 ReclaimPolicy: &deleteReclaimPolicy,
128 },
129 "provisioner: Required value": {
130 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
131 Provisioner: "",
132 ReclaimPolicy: &deleteReclaimPolicy,
133 },
134 "too long parameters": {
135 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
136 Provisioner: "kubernetes.io/foo",
137 Parameters: longParameters,
138 ReclaimPolicy: &deleteReclaimPolicy,
139 },
140 "invalid reclaimpolicy": {
141 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
142 Provisioner: "kubernetes.io/foo",
143 ReclaimPolicy: &recycleReclaimPolicy,
144 },
145 }
146
147
148 for testName, storageClass := range errorCases {
149 if errs := ValidateStorageClass(&storageClass); len(errs) == 0 {
150 t.Errorf("Expected failure for test: %s", testName)
151 }
152 }
153 }
154
155 func TestVolumeAttachmentValidation(t *testing.T) {
156 volumeName := "pv-name"
157 empty := ""
158 migrationEnabledSuccessCases := []storage.VolumeAttachment{{
159 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
160 Spec: storage.VolumeAttachmentSpec{
161 Attacher: "myattacher",
162 Source: storage.VolumeAttachmentSource{
163 PersistentVolumeName: &volumeName,
164 },
165 NodeName: "mynode",
166 },
167 }, {
168 ObjectMeta: metav1.ObjectMeta{Name: "foo-with-inlinespec"},
169 Spec: storage.VolumeAttachmentSpec{
170 Attacher: "myattacher",
171 Source: storage.VolumeAttachmentSource{
172 InlineVolumeSpec: &inlineSpec,
173 },
174 NodeName: "mynode",
175 },
176 }, {
177 ObjectMeta: metav1.ObjectMeta{Name: "foo-with-status"},
178 Spec: storage.VolumeAttachmentSpec{
179 Attacher: "myattacher",
180 Source: storage.VolumeAttachmentSource{
181 PersistentVolumeName: &volumeName,
182 },
183 NodeName: "mynode",
184 },
185 Status: storage.VolumeAttachmentStatus{
186 Attached: true,
187 AttachmentMetadata: map[string]string{
188 "foo": "bar",
189 },
190 AttachError: &storage.VolumeError{
191 Time: metav1.Time{},
192 Message: "hello world",
193 },
194 DetachError: &storage.VolumeError{
195 Time: metav1.Time{},
196 Message: "hello world",
197 },
198 },
199 }, {
200 ObjectMeta: metav1.ObjectMeta{Name: "foo-with-inlinespec-and-status"},
201 Spec: storage.VolumeAttachmentSpec{
202 Attacher: "myattacher",
203 Source: storage.VolumeAttachmentSource{
204 InlineVolumeSpec: &inlineSpec,
205 },
206 NodeName: "mynode",
207 },
208 Status: storage.VolumeAttachmentStatus{
209 Attached: true,
210 AttachmentMetadata: map[string]string{
211 "foo": "bar",
212 },
213 AttachError: &storage.VolumeError{
214 Time: metav1.Time{},
215 Message: "hello world",
216 },
217 DetachError: &storage.VolumeError{
218 Time: metav1.Time{},
219 Message: "hello world",
220 },
221 },
222 }}
223
224 for _, volumeAttachment := range migrationEnabledSuccessCases {
225 if errs := ValidateVolumeAttachment(&volumeAttachment); len(errs) != 0 {
226 t.Errorf("expected success: %v %v", volumeAttachment, errs)
227 }
228 }
229 migrationEnabledErrorCases := []storage.VolumeAttachment{{
230
231 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
232 Spec: storage.VolumeAttachmentSpec{
233 Attacher: "",
234 NodeName: "mynode",
235 Source: storage.VolumeAttachmentSource{
236 PersistentVolumeName: &volumeName,
237 },
238 },
239 }, {
240
241 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
242 Spec: storage.VolumeAttachmentSpec{
243 Attacher: "myattacher",
244 NodeName: "",
245 Source: storage.VolumeAttachmentSource{
246 PersistentVolumeName: &volumeName,
247 },
248 },
249 }, {
250
251 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
252 Spec: storage.VolumeAttachmentSpec{
253 Attacher: "myattacher",
254 NodeName: "node",
255 Source: storage.VolumeAttachmentSource{
256 PersistentVolumeName: nil,
257 },
258 },
259 }, {
260
261 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
262 Spec: storage.VolumeAttachmentSpec{
263 Attacher: "myattacher",
264 NodeName: "node",
265 Source: storage.VolumeAttachmentSource{
266 PersistentVolumeName: &empty,
267 },
268 },
269 }, {
270
271 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
272 Spec: storage.VolumeAttachmentSpec{
273 Attacher: "myattacher",
274 NodeName: "node",
275 Source: storage.VolumeAttachmentSource{
276 PersistentVolumeName: &volumeName,
277 },
278 },
279 Status: storage.VolumeAttachmentStatus{
280 Attached: true,
281 AttachmentMetadata: map[string]string{
282 "foo": "bar",
283 },
284 AttachError: &storage.VolumeError{
285 Time: metav1.Time{},
286 Message: "hello world",
287 },
288 DetachError: &storage.VolumeError{
289 Time: metav1.Time{},
290 Message: strings.Repeat("a", maxVolumeErrorMessageSize+1),
291 },
292 },
293 }, {
294
295 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
296 Spec: storage.VolumeAttachmentSpec{
297 Attacher: "myattacher",
298 NodeName: "node",
299 Source: storage.VolumeAttachmentSource{
300 PersistentVolumeName: &volumeName,
301 },
302 },
303 Status: storage.VolumeAttachmentStatus{
304 Attached: true,
305 AttachmentMetadata: map[string]string{
306 "foo": strings.Repeat("a", maxAttachedVolumeMetadataSize),
307 },
308 AttachError: &storage.VolumeError{
309 Time: metav1.Time{},
310 Message: "hello world",
311 },
312 DetachError: &storage.VolumeError{
313 Time: metav1.Time{},
314 Message: "hello world",
315 },
316 },
317 }, {
318
319 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
320 Spec: storage.VolumeAttachmentSpec{
321 Attacher: "myattacher",
322 NodeName: "node",
323 Source: storage.VolumeAttachmentSource{},
324 },
325 }, {
326
327 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
328 Spec: storage.VolumeAttachmentSpec{
329 Attacher: "myattacher",
330 NodeName: "node",
331 Source: storage.VolumeAttachmentSource{
332 PersistentVolumeName: &volumeName,
333 InlineVolumeSpec: &inlineSpec,
334 },
335 },
336 }, {
337
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 }
367
368 func TestVolumeAttachmentUpdateValidation(t *testing.T) {
369 volumeName := "foo"
370 newVolumeName := "bar"
371
372 old := storage.VolumeAttachment{
373 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
374 Spec: storage.VolumeAttachmentSpec{
375 Attacher: "myattacher",
376 Source: storage.VolumeAttachmentSource{},
377 NodeName: "mynode",
378 },
379 }
380
381 successCases := []storage.VolumeAttachment{{
382
383 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
384 Spec: storage.VolumeAttachmentSpec{
385 Attacher: "myattacher",
386 Source: storage.VolumeAttachmentSource{},
387 NodeName: "mynode",
388 },
389 }, {
390
391 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
392 Spec: storage.VolumeAttachmentSpec{
393 Attacher: "myattacher",
394 Source: storage.VolumeAttachmentSource{},
395 NodeName: "mynode",
396 },
397 Status: storage.VolumeAttachmentStatus{
398 Attached: true,
399 AttachmentMetadata: map[string]string{
400 "foo": "bar",
401 },
402 AttachError: &storage.VolumeError{
403 Time: metav1.Time{},
404 Message: "hello world",
405 },
406 DetachError: &storage.VolumeError{
407 Time: metav1.Time{},
408 Message: "hello world",
409 },
410 },
411 }}
412
413 for _, volumeAttachment := range successCases {
414 volumeAttachment.Spec.Source = storage.VolumeAttachmentSource{}
415 old.Spec.Source = storage.VolumeAttachmentSource{}
416
417 volumeAttachment.Spec.Source.PersistentVolumeName = &volumeName
418 old.Spec.Source.PersistentVolumeName = &volumeName
419 if errs := ValidateVolumeAttachmentUpdate(&volumeAttachment, &old); len(errs) != 0 {
420 t.Errorf("expected success: %+v", errs)
421 }
422
423 volumeAttachment.Spec.Source = storage.VolumeAttachmentSource{}
424 old.Spec.Source = storage.VolumeAttachmentSource{}
425
426 volumeAttachment.Spec.Source.InlineVolumeSpec = &inlineSpec
427 old.Spec.Source.InlineVolumeSpec = &inlineSpec
428 if errs := ValidateVolumeAttachmentUpdate(&volumeAttachment, &old); len(errs) != 0 {
429 t.Errorf("expected success: %+v", errs)
430 }
431 }
432
433
434 old.Spec.Source = storage.VolumeAttachmentSource{}
435 old.Spec.Source.PersistentVolumeName = &volumeName
436
437 errorCases := []storage.VolumeAttachment{{
438
439 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
440 Spec: storage.VolumeAttachmentSpec{
441 Attacher: "another-attacher",
442 Source: storage.VolumeAttachmentSource{
443 PersistentVolumeName: &volumeName,
444 },
445 NodeName: "mynode",
446 },
447 }, {
448
449 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
450 Spec: storage.VolumeAttachmentSpec{
451 Attacher: "myattacher",
452 Source: storage.VolumeAttachmentSource{
453 PersistentVolumeName: &newVolumeName,
454 },
455 NodeName: "mynode",
456 },
457 }, {
458
459 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
460 Spec: storage.VolumeAttachmentSpec{
461 Attacher: "myattacher",
462 Source: storage.VolumeAttachmentSource{
463 PersistentVolumeName: &volumeName,
464 },
465 NodeName: "anothernode",
466 },
467 }, {
468
469 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
470 Spec: storage.VolumeAttachmentSpec{
471 Attacher: "myattacher",
472 Source: storage.VolumeAttachmentSource{
473 InlineVolumeSpec: &inlineSpec,
474 },
475 NodeName: "mynode",
476 },
477 }, {
478
479 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
480 Spec: storage.VolumeAttachmentSpec{
481 Attacher: "myattacher",
482 Source: storage.VolumeAttachmentSource{
483 PersistentVolumeName: &volumeName,
484 },
485 NodeName: "mynode",
486 },
487 Status: storage.VolumeAttachmentStatus{
488 Attached: true,
489 AttachmentMetadata: map[string]string{
490 "foo": "bar",
491 },
492 AttachError: &storage.VolumeError{
493 Time: metav1.Time{},
494 Message: strings.Repeat("a", maxAttachedVolumeMetadataSize),
495 },
496 DetachError: &storage.VolumeError{
497 Time: metav1.Time{},
498 Message: "hello world",
499 },
500 },
501 }}
502
503 for _, volumeAttachment := range errorCases {
504 if errs := ValidateVolumeAttachmentUpdate(&volumeAttachment, &old); len(errs) == 0 {
505 t.Errorf("Expected failure for test: %+v", volumeAttachment)
506 }
507 }
508 }
509
510 func TestVolumeAttachmentValidationV1(t *testing.T) {
511 volumeName := "pv-name"
512 invalidVolumeName := "-invalid-@#$%^&*()-"
513 successCases := []storage.VolumeAttachment{{
514 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
515 Spec: storage.VolumeAttachmentSpec{
516 Attacher: "myattacher",
517 Source: storage.VolumeAttachmentSource{
518 PersistentVolumeName: &volumeName,
519 },
520 NodeName: "mynode",
521 },
522 }}
523
524 for _, volumeAttachment := range successCases {
525 if errs := ValidateVolumeAttachmentV1(&volumeAttachment); len(errs) != 0 {
526 t.Errorf("expected success: %+v", errs)
527 }
528 }
529
530 errorCases := []storage.VolumeAttachment{{
531
532 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
533 Spec: storage.VolumeAttachmentSpec{
534 Attacher: "invalid-@#$%^&*()",
535 NodeName: "mynode",
536 Source: storage.VolumeAttachmentSource{
537 PersistentVolumeName: &volumeName,
538 },
539 },
540 }, {
541
542 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
543 Spec: storage.VolumeAttachmentSpec{
544 Attacher: "myattacher",
545 NodeName: "mynode",
546 Source: storage.VolumeAttachmentSource{
547 PersistentVolumeName: &invalidVolumeName,
548 },
549 },
550 }}
551
552 for _, volumeAttachment := range errorCases {
553 if errs := ValidateVolumeAttachmentV1(&volumeAttachment); len(errs) == 0 {
554 t.Errorf("Expected failure for test: %+v", volumeAttachment)
555 }
556 }
557 }
558
559 func makeClass(mode *storage.VolumeBindingMode, topologies []api.TopologySelectorTerm) *storage.StorageClass {
560 return &storage.StorageClass{
561 ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "foo"},
562 Provisioner: "kubernetes.io/foo-provisioner",
563 ReclaimPolicy: &deleteReclaimPolicy,
564 VolumeBindingMode: mode,
565 AllowedTopologies: topologies,
566 }
567 }
568
569 type bindingTest struct {
570 class *storage.StorageClass
571 shouldSucceed bool
572 }
573
574 func TestValidateVolumeBindingMode(t *testing.T) {
575 cases := map[string]bindingTest{
576 "no mode": {
577 class: makeClass(nil, nil),
578 shouldSucceed: false,
579 },
580 "immediate mode": {
581 class: makeClass(&immediateMode1, nil),
582 shouldSucceed: true,
583 },
584 "waiting mode": {
585 class: makeClass(&waitingMode, nil),
586 shouldSucceed: true,
587 },
588 "invalid mode": {
589 class: makeClass(&invalidMode, nil),
590 shouldSucceed: false,
591 },
592 }
593
594 for testName, testCase := range cases {
595 errs := ValidateStorageClass(testCase.class)
596 if testCase.shouldSucceed && len(errs) != 0 {
597 t.Errorf("Expected success for test %q, got %v", testName, errs)
598 }
599 if !testCase.shouldSucceed && len(errs) == 0 {
600 t.Errorf("Expected failure for test %q, got success", testName)
601 }
602 }
603 }
604
605 type updateTest struct {
606 oldClass *storage.StorageClass
607 newClass *storage.StorageClass
608 shouldSucceed bool
609 }
610
611 func TestValidateUpdateVolumeBindingMode(t *testing.T) {
612 noBinding := makeClass(nil, nil)
613 immediateBinding1 := makeClass(&immediateMode1, nil)
614 immediateBinding2 := makeClass(&immediateMode2, nil)
615 waitBinding := makeClass(&waitingMode, nil)
616
617 cases := map[string]updateTest{
618 "old and new no mode": {
619 oldClass: noBinding,
620 newClass: noBinding,
621 shouldSucceed: true,
622 },
623 "old and new same mode ptr": {
624 oldClass: immediateBinding1,
625 newClass: immediateBinding1,
626 shouldSucceed: true,
627 },
628 "old and new same mode value": {
629 oldClass: immediateBinding1,
630 newClass: immediateBinding2,
631 shouldSucceed: true,
632 },
633 "old no mode, new mode": {
634 oldClass: noBinding,
635 newClass: waitBinding,
636 shouldSucceed: false,
637 },
638 "old mode, new no mode": {
639 oldClass: waitBinding,
640 newClass: noBinding,
641 shouldSucceed: false,
642 },
643 "old and new different modes": {
644 oldClass: waitBinding,
645 newClass: immediateBinding1,
646 shouldSucceed: false,
647 },
648 }
649
650 for testName, testCase := range cases {
651 errs := ValidateStorageClassUpdate(testCase.newClass, testCase.oldClass)
652 if testCase.shouldSucceed && len(errs) != 0 {
653 t.Errorf("Expected success for %v, got %v", testName, errs)
654 }
655 if !testCase.shouldSucceed && len(errs) == 0 {
656 t.Errorf("Expected failure for %v, got success", testName)
657 }
658 }
659 }
660
661 func TestValidateAllowedTopologies(t *testing.T) {
662
663 validTopology := []api.TopologySelectorTerm{{
664 MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
665 Key: "failure-domain.beta.kubernetes.io/zone",
666 Values: []string{"zone1"},
667 }, {
668 Key: "kubernetes.io/hostname",
669 Values: []string{"node1"},
670 }},
671 }, {
672 MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
673 Key: "failure-domain.beta.kubernetes.io/zone",
674 Values: []string{"zone2"},
675 }, {
676 Key: "kubernetes.io/hostname",
677 Values: []string{"node2"},
678 }},
679 }}
680
681 topologyInvalidKey := []api.TopologySelectorTerm{{
682 MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
683 Key: "/invalidkey",
684 Values: []string{"zone1"},
685 }},
686 }}
687
688 topologyLackOfValues := []api.TopologySelectorTerm{{
689 MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
690 Key: "kubernetes.io/hostname",
691 Values: []string{},
692 }},
693 }}
694
695 topologyDupValues := []api.TopologySelectorTerm{{
696 MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
697 Key: "kubernetes.io/hostname",
698 Values: []string{"node1", "node1"},
699 }},
700 }}
701
702 topologyMultiValues := []api.TopologySelectorTerm{{
703 MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
704 Key: "kubernetes.io/hostname",
705 Values: []string{"node1", "node2"},
706 }},
707 }}
708
709 topologyEmptyMatchLabelExpressions := []api.TopologySelectorTerm{{
710 MatchLabelExpressions: nil,
711 }}
712
713 topologyDupKeys := []api.TopologySelectorTerm{{
714 MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
715 Key: "kubernetes.io/hostname",
716 Values: []string{"node1"},
717 }, {
718 Key: "kubernetes.io/hostname",
719 Values: []string{"node2"},
720 }},
721 }}
722
723 topologyMultiTerm := []api.TopologySelectorTerm{{
724 MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
725 Key: "kubernetes.io/hostname",
726 Values: []string{"node1"},
727 }},
728 }, {
729 MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
730 Key: "kubernetes.io/hostname",
731 Values: []string{"node2"},
732 }},
733 }}
734
735 topologyDupTermsIdentical := []api.TopologySelectorTerm{{
736 MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
737 Key: "failure-domain.beta.kubernetes.io/zone",
738 Values: []string{"zone1"},
739 }, {
740 Key: "kubernetes.io/hostname",
741 Values: []string{"node1"},
742 }},
743 }, {
744 MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
745 Key: "failure-domain.beta.kubernetes.io/zone",
746 Values: []string{"zone1"},
747 }, {
748 Key: "kubernetes.io/hostname",
749 Values: []string{"node1"},
750 }},
751 }}
752
753 topologyExprsOneSameOneDiff := []api.TopologySelectorTerm{{
754 MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
755 Key: "failure-domain.beta.kubernetes.io/zone",
756 Values: []string{"zone1"},
757 }, {
758 Key: "kubernetes.io/hostname",
759 Values: []string{"node1"},
760 }},
761 }, {
762 MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
763 Key: "failure-domain.beta.kubernetes.io/zone",
764 Values: []string{"zone1"},
765 }, {
766 Key: "kubernetes.io/hostname",
767 Values: []string{"node2"},
768 }},
769 }}
770
771 topologyValuesOneSameOneDiff := []api.TopologySelectorTerm{{
772 MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
773 Key: "kubernetes.io/hostname",
774 Values: []string{"node1", "node2"},
775 }},
776 }, {
777 MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
778 Key: "kubernetes.io/hostname",
779 Values: []string{"node1", "node3"},
780 }},
781 }}
782
783 topologyDupTermsDiffExprOrder := []api.TopologySelectorTerm{{
784 MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
785 Key: "kubernetes.io/hostname",
786 Values: []string{"node1"},
787 }, {
788 Key: "failure-domain.beta.kubernetes.io/zone",
789 Values: []string{"zone1"},
790 }},
791 }, {
792 MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
793 Key: "failure-domain.beta.kubernetes.io/zone",
794 Values: []string{"zone1"},
795 }, {
796 Key: "kubernetes.io/hostname",
797 Values: []string{"node1"},
798 }},
799 }}
800
801 topologyDupTermsDiffValueOrder := []api.TopologySelectorTerm{{
802 MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
803 Key: "failure-domain.beta.kubernetes.io/zone",
804 Values: []string{"zone1", "zone2"},
805 }},
806 }, {
807 MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
808 Key: "failure-domain.beta.kubernetes.io/zone",
809 Values: []string{"zone2", "zone1"},
810 }},
811 }}
812
813 cases := map[string]bindingTest{
814 "no topology": {
815 class: makeClass(&waitingMode, nil),
816 shouldSucceed: true,
817 },
818 "valid topology": {
819 class: makeClass(&waitingMode, validTopology),
820 shouldSucceed: true,
821 },
822 "topology invalid key": {
823 class: makeClass(&waitingMode, topologyInvalidKey),
824 shouldSucceed: false,
825 },
826 "topology lack of values": {
827 class: makeClass(&waitingMode, topologyLackOfValues),
828 shouldSucceed: false,
829 },
830 "duplicate TopologySelectorRequirement values": {
831 class: makeClass(&waitingMode, topologyDupValues),
832 shouldSucceed: false,
833 },
834 "multiple TopologySelectorRequirement values": {
835 class: makeClass(&waitingMode, topologyMultiValues),
836 shouldSucceed: true,
837 },
838 "empty MatchLabelExpressions": {
839 class: makeClass(&waitingMode, topologyEmptyMatchLabelExpressions),
840 shouldSucceed: false,
841 },
842 "duplicate MatchLabelExpression keys": {
843 class: makeClass(&waitingMode, topologyDupKeys),
844 shouldSucceed: false,
845 },
846 "duplicate MatchLabelExpression keys but across separate terms": {
847 class: makeClass(&waitingMode, topologyMultiTerm),
848 shouldSucceed: true,
849 },
850 "duplicate AllowedTopologies terms - identical": {
851 class: makeClass(&waitingMode, topologyDupTermsIdentical),
852 shouldSucceed: false,
853 },
854 "two AllowedTopologies terms, with a pair of the same MatchLabelExpressions and a pair of different ones": {
855 class: makeClass(&waitingMode, topologyExprsOneSameOneDiff),
856 shouldSucceed: true,
857 },
858 "two AllowedTopologies terms, with a pair of the same Values and a pair of different ones": {
859 class: makeClass(&waitingMode, topologyValuesOneSameOneDiff),
860 shouldSucceed: true,
861 },
862 "duplicate AllowedTopologies terms - different MatchLabelExpressions order": {
863 class: makeClass(&waitingMode, topologyDupTermsDiffExprOrder),
864 shouldSucceed: false,
865 },
866 "duplicate AllowedTopologies terms - different TopologySelectorRequirement values order": {
867 class: makeClass(&waitingMode, topologyDupTermsDiffValueOrder),
868 shouldSucceed: false,
869 },
870 }
871
872 for testName, testCase := range cases {
873 errs := ValidateStorageClass(testCase.class)
874 if testCase.shouldSucceed && len(errs) != 0 {
875 t.Errorf("Expected success for test %q, got %v", testName, errs)
876 }
877 if !testCase.shouldSucceed && len(errs) == 0 {
878 t.Errorf("Expected failure for test %q, got success", testName)
879 }
880 }
881 }
882
883 func TestCSINodeValidation(t *testing.T) {
884 driverName := "driver-name"
885 driverName2 := "1io.kubernetes-storage-2-csi-driver3"
886 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"
887 nodeID := "nodeA"
888 longID := longName + longName + "abcdefghijklmnopqrstuvwxyz"
889 successCases := []storage.CSINode{{
890
891 ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
892 Spec: storage.CSINodeSpec{
893 Drivers: []storage.CSINodeDriver{{
894 Name: "io.kubernetes.storage.csi.driver",
895 NodeID: nodeID,
896 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
897 }},
898 },
899 }, {
900
901 ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
902 Spec: storage.CSINodeSpec{
903 Drivers: []storage.CSINodeDriver{{
904 Name: "io-kubernetes-storage-csi-driver",
905 NodeID: nodeID,
906 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
907 }},
908 },
909 }, {
910
911 ObjectMeta: metav1.ObjectMeta{Name: "foo3"},
912 Spec: storage.CSINodeSpec{
913 Drivers: []storage.CSINodeDriver{{
914 Name: "1io-kubernetes-storage-2-csi-driver3",
915 NodeID: nodeID,
916 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
917 }},
918 },
919 }, {
920
921 ObjectMeta: metav1.ObjectMeta{Name: "foo4"},
922 Spec: storage.CSINodeSpec{
923 Drivers: []storage.CSINodeDriver{{
924 Name: "io.kubernetes.storage-csi-driver",
925 NodeID: nodeID,
926 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
927 }},
928 },
929 }, {
930
931 ObjectMeta: metav1.ObjectMeta{Name: "foo5"},
932 Spec: storage.CSINodeSpec{
933 Drivers: []storage.CSINodeDriver{{
934 Name: driverName2,
935 NodeID: nodeID,
936 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
937 }},
938 },
939 }, {
940
941 ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
942 Spec: storage.CSINodeSpec{
943 Drivers: []storage.CSINodeDriver{{
944 Name: "a",
945 NodeID: nodeID,
946 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
947 }},
948 },
949 }, {
950
951 ObjectMeta: metav1.ObjectMeta{Name: "foo6"},
952 Spec: storage.CSINodeSpec{
953 Drivers: []storage.CSINodeDriver{{
954 Name: "driver1",
955 NodeID: "node1",
956 TopologyKeys: []string{"key1", "key2"},
957 }, {
958 Name: "driverB",
959 NodeID: "nodeA",
960 TopologyKeys: []string{"keyA", "keyB"},
961 }},
962 },
963 }, {
964
965 ObjectMeta: metav1.ObjectMeta{Name: "foo7"},
966 Spec: storage.CSINodeSpec{
967 Drivers: []storage.CSINodeDriver{{
968 Name: "driver1",
969 NodeID: "node1",
970 TopologyKeys: []string{"key1"},
971 }, {
972 Name: "driver2",
973 NodeID: "node1",
974 TopologyKeys: []string{"key1"},
975 }},
976 },
977 }, {
978
979 ObjectMeta: metav1.ObjectMeta{Name: "foo11"},
980 Spec: storage.CSINodeSpec{
981 Drivers: []storage.CSINodeDriver{{
982 Name: "io.kubernetes.storage.csi.driver",
983 NodeID: nodeID,
984 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
985 Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(0)},
986 }},
987 },
988 }, {
989
990 ObjectMeta: metav1.ObjectMeta{Name: "foo11"},
991 Spec: storage.CSINodeSpec{
992 Drivers: []storage.CSINodeDriver{{
993 Name: "io.kubernetes.storage.csi.driver",
994 NodeID: nodeID,
995 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
996 Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(1)},
997 }},
998 },
999 }, {
1000
1001 ObjectMeta: metav1.ObjectMeta{Name: "foo8"},
1002 Spec: storage.CSINodeSpec{
1003 Drivers: []storage.CSINodeDriver{{
1004 Name: "driver1",
1005 NodeID: "node1",
1006 TopologyKeys: []string{"zone_1", "zone.2"},
1007 }, {
1008 Name: "driver2",
1009 NodeID: "node1",
1010 TopologyKeys: []string{"zone-3", "zone.4"},
1011 }},
1012 },
1013 }, {
1014
1015 ObjectMeta: metav1.ObjectMeta{Name: "foo9"},
1016 Spec: storage.CSINodeSpec{
1017 Drivers: []storage.CSINodeDriver{{
1018 Name: "driver1",
1019 NodeID: "node1",
1020 TopologyKeys: []string{"company-com/zone1", "company.com/zone2"},
1021 }},
1022 },
1023 }, {
1024
1025 ObjectMeta: metav1.ObjectMeta{Name: "foo10"},
1026 Spec: storage.CSINodeSpec{
1027 Drivers: []storage.CSINodeDriver{{
1028 Name: driverName,
1029 NodeID: nodeID,
1030 }},
1031 },
1032 }}
1033
1034 for _, csiNode := range successCases {
1035 if errs := ValidateCSINode(&csiNode, shorterIDValidationOption); len(errs) != 0 {
1036 t.Errorf("expected success: %v", errs)
1037 }
1038 }
1039
1040 nodeIDCase := storage.CSINode{
1041
1042 ObjectMeta: metav1.ObjectMeta{Name: "foo7"},
1043 Spec: storage.CSINodeSpec{
1044 Drivers: []storage.CSINodeDriver{{
1045 Name: driverName,
1046 NodeID: longID,
1047 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1048 }},
1049 },
1050 }
1051
1052 if errs := ValidateCSINode(&nodeIDCase, longerIDValidateOption); len(errs) != 0 {
1053 t.Errorf("expected success: %v", errs)
1054 }
1055
1056 errorCases := []storage.CSINode{{
1057
1058 ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
1059 Spec: storage.CSINodeSpec{
1060 Drivers: []storage.CSINodeDriver{{
1061 Name: "",
1062 NodeID: nodeID,
1063 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1064 }},
1065 },
1066 }, {
1067
1068 ObjectMeta: metav1.ObjectMeta{Name: "foo3"},
1069 Spec: storage.CSINodeSpec{
1070 Drivers: []storage.CSINodeDriver{{
1071 Name: "_io.kubernetes.storage.csi.driver",
1072 NodeID: nodeID,
1073 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1074 }},
1075 },
1076 }, {
1077
1078 ObjectMeta: metav1.ObjectMeta{Name: "foo4"},
1079 Spec: storage.CSINodeSpec{
1080 Drivers: []storage.CSINodeDriver{{
1081 Name: "io.kubernetes.storage.csi.driver/",
1082 NodeID: nodeID,
1083 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1084 }},
1085 },
1086 }, {
1087
1088 ObjectMeta: metav1.ObjectMeta{Name: "foo5"},
1089 Spec: storage.CSINodeSpec{
1090 Drivers: []storage.CSINodeDriver{{
1091 Name: "io/kubernetes/storage/csi~driver",
1092 NodeID: nodeID,
1093 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1094 }},
1095 },
1096 }, {
1097
1098 ObjectMeta: metav1.ObjectMeta{Name: "foo6"},
1099 Spec: storage.CSINodeSpec{
1100 Drivers: []storage.CSINodeDriver{{
1101 Name: "io_kubernetes_storage_csi_driver",
1102 NodeID: nodeID,
1103 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1104 }},
1105 },
1106 }, {
1107
1108 ObjectMeta: metav1.ObjectMeta{Name: "foo7"},
1109 Spec: storage.CSINodeSpec{
1110 Drivers: []storage.CSINodeDriver{{
1111 Name: longName,
1112 NodeID: nodeID,
1113 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1114 }},
1115 },
1116 }, {
1117
1118 ObjectMeta: metav1.ObjectMeta{Name: "foo8"},
1119 Spec: storage.CSINodeSpec{
1120 Drivers: []storage.CSINodeDriver{{
1121 NodeID: nodeID,
1122 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1123 }},
1124 },
1125 }, {
1126
1127 ObjectMeta: metav1.ObjectMeta{Name: "foo9"},
1128 Spec: storage.CSINodeSpec{
1129 Drivers: []storage.CSINodeDriver{{
1130 Name: driverName,
1131 NodeID: nodeID,
1132 TopologyKeys: []string{"company.com/zone1", ""},
1133 }},
1134 },
1135 }, {
1136
1137 ObjectMeta: metav1.ObjectMeta{Name: "foo10"},
1138 Spec: storage.CSINodeSpec{
1139 Drivers: []storage.CSINodeDriver{{
1140 Name: "driver1",
1141 NodeID: "node1",
1142 TopologyKeys: []string{"key1", "key2"},
1143 }, {
1144 Name: "driver1",
1145 NodeID: "nodeX",
1146 TopologyKeys: []string{"keyA", "keyB"},
1147 }},
1148 },
1149 }, {
1150
1151 ObjectMeta: metav1.ObjectMeta{Name: "foo11"},
1152 Spec: storage.CSINodeSpec{
1153 Drivers: []storage.CSINodeDriver{{
1154 Name: "driver1",
1155 NodeID: "node1",
1156 TopologyKeys: []string{"key1", "key1"},
1157 }},
1158 },
1159 }, {
1160
1161 ObjectMeta: metav1.ObjectMeta{Name: "foo12"},
1162 Spec: storage.CSINodeSpec{
1163 Drivers: []storage.CSINodeDriver{{
1164 Name: "driver1",
1165 NodeID: "node1",
1166 TopologyKeys: []string{"key1"},
1167 }, {
1168 Name: "driver2",
1169 NodeID: "nodeX",
1170 TopologyKeys: []string{"keyA", "keyA"},
1171 }},
1172 },
1173 }, {
1174
1175 ObjectMeta: metav1.ObjectMeta{Name: "foo13"},
1176 Spec: storage.CSINodeSpec{
1177 Drivers: []storage.CSINodeDriver{{
1178 Name: driverName,
1179 NodeID: "",
1180 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1181 }},
1182 },
1183 }, {
1184
1185 ObjectMeta: metav1.ObjectMeta{Name: "foo11"},
1186 Spec: storage.CSINodeSpec{
1187 Drivers: []storage.CSINodeDriver{{
1188 Name: "io.kubernetes.storage.csi.driver",
1189 NodeID: nodeID,
1190 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1191 Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(-1)},
1192 }},
1193 },
1194 }, {
1195
1196 ObjectMeta: metav1.ObjectMeta{Name: "foo14"},
1197 Spec: storage.CSINodeSpec{
1198 Drivers: []storage.CSINodeDriver{{
1199 Name: driverName,
1200 NodeID: "node1",
1201 TopologyKeys: []string{"Company.Com/zone1", "company.com/zone2"},
1202 }},
1203 },
1204 },
1205 nodeIDCase,
1206 }
1207
1208 for _, csiNode := range errorCases {
1209 if errs := ValidateCSINode(&csiNode, shorterIDValidationOption); len(errs) == 0 {
1210 t.Errorf("Expected failure for test: %v", csiNode)
1211 }
1212 }
1213 }
1214
1215 func TestCSINodeUpdateValidation(t *testing.T) {
1216
1217
1218
1219 nodeID := "nodeA"
1220
1221 old := storage.CSINode{
1222 ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
1223 Spec: storage.CSINodeSpec{
1224 Drivers: []storage.CSINodeDriver{{
1225 Name: "io.kubernetes.storage.csi.driver-1",
1226 NodeID: nodeID,
1227 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1228 }, {
1229 Name: "io.kubernetes.storage.csi.driver-2",
1230 NodeID: nodeID,
1231 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1232 Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
1233 }},
1234 },
1235 }
1236
1237 successCases := []storage.CSINode{{
1238
1239 ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
1240 Spec: storage.CSINodeSpec{
1241 Drivers: []storage.CSINodeDriver{{
1242 Name: "io.kubernetes.storage.csi.driver-1",
1243 NodeID: nodeID,
1244 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1245 }, {
1246 Name: "io.kubernetes.storage.csi.driver-2",
1247 NodeID: nodeID,
1248 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1249 Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
1250 }},
1251 },
1252 }, {
1253
1254 ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
1255 Spec: storage.CSINodeSpec{
1256 Drivers: []storage.CSINodeDriver{{
1257 Name: "io.kubernetes.storage.csi.driver-1",
1258 NodeID: nodeID,
1259 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1260 }},
1261 },
1262 }, {
1263
1264 ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
1265 Spec: storage.CSINodeSpec{
1266 Drivers: []storage.CSINodeDriver{{
1267 Name: "io.kubernetes.storage.csi.driver-1",
1268 NodeID: nodeID,
1269 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1270 }, {
1271 Name: "io.kubernetes.storage.csi.driver-2",
1272 NodeID: nodeID,
1273 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1274 Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
1275 }, {
1276 Name: "io.kubernetes.storage.csi.driver-3",
1277 NodeID: nodeID,
1278 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1279 Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(30)},
1280 }},
1281 },
1282 }, {
1283
1284 ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
1285 Spec: storage.CSINodeSpec{
1286 Drivers: []storage.CSINodeDriver{{
1287 Name: "io.kubernetes.storage.csi.driver-1",
1288 NodeID: nodeID,
1289 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1290 }, {
1291 Name: "io.kubernetes.storage.csi.new-driver",
1292 NodeID: nodeID,
1293 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1294 Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(30)},
1295 }},
1296 },
1297 }}
1298
1299 for _, csiNode := range successCases {
1300 if errs := ValidateCSINodeUpdate(&csiNode, &old, shorterIDValidationOption); len(errs) != 0 {
1301 t.Errorf("expected success: %+v", errs)
1302 }
1303 }
1304
1305 errorCases := []storage.CSINode{{
1306
1307 ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
1308 Spec: storage.CSINodeSpec{
1309 Drivers: []storage.CSINodeDriver{{
1310 Name: "io.kubernetes.storage.csi.driver-1",
1311 NodeID: "nodeB",
1312 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1313 }, {
1314 Name: "io.kubernetes.storage.csi.driver-2",
1315 NodeID: nodeID,
1316 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1317 Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
1318 }},
1319 },
1320 }, {
1321
1322 ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
1323 Spec: storage.CSINodeSpec{
1324 Drivers: []storage.CSINodeDriver{{
1325 Name: "io.kubernetes.storage.csi.driver-1",
1326 NodeID: nodeID,
1327 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1328 }, {
1329 Name: "io.kubernetes.storage.csi.driver-2",
1330 NodeID: nodeID,
1331 TopologyKeys: []string{"company.com/zone2"},
1332 Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
1333 }},
1334 },
1335 }, {
1336
1337 ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
1338 Spec: storage.CSINodeSpec{
1339 Drivers: []storage.CSINodeDriver{{
1340 Name: "io.kubernetes.storage.csi.driver-1",
1341 NodeID: nodeID,
1342 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1343 Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(10)},
1344 }, {
1345 Name: "io.kubernetes.storage.csi.driver-2",
1346 NodeID: nodeID,
1347 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1348 Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
1349 }},
1350 },
1351 }, {
1352
1353 ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
1354 Spec: storage.CSINodeSpec{
1355 Drivers: []storage.CSINodeDriver{{
1356 Name: "io.kubernetes.storage.csi.driver-1",
1357 NodeID: nodeID,
1358 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1359 }, {
1360 Name: "io.kubernetes.storage.csi.driver-2",
1361 NodeID: nodeID,
1362 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1363 Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(21)},
1364 }},
1365 },
1366 }, {
1367
1368 ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
1369 Spec: storage.CSINodeSpec{
1370 Drivers: []storage.CSINodeDriver{{
1371 Name: "io.kubernetes.storage.csi.driver-1",
1372 NodeID: nodeID,
1373 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1374 }, {
1375 Name: "io.kubernetes.storage.csi.driver-2",
1376 NodeID: nodeID,
1377 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1378 Allocatable: &storage.VolumeNodeResources{Count: nil},
1379 }},
1380 },
1381 }, {
1382
1383 ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
1384 Spec: storage.CSINodeSpec{
1385 Drivers: []storage.CSINodeDriver{{
1386 Name: "io.kubernetes.storage.csi.driver-1",
1387 NodeID: nodeID,
1388 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1389 }, {
1390 Name: "io.kubernetes.storage.csi.driver-2",
1391 NodeID: nodeID,
1392 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
1393 }},
1394 },
1395 }}
1396
1397 for _, csiNode := range errorCases {
1398 if errs := ValidateCSINodeUpdate(&csiNode, &old, shorterIDValidationOption); len(errs) == 0 {
1399 t.Errorf("Expected failure for test: %+v", csiNode)
1400 }
1401 }
1402 }
1403
1404 func TestCSIDriverValidation(t *testing.T) {
1405
1406 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, true)()
1407
1408 driverName := "test-driver"
1409 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"
1410 invalidName := "-invalid-@#$%^&*()-"
1411 attachRequired := true
1412 attachNotRequired := false
1413 podInfoOnMount := true
1414 notPodInfoOnMount := false
1415 notRequiresRepublish := false
1416 storageCapacity := true
1417 notStorageCapacity := false
1418 seLinuxMount := true
1419 notSELinuxMount := false
1420 supportedFSGroupPolicy := storage.FileFSGroupPolicy
1421 invalidFSGroupPolicy := storage.FSGroupPolicy("invalid-mode")
1422 successCases := []storage.CSIDriver{{
1423 ObjectMeta: metav1.ObjectMeta{Name: driverName},
1424 Spec: storage.CSIDriverSpec{
1425 AttachRequired: &attachRequired,
1426 PodInfoOnMount: &podInfoOnMount,
1427 RequiresRepublish: ¬RequiresRepublish,
1428 StorageCapacity: &storageCapacity,
1429 SELinuxMount: &seLinuxMount,
1430 },
1431 }, {
1432
1433 ObjectMeta: metav1.ObjectMeta{Name: "io.kubernetes.storage.csi.driver"},
1434 Spec: storage.CSIDriverSpec{
1435 AttachRequired: &attachRequired,
1436 PodInfoOnMount: &podInfoOnMount,
1437 RequiresRepublish: ¬RequiresRepublish,
1438 StorageCapacity: ¬StorageCapacity,
1439 SELinuxMount: &seLinuxMount,
1440 },
1441 }, {
1442
1443 ObjectMeta: metav1.ObjectMeta{Name: "io-kubernetes-storage-csi-driver"},
1444 Spec: storage.CSIDriverSpec{
1445 AttachRequired: &attachRequired,
1446 PodInfoOnMount: ¬PodInfoOnMount,
1447 RequiresRepublish: ¬RequiresRepublish,
1448 StorageCapacity: &storageCapacity,
1449 SELinuxMount: &seLinuxMount,
1450 },
1451 }, {
1452
1453 ObjectMeta: metav1.ObjectMeta{Name: "1csi2driver3"},
1454 Spec: storage.CSIDriverSpec{
1455 AttachRequired: &attachRequired,
1456 PodInfoOnMount: &podInfoOnMount,
1457 RequiresRepublish: ¬RequiresRepublish,
1458 StorageCapacity: &storageCapacity,
1459 SELinuxMount: &seLinuxMount,
1460 },
1461 }, {
1462
1463 ObjectMeta: metav1.ObjectMeta{Name: "io.kubernetes.storage.csi-driver"},
1464 Spec: storage.CSIDriverSpec{
1465 AttachRequired: &attachRequired,
1466 PodInfoOnMount: &podInfoOnMount,
1467 RequiresRepublish: ¬RequiresRepublish,
1468 StorageCapacity: &storageCapacity,
1469 SELinuxMount: &seLinuxMount,
1470 },
1471 }, {
1472 ObjectMeta: metav1.ObjectMeta{Name: driverName},
1473 Spec: storage.CSIDriverSpec{
1474 AttachRequired: &attachRequired,
1475 PodInfoOnMount: ¬PodInfoOnMount,
1476 RequiresRepublish: ¬RequiresRepublish,
1477 StorageCapacity: &storageCapacity,
1478 SELinuxMount: &seLinuxMount,
1479 },
1480 }, {
1481 ObjectMeta: metav1.ObjectMeta{Name: driverName},
1482 Spec: storage.CSIDriverSpec{
1483 AttachRequired: &attachRequired,
1484 PodInfoOnMount: &podInfoOnMount,
1485 RequiresRepublish: ¬RequiresRepublish,
1486 StorageCapacity: &storageCapacity,
1487 SELinuxMount: &seLinuxMount,
1488 },
1489 }, {
1490 ObjectMeta: metav1.ObjectMeta{Name: driverName},
1491 Spec: storage.CSIDriverSpec{
1492 AttachRequired: &attachNotRequired,
1493 PodInfoOnMount: ¬PodInfoOnMount,
1494 RequiresRepublish: ¬RequiresRepublish,
1495 StorageCapacity: &storageCapacity,
1496 SELinuxMount: &seLinuxMount,
1497 },
1498 }, {
1499 ObjectMeta: metav1.ObjectMeta{Name: driverName},
1500 Spec: storage.CSIDriverSpec{
1501 AttachRequired: &attachNotRequired,
1502 PodInfoOnMount: ¬PodInfoOnMount,
1503 RequiresRepublish: ¬RequiresRepublish,
1504 StorageCapacity: &storageCapacity,
1505 VolumeLifecycleModes: []storage.VolumeLifecycleMode{
1506 storage.VolumeLifecyclePersistent,
1507 },
1508 SELinuxMount: &seLinuxMount,
1509 },
1510 }, {
1511 ObjectMeta: metav1.ObjectMeta{Name: driverName},
1512 Spec: storage.CSIDriverSpec{
1513 AttachRequired: &attachNotRequired,
1514 PodInfoOnMount: ¬PodInfoOnMount,
1515 RequiresRepublish: ¬RequiresRepublish,
1516 StorageCapacity: &storageCapacity,
1517 VolumeLifecycleModes: []storage.VolumeLifecycleMode{
1518 storage.VolumeLifecycleEphemeral,
1519 },
1520 SELinuxMount: &seLinuxMount,
1521 },
1522 }, {
1523 ObjectMeta: metav1.ObjectMeta{Name: driverName},
1524 Spec: storage.CSIDriverSpec{
1525 AttachRequired: &attachNotRequired,
1526 PodInfoOnMount: ¬PodInfoOnMount,
1527 RequiresRepublish: ¬RequiresRepublish,
1528 StorageCapacity: &storageCapacity,
1529 VolumeLifecycleModes: []storage.VolumeLifecycleMode{
1530 storage.VolumeLifecycleEphemeral,
1531 storage.VolumeLifecyclePersistent,
1532 },
1533 SELinuxMount: &seLinuxMount,
1534 },
1535 }, {
1536 ObjectMeta: metav1.ObjectMeta{Name: driverName},
1537 Spec: storage.CSIDriverSpec{
1538 AttachRequired: &attachNotRequired,
1539 PodInfoOnMount: ¬PodInfoOnMount,
1540 RequiresRepublish: ¬RequiresRepublish,
1541 StorageCapacity: &storageCapacity,
1542 VolumeLifecycleModes: []storage.VolumeLifecycleMode{
1543 storage.VolumeLifecycleEphemeral,
1544 storage.VolumeLifecyclePersistent,
1545 storage.VolumeLifecycleEphemeral,
1546 },
1547 SELinuxMount: &seLinuxMount,
1548 },
1549 }, {
1550 ObjectMeta: metav1.ObjectMeta{Name: driverName},
1551 Spec: storage.CSIDriverSpec{
1552 AttachRequired: &attachNotRequired,
1553 PodInfoOnMount: ¬PodInfoOnMount,
1554 RequiresRepublish: ¬RequiresRepublish,
1555 StorageCapacity: &storageCapacity,
1556 FSGroupPolicy: &supportedFSGroupPolicy,
1557 SELinuxMount: &seLinuxMount,
1558 },
1559 }, {
1560
1561 ObjectMeta: metav1.ObjectMeta{Name: driverName},
1562 Spec: storage.CSIDriverSpec{
1563 AttachRequired: &attachNotRequired,
1564 PodInfoOnMount: ¬PodInfoOnMount,
1565 RequiresRepublish: ¬RequiresRepublish,
1566 StorageCapacity: &storageCapacity,
1567 SELinuxMount: ¬SELinuxMount,
1568 },
1569 }}
1570
1571 for _, csiDriver := range successCases {
1572 if errs := ValidateCSIDriver(&csiDriver); len(errs) != 0 {
1573 t.Errorf("expected success: %v", errs)
1574 }
1575 }
1576 errorCases := []storage.CSIDriver{{
1577 ObjectMeta: metav1.ObjectMeta{Name: invalidName},
1578 Spec: storage.CSIDriverSpec{
1579 AttachRequired: &attachRequired,
1580 PodInfoOnMount: &podInfoOnMount,
1581 StorageCapacity: &storageCapacity,
1582 SELinuxMount: &seLinuxMount,
1583 },
1584 }, {
1585 ObjectMeta: metav1.ObjectMeta{Name: longName},
1586 Spec: storage.CSIDriverSpec{
1587 AttachRequired: &attachNotRequired,
1588 PodInfoOnMount: ¬PodInfoOnMount,
1589 StorageCapacity: &storageCapacity,
1590 SELinuxMount: &seLinuxMount,
1591 },
1592 }, {
1593
1594 ObjectMeta: metav1.ObjectMeta{Name: driverName},
1595 Spec: storage.CSIDriverSpec{
1596 AttachRequired: nil,
1597 PodInfoOnMount: &podInfoOnMount,
1598 StorageCapacity: &storageCapacity,
1599 SELinuxMount: &seLinuxMount,
1600 },
1601 }, {
1602
1603 ObjectMeta: metav1.ObjectMeta{Name: driverName},
1604 Spec: storage.CSIDriverSpec{
1605 AttachRequired: &attachNotRequired,
1606 PodInfoOnMount: nil,
1607 StorageCapacity: &storageCapacity,
1608 SELinuxMount: &seLinuxMount,
1609 },
1610 }, {
1611
1612 ObjectMeta: metav1.ObjectMeta{Name: driverName},
1613 Spec: storage.CSIDriverSpec{
1614 AttachRequired: &attachNotRequired,
1615 PodInfoOnMount: &podInfoOnMount,
1616 StorageCapacity: nil,
1617 SELinuxMount: &seLinuxMount,
1618 },
1619 }, {
1620
1621 ObjectMeta: metav1.ObjectMeta{Name: driverName},
1622 Spec: storage.CSIDriverSpec{
1623 AttachRequired: &attachNotRequired,
1624 PodInfoOnMount: ¬PodInfoOnMount,
1625 StorageCapacity: &storageCapacity,
1626 VolumeLifecycleModes: []storage.VolumeLifecycleMode{
1627 "no-such-mode",
1628 },
1629 SELinuxMount: &seLinuxMount,
1630 },
1631 }, {
1632
1633 ObjectMeta: metav1.ObjectMeta{Name: driverName},
1634 Spec: storage.CSIDriverSpec{
1635 AttachRequired: &attachNotRequired,
1636 PodInfoOnMount: ¬PodInfoOnMount,
1637 FSGroupPolicy: &invalidFSGroupPolicy,
1638 StorageCapacity: &storageCapacity,
1639 SELinuxMount: &seLinuxMount,
1640 },
1641 }, {
1642
1643 ObjectMeta: metav1.ObjectMeta{Name: driverName},
1644 Spec: storage.CSIDriverSpec{
1645 AttachRequired: &attachNotRequired,
1646 PodInfoOnMount: ¬PodInfoOnMount,
1647 StorageCapacity: &storageCapacity,
1648 },
1649 }}
1650
1651 for _, csiDriver := range errorCases {
1652 if errs := ValidateCSIDriver(&csiDriver); len(errs) == 0 {
1653 t.Errorf("Expected failure for test: %v", csiDriver)
1654 }
1655 }
1656 }
1657
1658 func TestCSIDriverValidationUpdate(t *testing.T) {
1659
1660 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, true)()
1661
1662 driverName := "test-driver"
1663 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"
1664 invalidName := "-invalid-@#$%^&*()-"
1665 attachRequired := true
1666 attachNotRequired := false
1667 podInfoOnMount := true
1668 storageCapacity := true
1669 notPodInfoOnMount := false
1670 gcp := "gcp"
1671 requiresRepublish := true
1672 notRequiresRepublish := false
1673 notStorageCapacity := false
1674 seLinuxMount := true
1675 notSELinuxMount := false
1676 resourceVersion := "1"
1677 old := storage.CSIDriver{
1678 ObjectMeta: metav1.ObjectMeta{Name: driverName, ResourceVersion: resourceVersion},
1679 Spec: storage.CSIDriverSpec{
1680 AttachRequired: &attachNotRequired,
1681 PodInfoOnMount: ¬PodInfoOnMount,
1682 RequiresRepublish: ¬RequiresRepublish,
1683 VolumeLifecycleModes: []storage.VolumeLifecycleMode{
1684 storage.VolumeLifecycleEphemeral,
1685 storage.VolumeLifecyclePersistent,
1686 },
1687 StorageCapacity: &storageCapacity,
1688 SELinuxMount: &seLinuxMount,
1689 },
1690 }
1691
1692 successCases := []struct {
1693 name string
1694 modify func(new *storage.CSIDriver)
1695 }{{
1696 name: "no change",
1697 modify: func(new *storage.CSIDriver) {},
1698 }, {
1699 name: "change TokenRequests",
1700 modify: func(new *storage.CSIDriver) {
1701 new.Spec.TokenRequests = []storage.TokenRequest{{Audience: gcp}}
1702 },
1703 }, {
1704 name: "change RequiresRepublish",
1705 modify: func(new *storage.CSIDriver) {
1706 new.Spec.RequiresRepublish = &requiresRepublish
1707 },
1708 }, {
1709 name: "StorageCapacity changed",
1710 modify: func(new *storage.CSIDriver) {
1711 new.Spec.StorageCapacity = ¬StorageCapacity
1712 },
1713 }, {
1714 name: "SELinuxMount changed",
1715 modify: func(new *storage.CSIDriver) {
1716 new.Spec.SELinuxMount = ¬SELinuxMount
1717 },
1718 }, {
1719 name: "change PodInfoOnMount",
1720 modify: func(new *storage.CSIDriver) {
1721 new.Spec.PodInfoOnMount = &podInfoOnMount
1722 },
1723 }, {
1724 name: "change FSGroupPolicy",
1725 modify: func(new *storage.CSIDriver) {
1726 fileFSGroupPolicy := storage.FileFSGroupPolicy
1727 new.Spec.FSGroupPolicy = &fileFSGroupPolicy
1728 },
1729 }}
1730 for _, test := range successCases {
1731 t.Run(test.name, func(t *testing.T) {
1732 new := old.DeepCopy()
1733 test.modify(new)
1734 if errs := ValidateCSIDriverUpdate(new, &old); len(errs) != 0 {
1735 t.Errorf("Expected success for %+v: %v", new, errs)
1736 }
1737 })
1738 }
1739
1740
1741 errorCases := []struct {
1742 name string
1743 modify func(new *storage.CSIDriver)
1744 }{{
1745 name: "invalid name",
1746 modify: func(new *storage.CSIDriver) {
1747 new.Name = invalidName
1748 },
1749 }, {
1750 name: "long name",
1751 modify: func(new *storage.CSIDriver) {
1752 new.Name = longName
1753 },
1754 }, {
1755 name: "AttachRequired not set",
1756 modify: func(new *storage.CSIDriver) {
1757 new.Spec.AttachRequired = nil
1758 },
1759 }, {
1760 name: "AttachRequired changed",
1761 modify: func(new *storage.CSIDriver) {
1762 new.Spec.AttachRequired = &attachRequired
1763 },
1764 }, {
1765 name: "PodInfoOnMount not set",
1766 modify: func(new *storage.CSIDriver) {
1767 new.Spec.PodInfoOnMount = nil
1768 },
1769 }, {
1770 name: "invalid volume lifecycle mode",
1771 modify: func(new *storage.CSIDriver) {
1772 new.Spec.VolumeLifecycleModes = []storage.VolumeLifecycleMode{
1773 "no-such-mode",
1774 }
1775 },
1776 }, {
1777 name: "volume lifecycle modes not set",
1778 modify: func(new *storage.CSIDriver) {
1779 new.Spec.VolumeLifecycleModes = nil
1780 },
1781 }, {
1782 name: "VolumeLifecyclePersistent removed",
1783 modify: func(new *storage.CSIDriver) {
1784 new.Spec.VolumeLifecycleModes = []storage.VolumeLifecycleMode{
1785 storage.VolumeLifecycleEphemeral,
1786 }
1787 },
1788 }, {
1789 name: "VolumeLifecycleEphemeral removed",
1790 modify: func(new *storage.CSIDriver) {
1791 new.Spec.VolumeLifecycleModes = []storage.VolumeLifecycleMode{
1792 storage.VolumeLifecyclePersistent,
1793 }
1794 },
1795 }, {
1796 name: "FSGroupPolicy invalidated",
1797 modify: func(new *storage.CSIDriver) {
1798 invalidFSGroupPolicy := storage.FSGroupPolicy("invalid")
1799 new.Spec.FSGroupPolicy = &invalidFSGroupPolicy
1800 },
1801 }, {
1802 name: "TokenRequests invalidated",
1803 modify: func(new *storage.CSIDriver) {
1804 new.Spec.TokenRequests = []storage.TokenRequest{{Audience: gcp}, {Audience: gcp}}
1805 },
1806 }, {
1807 name: "invalid nil StorageCapacity",
1808 modify: func(new *storage.CSIDriver) {
1809 new.Spec.StorageCapacity = nil
1810 },
1811 }, {
1812 name: "SELinuxMount not set",
1813 modify: func(new *storage.CSIDriver) {
1814 new.Spec.SELinuxMount = nil
1815 },
1816 }}
1817
1818 for _, test := range errorCases {
1819 t.Run(test.name, func(t *testing.T) {
1820 new := old.DeepCopy()
1821 test.modify(new)
1822 if errs := ValidateCSIDriverUpdate(new, &old); len(errs) == 0 {
1823 t.Errorf("Expected failure for test: %+v", new)
1824 }
1825 })
1826 }
1827 }
1828
1829 func TestCSIDriverStorageCapacityEnablement(t *testing.T) {
1830 run := func(t *testing.T, withField bool) {
1831 driverName := "test-driver"
1832 attachRequired := true
1833 podInfoOnMount := true
1834 requiresRepublish := true
1835 storageCapacity := true
1836 seLinuxMount := false
1837 csiDriver := storage.CSIDriver{
1838 ObjectMeta: metav1.ObjectMeta{Name: driverName},
1839 Spec: storage.CSIDriverSpec{
1840 AttachRequired: &attachRequired,
1841 PodInfoOnMount: &podInfoOnMount,
1842 RequiresRepublish: &requiresRepublish,
1843 SELinuxMount: &seLinuxMount,
1844 },
1845 }
1846 if withField {
1847 csiDriver.Spec.StorageCapacity = &storageCapacity
1848 }
1849 errs := ValidateCSIDriver(&csiDriver)
1850 success := withField
1851 if success && len(errs) != 0 {
1852 t.Errorf("expected success, got: %v", errs)
1853 }
1854 if !success && len(errs) == 0 {
1855 t.Errorf("expected error, got success")
1856 }
1857 }
1858
1859 yesNo := []bool{true, false}
1860 for _, withField := range yesNo {
1861 t.Run(fmt.Sprintf("with-field=%v", withField), func(t *testing.T) {
1862 run(t, withField)
1863 })
1864 }
1865 }
1866
1867 func TestValidateCSIStorageCapacity(t *testing.T) {
1868 storageClassName := "test-sc"
1869 invalidName := "-invalid-@#$%^&*()-"
1870
1871 goodCapacity := storage.CSIStorageCapacity{
1872 ObjectMeta: metav1.ObjectMeta{
1873 Name: "csc-329803da-fdd2-42e4-af6f-7b07e7ccc305",
1874 Namespace: metav1.NamespaceDefault,
1875 },
1876 StorageClassName: storageClassName,
1877 }
1878 goodTopology := metav1.LabelSelector{
1879 MatchLabels: map[string]string{"foo": "bar"},
1880 }
1881
1882 scenarios := map[string]struct {
1883 isExpectedFailure bool
1884 capacity *storage.CSIStorageCapacity
1885 }{
1886 "good-capacity": {
1887 capacity: &goodCapacity,
1888 },
1889 "missing-storage-class-name": {
1890 isExpectedFailure: true,
1891 capacity: func() *storage.CSIStorageCapacity {
1892 capacity := goodCapacity
1893 capacity.StorageClassName = ""
1894 return &capacity
1895 }(),
1896 },
1897 "bad-storage-class-name": {
1898 isExpectedFailure: true,
1899 capacity: func() *storage.CSIStorageCapacity {
1900 capacity := goodCapacity
1901 capacity.StorageClassName = invalidName
1902 return &capacity
1903 }(),
1904 },
1905 "good-capacity-value": {
1906 capacity: func() *storage.CSIStorageCapacity {
1907 capacity := goodCapacity
1908 capacity.Capacity = resource.NewQuantity(1, resource.BinarySI)
1909 return &capacity
1910 }(),
1911 },
1912 "bad-capacity-value": {
1913 isExpectedFailure: true,
1914 capacity: func() *storage.CSIStorageCapacity {
1915 capacity := goodCapacity
1916 capacity.Capacity = resource.NewQuantity(-11, resource.BinarySI)
1917 return &capacity
1918 }(),
1919 },
1920 "good-topology": {
1921 capacity: func() *storage.CSIStorageCapacity {
1922 capacity := goodCapacity
1923 capacity.NodeTopology = &goodTopology
1924 return &capacity
1925 }(),
1926 },
1927 "empty-topology": {
1928 capacity: func() *storage.CSIStorageCapacity {
1929 capacity := goodCapacity
1930 capacity.NodeTopology = &metav1.LabelSelector{}
1931 return &capacity
1932 }(),
1933 },
1934 "bad-topology-fields": {
1935 isExpectedFailure: true,
1936 capacity: func() *storage.CSIStorageCapacity {
1937 capacity := goodCapacity
1938 capacity.NodeTopology = &metav1.LabelSelector{
1939 MatchExpressions: []metav1.LabelSelectorRequirement{{
1940 Key: "foo",
1941 Operator: metav1.LabelSelectorOperator("no-such-operator"),
1942 Values: []string{
1943 "bar",
1944 },
1945 }},
1946 }
1947 return &capacity
1948 }(),
1949 },
1950 }
1951
1952 for name, scenario := range scenarios {
1953 t.Run(name, func(t *testing.T) {
1954 errs := ValidateCSIStorageCapacity(scenario.capacity, CSIStorageCapacityValidateOptions{false})
1955 if len(errs) == 0 && scenario.isExpectedFailure {
1956 t.Errorf("Unexpected success")
1957 }
1958 if len(errs) > 0 && !scenario.isExpectedFailure {
1959 t.Errorf("Unexpected failure: %+v", errs)
1960 }
1961 })
1962 }
1963
1964 }
1965
1966 func TestCSIServiceAccountToken(t *testing.T) {
1967 driverName := "test-driver"
1968 gcp := "gcp"
1969 aws := "aws"
1970 notRequiresRepublish := false
1971 tests := []struct {
1972 desc string
1973 csiDriver *storage.CSIDriver
1974 wantErr bool
1975 }{{
1976 desc: "invalid - TokenRequests has tokens with the same audience",
1977 csiDriver: &storage.CSIDriver{
1978 ObjectMeta: metav1.ObjectMeta{Name: driverName},
1979 Spec: storage.CSIDriverSpec{
1980 TokenRequests: []storage.TokenRequest{{Audience: gcp}, {Audience: gcp}},
1981 RequiresRepublish: ¬RequiresRepublish,
1982 },
1983 },
1984 wantErr: true,
1985 }, {
1986 desc: "invalid - TokenRequests has tokens with ExpirationSeconds less than 10min",
1987 csiDriver: &storage.CSIDriver{
1988 ObjectMeta: metav1.ObjectMeta{Name: driverName},
1989 Spec: storage.CSIDriverSpec{
1990 TokenRequests: []storage.TokenRequest{{Audience: gcp, ExpirationSeconds: utilpointer.Int64(10)}},
1991 RequiresRepublish: ¬RequiresRepublish,
1992 },
1993 },
1994 wantErr: true,
1995 }, {
1996 desc: "invalid - TokenRequests has tokens with ExpirationSeconds longer than 1<<32 min",
1997 csiDriver: &storage.CSIDriver{
1998 ObjectMeta: metav1.ObjectMeta{Name: driverName},
1999 Spec: storage.CSIDriverSpec{
2000 TokenRequests: []storage.TokenRequest{{Audience: gcp, ExpirationSeconds: utilpointer.Int64(1<<32 + 1)}},
2001 RequiresRepublish: ¬RequiresRepublish,
2002 },
2003 },
2004 wantErr: true,
2005 }, {
2006 desc: "valid - TokenRequests has at most one token with empty string audience",
2007 csiDriver: &storage.CSIDriver{
2008 ObjectMeta: metav1.ObjectMeta{Name: driverName},
2009 Spec: storage.CSIDriverSpec{
2010 TokenRequests: []storage.TokenRequest{{Audience: ""}},
2011 RequiresRepublish: ¬RequiresRepublish,
2012 },
2013 },
2014 }, {
2015 desc: "valid - TokenRequests has tokens with different audience",
2016 csiDriver: &storage.CSIDriver{
2017 ObjectMeta: metav1.ObjectMeta{Name: driverName},
2018 Spec: storage.CSIDriverSpec{
2019 TokenRequests: []storage.TokenRequest{{}, {Audience: gcp}, {Audience: aws}},
2020 RequiresRepublish: ¬RequiresRepublish,
2021 },
2022 },
2023 }}
2024
2025 for _, test := range tests {
2026 test.csiDriver.Spec.AttachRequired = new(bool)
2027 test.csiDriver.Spec.PodInfoOnMount = new(bool)
2028 test.csiDriver.Spec.StorageCapacity = new(bool)
2029 test.csiDriver.Spec.SELinuxMount = new(bool)
2030 if errs := ValidateCSIDriver(test.csiDriver); test.wantErr != (len(errs) != 0) {
2031 t.Errorf("ValidateCSIDriver = %v, want err: %v", errs, test.wantErr)
2032 }
2033 }
2034 }
2035
2036 func TestCSIDriverValidationSELinuxMountEnabledDisabled(t *testing.T) {
2037 tests := []struct {
2038 name string
2039 featureEnabled bool
2040 seLinuxMountValue *bool
2041 expectError bool
2042 }{{
2043 name: "feature enabled, nil value",
2044 featureEnabled: true,
2045 seLinuxMountValue: nil,
2046 expectError: true,
2047 }, {
2048 name: "feature enabled, non-nil value",
2049 featureEnabled: true,
2050 seLinuxMountValue: utilpointer.Bool(true),
2051 expectError: false,
2052 }, {
2053 name: "feature disabled, nil value",
2054 featureEnabled: false,
2055 seLinuxMountValue: nil,
2056 expectError: false,
2057 }, {
2058 name: "feature disabled, non-nil value",
2059 featureEnabled: false,
2060 seLinuxMountValue: utilpointer.Bool(true),
2061 expectError: false,
2062 }}
2063 for _, test := range tests {
2064 t.Run(test.name, func(t *testing.T) {
2065 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, test.featureEnabled)()
2066 csiDriver := &storage.CSIDriver{
2067 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
2068 Spec: storage.CSIDriverSpec{
2069 AttachRequired: utilpointer.Bool(true),
2070 PodInfoOnMount: utilpointer.Bool(true),
2071 RequiresRepublish: utilpointer.Bool(true),
2072 StorageCapacity: utilpointer.Bool(true),
2073 SELinuxMount: test.seLinuxMountValue,
2074 },
2075 }
2076 err := ValidateCSIDriver(csiDriver)
2077 if test.expectError && err == nil {
2078 t.Error("Expected validation error, got nil")
2079 }
2080 if !test.expectError && err != nil {
2081 t.Errorf("Validation returned error: %s", err)
2082 }
2083 })
2084 }
2085
2086 updateTests := []struct {
2087 name string
2088 featureEnabled bool
2089 oldValue *bool
2090 newValue *bool
2091 expectError bool
2092 }{{
2093 name: "feature enabled, nil->nil",
2094 featureEnabled: true,
2095 oldValue: nil,
2096 newValue: nil,
2097 expectError: true,
2098 }, {
2099 name: "feature enabled, nil->set",
2100 featureEnabled: true,
2101 oldValue: nil,
2102 newValue: utilpointer.Bool(true),
2103 expectError: false,
2104 }, {
2105 name: "feature enabled, set->set",
2106 featureEnabled: true,
2107 oldValue: utilpointer.Bool(true),
2108 newValue: utilpointer.Bool(true),
2109 expectError: false,
2110 }, {
2111 name: "feature enabled, set->nil",
2112 featureEnabled: true,
2113 oldValue: utilpointer.Bool(true),
2114 newValue: nil,
2115 expectError: true,
2116 }, {
2117 name: "feature disabled, nil->nil",
2118 featureEnabled: false,
2119 oldValue: nil,
2120 newValue: nil,
2121 expectError: false,
2122 }, {
2123 name: "feature disabled, nil->set",
2124 featureEnabled: false,
2125 oldValue: nil,
2126 newValue: utilpointer.Bool(true),
2127 expectError: false,
2128 }, {
2129 name: "feature disabled, set->set",
2130 featureEnabled: false,
2131 oldValue: utilpointer.Bool(true),
2132 newValue: utilpointer.Bool(true),
2133 expectError: false,
2134 }, {
2135 name: "feature disabled, set->nil",
2136 featureEnabled: false,
2137 oldValue: utilpointer.Bool(true),
2138 newValue: nil,
2139 expectError: false,
2140 }}
2141 for _, test := range updateTests {
2142 t.Run(test.name, func(t *testing.T) {
2143 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, test.featureEnabled)()
2144 oldCSIDriver := &storage.CSIDriver{
2145 ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "1"},
2146 Spec: storage.CSIDriverSpec{
2147 AttachRequired: utilpointer.Bool(true),
2148 PodInfoOnMount: utilpointer.Bool(true),
2149 RequiresRepublish: utilpointer.Bool(true),
2150 StorageCapacity: utilpointer.Bool(true),
2151 SELinuxMount: test.oldValue,
2152 },
2153 }
2154 newCSIDriver := oldCSIDriver.DeepCopy()
2155 newCSIDriver.Spec.SELinuxMount = test.newValue
2156 err := ValidateCSIDriverUpdate(newCSIDriver, oldCSIDriver)
2157 if test.expectError && err == nil {
2158 t.Error("Expected validation error, got nil")
2159 }
2160 if !test.expectError && err != nil {
2161 t.Errorf("Validation returned error: %s", err)
2162 }
2163 })
2164 }
2165 }
2166
2167 func TestValidateVolumeAttributesClass(t *testing.T) {
2168 successCases := []storage.VolumeAttributesClass{
2169 {
2170
2171 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
2172 DriverName: "foo",
2173 Parameters: map[string]string{
2174 "foo-parameter": "free-form-string",
2175 },
2176 },
2177 {
2178
2179 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
2180 DriverName: "kubernetes.io/foo",
2181 Parameters: map[string]string{
2182 "kubernetes.io/foo-parameter": "free/form/string",
2183 "foo-parameter": "free-form-string",
2184 "foo-parameter2": "{\"embedded\": \"json\", \"with\": {\"structures\":\"inside\"}}",
2185 "foo-parameter3": "",
2186 },
2187 }}
2188
2189
2190 for testName, v := range successCases {
2191 if errs := ValidateVolumeAttributesClass(&v); len(errs) != 0 {
2192 t.Errorf("Expected success for %d, got %v", testName, errs)
2193 }
2194 }
2195
2196
2197 longParameters := make(map[string]string)
2198 totalSize := 0
2199 for totalSize < maxProvisionerParameterSize {
2200 k := fmt.Sprintf("param/%d", totalSize)
2201 v := fmt.Sprintf("value-%d", totalSize)
2202 longParameters[k] = v
2203 totalSize = totalSize + len(k) + len(v)
2204 }
2205
2206 errorCases := map[string]storage.VolumeAttributesClass{
2207 "namespace is present": {
2208 ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
2209 DriverName: "kubernetes.io/foo",
2210 Parameters: map[string]string{
2211 "foo-parameter": "free-form-string",
2212 },
2213 },
2214 "invalid driverName": {
2215 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
2216 DriverName: "kubernetes.io/invalid/foo",
2217 Parameters: map[string]string{
2218 "foo-parameter": "free-form-string",
2219 },
2220 },
2221 "invalid driverName with invalid chars": {
2222 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
2223 DriverName: "^/ ",
2224 Parameters: map[string]string{
2225 "foo-parameter": "free-form-string",
2226 },
2227 },
2228 "empty parameters": {
2229 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
2230 DriverName: "kubernetes.io/foo",
2231 Parameters: map[string]string{},
2232 },
2233 "nil parameters": {
2234 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
2235 DriverName: "kubernetes.io/foo",
2236 },
2237 "invalid empty parameter name": {
2238 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
2239 DriverName: "kubernetes.io/foo",
2240 Parameters: map[string]string{
2241 "": "value",
2242 },
2243 },
2244 "driverName: Required value": {
2245 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
2246 DriverName: "",
2247 Parameters: map[string]string{
2248 "foo-parameter": "free-form-string",
2249 },
2250 },
2251 "driverName: whitespace": {
2252 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
2253 DriverName: " ",
2254 Parameters: map[string]string{
2255 "foo-parameter": "free-form-string",
2256 },
2257 },
2258 "too long parameters": {
2259 ObjectMeta: metav1.ObjectMeta{Name: "foo"},
2260 DriverName: "kubernetes.io/foo",
2261 Parameters: longParameters,
2262 },
2263 }
2264
2265
2266 for testName, v := range errorCases {
2267 if errs := ValidateVolumeAttributesClass(&v); len(errs) == 0 {
2268 t.Errorf("Expected failure for test: %s", testName)
2269 }
2270 }
2271 }
2272
2273 func TestValidateVolumeAttributesClassUpdate(t *testing.T) {
2274 cases := map[string]struct {
2275 oldClass *storage.VolumeAttributesClass
2276 newClass *storage.VolumeAttributesClass
2277 shouldSucceed bool
2278 }{
2279 "invalid driverName update": {
2280 oldClass: &storage.VolumeAttributesClass{
2281 DriverName: "kubernetes.io/foo",
2282 },
2283 newClass: &storage.VolumeAttributesClass{
2284 DriverName: "kubernetes.io/bar",
2285 },
2286 shouldSucceed: false,
2287 },
2288 "invalid parameter update which changes values": {
2289 oldClass: &storage.VolumeAttributesClass{
2290 DriverName: "kubernetes.io/foo",
2291 Parameters: map[string]string{
2292 "foo": "bar1",
2293 },
2294 },
2295 newClass: &storage.VolumeAttributesClass{
2296 DriverName: "kubernetes.io/foo",
2297 Parameters: map[string]string{
2298 "foo": "bar2",
2299 },
2300 },
2301 shouldSucceed: false,
2302 },
2303 "invalid parameter update which add new item": {
2304 oldClass: &storage.VolumeAttributesClass{
2305 DriverName: "kubernetes.io/foo",
2306 Parameters: map[string]string{},
2307 },
2308 newClass: &storage.VolumeAttributesClass{
2309 DriverName: "kubernetes.io/foo",
2310 Parameters: map[string]string{
2311 "foo": "bar",
2312 },
2313 },
2314 shouldSucceed: false,
2315 },
2316 "invalid parameter update which remove a item": {
2317 oldClass: &storage.VolumeAttributesClass{
2318 DriverName: "kubernetes.io/foo",
2319 Parameters: map[string]string{
2320 "foo": "bar",
2321 },
2322 },
2323 newClass: &storage.VolumeAttributesClass{
2324 DriverName: "kubernetes.io/foo",
2325 Parameters: map[string]string{},
2326 },
2327 shouldSucceed: false,
2328 },
2329 }
2330
2331 for testName, testCase := range cases {
2332 errs := ValidateVolumeAttributesClassUpdate(testCase.newClass, testCase.oldClass)
2333 if testCase.shouldSucceed && len(errs) != 0 {
2334 t.Errorf("Expected success for %v, got %v", testName, errs)
2335 }
2336 if !testCase.shouldSucceed && len(errs) == 0 {
2337 t.Errorf("Expected failure for %v, got success", testName)
2338 }
2339 }
2340 }
2341
View as plain text