1
16
17 package helper
18
19 import (
20 "fmt"
21 "reflect"
22 "testing"
23
24 v1 "k8s.io/api/core/v1"
25 "k8s.io/apimachinery/pkg/api/resource"
26 "k8s.io/apimachinery/pkg/labels"
27 )
28
29 func TestIsNativeResource(t *testing.T) {
30 testCases := []struct {
31 resourceName v1.ResourceName
32 expectVal bool
33 }{
34 {
35 resourceName: "pod.alpha.kubernetes.io/opaque-int-resource-foo",
36 expectVal: true,
37 },
38 {
39 resourceName: "kubernetes.io/resource-foo",
40 expectVal: true,
41 },
42 {
43 resourceName: "foo",
44 expectVal: true,
45 },
46 {
47 resourceName: "a/b",
48 expectVal: false,
49 },
50 {
51 resourceName: "",
52 expectVal: true,
53 },
54 }
55
56 for _, tc := range testCases {
57 tc := tc
58 t.Run(fmt.Sprintf("resourceName input=%s, expected value=%v", tc.resourceName, tc.expectVal), func(t *testing.T) {
59 t.Parallel()
60 v := IsNativeResource(tc.resourceName)
61 if v != tc.expectVal {
62 t.Errorf("Got %v but expected %v", v, tc.expectVal)
63 }
64 })
65 }
66 }
67
68 func TestHugePageSizeFromResourceName(t *testing.T) {
69 expected100m, _ := resource.ParseQuantity("100m")
70 testCases := []struct {
71 resourceName v1.ResourceName
72 expectVal resource.Quantity
73 expectErr bool
74 }{
75 {
76 resourceName: "pod.alpha.kubernetes.io/opaque-int-resource-foo",
77 expectVal: resource.Quantity{},
78 expectErr: true,
79 },
80 {
81 resourceName: "hugepages-",
82 expectVal: resource.Quantity{},
83 expectErr: true,
84 },
85 {
86 resourceName: "hugepages-100m",
87 expectVal: expected100m,
88 expectErr: false,
89 },
90 {
91 resourceName: "",
92 expectVal: resource.Quantity{},
93 expectErr: true,
94 },
95 }
96
97 for i, tc := range testCases {
98 i := i
99 tc := tc
100 t.Run(fmt.Sprintf("resourceName input=%s, expected value=%v", tc.resourceName, tc.expectVal), func(t *testing.T) {
101 t.Parallel()
102 v, err := HugePageSizeFromResourceName(tc.resourceName)
103 if err == nil && tc.expectErr {
104 t.Errorf("[%v]expected error but got none.", i)
105 }
106 if err != nil && !tc.expectErr {
107 t.Errorf("[%v]did not expect error but got: %v", i, err)
108 }
109 if v != tc.expectVal {
110 t.Errorf("Got %v but expected %v", v, tc.expectVal)
111 }
112 })
113 }
114 }
115
116 func TestHugePageSizeFromMedium(t *testing.T) {
117 testCases := []struct {
118 description string
119 medium v1.StorageMedium
120 expectVal resource.Quantity
121 expectErr bool
122 }{
123 {
124 description: "Invalid hugepages medium",
125 medium: "Memory",
126 expectVal: resource.Quantity{},
127 expectErr: true,
128 },
129 {
130 description: "Invalid hugepages medium",
131 medium: "Memory",
132 expectVal: resource.Quantity{},
133 expectErr: true,
134 },
135 {
136 description: "Invalid: HugePages without size",
137 medium: "HugePages",
138 expectVal: resource.Quantity{},
139 expectErr: true,
140 },
141 {
142 description: "Invalid: HugePages without size",
143 medium: "HugePages",
144 expectVal: resource.Quantity{},
145 expectErr: true,
146 },
147 {
148 description: "Valid: HugePages-1Gi",
149 medium: "HugePages-1Gi",
150 expectVal: resource.MustParse("1Gi"),
151 expectErr: false,
152 },
153 {
154 description: "Valid: HugePages-2Mi",
155 medium: "HugePages-2Mi",
156 expectVal: resource.MustParse("2Mi"),
157 expectErr: false,
158 },
159 {
160 description: "Valid: HugePages-64Ki",
161 medium: "HugePages-64Ki",
162 expectVal: resource.MustParse("64Ki"),
163 expectErr: false,
164 },
165 }
166 for i, tc := range testCases {
167 i := i
168 tc := tc
169 t.Run(tc.description, func(t *testing.T) {
170 t.Parallel()
171 v, err := HugePageSizeFromMedium(tc.medium)
172 if err == nil && tc.expectErr {
173 t.Errorf("[%v]expected error but got none.", i)
174 }
175 if err != nil && !tc.expectErr {
176 t.Errorf("[%v]did not expect error but got: %v", i, err)
177 }
178 if v != tc.expectVal {
179 t.Errorf("Got %v but expected %v", v, tc.expectVal)
180 }
181 })
182 }
183 }
184
185 func TestIsOvercommitAllowed(t *testing.T) {
186 testCases := []struct {
187 resourceName v1.ResourceName
188 expectVal bool
189 }{
190 {
191 resourceName: "pod.alpha.kubernetes.io/opaque-int-resource-foo",
192 expectVal: true,
193 },
194 {
195 resourceName: "kubernetes.io/resource-foo",
196 expectVal: true,
197 },
198 {
199 resourceName: "hugepages-100m",
200 expectVal: false,
201 },
202 {
203 resourceName: "",
204 expectVal: true,
205 },
206 }
207
208 for _, tc := range testCases {
209 tc := tc
210 t.Run(fmt.Sprintf("resourceName input=%s, expected value=%v", tc.resourceName, tc.expectVal), func(t *testing.T) {
211 t.Parallel()
212 v := IsOvercommitAllowed(tc.resourceName)
213 if v != tc.expectVal {
214 t.Errorf("Got %v but expected %v", v, tc.expectVal)
215 }
216 })
217 }
218 }
219
220 func TestGetAccessModesFromString(t *testing.T) {
221 modes := GetAccessModesFromString("ROX")
222 if !ContainsAccessMode(modes, v1.ReadOnlyMany) {
223 t.Errorf("Expected mode %s, but got %+v", v1.ReadOnlyMany, modes)
224 }
225
226 modes = GetAccessModesFromString("ROX,RWX")
227 if !ContainsAccessMode(modes, v1.ReadOnlyMany) {
228 t.Errorf("Expected mode %s, but got %+v", v1.ReadOnlyMany, modes)
229 }
230 if !ContainsAccessMode(modes, v1.ReadWriteMany) {
231 t.Errorf("Expected mode %s, but got %+v", v1.ReadWriteMany, modes)
232 }
233
234 modes = GetAccessModesFromString("RWO,ROX,RWX")
235 if !ContainsAccessMode(modes, v1.ReadWriteOnce) {
236 t.Errorf("Expected mode %s, but got %+v", v1.ReadWriteOnce, modes)
237 }
238 if !ContainsAccessMode(modes, v1.ReadOnlyMany) {
239 t.Errorf("Expected mode %s, but got %+v", v1.ReadOnlyMany, modes)
240 }
241 if !ContainsAccessMode(modes, v1.ReadWriteMany) {
242 t.Errorf("Expected mode %s, but got %+v", v1.ReadWriteMany, modes)
243 }
244
245 modes = GetAccessModesFromString("RWO,ROX,RWX,RWOP")
246 if !ContainsAccessMode(modes, v1.ReadWriteOnce) {
247 t.Errorf("Expected mode %s, but got %+v", v1.ReadWriteOnce, modes)
248 }
249 if !ContainsAccessMode(modes, v1.ReadOnlyMany) {
250 t.Errorf("Expected mode %s, but got %+v", v1.ReadOnlyMany, modes)
251 }
252 if !ContainsAccessMode(modes, v1.ReadWriteMany) {
253 t.Errorf("Expected mode %s, but got %+v", v1.ReadWriteMany, modes)
254 }
255 if !ContainsAccessMode(modes, v1.ReadWriteOncePod) {
256 t.Errorf("Expected mode %s, but got %+v", v1.ReadWriteOncePod, modes)
257 }
258 }
259
260 func TestRemoveDuplicateAccessModes(t *testing.T) {
261 modes := []v1.PersistentVolumeAccessMode{
262 v1.ReadWriteOnce, v1.ReadOnlyMany, v1.ReadOnlyMany, v1.ReadOnlyMany,
263 }
264 modes = removeDuplicateAccessModes(modes)
265 if len(modes) != 2 {
266 t.Errorf("Expected 2 distinct modes in set but found %v", len(modes))
267 }
268 }
269
270 func TestTopologySelectorRequirementsAsSelector(t *testing.T) {
271 mustParse := func(s string) labels.Selector {
272 out, e := labels.Parse(s)
273 if e != nil {
274 panic(e)
275 }
276 return out
277 }
278 tc := []struct {
279 in []v1.TopologySelectorLabelRequirement
280 out labels.Selector
281 expectErr bool
282 }{
283 {in: nil, out: labels.Nothing()},
284 {in: []v1.TopologySelectorLabelRequirement{}, out: labels.Nothing()},
285 {
286 in: []v1.TopologySelectorLabelRequirement{{
287 Key: "foo",
288 Values: []string{"bar", "baz"},
289 }},
290 out: mustParse("foo in (baz,bar)"),
291 },
292 {
293 in: []v1.TopologySelectorLabelRequirement{{
294 Key: "foo",
295 Values: []string{},
296 }},
297 expectErr: true,
298 },
299 {
300 in: []v1.TopologySelectorLabelRequirement{
301 {
302 Key: "foo",
303 Values: []string{"bar", "baz"},
304 },
305 {
306 Key: "invalid",
307 Values: []string{},
308 },
309 },
310 expectErr: true,
311 },
312 {
313 in: []v1.TopologySelectorLabelRequirement{{
314 Key: "/invalidkey",
315 Values: []string{"bar", "baz"},
316 }},
317 expectErr: true,
318 },
319 }
320
321 for i, tc := range tc {
322 out, err := TopologySelectorRequirementsAsSelector(tc.in)
323 if err == nil && tc.expectErr {
324 t.Errorf("[%v]expected error but got none.", i)
325 }
326 if err != nil && !tc.expectErr {
327 t.Errorf("[%v]did not expect error but got: %v", i, err)
328 }
329 if !reflect.DeepEqual(out, tc.out) {
330 t.Errorf("[%v]expected:\n\t%+v\nbut got:\n\t%+v", i, tc.out, out)
331 }
332 }
333 }
334
335 func TestMatchTopologySelectorTerms(t *testing.T) {
336 type args struct {
337 topologySelectorTerms []v1.TopologySelectorTerm
338 labels labels.Set
339 }
340
341 tests := []struct {
342 name string
343 args args
344 want bool
345 }{
346 {
347 name: "nil term list",
348 args: args{
349 topologySelectorTerms: nil,
350 labels: nil,
351 },
352 want: true,
353 },
354 {
355 name: "nil term",
356 args: args{
357 topologySelectorTerms: []v1.TopologySelectorTerm{
358 {
359 MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{},
360 },
361 },
362 labels: nil,
363 },
364 want: false,
365 },
366 {
367 name: "label matches MatchLabelExpressions terms",
368 args: args{
369 topologySelectorTerms: []v1.TopologySelectorTerm{
370 {
371 MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{{
372 Key: "label_1",
373 Values: []string{"label_1_val"},
374 }},
375 },
376 },
377 labels: map[string]string{"label_1": "label_1_val"},
378 },
379 want: true,
380 },
381 {
382 name: "label does not match MatchLabelExpressions terms",
383 args: args{
384 topologySelectorTerms: []v1.TopologySelectorTerm{
385 {
386 MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{{
387 Key: "label_1",
388 Values: []string{"label_1_val"},
389 }},
390 },
391 },
392 labels: map[string]string{"label_1": "label_1_val-failed"},
393 },
394 want: false,
395 },
396 {
397 name: "multi-values in one requirement, one matched",
398 args: args{
399 topologySelectorTerms: []v1.TopologySelectorTerm{
400 {
401 MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{{
402 Key: "label_1",
403 Values: []string{"label_1_val1", "label_1_val2"},
404 }},
405 },
406 },
407 labels: map[string]string{"label_1": "label_1_val2"},
408 },
409 want: true,
410 },
411 {
412 name: "multi-terms was set, one matched",
413 args: args{
414 topologySelectorTerms: []v1.TopologySelectorTerm{
415 {
416 MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{{
417 Key: "label_1",
418 Values: []string{"label_1_val"},
419 }},
420 },
421 {
422 MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{{
423 Key: "label_2",
424 Values: []string{"label_2_val"},
425 }},
426 },
427 },
428 labels: map[string]string{
429 "label_2": "label_2_val",
430 },
431 },
432 want: true,
433 },
434 {
435 name: "multi-requirement in one term, fully matched",
436 args: args{
437 topologySelectorTerms: []v1.TopologySelectorTerm{
438 {
439 MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
440 {
441 Key: "label_1",
442 Values: []string{"label_1_val"},
443 },
444 {
445 Key: "label_2",
446 Values: []string{"label_2_val"},
447 },
448 },
449 },
450 },
451 labels: map[string]string{
452 "label_1": "label_1_val",
453 "label_2": "label_2_val",
454 },
455 },
456 want: true,
457 },
458 {
459 name: "multi-requirement in one term, partial matched",
460 args: args{
461 topologySelectorTerms: []v1.TopologySelectorTerm{
462 {
463 MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
464 {
465 Key: "label_1",
466 Values: []string{"label_1_val"},
467 },
468 {
469 Key: "label_2",
470 Values: []string{"label_2_val"},
471 },
472 },
473 },
474 },
475 labels: map[string]string{
476 "label_1": "label_1_val-failed",
477 "label_2": "label_2_val",
478 },
479 },
480 want: false,
481 },
482 }
483
484 for _, tt := range tests {
485 t.Run(tt.name, func(t *testing.T) {
486 if got := MatchTopologySelectorTerms(tt.args.topologySelectorTerms, tt.args.labels); got != tt.want {
487 t.Errorf("MatchTopologySelectorTermsORed() = %v, want %v", got, tt.want)
488 }
489 })
490 }
491 }
492
493 func TestNodeSelectorRequirementKeyExistsInNodeSelectorTerms(t *testing.T) {
494 tests := []struct {
495 name string
496 reqs []v1.NodeSelectorRequirement
497 terms []v1.NodeSelectorTerm
498 exists bool
499 }{
500 {
501 name: "empty set of keys in empty set of terms",
502 reqs: []v1.NodeSelectorRequirement{},
503 terms: []v1.NodeSelectorTerm{},
504 exists: false,
505 },
506 {
507 name: "key existence in terms with all keys specified",
508 reqs: []v1.NodeSelectorRequirement{
509 {
510 Key: "key1",
511 Operator: v1.NodeSelectorOpIn,
512 Values: []string{"test-value1"},
513 },
514 {
515 Key: "key2",
516 Operator: v1.NodeSelectorOpIn,
517 Values: []string{"test-value2"},
518 },
519 },
520 terms: []v1.NodeSelectorTerm{
521 {
522 MatchExpressions: []v1.NodeSelectorRequirement{
523 {
524 Key: "key2",
525 Operator: v1.NodeSelectorOpIn,
526 Values: []string{"test-value2"},
527 },
528 {
529 Key: "key3",
530 Operator: v1.NodeSelectorOpIn,
531 Values: []string{"test-value3"},
532 },
533 },
534 },
535 {
536 MatchExpressions: []v1.NodeSelectorRequirement{
537 {
538 Key: "key1",
539 Operator: v1.NodeSelectorOpIn,
540 Values: []string{"test-value11, test-value12"},
541 },
542 {
543 Key: "key4",
544 Operator: v1.NodeSelectorOpIn,
545 Values: []string{"test-value41, test-value42"},
546 },
547 },
548 },
549 },
550 exists: true,
551 },
552 {
553 name: "key existence in terms with one of the keys specified",
554 reqs: []v1.NodeSelectorRequirement{
555 {
556 Key: "key1",
557 Operator: v1.NodeSelectorOpIn,
558 Values: []string{"test-value1"},
559 },
560 {
561 Key: "key2",
562 Operator: v1.NodeSelectorOpIn,
563 Values: []string{"test-value2"},
564 },
565 {
566 Key: "key3",
567 Operator: v1.NodeSelectorOpIn,
568 Values: []string{"test-value3"},
569 },
570 {
571 Key: "key6",
572 Operator: v1.NodeSelectorOpIn,
573 Values: []string{"test-value6"},
574 },
575 },
576 terms: []v1.NodeSelectorTerm{
577 {
578 MatchExpressions: []v1.NodeSelectorRequirement{
579 {
580 Key: "key2",
581 Operator: v1.NodeSelectorOpIn,
582 Values: []string{"test-value2"},
583 }, {
584 Key: "key4",
585 Operator: v1.NodeSelectorOpIn,
586 Values: []string{"test-value4"},
587 },
588 },
589 },
590 {
591 MatchExpressions: []v1.NodeSelectorRequirement{
592 {
593 Key: "key5",
594 Operator: v1.NodeSelectorOpIn,
595 Values: []string{"test-value5"},
596 },
597 },
598 },
599 },
600 exists: true,
601 },
602 {
603 name: "key existence in terms without any of the keys specified",
604 reqs: []v1.NodeSelectorRequirement{
605 {
606 Key: "key2",
607 Operator: v1.NodeSelectorOpIn,
608 Values: []string{"test-value2"},
609 },
610 {
611 Key: "key3",
612 Operator: v1.NodeSelectorOpIn,
613 Values: []string{"test-value3"},
614 },
615 },
616 terms: []v1.NodeSelectorTerm{
617 {
618 MatchExpressions: []v1.NodeSelectorRequirement{
619 {
620 Key: "key4",
621 Operator: v1.NodeSelectorOpIn,
622 Values: []string{"test-value"},
623 },
624 {
625 Key: "key5",
626 Operator: v1.NodeSelectorOpIn,
627 Values: []string{"test-value"},
628 },
629 },
630 },
631 {
632 MatchExpressions: []v1.NodeSelectorRequirement{
633 {
634 Key: "key6",
635 Operator: v1.NodeSelectorOpIn,
636 Values: []string{"test-value"},
637 },
638 },
639 },
640 {
641 MatchExpressions: []v1.NodeSelectorRequirement{
642 {
643 Key: "key7",
644 Operator: v1.NodeSelectorOpIn,
645 Values: []string{"test-value"},
646 },
647 {
648 Key: "key8",
649 Operator: v1.NodeSelectorOpIn,
650 Values: []string{"test-value"},
651 },
652 },
653 },
654 },
655 exists: false,
656 },
657 {
658 name: "key existence in empty set of terms",
659 reqs: []v1.NodeSelectorRequirement{
660 {
661 Key: "key2",
662 Operator: v1.NodeSelectorOpIn,
663 Values: []string{"test-value2"},
664 },
665 {
666 Key: "key3",
667 Operator: v1.NodeSelectorOpIn,
668 Values: []string{"test-value3"},
669 },
670 },
671 terms: []v1.NodeSelectorTerm{},
672 exists: false,
673 },
674 }
675 for _, test := range tests {
676 keyExists := NodeSelectorRequirementKeysExistInNodeSelectorTerms(test.reqs, test.terms)
677 if test.exists != keyExists {
678 t.Errorf("test %s failed. Expected %v but got %v", test.name, test.exists, keyExists)
679 }
680 }
681 }
682
683 func TestHugePageUnitSizeFromByteSize(t *testing.T) {
684 tests := []struct {
685 size int64
686 expected string
687 wantErr bool
688 }{
689 {
690 size: 1024,
691 expected: "1KB",
692 wantErr: false,
693 },
694 {
695 size: 33554432,
696 expected: "32MB",
697 wantErr: false,
698 },
699 {
700 size: 3221225472,
701 expected: "3GB",
702 wantErr: false,
703 },
704 {
705 size: 1024 * 1024 * 1023 * 3,
706 expected: "3069MB",
707 wantErr: true,
708 },
709 }
710 for _, test := range tests {
711 size := test.size
712 result, err := HugePageUnitSizeFromByteSize(size)
713 if err != nil {
714 if test.wantErr {
715 t.Logf("HugePageUnitSizeFromByteSize() expected error = %v", err)
716 } else {
717 t.Errorf("HugePageUnitSizeFromByteSize() error = %v, wantErr %v", err, test.wantErr)
718 }
719 continue
720 }
721 if test.expected != result {
722 t.Errorf("HugePageUnitSizeFromByteSize() expected %v but got %v", test.expected, result)
723 }
724 }
725 }
726
727 func TestLoadBalancerStatusEqual(t *testing.T) {
728
729 testCases := []struct {
730 left *v1.LoadBalancerStatus
731 right *v1.LoadBalancerStatus
732 name string
733 expectVal bool
734 }{{
735 name: "left equals right",
736 left: &v1.LoadBalancerStatus{
737 Ingress: []v1.LoadBalancerIngress{{
738 IP: "1.1.1.1",
739 Hostname: "host1",
740 }},
741 },
742 right: &v1.LoadBalancerStatus{
743 Ingress: []v1.LoadBalancerIngress{{
744 IP: "1.1.1.1",
745 Hostname: "host1",
746 }},
747 },
748 expectVal: true,
749 }, {
750 name: "length of LoadBalancerIngress slice is not equal",
751 left: &v1.LoadBalancerStatus{
752 Ingress: []v1.LoadBalancerIngress{{
753 IP: "1.1.1.1",
754 Hostname: "host1",
755 }, {
756 IP: "1.1.1.2",
757 Hostname: "host1",
758 }},
759 },
760 right: &v1.LoadBalancerStatus{
761 Ingress: []v1.LoadBalancerIngress{{
762 IP: "1.1.1.1",
763 Hostname: "host1",
764 }},
765 },
766 expectVal: false,
767 }, {
768 name: "LoadBalancerIngress ip is not equal",
769 left: &v1.LoadBalancerStatus{
770 Ingress: []v1.LoadBalancerIngress{{
771 IP: "1.1.1.2",
772 Hostname: "host1",
773 }},
774 },
775 right: &v1.LoadBalancerStatus{
776 Ingress: []v1.LoadBalancerIngress{{
777 IP: "1.1.1.1",
778 Hostname: "host1",
779 }},
780 },
781 expectVal: false,
782 }, {
783 name: "LoadBalancerIngress hostname is not equal",
784 left: &v1.LoadBalancerStatus{
785 Ingress: []v1.LoadBalancerIngress{{
786 IP: "1.1.1.1",
787 Hostname: "host2",
788 }},
789 },
790 right: &v1.LoadBalancerStatus{
791 Ingress: []v1.LoadBalancerIngress{{
792 IP: "1.1.1.1",
793 Hostname: "host1",
794 }},
795 },
796 expectVal: false,
797 }}
798
799 for _, tc := range testCases {
800 t.Run(tc.name, func(t *testing.T) {
801 v := LoadBalancerStatusEqual(tc.left, tc.right)
802 if v != tc.expectVal {
803 t.Errorf("test %s failed. left input=%v, right input=%v, Got %v but expected %v",
804 tc.name, tc.left, tc.right, v, tc.expectVal)
805 }
806 })
807 }
808 }
809
View as plain text