1
16
17 package container
18
19 import (
20 "reflect"
21 "testing"
22 "time"
23
24 "github.com/google/go-cmp/cmp"
25 "github.com/stretchr/testify/assert"
26
27 v1 "k8s.io/api/core/v1"
28 "k8s.io/apimachinery/pkg/api/resource"
29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30 )
31
32 func TestEnvVarsToMap(t *testing.T) {
33 vars := []EnvVar{
34 {
35 Name: "foo",
36 Value: "bar",
37 },
38 {
39 Name: "zoo",
40 Value: "baz",
41 },
42 }
43
44 varMap := envVarsToMap(vars)
45
46 if e, a := len(vars), len(varMap); e != a {
47 t.Errorf("Unexpected map length; expected: %d, got %d", e, a)
48 }
49
50 if a := varMap["foo"]; a != "bar" {
51 t.Errorf("Unexpected value of key 'foo': %v", a)
52 }
53
54 if a := varMap["zoo"]; a != "baz" {
55 t.Errorf("Unexpected value of key 'zoo': %v", a)
56 }
57 }
58
59 func TestExpandCommandAndArgs(t *testing.T) {
60 cases := []struct {
61 name string
62 container *v1.Container
63 envs []EnvVar
64 expectedCommand []string
65 expectedArgs []string
66 }{
67 {
68 name: "none",
69 container: &v1.Container{},
70 },
71 {
72 name: "command expanded",
73 container: &v1.Container{
74 Command: []string{"foo", "$(VAR_TEST)", "$(VAR_TEST2)"},
75 },
76 envs: []EnvVar{
77 {
78 Name: "VAR_TEST",
79 Value: "zoo",
80 },
81 {
82 Name: "VAR_TEST2",
83 Value: "boo",
84 },
85 },
86 expectedCommand: []string{"foo", "zoo", "boo"},
87 },
88 {
89 name: "args expanded",
90 container: &v1.Container{
91 Args: []string{"zap", "$(VAR_TEST)", "$(VAR_TEST2)"},
92 },
93 envs: []EnvVar{
94 {
95 Name: "VAR_TEST",
96 Value: "hap",
97 },
98 {
99 Name: "VAR_TEST2",
100 Value: "trap",
101 },
102 },
103 expectedArgs: []string{"zap", "hap", "trap"},
104 },
105 {
106 name: "both expanded",
107 container: &v1.Container{
108 Command: []string{"$(VAR_TEST2)--$(VAR_TEST)", "foo", "$(VAR_TEST3)"},
109 Args: []string{"foo", "$(VAR_TEST)", "$(VAR_TEST2)"},
110 },
111 envs: []EnvVar{
112 {
113 Name: "VAR_TEST",
114 Value: "zoo",
115 },
116 {
117 Name: "VAR_TEST2",
118 Value: "boo",
119 },
120 {
121 Name: "VAR_TEST3",
122 Value: "roo",
123 },
124 },
125 expectedCommand: []string{"boo--zoo", "foo", "roo"},
126 expectedArgs: []string{"foo", "zoo", "boo"},
127 },
128 }
129
130 for _, tc := range cases {
131 actualCommand, actualArgs := ExpandContainerCommandAndArgs(tc.container, tc.envs)
132
133 if e, a := tc.expectedCommand, actualCommand; !reflect.DeepEqual(e, a) {
134 t.Errorf("%v: unexpected command; expected %v, got %v", tc.name, e, a)
135 }
136
137 if e, a := tc.expectedArgs, actualArgs; !reflect.DeepEqual(e, a) {
138 t.Errorf("%v: unexpected args; expected %v, got %v", tc.name, e, a)
139 }
140
141 }
142 }
143
144 func TestExpandVolumeMountsWithSubpath(t *testing.T) {
145 cases := []struct {
146 name string
147 container *v1.Container
148 envs []EnvVar
149 expectedSubPath string
150 expectedMountPath string
151 expectedOk bool
152 }{
153 {
154 name: "subpath with no expansion",
155 container: &v1.Container{
156 VolumeMounts: []v1.VolumeMount{{SubPathExpr: "foo"}},
157 },
158 expectedSubPath: "foo",
159 expectedMountPath: "",
160 expectedOk: true,
161 },
162 {
163 name: "volumes with expanded subpath",
164 container: &v1.Container{
165 VolumeMounts: []v1.VolumeMount{{SubPathExpr: "foo/$(POD_NAME)"}},
166 },
167 envs: []EnvVar{
168 {
169 Name: "POD_NAME",
170 Value: "bar",
171 },
172 },
173 expectedSubPath: "foo/bar",
174 expectedMountPath: "",
175 expectedOk: true,
176 },
177 {
178 name: "volumes expanded with empty subpath",
179 container: &v1.Container{
180 VolumeMounts: []v1.VolumeMount{{SubPathExpr: ""}},
181 },
182 envs: []EnvVar{
183 {
184 Name: "POD_NAME",
185 Value: "bar",
186 },
187 },
188 expectedSubPath: "",
189 expectedMountPath: "",
190 expectedOk: true,
191 },
192 {
193 name: "volumes expanded with no envs subpath",
194 container: &v1.Container{
195 VolumeMounts: []v1.VolumeMount{{SubPathExpr: "/foo/$(POD_NAME)"}},
196 },
197 expectedSubPath: "/foo/$(POD_NAME)",
198 expectedMountPath: "",
199 expectedOk: false,
200 },
201 {
202 name: "volumes expanded with leading environment variable",
203 container: &v1.Container{
204 VolumeMounts: []v1.VolumeMount{{SubPathExpr: "$(POD_NAME)/bar"}},
205 },
206 envs: []EnvVar{
207 {
208 Name: "POD_NAME",
209 Value: "foo",
210 },
211 },
212 expectedSubPath: "foo/bar",
213 expectedMountPath: "",
214 expectedOk: true,
215 },
216 {
217 name: "volumes with volume and subpath",
218 container: &v1.Container{
219 VolumeMounts: []v1.VolumeMount{{MountPath: "/foo", SubPathExpr: "$(POD_NAME)/bar"}},
220 },
221 envs: []EnvVar{
222 {
223 Name: "POD_NAME",
224 Value: "foo",
225 },
226 },
227 expectedSubPath: "foo/bar",
228 expectedMountPath: "/foo",
229 expectedOk: true,
230 },
231 {
232 name: "volumes with volume and no subpath",
233 container: &v1.Container{
234 VolumeMounts: []v1.VolumeMount{{MountPath: "/foo"}},
235 },
236 envs: []EnvVar{
237 {
238 Name: "POD_NAME",
239 Value: "foo",
240 },
241 },
242 expectedSubPath: "",
243 expectedMountPath: "/foo",
244 expectedOk: true,
245 },
246 {
247 name: "subpaths with empty environment variable",
248 container: &v1.Container{
249 VolumeMounts: []v1.VolumeMount{{SubPathExpr: "foo/$(POD_NAME)/$(ANNOTATION)"}},
250 },
251 envs: []EnvVar{
252 {
253 Name: "ANNOTATION",
254 Value: "",
255 },
256 },
257 expectedSubPath: "foo/$(POD_NAME)/$(ANNOTATION)",
258 expectedMountPath: "",
259 expectedOk: false,
260 },
261 {
262 name: "subpaths with missing env variables",
263 container: &v1.Container{
264 VolumeMounts: []v1.VolumeMount{{SubPathExpr: "foo/$(ODD_NAME)/$(POD_NAME)"}},
265 },
266 envs: []EnvVar{
267 {
268 Name: "ODD_NAME",
269 Value: "bar",
270 },
271 },
272 expectedSubPath: "foo/$(ODD_NAME)/$(POD_NAME)",
273 expectedMountPath: "",
274 expectedOk: false,
275 },
276 {
277 name: "subpaths with empty expansion",
278 container: &v1.Container{
279 VolumeMounts: []v1.VolumeMount{{SubPathExpr: "$()"}},
280 },
281 expectedSubPath: "$()",
282 expectedMountPath: "",
283 expectedOk: false,
284 },
285 {
286 name: "subpaths with nested expandable envs",
287 container: &v1.Container{
288 VolumeMounts: []v1.VolumeMount{{SubPathExpr: "$(POD_NAME$(ANNOTATION))"}},
289 },
290 envs: []EnvVar{
291 {
292 Name: "POD_NAME",
293 Value: "foo",
294 },
295 {
296 Name: "ANNOTATION",
297 Value: "bar",
298 },
299 },
300 expectedSubPath: "$(POD_NAME$(ANNOTATION))",
301 expectedMountPath: "",
302 expectedOk: false,
303 },
304 }
305
306 for _, tc := range cases {
307 actualSubPath, err := ExpandContainerVolumeMounts(tc.container.VolumeMounts[0], tc.envs)
308 ok := err == nil
309 if e, a := tc.expectedOk, ok; !reflect.DeepEqual(e, a) {
310 t.Errorf("%v: unexpected validation failure of subpath; expected %v, got %v", tc.name, e, a)
311 }
312 if !ok {
313
314 continue
315 }
316 if e, a := tc.expectedSubPath, actualSubPath; !reflect.DeepEqual(e, a) {
317 t.Errorf("%v: unexpected subpath; expected %v, got %v", tc.name, e, a)
318 }
319 if e, a := tc.expectedMountPath, tc.container.VolumeMounts[0].MountPath; !reflect.DeepEqual(e, a) {
320 t.Errorf("%v: unexpected mountpath; expected %v, got %v", tc.name, e, a)
321 }
322 }
323
324 }
325
326 func TestGetContainerSpec(t *testing.T) {
327 for _, tc := range []struct {
328 name string
329 havePod *v1.Pod
330 haveName string
331 wantContainer *v1.Container
332 }{
333 {
334 name: "regular container",
335 havePod: &v1.Pod{
336 Spec: v1.PodSpec{
337 Containers: []v1.Container{
338 {Name: "plain-ole-container"},
339 },
340 InitContainers: []v1.Container{
341 {Name: "init-container"},
342 },
343 },
344 },
345 haveName: "plain-ole-container",
346 wantContainer: &v1.Container{Name: "plain-ole-container"},
347 },
348 {
349 name: "init container",
350 havePod: &v1.Pod{
351 Spec: v1.PodSpec{
352 Containers: []v1.Container{
353 {Name: "plain-ole-container"},
354 },
355 InitContainers: []v1.Container{
356 {Name: "init-container"},
357 },
358 },
359 },
360 haveName: "init-container",
361 wantContainer: &v1.Container{Name: "init-container"},
362 },
363 {
364 name: "ephemeral container",
365 havePod: &v1.Pod{
366 Spec: v1.PodSpec{
367 Containers: []v1.Container{
368 {Name: "plain-ole-container"},
369 },
370 InitContainers: []v1.Container{
371 {Name: "init-container"},
372 },
373 EphemeralContainers: []v1.EphemeralContainer{
374 {EphemeralContainerCommon: v1.EphemeralContainerCommon{
375 Name: "debug-container",
376 }},
377 },
378 },
379 },
380 haveName: "debug-container",
381 wantContainer: &v1.Container{Name: "debug-container"},
382 },
383 } {
384 t.Run(tc.name, func(t *testing.T) {
385 gotContainer := GetContainerSpec(tc.havePod, tc.haveName)
386 if diff := cmp.Diff(tc.wantContainer, gotContainer); diff != "" {
387 t.Fatalf("GetContainerSpec for %q returned diff (-want +got):%v", tc.name, diff)
388 }
389 })
390 }
391 }
392
393 func TestShouldContainerBeRestarted(t *testing.T) {
394 pod := &v1.Pod{
395 ObjectMeta: metav1.ObjectMeta{
396 UID: "12345678",
397 Name: "foo",
398 Namespace: "new",
399 },
400 Spec: v1.PodSpec{
401 Containers: []v1.Container{
402 {Name: "no-history"},
403 {Name: "alive"},
404 {Name: "succeed"},
405 {Name: "failed"},
406 {Name: "unknown"},
407 },
408 },
409 }
410 podStatus := &PodStatus{
411 ID: pod.UID,
412 Name: pod.Name,
413 Namespace: pod.Namespace,
414 ContainerStatuses: []*Status{
415 {
416 Name: "alive",
417 State: ContainerStateRunning,
418 },
419 {
420 Name: "succeed",
421 State: ContainerStateExited,
422 ExitCode: 0,
423 },
424 {
425 Name: "failed",
426 State: ContainerStateExited,
427 ExitCode: 1,
428 },
429 {
430 Name: "alive",
431 State: ContainerStateExited,
432 ExitCode: 2,
433 },
434 {
435 Name: "unknown",
436 State: ContainerStateUnknown,
437 },
438 {
439 Name: "failed",
440 State: ContainerStateExited,
441 ExitCode: 3,
442 },
443 },
444 }
445 policies := []v1.RestartPolicy{
446 v1.RestartPolicyNever,
447 v1.RestartPolicyOnFailure,
448 v1.RestartPolicyAlways,
449 }
450
451
452 expected := map[string][]bool{
453 "no-history": {true, true, true},
454 "alive": {false, false, false},
455 "succeed": {false, false, true},
456 "failed": {false, true, true},
457 "unknown": {true, true, true},
458 }
459 for _, c := range pod.Spec.Containers {
460 for i, policy := range policies {
461 pod.Spec.RestartPolicy = policy
462 e := expected[c.Name][i]
463 r := ShouldContainerBeRestarted(&c, pod, podStatus)
464 if r != e {
465 t.Errorf("Restart for container %q with restart policy %q expected %t, got %t",
466 c.Name, policy, e, r)
467 }
468 }
469 }
470
471
472 pod.DeletionTimestamp = &metav1.Time{Time: time.Now()}
473 expected = map[string][]bool{
474 "no-history": {false, false, false},
475 "alive": {false, false, false},
476 "succeed": {false, false, false},
477 "failed": {false, false, false},
478 "unknown": {false, false, false},
479 }
480 for _, c := range pod.Spec.Containers {
481 for i, policy := range policies {
482 pod.Spec.RestartPolicy = policy
483 e := expected[c.Name][i]
484 r := ShouldContainerBeRestarted(&c, pod, podStatus)
485 if r != e {
486 t.Errorf("Restart for container %q with restart policy %q expected %t, got %t",
487 c.Name, policy, e, r)
488 }
489 }
490 }
491 }
492
493 func TestHasPrivilegedContainer(t *testing.T) {
494 newBoolPtr := func(b bool) *bool {
495 return &b
496 }
497 tests := map[string]struct {
498 securityContext *v1.SecurityContext
499 expected bool
500 }{
501 "nil security context": {
502 securityContext: nil,
503 expected: false,
504 },
505 "nil privileged": {
506 securityContext: &v1.SecurityContext{},
507 expected: false,
508 },
509 "false privileged": {
510 securityContext: &v1.SecurityContext{Privileged: newBoolPtr(false)},
511 expected: false,
512 },
513 "true privileged": {
514 securityContext: &v1.SecurityContext{Privileged: newBoolPtr(true)},
515 expected: true,
516 },
517 }
518
519 for k, v := range tests {
520 pod := &v1.Pod{
521 Spec: v1.PodSpec{
522 Containers: []v1.Container{
523 {SecurityContext: v.securityContext},
524 },
525 },
526 }
527 actual := HasPrivilegedContainer(pod)
528 if actual != v.expected {
529 t.Errorf("%s expected %t but got %t", k, v.expected, actual)
530 }
531 }
532
533 for k, v := range tests {
534 pod := &v1.Pod{
535 Spec: v1.PodSpec{
536 InitContainers: []v1.Container{
537 {SecurityContext: v.securityContext},
538 },
539 },
540 }
541 actual := HasPrivilegedContainer(pod)
542 if actual != v.expected {
543 t.Errorf("%s expected %t but got %t", k, v.expected, actual)
544 }
545 }
546 }
547
548 func TestMakePortMappings(t *testing.T) {
549 port := func(name string, protocol v1.Protocol, containerPort, hostPort int32, ip string) v1.ContainerPort {
550 return v1.ContainerPort{
551 Name: name,
552 Protocol: protocol,
553 ContainerPort: containerPort,
554 HostPort: hostPort,
555 HostIP: ip,
556 }
557 }
558 portMapping := func(protocol v1.Protocol, containerPort, hostPort int, ip string) PortMapping {
559 return PortMapping{
560 Protocol: protocol,
561 ContainerPort: containerPort,
562 HostPort: hostPort,
563 HostIP: ip,
564 }
565 }
566
567 tests := []struct {
568 container *v1.Container
569 expectedPortMappings []PortMapping
570 }{
571 {
572 &v1.Container{
573 Name: "fooContainer",
574 Ports: []v1.ContainerPort{
575 port("", v1.ProtocolTCP, 80, 8080, "127.0.0.1"),
576 port("", v1.ProtocolTCP, 443, 4343, "192.168.0.1"),
577 port("foo", v1.ProtocolUDP, 555, 5555, ""),
578
579 port("foo", v1.ProtocolUDP, 888, 8888, ""),
580
581 port("", v1.ProtocolTCP, 80, 8080, "::"),
582
583 port("", v1.ProtocolTCP, 1234, 5678, ""),
584 },
585 },
586 []PortMapping{
587 portMapping(v1.ProtocolTCP, 80, 8080, "127.0.0.1"),
588 portMapping(v1.ProtocolTCP, 443, 4343, "192.168.0.1"),
589 portMapping(v1.ProtocolUDP, 555, 5555, ""),
590 portMapping(v1.ProtocolTCP, 80, 8080, "::"),
591 portMapping(v1.ProtocolTCP, 1234, 5678, ""),
592 },
593 },
594 {
595
596 &v1.Container{
597 Name: "fooContainer",
598 Ports: []v1.ContainerPort{
599 port("", v1.ProtocolTCP, 443, 4343, "192.168.0.1"),
600 port("", v1.ProtocolTCP, 4343, 4343, "192.168.0.1"),
601 },
602 },
603 []PortMapping{
604 portMapping(v1.ProtocolTCP, 443, 4343, "192.168.0.1"),
605 portMapping(v1.ProtocolTCP, 4343, 4343, "192.168.0.1"),
606 },
607 },
608 {
609
610 &v1.Container{
611 Name: "fooContainer",
612 Ports: []v1.ContainerPort{
613 port("", v1.ProtocolTCP, 443, 4343, ""),
614 port("", v1.ProtocolTCP, 443, 4343, ""),
615 },
616 },
617 []PortMapping{
618 portMapping(v1.ProtocolTCP, 443, 4343, ""),
619 },
620 },
621 {
622
623 &v1.Container{
624 Name: "fooContainer",
625 Ports: []v1.ContainerPort{
626 port("", v1.ProtocolTCP, 443, 4343, "192.168.0.1"),
627 port("", v1.ProtocolTCP, 443, 4343, "172.16.0.1"),
628 },
629 },
630 []PortMapping{
631 portMapping(v1.ProtocolTCP, 443, 4343, "192.168.0.1"),
632 portMapping(v1.ProtocolTCP, 443, 4343, "172.16.0.1"),
633 },
634 },
635 }
636
637 for i, tt := range tests {
638 actual := MakePortMappings(tt.container)
639 assert.Equal(t, tt.expectedPortMappings, actual, "[%d]", i)
640 }
641 }
642
643 func TestHashContainer(t *testing.T) {
644 testCases := []struct {
645 name string
646 image string
647 args []string
648 containerPort int32
649 expectedHash uint64
650 }{
651 {
652 name: "test_container",
653 image: "foo/image:v1",
654 args: []string{
655 "/bin/sh",
656 "-c",
657 "echo abc",
658 },
659 containerPort: int32(8001),
660 expectedHash: uint64(0x3c42280f),
661 },
662 }
663
664 for _, tc := range testCases {
665 container := v1.Container{
666 Name: tc.name,
667 Image: tc.image,
668 Args: tc.args,
669 Ports: []v1.ContainerPort{{ContainerPort: tc.containerPort}},
670 }
671
672 hashVal := HashContainer(&container)
673 assert.Equal(t, tc.expectedHash, hashVal, "the hash value here should not be changed.")
674 }
675 }
676
677 func TestShouldRecordEvent(t *testing.T) {
678 var innerEventRecorder = &innerEventRecorder{
679 recorder: nil,
680 }
681
682 _, actual := innerEventRecorder.shouldRecordEvent(nil)
683 assert.Equal(t, false, actual)
684
685 var obj = &v1.ObjectReference{Namespace: "claimrefns", Name: "claimrefname"}
686
687 _, actual = innerEventRecorder.shouldRecordEvent(obj)
688 assert.Equal(t, true, actual)
689
690 obj = &v1.ObjectReference{Namespace: "system", Name: "infra", FieldPath: "implicitly required container "}
691
692 _, actual = innerEventRecorder.shouldRecordEvent(obj)
693 assert.Equal(t, false, actual)
694
695 var nilObj *v1.ObjectReference = nil
696 _, actual = innerEventRecorder.shouldRecordEvent(nilObj)
697 assert.Equal(t, false, actual, "should not panic if the typed nil was used, see https://github.com/kubernetes/kubernetes/issues/95552")
698 }
699
700 func TestHasWindowsHostProcessContainer(t *testing.T) {
701 trueVar := true
702 falseVar := false
703 const containerName = "container"
704
705 testCases := []struct {
706 name string
707 podSpec *v1.PodSpec
708 expectedResult bool
709 }{
710 {
711 name: "hostprocess not set anywhere",
712 podSpec: &v1.PodSpec{
713 Containers: []v1.Container{{
714 Name: containerName,
715 }},
716 },
717 expectedResult: false,
718 },
719 {
720 name: "pod with hostprocess=false",
721 podSpec: &v1.PodSpec{
722 HostNetwork: true,
723 SecurityContext: &v1.PodSecurityContext{
724 WindowsOptions: &v1.WindowsSecurityContextOptions{
725 HostProcess: &falseVar,
726 },
727 },
728 Containers: []v1.Container{{
729 Name: containerName,
730 }},
731 },
732 expectedResult: false,
733 },
734 {
735 name: "pod with hostprocess=true",
736 podSpec: &v1.PodSpec{
737 HostNetwork: true,
738 SecurityContext: &v1.PodSecurityContext{
739 WindowsOptions: &v1.WindowsSecurityContextOptions{
740 HostProcess: &trueVar,
741 },
742 },
743 Containers: []v1.Container{{
744 Name: containerName,
745 }},
746 },
747 expectedResult: true,
748 },
749 {
750 name: "container with hostprocess=false",
751 podSpec: &v1.PodSpec{
752 HostNetwork: true,
753 Containers: []v1.Container{{
754 Name: containerName,
755 SecurityContext: &v1.SecurityContext{
756 WindowsOptions: &v1.WindowsSecurityContextOptions{
757 HostProcess: &falseVar,
758 },
759 },
760 }},
761 },
762 expectedResult: false,
763 },
764 {
765 name: "container with hostprocess=true",
766 podSpec: &v1.PodSpec{
767 HostNetwork: true,
768 Containers: []v1.Container{{
769 Name: containerName,
770 SecurityContext: &v1.SecurityContext{
771 WindowsOptions: &v1.WindowsSecurityContextOptions{
772 HostProcess: &trueVar,
773 },
774 },
775 }},
776 },
777 expectedResult: true,
778 },
779 {
780 name: "pod with hostprocess=false, container with hostprocess=true",
781 podSpec: &v1.PodSpec{
782 HostNetwork: true,
783 SecurityContext: &v1.PodSecurityContext{
784 WindowsOptions: &v1.WindowsSecurityContextOptions{
785 HostProcess: &falseVar,
786 },
787 },
788 Containers: []v1.Container{{
789 Name: containerName,
790 SecurityContext: &v1.SecurityContext{
791 WindowsOptions: &v1.WindowsSecurityContextOptions{
792 HostProcess: &trueVar,
793 },
794 },
795 }},
796 },
797 expectedResult: true,
798 },
799 {
800 name: "pod with hostprocess=true, container with hostprocess=flase",
801 podSpec: &v1.PodSpec{
802 HostNetwork: true,
803 SecurityContext: &v1.PodSecurityContext{
804 WindowsOptions: &v1.WindowsSecurityContextOptions{
805 HostProcess: &trueVar,
806 },
807 },
808 Containers: []v1.Container{{
809 Name: containerName,
810 SecurityContext: &v1.SecurityContext{
811 WindowsOptions: &v1.WindowsSecurityContextOptions{
812 HostProcess: &falseVar,
813 },
814 },
815 }},
816 },
817 expectedResult: false,
818 },
819 {
820 name: "containers with hostproces=mixed",
821 podSpec: &v1.PodSpec{
822 Containers: []v1.Container{
823 {
824 Name: containerName,
825 SecurityContext: &v1.SecurityContext{
826 WindowsOptions: &v1.WindowsSecurityContextOptions{
827 HostProcess: &falseVar,
828 },
829 },
830 },
831 {
832 Name: containerName,
833 SecurityContext: &v1.SecurityContext{
834 WindowsOptions: &v1.WindowsSecurityContextOptions{
835 HostProcess: &trueVar,
836 },
837 },
838 },
839 },
840 },
841 expectedResult: true,
842 },
843 {
844 name: "pod with hostProcess=false, containers with hostproces=mixed",
845 podSpec: &v1.PodSpec{
846 SecurityContext: &v1.PodSecurityContext{
847 WindowsOptions: &v1.WindowsSecurityContextOptions{
848 HostProcess: &falseVar,
849 },
850 },
851 Containers: []v1.Container{
852 {
853 Name: containerName,
854 SecurityContext: &v1.SecurityContext{
855 WindowsOptions: &v1.WindowsSecurityContextOptions{
856 HostProcess: &falseVar,
857 },
858 },
859 },
860 {
861 Name: containerName,
862 SecurityContext: &v1.SecurityContext{
863 WindowsOptions: &v1.WindowsSecurityContextOptions{
864 HostProcess: &trueVar,
865 },
866 },
867 },
868 },
869 },
870 expectedResult: true,
871 },
872 {
873 name: "pod with hostProcess=true, containers with hostproces=mixed",
874 podSpec: &v1.PodSpec{
875 SecurityContext: &v1.PodSecurityContext{
876 WindowsOptions: &v1.WindowsSecurityContextOptions{
877 HostProcess: &trueVar,
878 },
879 },
880 Containers: []v1.Container{
881 {
882 Name: containerName,
883 SecurityContext: &v1.SecurityContext{
884 WindowsOptions: &v1.WindowsSecurityContextOptions{
885 HostProcess: &falseVar,
886 },
887 },
888 },
889 {
890 Name: containerName,
891 SecurityContext: &v1.SecurityContext{
892 WindowsOptions: &v1.WindowsSecurityContextOptions{
893 HostProcess: &trueVar,
894 },
895 },
896 },
897 },
898 },
899 expectedResult: true,
900 },
901 }
902
903 for _, testCase := range testCases {
904 t.Run(testCase.name, func(t *testing.T) {
905 pod := &v1.Pod{}
906 pod.Spec = *testCase.podSpec
907 result := HasWindowsHostProcessContainer(pod)
908 assert.Equal(t, result, testCase.expectedResult)
909 })
910 }
911 }
912
913 func TestHashContainerWithoutResources(t *testing.T) {
914 cpu100m := resource.MustParse("100m")
915 cpu200m := resource.MustParse("200m")
916 mem100M := resource.MustParse("100Mi")
917 mem200M := resource.MustParse("200Mi")
918 cpuPolicyRestartNotRequired := v1.ContainerResizePolicy{ResourceName: v1.ResourceCPU, RestartPolicy: v1.NotRequired}
919 memPolicyRestartNotRequired := v1.ContainerResizePolicy{ResourceName: v1.ResourceMemory, RestartPolicy: v1.NotRequired}
920 cpuPolicyRestartRequired := v1.ContainerResizePolicy{ResourceName: v1.ResourceCPU, RestartPolicy: v1.RestartContainer}
921 memPolicyRestartRequired := v1.ContainerResizePolicy{ResourceName: v1.ResourceMemory, RestartPolicy: v1.RestartContainer}
922
923 type testCase struct {
924 name string
925 container *v1.Container
926 expectedHash uint64
927 }
928
929 tests := []testCase{
930 {
931 "Burstable pod with CPU policy restart required",
932 &v1.Container{
933 Name: "foo",
934 Image: "bar",
935 Resources: v1.ResourceRequirements{
936 Limits: v1.ResourceList{v1.ResourceCPU: cpu200m, v1.ResourceMemory: mem200M},
937 Requests: v1.ResourceList{v1.ResourceCPU: cpu100m, v1.ResourceMemory: mem100M},
938 },
939 ResizePolicy: []v1.ContainerResizePolicy{cpuPolicyRestartRequired, memPolicyRestartNotRequired},
940 },
941 0x5f62cb4c,
942 },
943 {
944 "Burstable pod with memory policy restart required",
945 &v1.Container{
946 Name: "foo",
947 Image: "bar",
948 Resources: v1.ResourceRequirements{
949 Limits: v1.ResourceList{v1.ResourceCPU: cpu200m, v1.ResourceMemory: mem200M},
950 Requests: v1.ResourceList{v1.ResourceCPU: cpu100m, v1.ResourceMemory: mem100M},
951 },
952 ResizePolicy: []v1.ContainerResizePolicy{cpuPolicyRestartNotRequired, memPolicyRestartRequired},
953 },
954 0xcdab9e00,
955 },
956 {
957 "Guaranteed pod with CPU policy restart required",
958 &v1.Container{
959 Name: "foo",
960 Image: "bar",
961 Resources: v1.ResourceRequirements{
962 Limits: v1.ResourceList{v1.ResourceCPU: cpu100m, v1.ResourceMemory: mem100M},
963 Requests: v1.ResourceList{v1.ResourceCPU: cpu100m, v1.ResourceMemory: mem100M},
964 },
965 ResizePolicy: []v1.ContainerResizePolicy{cpuPolicyRestartRequired, memPolicyRestartNotRequired},
966 },
967 0x5f62cb4c,
968 },
969 {
970 "Guaranteed pod with memory policy restart required",
971 &v1.Container{
972 Name: "foo",
973 Image: "bar",
974 Resources: v1.ResourceRequirements{
975 Limits: v1.ResourceList{v1.ResourceCPU: cpu100m, v1.ResourceMemory: mem100M},
976 Requests: v1.ResourceList{v1.ResourceCPU: cpu100m, v1.ResourceMemory: mem100M},
977 },
978 ResizePolicy: []v1.ContainerResizePolicy{cpuPolicyRestartNotRequired, memPolicyRestartRequired},
979 },
980 0xcdab9e00,
981 },
982 }
983 for _, tc := range tests {
984 t.Run(tc.name, func(t *testing.T) {
985 containerCopy := tc.container.DeepCopy()
986 hash := HashContainerWithoutResources(tc.container)
987 assert.Equal(t, tc.expectedHash, hash, "[%s]", tc.name)
988 assert.Equal(t, containerCopy, tc.container, "[%s]", tc.name)
989 })
990 }
991 }
992
View as plain text