k8s.io/kubernetes@v1.29.3/pkg/kubelet/container/helpers_test.go (about) 1 /* 2 Copyright 2015 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 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 // if ExpandContainerVolumeMounts returns an error, we don't care what the actualSubPath value is 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 // test policies 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 // test deleted pod 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 // Test init containers as well. 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 // Duplicated, should be ignored. 579 port("foo", v1.ProtocolUDP, 888, 8888, ""), 580 // Duplicated with different address family, shouldn't be ignored 581 port("", v1.ProtocolTCP, 80, 8080, "::"), 582 // No address family specified 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 // The same container port can be mapped to different host ports 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 // The same container port AND same container host is not OK 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 // multihomed nodes - multiple IP scenario 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 }