k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e_node/container_lifecycle_test.go (about) 1 /* 2 Copyright 2023 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 e2enode 18 19 import ( 20 "context" 21 "fmt" 22 "time" 23 24 "github.com/onsi/ginkgo/v2" 25 "github.com/onsi/gomega" 26 v1 "k8s.io/api/core/v1" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" 29 admissionapi "k8s.io/pod-security-admission/api" 30 31 "k8s.io/kubernetes/test/e2e/framework" 32 e2epod "k8s.io/kubernetes/test/e2e/framework/pod" 33 "k8s.io/kubernetes/test/e2e/nodefeature" 34 imageutils "k8s.io/kubernetes/test/utils/image" 35 "k8s.io/utils/ptr" 36 ) 37 38 const ( 39 LivenessPrefix = "Liveness" 40 PostStartPrefix = "PostStart" 41 PreStopPrefix = "PreStop" 42 ReadinessPrefix = "Readiness" 43 StartupPrefix = "Startup" 44 ) 45 46 var containerRestartPolicyAlways = v1.ContainerRestartPolicyAlways 47 48 func prefixedName(namePrefix string, name string) string { 49 return fmt.Sprintf("%s-%s", namePrefix, name) 50 } 51 52 var _ = SIGDescribe(framework.WithNodeConformance(), "Containers Lifecycle", func() { 53 f := framework.NewDefaultFramework("containers-lifecycle-test") 54 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged 55 56 ginkgo.It("should launch init container serially before a regular container", func() { 57 58 init1 := "init-1" 59 init2 := "init-2" 60 init3 := "init-3" 61 regular1 := "regular-1" 62 63 podSpec := &v1.Pod{ 64 ObjectMeta: metav1.ObjectMeta{ 65 Name: "initcontainer-test-pod", 66 }, 67 Spec: v1.PodSpec{ 68 RestartPolicy: v1.RestartPolicyNever, 69 InitContainers: []v1.Container{ 70 { 71 Name: init1, 72 Image: busyboxImage, 73 Command: ExecCommand(init1, execCommand{ 74 Delay: 1, 75 ExitCode: 0, 76 }), 77 }, 78 { 79 Name: init2, 80 Image: busyboxImage, 81 Command: ExecCommand(init2, execCommand{ 82 Delay: 1, 83 ExitCode: 0, 84 }), 85 }, 86 { 87 Name: init3, 88 Image: busyboxImage, 89 Command: ExecCommand(init3, execCommand{ 90 Delay: 1, 91 ExitCode: 0, 92 }), 93 }, 94 }, 95 Containers: []v1.Container{ 96 { 97 Name: regular1, 98 Image: busyboxImage, 99 Command: ExecCommand(regular1, execCommand{ 100 StartDelay: 5, 101 Delay: 1, 102 ExitCode: 0, 103 }), 104 StartupProbe: &v1.Probe{ 105 ProbeHandler: v1.ProbeHandler{ 106 Exec: &v1.ExecAction{ 107 Command: []string{ 108 "test", 109 "-f", 110 "started", 111 }, 112 }, 113 }, 114 }, 115 }, 116 }, 117 }, 118 } 119 120 preparePod(podSpec) 121 122 /// generates an out file output like: 123 // 124 // 1682076093 4905.79 init-1 Starting 0 125 // 1682076093 4905.80 init-1 Started 126 // 1682076093 4905.80 init-1 Delaying 1 127 // 1682076094 4906.80 init-1 Exiting 128 // 1682076095 4907.70 init-2 Starting 0 129 // 1682076095 4907.71 init-2 Started 130 // 1682076095 4907.71 init-2 Delaying 1 131 // 1682076096 4908.71 init-2 Exiting 132 // 1682076097 4909.74 init-3 Starting 0 133 // 1682076097 4909.74 init-3 Started 134 // 1682076097 4909.74 init-3 Delaying 1 135 // 1682076098 4910.75 init-3 Exiting 136 // 1682076099 4911.70 regular-1 Starting 5 137 // 1682076104 4916.71 regular-1 Started 138 // 1682076104 4916.71 regular-1 Delaying 1 139 // 1682076105 4917.72 regular-1 Exiting 140 141 client := e2epod.NewPodClient(f) 142 podSpec = client.Create(context.TODO(), podSpec) 143 ginkgo.By("Waiting for the pod to finish") 144 err := e2epod.WaitTimeoutForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, podSpec.Name, podSpec.Namespace, 1*time.Minute) 145 framework.ExpectNoError(err) 146 147 ginkgo.By("Parsing results") 148 podSpec, err = client.Get(context.Background(), podSpec.Name, metav1.GetOptions{}) 149 framework.ExpectNoError(err) 150 results := parseOutput(context.TODO(), f, podSpec) 151 152 // which we then use to make assertions regarding container ordering 153 ginkgo.By("Analyzing results") 154 framework.ExpectNoError(results.StartsBefore(init1, init2)) 155 framework.ExpectNoError(results.ExitsBefore(init1, init2)) 156 157 framework.ExpectNoError(results.StartsBefore(init2, init3)) 158 framework.ExpectNoError(results.ExitsBefore(init2, init3)) 159 160 framework.ExpectNoError(results.StartsBefore(init3, regular1)) 161 framework.ExpectNoError(results.ExitsBefore(init3, regular1)) 162 }) 163 164 ginkgo.It("should not launch regular containers if an init container fails", func() { 165 166 init1 := "init-1" 167 regular1 := "regular-1" 168 169 podSpec := &v1.Pod{ 170 ObjectMeta: metav1.ObjectMeta{ 171 Name: "initcontainer-test-pod-failure", 172 }, 173 Spec: v1.PodSpec{ 174 RestartPolicy: v1.RestartPolicyNever, 175 InitContainers: []v1.Container{ 176 { 177 Name: init1, 178 Image: busyboxImage, 179 Command: ExecCommand(init1, execCommand{ 180 Delay: 1, 181 ExitCode: 1, 182 }), 183 }, 184 }, 185 Containers: []v1.Container{ 186 { 187 Name: regular1, 188 Image: busyboxImage, 189 Command: ExecCommand(regular1, execCommand{ 190 Delay: 1, 191 ExitCode: 0, 192 }), 193 }, 194 }, 195 }, 196 } 197 198 preparePod(podSpec) 199 200 client := e2epod.NewPodClient(f) 201 podSpec = client.Create(context.TODO(), podSpec) 202 ginkgo.By("Waiting for the pod to fail") 203 err := e2epod.WaitForPodFailedReason(context.TODO(), f.ClientSet, podSpec, "", 1*time.Minute) 204 framework.ExpectNoError(err) 205 206 ginkgo.By("Parsing results") 207 podSpec, err = client.Get(context.Background(), podSpec.Name, metav1.GetOptions{}) 208 framework.ExpectNoError(err) 209 results := parseOutput(context.TODO(), f, podSpec) 210 211 ginkgo.By("Analyzing results") 212 // init container should start and exit with an error, and the regular container should never start 213 framework.ExpectNoError(results.Starts(init1)) 214 framework.ExpectNoError(results.Exits(init1)) 215 216 framework.ExpectNoError(results.DoesntStart(regular1)) 217 }) 218 219 ginkgo.It("should run Init container to completion before call to PostStart of regular container", func() { 220 init1 := "init-1" 221 regular1 := "regular-1" 222 223 podSpec := &v1.Pod{ 224 ObjectMeta: metav1.ObjectMeta{ 225 Name: "initcontainer-test-pod-with-post-start", 226 }, 227 Spec: v1.PodSpec{ 228 RestartPolicy: v1.RestartPolicyNever, 229 InitContainers: []v1.Container{ 230 { 231 Name: init1, 232 Image: busyboxImage, 233 Command: ExecCommand(init1, execCommand{ 234 Delay: 1, 235 ExitCode: 0, 236 }), 237 }, 238 }, 239 Containers: []v1.Container{ 240 { 241 Name: regular1, 242 Image: busyboxImage, 243 Command: ExecCommand(regular1, execCommand{ 244 // Allocate sufficient time for its postStart hook 245 // to complete. 246 // Note that we've observed approximately a 2s 247 // delay before the postStart hook is called. 248 // 10s > 1s + 2s(estimated maximum delay) + other possible delays 249 Delay: 10, 250 ExitCode: 0, 251 }), 252 Lifecycle: &v1.Lifecycle{ 253 PostStart: &v1.LifecycleHandler{ 254 Exec: &v1.ExecAction{ 255 Command: ExecCommand(prefixedName(PostStartPrefix, regular1), execCommand{ 256 Delay: 1, 257 ExitCode: 0, 258 ContainerName: regular1, 259 }), 260 }, 261 }, 262 }, 263 }, 264 }, 265 }, 266 } 267 268 preparePod(podSpec) 269 270 client := e2epod.NewPodClient(f) 271 podSpec = client.Create(context.TODO(), podSpec) 272 ginkgo.By("Waiting for the pod to finish") 273 err := e2epod.WaitTimeoutForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, podSpec.Name, podSpec.Namespace, 1*time.Minute) 274 framework.ExpectNoError(err) 275 276 ginkgo.By("Parsing results") 277 podSpec, err = client.Get(context.Background(), podSpec.Name, metav1.GetOptions{}) 278 framework.ExpectNoError(err) 279 results := parseOutput(context.TODO(), f, podSpec) 280 281 ginkgo.By("Analyzing results") 282 // init container should start and exit with an error, and the regular container should never start 283 framework.ExpectNoError(results.StartsBefore(init1, prefixedName(PostStartPrefix, regular1))) 284 framework.ExpectNoError(results.ExitsBefore(init1, prefixedName(PostStartPrefix, regular1))) 285 286 framework.ExpectNoError(results.RunTogether(regular1, prefixedName(PostStartPrefix, regular1))) 287 }) 288 289 ginkgo.It("should restart failing container when pod restartPolicy is Always", func() { 290 291 regular1 := "regular-1" 292 293 podSpec := &v1.Pod{ 294 ObjectMeta: metav1.ObjectMeta{ 295 Name: "container-must-be-restarted", 296 }, 297 Spec: v1.PodSpec{ 298 RestartPolicy: v1.RestartPolicyAlways, 299 Containers: []v1.Container{ 300 { 301 Name: regular1, 302 Image: busyboxImage, 303 Command: ExecCommand(regular1, execCommand{ 304 Delay: 1, 305 ExitCode: 1, 306 }), 307 }, 308 }, 309 }, 310 } 311 312 preparePod(podSpec) 313 314 client := e2epod.NewPodClient(f) 315 podSpec = client.Create(context.TODO(), podSpec) 316 ginkgo.By("Waiting for the pod, it will not finish") 317 err := WaitForPodContainerRestartCount(context.TODO(), f.ClientSet, podSpec.Namespace, podSpec.Name, 0, 3, 2*time.Minute) 318 framework.ExpectNoError(err) 319 320 ginkgo.By("Parsing results") 321 podSpec, err = client.Get(context.Background(), podSpec.Name, metav1.GetOptions{}) 322 framework.ExpectNoError(err) 323 results := parseOutput(context.TODO(), f, podSpec) 324 325 ginkgo.By("Analyzing results") 326 // container must be restarted 327 framework.ExpectNoError(results.Starts(regular1)) 328 framework.ExpectNoError(results.StartsBefore(regular1, regular1)) 329 framework.ExpectNoError(results.ExitsBefore(regular1, regular1)) 330 }) 331 332 ginkgo.It("should not launch second container before PostStart of the first container completed", func() { 333 334 regular1 := "regular-1" 335 regular2 := "regular-2" 336 337 podSpec := &v1.Pod{ 338 ObjectMeta: metav1.ObjectMeta{ 339 Name: "post-start-blocks-second-container", 340 }, 341 Spec: v1.PodSpec{ 342 RestartPolicy: v1.RestartPolicyNever, 343 Containers: []v1.Container{ 344 { 345 Name: regular1, 346 Image: busyboxImage, 347 Command: ExecCommand(regular1, execCommand{ 348 // Allocate sufficient time for its postStart hook 349 // to complete. 350 // Note that we've observed approximately a 2s 351 // delay before the postStart hook is called. 352 // 10s > 1s + 2s(estimated maximum delay) + other possible delays 353 Delay: 10, 354 ExitCode: 0, 355 }), 356 Lifecycle: &v1.Lifecycle{ 357 PostStart: &v1.LifecycleHandler{ 358 Exec: &v1.ExecAction{ 359 Command: ExecCommand(prefixedName(PostStartPrefix, regular1), execCommand{ 360 Delay: 1, 361 ExitCode: 0, 362 ContainerName: regular1, 363 }), 364 }, 365 }, 366 }, 367 }, 368 { 369 Name: regular2, 370 Image: busyboxImage, 371 Command: ExecCommand(regular2, execCommand{ 372 Delay: 1, 373 ExitCode: 0, 374 }), 375 }, 376 }, 377 }, 378 } 379 380 preparePod(podSpec) 381 382 client := e2epod.NewPodClient(f) 383 podSpec = client.Create(context.TODO(), podSpec) 384 ginkgo.By("Waiting for the pod to finish") 385 err := e2epod.WaitTimeoutForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, podSpec.Name, podSpec.Namespace, 1*time.Minute) 386 framework.ExpectNoError(err) 387 388 ginkgo.By("Parsing results") 389 podSpec, err = client.Get(context.Background(), podSpec.Name, metav1.GetOptions{}) 390 framework.ExpectNoError(err) 391 results := parseOutput(context.TODO(), f, podSpec) 392 393 ginkgo.By("Analyzing results") 394 // second container should not start before the PostStart of a first container completed 395 framework.ExpectNoError(results.StartsBefore(prefixedName(PostStartPrefix, regular1), regular2)) 396 framework.ExpectNoError(results.ExitsBefore(prefixedName(PostStartPrefix, regular1), regular2)) 397 }) 398 399 ginkgo.When("have init container in a Pod with restartPolicy=Never", func() { 400 401 ginkgo.When("an init container fails to start because of a bad image", ginkgo.Ordered, func() { 402 403 init1 := "init1-1" 404 regular1 := "regular-1" 405 406 podSpec := &v1.Pod{ 407 ObjectMeta: metav1.ObjectMeta{ 408 Name: "bad-image", 409 }, 410 Spec: v1.PodSpec{ 411 RestartPolicy: v1.RestartPolicyNever, 412 InitContainers: []v1.Container{ 413 { 414 Name: init1, 415 Image: imageutils.GetE2EImage(imageutils.InvalidRegistryImage), 416 Command: ExecCommand(init1, execCommand{ 417 Delay: 600, 418 ExitCode: 0, 419 }), 420 }, 421 }, 422 Containers: []v1.Container{ 423 { 424 Name: regular1, 425 Image: busyboxImage, 426 Command: ExecCommand(regular1, execCommand{ 427 Delay: 1, 428 ExitCode: 0, 429 }), 430 }, 431 }, 432 }, 433 } 434 435 preparePod(podSpec) 436 var results containerOutputList 437 438 ginkgo.It("should mark a Pod as failed and produce log", func() { 439 client := e2epod.NewPodClient(f) 440 podSpec = client.Create(context.TODO(), podSpec) 441 442 err := WaitForPodInitContainerToFail(context.TODO(), f.ClientSet, podSpec.Namespace, podSpec.Name, 0, "ImagePullBackOff", f.Timeouts.PodStart) 443 framework.ExpectNoError(err) 444 445 podSpec, err = client.Get(context.Background(), podSpec.Name, metav1.GetOptions{}) 446 framework.ExpectNoError(err) 447 results = parseOutput(context.TODO(), f, podSpec) 448 }) 449 ginkgo.It("should not start an init container", func() { 450 framework.ExpectNoError(results.DoesntStart(init1)) 451 }) 452 ginkgo.It("should not start a regular container", func() { 453 framework.ExpectNoError(results.DoesntStart(regular1)) 454 }) 455 }) 456 }) 457 458 ginkgo.It("shouldn't restart init containers upon regular container restart", func() { 459 init1 := "init-1" 460 init2 := "init-2" 461 init3 := "init-3" 462 regular1 := "regular-1" 463 464 podSpec := &v1.Pod{ 465 ObjectMeta: metav1.ObjectMeta{ 466 Name: "initcontainer-test-pod", 467 }, 468 Spec: v1.PodSpec{ 469 RestartPolicy: v1.RestartPolicyAlways, 470 InitContainers: []v1.Container{ 471 { 472 Name: init1, 473 Image: busyboxImage, 474 Command: ExecCommand(init1, execCommand{ 475 Delay: 1, 476 ExitCode: 0, 477 }), 478 }, 479 { 480 Name: init2, 481 Image: busyboxImage, 482 Command: ExecCommand(init2, execCommand{ 483 Delay: 1, 484 ExitCode: 0, 485 }), 486 }, 487 { 488 Name: init3, 489 Image: busyboxImage, 490 Command: ExecCommand(init3, execCommand{ 491 Delay: 1, 492 ExitCode: 0, 493 }), 494 }, 495 }, 496 Containers: []v1.Container{ 497 { 498 Name: regular1, 499 Image: busyboxImage, 500 Command: ExecCommand(regular1, execCommand{ 501 Delay: 10, 502 ExitCode: -1, 503 }), 504 }, 505 }, 506 }, 507 } 508 509 preparePod(podSpec) 510 511 client := e2epod.NewPodClient(f) 512 podSpec = client.Create(context.TODO(), podSpec) 513 ginkgo.By("Waiting for the pod to restart a few times") 514 err := WaitForPodContainerRestartCount(context.TODO(), f.ClientSet, podSpec.Namespace, podSpec.Name, 0, 3, 2*time.Minute) 515 framework.ExpectNoError(err) 516 517 ginkgo.By("Parsing results") 518 podSpec, err = client.Get(context.Background(), podSpec.Name, metav1.GetOptions{}) 519 framework.ExpectNoError(err) 520 results := parseOutput(context.TODO(), f, podSpec) 521 522 ginkgo.By("Analyzing results") 523 framework.ExpectNoError(results.StartsBefore(init1, init2)) 524 framework.ExpectNoError(results.ExitsBefore(init1, init2)) 525 526 framework.ExpectNoError(results.StartsBefore(init2, init3)) 527 framework.ExpectNoError(results.ExitsBefore(init2, init3)) 528 529 framework.ExpectNoError(results.StartsBefore(init3, regular1)) 530 framework.ExpectNoError(results.ExitsBefore(init3, regular1)) 531 532 // ensure that the init containers never restarted 533 framework.ExpectNoError(results.HasNotRestarted(init1)) 534 framework.ExpectNoError(results.HasNotRestarted(init2)) 535 framework.ExpectNoError(results.HasNotRestarted(init3)) 536 // while the regular container did 537 framework.ExpectNoError(results.HasRestarted(regular1)) 538 }) 539 540 ginkgo.When("a pod cannot terminate gracefully", func() { 541 testPod := func(name string, gracePeriod int64) *v1.Pod { 542 return &v1.Pod{ 543 ObjectMeta: metav1.ObjectMeta{ 544 Name: name, 545 }, 546 Spec: v1.PodSpec{ 547 Containers: []v1.Container{ 548 { 549 Name: "busybox", 550 Image: imageutils.GetE2EImage(imageutils.BusyBox), 551 Command: []string{ 552 "sleep", 553 "10000", 554 }, 555 }, 556 }, 557 TerminationGracePeriodSeconds: &gracePeriod, 558 }, 559 } 560 } 561 562 // To account for the time it takes to delete the pod, we add a buffer. Its sized 563 // so that we allow up to 2x the grace time to delete the pod. Its extra large to 564 // reduce test flakes. 565 bufferSeconds := int64(30) 566 567 f.It("should respect termination grace period seconds", f.WithNodeConformance(), func() { 568 client := e2epod.NewPodClient(f) 569 gracePeriod := int64(30) 570 571 ginkgo.By("creating a pod with a termination grace period seconds") 572 pod := testPod("pod-termination-grace-period", gracePeriod) 573 pod = client.Create(context.TODO(), pod) 574 575 ginkgo.By("ensuring the pod is running") 576 err := e2epod.WaitForPodRunningInNamespace(context.TODO(), f.ClientSet, pod) 577 framework.ExpectNoError(err) 578 579 ginkgo.By("deleting the pod gracefully") 580 err = client.Delete(context.TODO(), pod.Name, metav1.DeleteOptions{}) 581 framework.ExpectNoError(err) 582 583 ginkgo.By("ensuring the pod is terminated within the grace period seconds + buffer seconds") 584 err = e2epod.WaitForPodNotFoundInNamespace(context.TODO(), f.ClientSet, pod.Name, pod.Namespace, time.Duration(gracePeriod+bufferSeconds)*time.Second) 585 framework.ExpectNoError(err) 586 }) 587 588 f.It("should respect termination grace period seconds with long-running preStop hook", f.WithNodeConformance(), func() { 589 client := e2epod.NewPodClient(f) 590 gracePeriod := int64(30) 591 592 ginkgo.By("creating a pod with a termination grace period seconds and long-running preStop hook") 593 pod := testPod("pod-termination-grace-period", gracePeriod) 594 pod.Spec.Containers[0].Lifecycle = &v1.Lifecycle{ 595 PreStop: &v1.LifecycleHandler{ 596 Exec: &v1.ExecAction{ 597 Command: []string{ 598 "sleep", 599 "10000", 600 }, 601 }, 602 }, 603 } 604 pod = client.Create(context.TODO(), pod) 605 606 ginkgo.By("ensuring the pod is running") 607 err := e2epod.WaitForPodRunningInNamespace(context.TODO(), f.ClientSet, pod) 608 framework.ExpectNoError(err) 609 610 ginkgo.By("deleting the pod gracefully") 611 err = client.Delete(context.TODO(), pod.Name, metav1.DeleteOptions{}) 612 framework.ExpectNoError(err) 613 614 ginkgo.By("ensuring the pod is terminated within the grace period seconds + buffer seconds") 615 err = e2epod.WaitForPodNotFoundInNamespace(context.TODO(), f.ClientSet, pod.Name, pod.Namespace, time.Duration(gracePeriod+bufferSeconds)*time.Second) 616 framework.ExpectNoError(err) 617 }) 618 }) 619 620 ginkgo.It("should call the container's preStop hook and terminate it if its startup probe fails", func() { 621 regular1 := "regular-1" 622 623 podSpec := &v1.Pod{ 624 ObjectMeta: metav1.ObjectMeta{ 625 Name: "test-pod", 626 }, 627 Spec: v1.PodSpec{ 628 RestartPolicy: v1.RestartPolicyNever, 629 Containers: []v1.Container{ 630 { 631 Name: regular1, 632 Image: busyboxImage, 633 Command: ExecCommand(regular1, execCommand{ 634 Delay: 100, 635 TerminationSeconds: 15, 636 ExitCode: 0, 637 }), 638 StartupProbe: &v1.Probe{ 639 ProbeHandler: v1.ProbeHandler{ 640 Exec: &v1.ExecAction{ 641 Command: []string{ 642 "sh", 643 "-c", 644 "exit 1", 645 }, 646 }, 647 }, 648 InitialDelaySeconds: 10, 649 FailureThreshold: 1, 650 }, 651 Lifecycle: &v1.Lifecycle{ 652 PreStop: &v1.LifecycleHandler{ 653 Exec: &v1.ExecAction{ 654 Command: ExecCommand(prefixedName(PreStopPrefix, regular1), execCommand{ 655 Delay: 1, 656 ExitCode: 0, 657 ContainerName: regular1, 658 }), 659 }, 660 }, 661 }, 662 }, 663 }, 664 }, 665 } 666 667 preparePod(podSpec) 668 669 client := e2epod.NewPodClient(f) 670 podSpec = client.Create(context.TODO(), podSpec) 671 672 ginkgo.By("Waiting for the pod to complete") 673 err := e2epod.WaitForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, podSpec.Name, podSpec.Namespace) 674 framework.ExpectNoError(err) 675 676 ginkgo.By("Parsing results") 677 podSpec, err = client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{}) 678 framework.ExpectNoError(err) 679 results := parseOutput(context.TODO(), f, podSpec) 680 681 ginkgo.By("Analyzing results") 682 framework.ExpectNoError(results.RunTogether(regular1, prefixedName(PreStopPrefix, regular1))) 683 framework.ExpectNoError(results.Starts(prefixedName(PreStopPrefix, regular1))) 684 framework.ExpectNoError(results.Exits(regular1)) 685 }) 686 687 ginkgo.It("should call the container's preStop hook and terminate it if its liveness probe fails", func() { 688 regular1 := "regular-1" 689 690 podSpec := &v1.Pod{ 691 ObjectMeta: metav1.ObjectMeta{ 692 Name: "test-pod", 693 }, 694 Spec: v1.PodSpec{ 695 RestartPolicy: v1.RestartPolicyNever, 696 Containers: []v1.Container{ 697 { 698 Name: regular1, 699 Image: busyboxImage, 700 Command: ExecCommand(regular1, execCommand{ 701 Delay: 100, 702 TerminationSeconds: 15, 703 ExitCode: 0, 704 }), 705 LivenessProbe: &v1.Probe{ 706 ProbeHandler: v1.ProbeHandler{ 707 Exec: &v1.ExecAction{ 708 Command: []string{ 709 "sh", 710 "-c", 711 "exit 1", 712 }, 713 }, 714 }, 715 InitialDelaySeconds: 10, 716 FailureThreshold: 1, 717 }, 718 Lifecycle: &v1.Lifecycle{ 719 PreStop: &v1.LifecycleHandler{ 720 Exec: &v1.ExecAction{ 721 Command: ExecCommand(prefixedName(PreStopPrefix, regular1), execCommand{ 722 Delay: 1, 723 ExitCode: 0, 724 ContainerName: regular1, 725 }), 726 }, 727 }, 728 }, 729 }, 730 }, 731 }, 732 } 733 734 preparePod(podSpec) 735 736 client := e2epod.NewPodClient(f) 737 podSpec = client.Create(context.TODO(), podSpec) 738 739 ginkgo.By("Waiting for the pod to complete") 740 err := e2epod.WaitForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, podSpec.Name, podSpec.Namespace) 741 framework.ExpectNoError(err) 742 743 ginkgo.By("Parsing results") 744 podSpec, err = client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{}) 745 framework.ExpectNoError(err) 746 results := parseOutput(context.TODO(), f, podSpec) 747 748 ginkgo.By("Analyzing results") 749 framework.ExpectNoError(results.RunTogether(regular1, prefixedName(PreStopPrefix, regular1))) 750 framework.ExpectNoError(results.Starts(prefixedName(PreStopPrefix, regular1))) 751 framework.ExpectNoError(results.Exits(regular1)) 752 }) 753 754 ginkgo.When("a pod is terminating because its liveness probe fails", func() { 755 regular1 := "regular-1" 756 757 testPod := func() *v1.Pod { 758 return &v1.Pod{ 759 ObjectMeta: metav1.ObjectMeta{ 760 Name: "test-pod", 761 }, 762 Spec: v1.PodSpec{ 763 RestartPolicy: v1.RestartPolicyNever, 764 TerminationGracePeriodSeconds: ptr.To(int64(100)), 765 Containers: []v1.Container{ 766 { 767 Name: regular1, 768 Image: imageutils.GetE2EImage(imageutils.BusyBox), 769 Command: ExecCommand(regular1, execCommand{ 770 Delay: 100, 771 TerminationSeconds: 15, 772 ExitCode: 0, 773 }), 774 LivenessProbe: &v1.Probe{ 775 ProbeHandler: v1.ProbeHandler{ 776 Exec: &v1.ExecAction{ 777 Command: ExecCommand(prefixedName(LivenessPrefix, regular1), execCommand{ 778 ExitCode: 1, 779 ContainerName: regular1, 780 }), 781 }, 782 }, 783 InitialDelaySeconds: 10, 784 PeriodSeconds: 1, 785 FailureThreshold: 1, 786 }, 787 }, 788 }, 789 }, 790 } 791 } 792 793 f.It("should execute readiness probe while in preStop, but not liveness", f.WithNodeConformance(), func() { 794 client := e2epod.NewPodClient(f) 795 podSpec := testPod() 796 797 ginkgo.By("creating a pod with a readiness probe and a preStop hook") 798 podSpec.Spec.Containers[0].Lifecycle = &v1.Lifecycle{ 799 PreStop: &v1.LifecycleHandler{ 800 Exec: &v1.ExecAction{ 801 Command: ExecCommand(prefixedName(PreStopPrefix, regular1), execCommand{ 802 Delay: 1, 803 ExitCode: 0, 804 ContainerName: regular1, 805 }), 806 }, 807 }, 808 } 809 podSpec.Spec.Containers[0].ReadinessProbe = &v1.Probe{ 810 ProbeHandler: v1.ProbeHandler{ 811 Exec: &v1.ExecAction{ 812 Command: ExecCommand(prefixedName(ReadinessPrefix, regular1), execCommand{ 813 ExitCode: 0, 814 ContainerName: regular1, 815 }), 816 }, 817 }, 818 InitialDelaySeconds: 1, 819 PeriodSeconds: 1, 820 } 821 822 preparePod(podSpec) 823 824 podSpec = client.Create(context.TODO(), podSpec) 825 826 ginkgo.By("Waiting for the pod to complete") 827 err := e2epod.WaitForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, podSpec.Name, podSpec.Namespace) 828 framework.ExpectNoError(err) 829 830 ginkgo.By("Parsing results") 831 podSpec, err = client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{}) 832 framework.ExpectNoError(err) 833 results := parseOutput(context.TODO(), f, podSpec) 834 835 ginkgo.By("Analyzing results") 836 // readiness probes are called during pod termination 837 framework.ExpectNoError(results.RunTogether(prefixedName(PreStopPrefix, regular1), prefixedName(ReadinessPrefix, regular1))) 838 // liveness probes are not called during pod termination 839 err = results.RunTogether(prefixedName(PreStopPrefix, regular1), prefixedName(LivenessPrefix, regular1)) 840 gomega.Expect(err).To(gomega.HaveOccurred()) 841 }) 842 843 f.It("should continue running liveness probes for restartable init containers and restart them while in preStop", f.WithNodeConformance(), func() { 844 client := e2epod.NewPodClient(f) 845 podSpec := testPod() 846 restartableInit1 := "restartable-init-1" 847 848 ginkgo.By("creating a pod with a restartable init container and a preStop hook") 849 podSpec.Spec.InitContainers = []v1.Container{{ 850 RestartPolicy: &containerRestartPolicyAlways, 851 Name: restartableInit1, 852 Image: imageutils.GetE2EImage(imageutils.BusyBox), 853 Command: ExecCommand(restartableInit1, execCommand{ 854 Delay: 100, 855 TerminationSeconds: 1, 856 ExitCode: 0, 857 }), 858 LivenessProbe: &v1.Probe{ 859 ProbeHandler: v1.ProbeHandler{ 860 Exec: &v1.ExecAction{ 861 Command: ExecCommand(prefixedName(LivenessPrefix, restartableInit1), execCommand{ 862 ExitCode: 1, 863 ContainerName: restartableInit1, 864 }), 865 }, 866 }, 867 InitialDelaySeconds: 1, 868 PeriodSeconds: 1, 869 FailureThreshold: 1, 870 }, 871 }} 872 podSpec.Spec.Containers[0].Lifecycle = &v1.Lifecycle{ 873 PreStop: &v1.LifecycleHandler{ 874 Exec: &v1.ExecAction{ 875 Command: ExecCommand(prefixedName(PreStopPrefix, regular1), execCommand{ 876 Delay: 40, 877 ExitCode: 0, 878 ContainerName: regular1, 879 }), 880 }, 881 }, 882 } 883 884 preparePod(podSpec) 885 886 podSpec = client.Create(context.TODO(), podSpec) 887 888 ginkgo.By("Waiting for the pod to complete") 889 err := e2epod.WaitForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, podSpec.Name, podSpec.Namespace) 890 framework.ExpectNoError(err) 891 892 ginkgo.By("Parsing results") 893 podSpec, err = client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{}) 894 framework.ExpectNoError(err) 895 results := parseOutput(context.TODO(), f, podSpec) 896 897 ginkgo.By("Analyzing results") 898 // FIXME ExpectNoError: this will be implemented in KEP 4438 899 // liveness probes are called for restartable init containers during pod termination 900 err = results.RunTogether(prefixedName(PreStopPrefix, regular1), prefixedName(LivenessPrefix, restartableInit1)) 901 gomega.Expect(err).To(gomega.HaveOccurred()) 902 // FIXME ExpectNoError: this will be implemented in KEP 4438 903 // restartable init containers are restarted during pod termination 904 err = results.RunTogether(prefixedName(PreStopPrefix, regular1), restartableInit1) 905 gomega.Expect(err).To(gomega.HaveOccurred()) 906 }) 907 }) 908 }) 909 910 var _ = SIGDescribe(framework.WithSerial(), "Containers Lifecycle", func() { 911 f := framework.NewDefaultFramework("containers-lifecycle-test-serial") 912 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged 913 914 ginkgo.It("should restart the containers in right order after the node reboot", func(ctx context.Context) { 915 init1 := "init-1" 916 init2 := "init-2" 917 init3 := "init-3" 918 regular1 := "regular-1" 919 920 podLabels := map[string]string{ 921 "test": "containers-lifecycle-test-serial", 922 "namespace": f.Namespace.Name, 923 } 924 pod := &v1.Pod{ 925 ObjectMeta: metav1.ObjectMeta{ 926 Name: "initialized-pod", 927 Labels: podLabels, 928 }, 929 Spec: v1.PodSpec{ 930 RestartPolicy: v1.RestartPolicyAlways, 931 InitContainers: []v1.Container{ 932 { 933 Name: init1, 934 Image: busyboxImage, 935 Command: ExecCommand(init1, execCommand{ 936 Delay: 5, 937 ExitCode: 0, 938 }), 939 }, 940 { 941 Name: init2, 942 Image: busyboxImage, 943 Command: ExecCommand(init2, execCommand{ 944 Delay: 5, 945 ExitCode: 0, 946 }), 947 }, 948 { 949 Name: init3, 950 Image: busyboxImage, 951 Command: ExecCommand(init3, execCommand{ 952 Delay: 5, 953 ExitCode: 0, 954 }), 955 }, 956 }, 957 Containers: []v1.Container{ 958 { 959 Name: regular1, 960 Image: busyboxImage, 961 Command: ExecCommand(regular1, execCommand{ 962 Delay: 30, 963 ExitCode: 0, 964 }), 965 }, 966 }, 967 }, 968 } 969 preparePod(pod) 970 971 client := e2epod.NewPodClient(f) 972 pod = client.Create(ctx, pod) 973 ginkgo.By("Waiting for the pod to be initialized and run") 974 err := e2epod.WaitForPodRunningInNamespace(ctx, f.ClientSet, pod) 975 framework.ExpectNoError(err) 976 977 ginkgo.By("Getting the current pod sandbox ID") 978 rs, _, err := getCRIClient() 979 framework.ExpectNoError(err) 980 981 sandboxes, err := rs.ListPodSandbox(ctx, &runtimeapi.PodSandboxFilter{ 982 LabelSelector: podLabels, 983 }) 984 framework.ExpectNoError(err) 985 gomega.Expect(sandboxes).To(gomega.HaveLen(1)) 986 podSandboxID := sandboxes[0].Id 987 988 ginkgo.By("Stopping the kubelet") 989 restartKubelet := stopKubelet() 990 gomega.Eventually(ctx, func() bool { 991 return kubeletHealthCheck(kubeletHealthCheckURL) 992 }, f.Timeouts.PodStart, f.Timeouts.Poll).Should(gomega.BeFalse()) 993 994 ginkgo.By("Stopping the pod sandbox to simulate the node reboot") 995 err = rs.StopPodSandbox(ctx, podSandboxID) 996 framework.ExpectNoError(err) 997 998 ginkgo.By("Restarting the kubelet") 999 restartKubelet() 1000 gomega.Eventually(ctx, func() bool { 1001 return kubeletHealthCheck(kubeletHealthCheckURL) 1002 }, f.Timeouts.PodStart, f.Timeouts.Poll).Should(gomega.BeTrue()) 1003 1004 ginkgo.By("Waiting for the pod to be re-initialized and run") 1005 err = e2epod.WaitForPodCondition(ctx, f.ClientSet, pod.Namespace, pod.Name, "re-initialized", f.Timeouts.PodStart, func(pod *v1.Pod) (bool, error) { 1006 if pod.Status.ContainerStatuses[0].RestartCount < 2 { 1007 return false, nil 1008 } 1009 if pod.Status.Phase != v1.PodRunning { 1010 return false, nil 1011 } 1012 return true, nil 1013 }) 1014 framework.ExpectNoError(err) 1015 1016 ginkgo.By("Parsing results") 1017 pod, err = client.Get(ctx, pod.Name, metav1.GetOptions{}) 1018 framework.ExpectNoError(err) 1019 results := parseOutput(context.TODO(), f, pod) 1020 1021 ginkgo.By("Analyzing results") 1022 init1Started, err := results.FindIndex(init1, "Started", 0) 1023 framework.ExpectNoError(err) 1024 init2Started, err := results.FindIndex(init2, "Started", 0) 1025 framework.ExpectNoError(err) 1026 init3Started, err := results.FindIndex(init3, "Started", 0) 1027 framework.ExpectNoError(err) 1028 regular1Started, err := results.FindIndex(regular1, "Started", 0) 1029 framework.ExpectNoError(err) 1030 1031 init1Restarted, err := results.FindIndex(init1, "Started", init1Started+1) 1032 framework.ExpectNoError(err) 1033 init2Restarted, err := results.FindIndex(init2, "Started", init2Started+1) 1034 framework.ExpectNoError(err) 1035 init3Restarted, err := results.FindIndex(init3, "Started", init3Started+1) 1036 framework.ExpectNoError(err) 1037 regular1Restarted, err := results.FindIndex(regular1, "Started", regular1Started+1) 1038 framework.ExpectNoError(err) 1039 1040 framework.ExpectNoError(init1Started.IsBefore(init2Started)) 1041 framework.ExpectNoError(init2Started.IsBefore(init3Started)) 1042 framework.ExpectNoError(init3Started.IsBefore(regular1Started)) 1043 1044 framework.ExpectNoError(init1Restarted.IsBefore(init2Restarted)) 1045 framework.ExpectNoError(init2Restarted.IsBefore(init3Restarted)) 1046 framework.ExpectNoError(init3Restarted.IsBefore(regular1Restarted)) 1047 }) 1048 }) 1049 1050 var _ = SIGDescribe(nodefeature.SidecarContainers, "Containers Lifecycle", func() { 1051 f := framework.NewDefaultFramework("containers-lifecycle-test") 1052 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged 1053 1054 ginkgo.When("using a Pod with restartPolicy=Never, three init container and two restartable init containers", ginkgo.Ordered, func() { 1055 1056 init1 := "init-1" 1057 restartableInit1 := "restartable-init-1" 1058 init2 := "init-2" 1059 restartableInit2 := "restartable-init-2" 1060 init3 := "init-3" 1061 regular1 := "regular-1" 1062 1063 podSpec := &v1.Pod{ 1064 ObjectMeta: metav1.ObjectMeta{ 1065 Name: "restartable-init-containers-start-serially", 1066 }, 1067 Spec: v1.PodSpec{ 1068 RestartPolicy: v1.RestartPolicyNever, 1069 InitContainers: []v1.Container{ 1070 { 1071 Name: init1, 1072 Image: busyboxImage, 1073 Command: ExecCommand(init1, execCommand{ 1074 Delay: 1, 1075 ExitCode: 0, 1076 }), 1077 }, 1078 { 1079 Name: restartableInit1, 1080 Image: busyboxImage, 1081 Command: ExecCommand(restartableInit1, execCommand{ 1082 Delay: 600, 1083 ExitCode: 0, 1084 }), 1085 RestartPolicy: &containerRestartPolicyAlways, 1086 }, 1087 { 1088 Name: init2, 1089 Image: busyboxImage, 1090 Command: ExecCommand(init2, execCommand{ 1091 Delay: 1, 1092 ExitCode: 0, 1093 }), 1094 }, 1095 { 1096 Name: restartableInit2, 1097 Image: busyboxImage, 1098 Command: ExecCommand(restartableInit2, execCommand{ 1099 Delay: 600, 1100 ExitCode: 0, 1101 }), 1102 RestartPolicy: &containerRestartPolicyAlways, 1103 }, 1104 { 1105 Name: init3, 1106 Image: busyboxImage, 1107 Command: ExecCommand(init3, execCommand{ 1108 Delay: 1, 1109 ExitCode: 0, 1110 }), 1111 }, 1112 }, 1113 Containers: []v1.Container{ 1114 { 1115 Name: regular1, 1116 Image: busyboxImage, 1117 Command: ExecCommand(regular1, execCommand{ 1118 Delay: 1, 1119 ExitCode: 0, 1120 }), 1121 }, 1122 }, 1123 }, 1124 } 1125 1126 preparePod(podSpec) 1127 var results containerOutputList 1128 1129 ginkgo.It("should finish and produce log", func() { 1130 client := e2epod.NewPodClient(f) 1131 podSpec = client.Create(context.TODO(), podSpec) 1132 1133 err := e2epod.WaitTimeoutForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, podSpec.Name, podSpec.Namespace, 5*time.Minute) 1134 framework.ExpectNoError(err) 1135 1136 podSpec, err := client.Get(context.Background(), podSpec.Name, metav1.GetOptions{}) 1137 framework.ExpectNoError(err) 1138 1139 // pod should exit successfully 1140 gomega.Expect(podSpec.Status.Phase).To(gomega.Equal(v1.PodSucceeded)) 1141 1142 results = parseOutput(context.TODO(), f, podSpec) 1143 }) 1144 1145 ginkgo.It("should run the first init container to completion before starting first restartable init container", func() { 1146 framework.ExpectNoError(results.StartsBefore(init1, restartableInit1)) 1147 framework.ExpectNoError(results.ExitsBefore(init1, restartableInit1)) 1148 }) 1149 1150 ginkgo.It("should start first restartable init container before starting second init container", func() { 1151 framework.ExpectNoError(results.StartsBefore(restartableInit1, init2)) 1152 }) 1153 1154 ginkgo.It("should run first init container and first restartable init container together", func() { 1155 framework.ExpectNoError(results.RunTogether(restartableInit1, init2)) 1156 }) 1157 1158 ginkgo.It("should run second init container to completion before starting second restartable init container", func() { 1159 framework.ExpectNoError(results.StartsBefore(init2, restartableInit2)) 1160 framework.ExpectNoError(results.ExitsBefore(init2, restartableInit2)) 1161 }) 1162 1163 ginkgo.It("should start second restartable init container before third init container", func() { 1164 framework.ExpectNoError(results.StartsBefore(restartableInit2, init3)) 1165 }) 1166 1167 ginkgo.It("should run both restartable init containers and third init container together", func() { 1168 framework.ExpectNoError(results.RunTogether(restartableInit1, restartableInit2)) 1169 framework.ExpectNoError(results.RunTogether(restartableInit1, init3)) 1170 framework.ExpectNoError(results.RunTogether(restartableInit2, init3)) 1171 }) 1172 1173 ginkgo.It("should run third init container to completion before starting regular container", func() { 1174 framework.ExpectNoError(results.StartsBefore(init3, regular1)) 1175 framework.ExpectNoError(results.ExitsBefore(init3, regular1)) 1176 }) 1177 1178 ginkgo.It("should run both restartable init containers and a regular container together", func() { 1179 framework.ExpectNoError(results.RunTogether(restartableInit1, regular1)) 1180 framework.ExpectNoError(results.RunTogether(restartableInit2, regular1)) 1181 }) 1182 }) 1183 1184 ginkgo.When("using a restartable init container in a Pod with restartPolicy=Never", func() { 1185 ginkgo.When("a restartable init container runs continuously", ginkgo.Ordered, func() { 1186 1187 restartableInit1 := "restartable-init-1" 1188 regular1 := "regular-1" 1189 1190 podSpec := &v1.Pod{ 1191 ObjectMeta: metav1.ObjectMeta{ 1192 Name: "restartable-init-container-run-continuously", 1193 }, 1194 Spec: v1.PodSpec{ 1195 RestartPolicy: v1.RestartPolicyNever, 1196 InitContainers: []v1.Container{ 1197 { 1198 Name: restartableInit1, 1199 Image: busyboxImage, 1200 Command: ExecCommand(restartableInit1, execCommand{ 1201 Delay: 600, 1202 ExitCode: 0, 1203 }), 1204 RestartPolicy: &containerRestartPolicyAlways, 1205 }, 1206 }, 1207 Containers: []v1.Container{ 1208 { 1209 Name: regular1, 1210 Image: busyboxImage, 1211 Command: ExecCommand(regular1, execCommand{ 1212 Delay: 1, 1213 ExitCode: 0, 1214 }), 1215 }, 1216 }, 1217 }, 1218 } 1219 1220 preparePod(podSpec) 1221 var results containerOutputList 1222 1223 ginkgo.It("should complete a Pod successfully and produce log", func() { 1224 client := e2epod.NewPodClient(f) 1225 podSpec = client.Create(context.TODO(), podSpec) 1226 1227 err := e2epod.WaitTimeoutForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, podSpec.Name, podSpec.Namespace, 5*time.Minute) 1228 framework.ExpectNoError(err) 1229 1230 podSpec, err := client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{}) 1231 framework.ExpectNoError(err) 1232 1233 // pod should exit successfully 1234 gomega.Expect(podSpec.Status.Phase).To(gomega.Equal(v1.PodSucceeded)) 1235 1236 results = parseOutput(context.TODO(), f, podSpec) 1237 }) 1238 ginkgo.It("should not restart a restartable init container", func() { 1239 framework.ExpectNoError(results.DoesntStartAfter(restartableInit1, regular1)) 1240 }) 1241 ginkgo.It("should run a regular container to completion", func() { 1242 framework.ExpectNoError(results.Exits(regular1)) 1243 }) 1244 }) 1245 1246 ginkgo.When("a restartable init container fails to start because of a bad image", ginkgo.Ordered, func() { 1247 1248 restartableInit1 := "restartable-init-1" 1249 regular1 := "regular-1" 1250 1251 podSpec := &v1.Pod{ 1252 ObjectMeta: metav1.ObjectMeta{ 1253 Name: "restartable-init-runs-with-pod", 1254 }, 1255 Spec: v1.PodSpec{ 1256 RestartPolicy: v1.RestartPolicyNever, 1257 InitContainers: []v1.Container{ 1258 { 1259 Name: restartableInit1, 1260 Image: imageutils.GetE2EImage(imageutils.InvalidRegistryImage), 1261 Command: ExecCommand(restartableInit1, execCommand{ 1262 Delay: 600, 1263 ExitCode: 0, 1264 }), 1265 RestartPolicy: &containerRestartPolicyAlways, 1266 }, 1267 }, 1268 Containers: []v1.Container{ 1269 { 1270 Name: regular1, 1271 Image: busyboxImage, 1272 Command: ExecCommand(regular1, execCommand{ 1273 Delay: 1, 1274 ExitCode: 0, 1275 }), 1276 }, 1277 }, 1278 }, 1279 } 1280 1281 preparePod(podSpec) 1282 var results containerOutputList 1283 1284 ginkgo.It("should mark a Pod as failed and produce log", func() { 1285 client := e2epod.NewPodClient(f) 1286 podSpec = client.Create(context.TODO(), podSpec) 1287 1288 // restartable init container should be in image pull backoff 1289 err := WaitForPodInitContainerToFail(context.TODO(), f.ClientSet, podSpec.Namespace, podSpec.Name, 0, "ImagePullBackOff", f.Timeouts.PodStart) 1290 framework.ExpectNoError(err) 1291 1292 podSpec, err = client.Get(context.Background(), podSpec.Name, metav1.GetOptions{}) 1293 framework.ExpectNoError(err) 1294 results = parseOutput(context.TODO(), f, podSpec) 1295 }) 1296 ginkgo.It("should not start a restartable init container", func() { 1297 framework.ExpectNoError(results.DoesntStart(restartableInit1)) 1298 }) 1299 ginkgo.It("should not start a regular container", func() { 1300 framework.ExpectNoError(results.DoesntStart(regular1)) 1301 }) 1302 }) 1303 1304 // TODO: add a test case similar to one above, but with startup probe never succeeding 1305 1306 ginkgo.When("a restartable init container starts and exits with exit code 0 continuously", ginkgo.Ordered, func() { 1307 1308 restartableInit1 := "restartable-init-1" 1309 init1 := "init-1" 1310 regular1 := "regular-1" 1311 1312 podSpec := &v1.Pod{ 1313 ObjectMeta: metav1.ObjectMeta{ 1314 Name: "restartable-init-container-exit-0-continuously", 1315 }, 1316 Spec: v1.PodSpec{ 1317 RestartPolicy: v1.RestartPolicyNever, 1318 InitContainers: []v1.Container{ 1319 { 1320 Name: restartableInit1, 1321 Image: busyboxImage, 1322 Command: ExecCommand(restartableInit1, execCommand{ 1323 Delay: 5, 1324 ExitCode: 0, 1325 }), 1326 RestartPolicy: &containerRestartPolicyAlways, 1327 }, 1328 { 1329 Name: init1, 1330 Image: busyboxImage, 1331 Command: ExecCommand(init1, execCommand{ 1332 Delay: 5, 1333 ExitCode: 0, 1334 }), 1335 }, 1336 }, 1337 Containers: []v1.Container{ 1338 { 1339 Name: regular1, 1340 Image: busyboxImage, 1341 Command: ExecCommand(regular1, execCommand{ 1342 Delay: 60, 1343 ExitCode: 0, 1344 }), 1345 }, 1346 }, 1347 }, 1348 } 1349 1350 preparePod(podSpec) 1351 var results containerOutputList 1352 1353 ginkgo.It("should complete a Pod successfully and produce log", func() { 1354 client := e2epod.NewPodClient(f) 1355 podSpec = client.Create(context.TODO(), podSpec) 1356 1357 err := e2epod.WaitTimeoutForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, podSpec.Name, podSpec.Namespace, 5*time.Minute) 1358 framework.ExpectNoError(err) 1359 1360 podSpec, err := client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{}) 1361 framework.ExpectNoError(err) 1362 1363 // pod should exit successfully 1364 gomega.Expect(podSpec.Status.Phase).To(gomega.Equal(v1.PodSucceeded)) 1365 results = parseOutput(context.TODO(), f, podSpec) 1366 }) 1367 ginkgo.It("should restart a restartable init container before the regular container started", func() { 1368 framework.ExpectNoError(results.StartsBefore(restartableInit1, regular1)) 1369 }) 1370 ginkgo.It("should restart a restartable init container after the regular container started", func() { 1371 framework.ExpectNoError(results.StartsBefore(regular1, restartableInit1)) 1372 }) 1373 ginkgo.It("should run a regular container to completion", func() { 1374 framework.ExpectNoError(results.Exits(regular1)) 1375 }) 1376 }) 1377 1378 ginkgo.When("a restartable init container starts and exits with exit code 1 continuously", ginkgo.Ordered, func() { 1379 restartableInit1 := "restartable-init-1" 1380 init1 := "init-1" 1381 regular1 := "regular-1" 1382 1383 podSpec := &v1.Pod{ 1384 ObjectMeta: metav1.ObjectMeta{ 1385 Name: "restartable-init-container-exit-1-continuously", 1386 }, 1387 Spec: v1.PodSpec{ 1388 RestartPolicy: v1.RestartPolicyNever, 1389 InitContainers: []v1.Container{ 1390 { 1391 Name: restartableInit1, 1392 Image: busyboxImage, 1393 Command: ExecCommand(restartableInit1, execCommand{ 1394 Delay: 5, 1395 ExitCode: 1, 1396 }), 1397 RestartPolicy: &containerRestartPolicyAlways, 1398 }, 1399 { 1400 Name: init1, 1401 Image: busyboxImage, 1402 Command: ExecCommand(init1, execCommand{ 1403 Delay: 5, 1404 ExitCode: 0, 1405 }), 1406 }, 1407 }, 1408 Containers: []v1.Container{ 1409 { 1410 Name: regular1, 1411 Image: busyboxImage, 1412 Command: ExecCommand(regular1, execCommand{ 1413 Delay: 60, 1414 ExitCode: 0, 1415 }), 1416 }, 1417 }, 1418 }, 1419 } 1420 1421 preparePod(podSpec) 1422 var results containerOutputList 1423 1424 ginkgo.It("should complete a Pod successfully and produce log", func() { 1425 client := e2epod.NewPodClient(f) 1426 podSpec = client.Create(context.TODO(), podSpec) 1427 1428 err := e2epod.WaitTimeoutForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, podSpec.Name, podSpec.Namespace, 5*time.Minute) 1429 framework.ExpectNoError(err) 1430 1431 podSpec, err := client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{}) 1432 framework.ExpectNoError(err) 1433 1434 // pod should exit successfully 1435 gomega.Expect(podSpec.Status.Phase).To(gomega.Equal(v1.PodSucceeded)) 1436 1437 results = parseOutput(context.TODO(), f, podSpec) 1438 }) 1439 ginkgo.It("should restart a restartable init container before the regular container started", func() { 1440 framework.ExpectNoError(results.StartsBefore(restartableInit1, regular1)) 1441 }) 1442 ginkgo.It("should restart a restartable init container after the regular container started", func() { 1443 framework.ExpectNoError(results.StartsBefore(regular1, restartableInit1)) 1444 }) 1445 ginkgo.It("should run a regular container to completion", func() { 1446 framework.ExpectNoError(results.Exits(regular1)) 1447 }) 1448 }) 1449 1450 ginkgo.When("an Init container before restartable init container fails", ginkgo.Ordered, func() { 1451 1452 init1 := "init-1" 1453 restartableInit1 := "restartable-init-1" 1454 regular1 := "regular-1" 1455 1456 podSpec := &v1.Pod{ 1457 ObjectMeta: metav1.ObjectMeta{ 1458 Name: "init-container-fails-before-restartable-init-starts", 1459 }, 1460 Spec: v1.PodSpec{ 1461 RestartPolicy: v1.RestartPolicyNever, 1462 InitContainers: []v1.Container{ 1463 { 1464 Name: init1, 1465 Image: busyboxImage, 1466 Command: ExecCommand(init1, execCommand{ 1467 Delay: 1, 1468 ExitCode: 1, 1469 }), 1470 }, 1471 { 1472 Name: restartableInit1, 1473 Image: busyboxImage, 1474 Command: ExecCommand(restartableInit1, execCommand{ 1475 Delay: 600, 1476 ExitCode: 0, 1477 }), 1478 RestartPolicy: &containerRestartPolicyAlways, 1479 }, 1480 }, 1481 Containers: []v1.Container{ 1482 { 1483 Name: regular1, 1484 Image: busyboxImage, 1485 Command: ExecCommand(regular1, execCommand{ 1486 Delay: 600, 1487 ExitCode: 0, 1488 }), 1489 }, 1490 }, 1491 }, 1492 } 1493 1494 preparePod(podSpec) 1495 var results containerOutputList 1496 1497 ginkgo.It("should mark a Pod as failed and produce log", func() { 1498 client := e2epod.NewPodClient(f) 1499 podSpec = client.Create(context.TODO(), podSpec) 1500 1501 err := e2epod.WaitForPodFailedReason(context.TODO(), f.ClientSet, podSpec, "", 1*time.Minute) 1502 framework.ExpectNoError(err) 1503 1504 podSpec, err := client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{}) 1505 framework.ExpectNoError(err) 1506 results = parseOutput(context.TODO(), f, podSpec) 1507 }) 1508 ginkgo.It("should mark an Init container as failed", func() { 1509 framework.ExpectNoError(results.Exits(init1)) 1510 }) 1511 ginkgo.It("should not start restartable init container", func() { 1512 framework.ExpectNoError(results.DoesntStart(restartableInit1)) 1513 }) 1514 }) 1515 1516 ginkgo.When("an Init container after restartable init container fails", ginkgo.Ordered, func() { 1517 1518 init1 := "init-1" 1519 restartableInit1 := "restartable-init-1" 1520 regular1 := "regular-1" 1521 1522 podSpec := &v1.Pod{ 1523 ObjectMeta: metav1.ObjectMeta{ 1524 Name: "restartable-init-container-fails-before-init-container", 1525 }, 1526 Spec: v1.PodSpec{ 1527 RestartPolicy: v1.RestartPolicyNever, 1528 InitContainers: []v1.Container{ 1529 { 1530 Name: restartableInit1, 1531 Image: busyboxImage, 1532 Command: ExecCommand(restartableInit1, execCommand{ 1533 Delay: 5, 1534 ExitCode: 1, 1535 }), 1536 RestartPolicy: &containerRestartPolicyAlways, 1537 }, 1538 { 1539 Name: init1, 1540 Image: busyboxImage, 1541 Command: ExecCommand(init1, execCommand{ 1542 Delay: 1, 1543 ExitCode: 1, 1544 }), 1545 }, 1546 }, 1547 Containers: []v1.Container{ 1548 { 1549 Name: regular1, 1550 Image: busyboxImage, 1551 Command: ExecCommand(regular1, execCommand{ 1552 Delay: 600, 1553 ExitCode: 0, 1554 }), 1555 }, 1556 }, 1557 }, 1558 } 1559 1560 preparePod(podSpec) 1561 var results containerOutputList 1562 1563 ginkgo.It("should mark a Pod as failed and produce log", func() { 1564 client := e2epod.NewPodClient(f) 1565 podSpec = client.Create(context.TODO(), podSpec) 1566 1567 err := e2epod.WaitForPodFailedReason(context.TODO(), f.ClientSet, podSpec, "", 1*time.Minute) 1568 framework.ExpectNoError(err) 1569 1570 podSpec, err := client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{}) 1571 framework.ExpectNoError(err) 1572 results = parseOutput(context.TODO(), f, podSpec) 1573 }) 1574 ginkgo.It("should mark an Init container as failed", func() { 1575 framework.ExpectNoError(results.Exits(init1)) 1576 }) 1577 // TODO: how will we be able to test it if restartable init container 1578 // will never fail and there will be no termination log? Or will be? 1579 ginkgo.It("should be running restartable init container and a failed Init container in parallel", func() { 1580 framework.ExpectNoError(results.RunTogether(restartableInit1, init1)) 1581 }) 1582 }) 1583 }) 1584 1585 ginkgo.When("using a restartable init container in a Pod with restartPolicy=OnFailure", ginkgo.Ordered, func() { 1586 // this test case the same as for restartPolicy=Never 1587 ginkgo.When("a restartable init container runs continuously", func() { 1588 1589 restartableInit1 := "restartable-init-1" 1590 regular1 := "regular-1" 1591 1592 podSpec := &v1.Pod{ 1593 ObjectMeta: metav1.ObjectMeta{ 1594 Name: "restartable-init-container-run-continuously", 1595 }, 1596 Spec: v1.PodSpec{ 1597 RestartPolicy: v1.RestartPolicyOnFailure, 1598 InitContainers: []v1.Container{ 1599 { 1600 Name: restartableInit1, 1601 Image: busyboxImage, 1602 Command: ExecCommand(restartableInit1, execCommand{ 1603 Delay: 600, 1604 ExitCode: 0, 1605 }), 1606 RestartPolicy: &containerRestartPolicyAlways, 1607 }, 1608 }, 1609 Containers: []v1.Container{ 1610 { 1611 Name: regular1, 1612 Image: busyboxImage, 1613 Command: ExecCommand(regular1, execCommand{ 1614 Delay: 1, 1615 ExitCode: 0, 1616 }), 1617 }, 1618 }, 1619 }, 1620 } 1621 1622 preparePod(podSpec) 1623 var results containerOutputList 1624 1625 ginkgo.It("should complete a Pod successfully and produce log", func() { 1626 client := e2epod.NewPodClient(f) 1627 podSpec = client.Create(context.TODO(), podSpec) 1628 1629 err := e2epod.WaitTimeoutForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, podSpec.Name, podSpec.Namespace, 5*time.Minute) 1630 framework.ExpectNoError(err) 1631 1632 podSpec, err := client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{}) 1633 framework.ExpectNoError(err) 1634 1635 // pod should exit successfully 1636 gomega.Expect(podSpec.Status.Phase).To(gomega.Equal(v1.PodSucceeded)) 1637 1638 results = parseOutput(context.TODO(), f, podSpec) 1639 }) 1640 ginkgo.It("should not restart a restartable init container", func() { 1641 framework.ExpectNoError(results.DoesntStartAfter(restartableInit1, regular1)) 1642 }) 1643 ginkgo.It("should run a regular container to completion", func() { 1644 framework.ExpectNoError(results.Exits(regular1)) 1645 }) 1646 }) 1647 1648 ginkgo.When("a restartable init container fails to start because of a bad image", ginkgo.Ordered, func() { 1649 1650 restartableInit1 := "restartable-init-1" 1651 regular1 := "regular-1" 1652 1653 podSpec := &v1.Pod{ 1654 ObjectMeta: metav1.ObjectMeta{ 1655 Name: "restartable-init-runs-with-pod", 1656 }, 1657 Spec: v1.PodSpec{ 1658 RestartPolicy: v1.RestartPolicyOnFailure, 1659 InitContainers: []v1.Container{ 1660 { 1661 Name: restartableInit1, 1662 Image: imageutils.GetE2EImage(imageutils.InvalidRegistryImage), 1663 Command: ExecCommand(restartableInit1, execCommand{ 1664 Delay: 600, 1665 ExitCode: 0, 1666 }), 1667 RestartPolicy: &containerRestartPolicyAlways, 1668 }, 1669 }, 1670 Containers: []v1.Container{ 1671 { 1672 Name: regular1, 1673 Image: busyboxImage, 1674 Command: ExecCommand(regular1, execCommand{ 1675 Delay: 1, 1676 ExitCode: 0, 1677 }), 1678 }, 1679 }, 1680 }, 1681 } 1682 1683 preparePod(podSpec) 1684 var results containerOutputList 1685 1686 ginkgo.It("should mark a Pod as failed and produce log", func() { 1687 client := e2epod.NewPodClient(f) 1688 podSpec = client.Create(context.TODO(), podSpec) 1689 1690 // restartable init container should be in image pull backoff 1691 err := WaitForPodInitContainerToFail(context.TODO(), f.ClientSet, podSpec.Namespace, podSpec.Name, 0, "ImagePullBackOff", f.Timeouts.PodStart) 1692 framework.ExpectNoError(err) 1693 1694 podSpec, err = client.Get(context.Background(), podSpec.Name, metav1.GetOptions{}) 1695 framework.ExpectNoError(err) 1696 results = parseOutput(context.TODO(), f, podSpec) 1697 }) 1698 ginkgo.It("should not start a restartable init container", func() { 1699 framework.ExpectNoError(results.DoesntStart(restartableInit1)) 1700 }) 1701 ginkgo.It("should not start a regular container", func() { 1702 framework.ExpectNoError(results.DoesntStart(regular1)) 1703 }) 1704 }) 1705 1706 // TODO: add a test case similar to one above, but with startup probe never succeeding 1707 1708 // this test case the same as for restartPolicy=Never 1709 ginkgo.When("a restartable init container starts and exits with exit code 0 continuously", ginkgo.Ordered, func() { 1710 1711 restartableInit1 := "restartable-init-1" 1712 init1 := "init-1" 1713 regular1 := "regular-1" 1714 1715 podSpec := &v1.Pod{ 1716 ObjectMeta: metav1.ObjectMeta{ 1717 Name: "restartable-init-container-exit-0-continuously", 1718 }, 1719 Spec: v1.PodSpec{ 1720 RestartPolicy: v1.RestartPolicyOnFailure, 1721 InitContainers: []v1.Container{ 1722 { 1723 Name: restartableInit1, 1724 Image: busyboxImage, 1725 Command: ExecCommand(restartableInit1, execCommand{ 1726 Delay: 5, 1727 ExitCode: 0, 1728 }), 1729 RestartPolicy: &containerRestartPolicyAlways, 1730 }, 1731 { 1732 Name: init1, 1733 Image: busyboxImage, 1734 Command: ExecCommand(init1, execCommand{ 1735 Delay: 5, 1736 ExitCode: 0, 1737 }), 1738 }, 1739 }, 1740 Containers: []v1.Container{ 1741 { 1742 Name: regular1, 1743 Image: busyboxImage, 1744 Command: ExecCommand(regular1, execCommand{ 1745 Delay: 60, 1746 ExitCode: 0, 1747 }), 1748 }, 1749 }, 1750 }, 1751 } 1752 1753 preparePod(podSpec) 1754 var results containerOutputList 1755 1756 ginkgo.It("should complete a Pod successfully and produce log", func() { 1757 client := e2epod.NewPodClient(f) 1758 podSpec = client.Create(context.TODO(), podSpec) 1759 1760 err := e2epod.WaitTimeoutForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, podSpec.Name, podSpec.Namespace, 5*time.Minute) 1761 framework.ExpectNoError(err) 1762 1763 podSpec, err := client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{}) 1764 framework.ExpectNoError(err) 1765 1766 // pod should exit successfully 1767 gomega.Expect(podSpec.Status.Phase).To(gomega.Equal(v1.PodSucceeded)) 1768 1769 results = parseOutput(context.TODO(), f, podSpec) 1770 }) 1771 ginkgo.It("should restart a restartable init container before the regular container started", func() { 1772 framework.ExpectNoError(results.StartsBefore(restartableInit1, regular1)) 1773 }) 1774 ginkgo.It("should restart a restartable init container after the regular container started", func() { 1775 framework.ExpectNoError(results.StartsBefore(regular1, restartableInit1)) 1776 }) 1777 ginkgo.It("should run a regular container to completion", func() { 1778 framework.ExpectNoError(results.Exits(regular1)) 1779 }) 1780 }) 1781 1782 // this test case the same as for restartPolicy=Never 1783 ginkgo.When("a restartable init container starts and exits with exit code 1 continuously", ginkgo.Ordered, func() { 1784 1785 restartableInit1 := "restartable-init-1" 1786 init1 := "init-1" 1787 regular1 := "regular-1" 1788 1789 podSpec := &v1.Pod{ 1790 ObjectMeta: metav1.ObjectMeta{ 1791 Name: "restartable-init-container-exit-1-continuously", 1792 }, 1793 Spec: v1.PodSpec{ 1794 RestartPolicy: v1.RestartPolicyOnFailure, 1795 InitContainers: []v1.Container{ 1796 { 1797 Name: restartableInit1, 1798 Image: busyboxImage, 1799 Command: ExecCommand(restartableInit1, execCommand{ 1800 Delay: 5, 1801 ExitCode: 1, 1802 }), 1803 RestartPolicy: &containerRestartPolicyAlways, 1804 }, 1805 { 1806 Name: init1, 1807 Image: busyboxImage, 1808 Command: ExecCommand(init1, execCommand{ 1809 Delay: 5, 1810 ExitCode: 0, 1811 }), 1812 }, 1813 }, 1814 Containers: []v1.Container{ 1815 { 1816 Name: regular1, 1817 Image: busyboxImage, 1818 Command: ExecCommand(regular1, execCommand{ 1819 Delay: 60, 1820 ExitCode: 0, 1821 }), 1822 }, 1823 }, 1824 }, 1825 } 1826 1827 preparePod(podSpec) 1828 var results containerOutputList 1829 1830 ginkgo.It("should complete a Pod successfully and produce log", func() { 1831 client := e2epod.NewPodClient(f) 1832 podSpec = client.Create(context.TODO(), podSpec) 1833 1834 err := e2epod.WaitTimeoutForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, podSpec.Name, podSpec.Namespace, 5*time.Minute) 1835 framework.ExpectNoError(err) 1836 1837 podSpec, err := client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{}) 1838 framework.ExpectNoError(err) 1839 1840 // pod should exit successfully 1841 gomega.Expect(podSpec.Status.Phase).To(gomega.Equal(v1.PodSucceeded)) 1842 1843 results = parseOutput(context.TODO(), f, podSpec) 1844 }) 1845 ginkgo.It("should restart a restartable init container before the regular container started", func() { 1846 framework.ExpectNoError(results.StartsBefore(restartableInit1, regular1)) 1847 }) 1848 ginkgo.It("should restart a restartable init container after the regular container started", func() { 1849 framework.ExpectNoError(results.StartsBefore(regular1, restartableInit1)) 1850 }) 1851 ginkgo.It("should run a regular container to completion", func() { 1852 framework.ExpectNoError(results.Exits(regular1)) 1853 }) 1854 }) 1855 1856 ginkgo.When("an Init container before restartable init container continuously fails", ginkgo.Ordered, func() { 1857 1858 init1 := "init-1" 1859 restartableInit1 := "restartable-init-1" 1860 regular1 := "regular-1" 1861 1862 podSpec := &v1.Pod{ 1863 ObjectMeta: metav1.ObjectMeta{ 1864 Name: "init-container-fails-before-restartable-init-starts", 1865 }, 1866 Spec: v1.PodSpec{ 1867 RestartPolicy: v1.RestartPolicyOnFailure, 1868 InitContainers: []v1.Container{ 1869 { 1870 Name: init1, 1871 Image: busyboxImage, 1872 Command: ExecCommand(init1, execCommand{ 1873 Delay: 1, 1874 ExitCode: 1, 1875 }), 1876 }, 1877 { 1878 Name: restartableInit1, 1879 Image: busyboxImage, 1880 Command: ExecCommand(restartableInit1, execCommand{ 1881 Delay: 600, 1882 ExitCode: 0, 1883 }), 1884 RestartPolicy: &containerRestartPolicyAlways, 1885 }, 1886 }, 1887 Containers: []v1.Container{ 1888 { 1889 Name: regular1, 1890 Image: busyboxImage, 1891 Command: ExecCommand(regular1, execCommand{ 1892 Delay: 600, 1893 ExitCode: 0, 1894 }), 1895 }, 1896 }, 1897 }, 1898 } 1899 1900 preparePod(podSpec) 1901 var results containerOutputList 1902 1903 ginkgo.It("should continuously run Pod keeping it Pending", func() { 1904 client := e2epod.NewPodClient(f) 1905 podSpec = client.Create(context.TODO(), podSpec) 1906 1907 err := e2epod.WaitForPodCondition(context.TODO(), f.ClientSet, podSpec.Namespace, podSpec.Name, "pending and restarting 3 times", 5*time.Minute, func(pod *v1.Pod) (bool, error) { 1908 if pod.Status.Phase != v1.PodPending { 1909 return false, fmt.Errorf("pod should be in pending phase") 1910 } 1911 if len(pod.Status.InitContainerStatuses) < 1 { 1912 return false, nil 1913 } 1914 containerStatus := pod.Status.InitContainerStatuses[0] 1915 return containerStatus.RestartCount >= 3, nil 1916 }) 1917 framework.ExpectNoError(err) 1918 1919 podSpec, err := client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{}) 1920 framework.ExpectNoError(err) 1921 results = parseOutput(context.TODO(), f, podSpec) 1922 }) 1923 ginkgo.It("should have Init container restartCount greater than 0", func() { 1924 framework.ExpectNoError(results.HasRestarted(init1)) 1925 }) 1926 ginkgo.It("should not start restartable init container", func() { 1927 framework.ExpectNoError(results.DoesntStart(restartableInit1)) 1928 }) 1929 }) 1930 1931 ginkgo.When("an Init container after restartable init container fails", ginkgo.Ordered, func() { 1932 1933 init1 := "init-1" 1934 restartableInit1 := "restartable-init-1" 1935 regular1 := "regular-1" 1936 1937 podSpec := &v1.Pod{ 1938 ObjectMeta: metav1.ObjectMeta{ 1939 Name: "restartable-init-container-fails-before-init-container", 1940 }, 1941 Spec: v1.PodSpec{ 1942 RestartPolicy: v1.RestartPolicyOnFailure, 1943 InitContainers: []v1.Container{ 1944 { 1945 Name: restartableInit1, 1946 Image: busyboxImage, 1947 Command: ExecCommand(restartableInit1, execCommand{ 1948 Delay: 5, 1949 ExitCode: 1, 1950 }), 1951 RestartPolicy: &containerRestartPolicyAlways, 1952 }, 1953 { 1954 Name: init1, 1955 Image: busyboxImage, 1956 Command: ExecCommand(init1, execCommand{ 1957 Delay: 1, 1958 ExitCode: 1, 1959 }), 1960 }, 1961 }, 1962 Containers: []v1.Container{ 1963 { 1964 Name: regular1, 1965 Image: busyboxImage, 1966 Command: ExecCommand(regular1, execCommand{ 1967 Delay: 600, 1968 ExitCode: 0, 1969 }), 1970 }, 1971 }, 1972 }, 1973 } 1974 1975 preparePod(podSpec) 1976 var results containerOutputList 1977 1978 ginkgo.It("should continuously run Pod keeping it Pending", func() { 1979 client := e2epod.NewPodClient(f) 1980 podSpec = client.Create(context.TODO(), podSpec) 1981 1982 err := e2epod.WaitForPodCondition(context.TODO(), f.ClientSet, podSpec.Namespace, podSpec.Name, "pending and restarting 3 times", 5*time.Minute, func(pod *v1.Pod) (bool, error) { 1983 if pod.Status.Phase != v1.PodPending { 1984 return false, fmt.Errorf("pod should be in pending phase") 1985 } 1986 if len(pod.Status.InitContainerStatuses) < 1 { 1987 return false, nil 1988 } 1989 containerStatus := pod.Status.InitContainerStatuses[0] 1990 return containerStatus.RestartCount >= 3, nil 1991 }) 1992 framework.ExpectNoError(err) 1993 1994 podSpec, err := client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{}) 1995 framework.ExpectNoError(err) 1996 results = parseOutput(context.TODO(), f, podSpec) 1997 }) 1998 ginkgo.It("should have Init container restartCount greater than 0", func() { 1999 framework.ExpectNoError(results.HasRestarted(init1)) 2000 }) 2001 // TODO: how will we be able to test it if restartable init container will never fail and there will be no termination log? Or will be? 2002 ginkgo.It("should be running restartable init container and a failed Init container in parallel", func() { 2003 framework.ExpectNoError(results.RunTogether(restartableInit1, init1)) 2004 }) 2005 }) 2006 }) 2007 2008 ginkgo.When("using a restartable init container in a Pod with restartPolicy=Always", ginkgo.Ordered, func() { 2009 ginkgo.When("a restartable init container runs continuously", func() { 2010 2011 restartableInit1 := "restartable-init-1" 2012 regular1 := "regular-1" 2013 2014 podSpec := &v1.Pod{ 2015 ObjectMeta: metav1.ObjectMeta{ 2016 Name: "restartable-init-container-run-continuously", 2017 }, 2018 Spec: v1.PodSpec{ 2019 RestartPolicy: v1.RestartPolicyAlways, 2020 InitContainers: []v1.Container{ 2021 { 2022 Name: restartableInit1, 2023 Image: busyboxImage, 2024 Command: ExecCommand(restartableInit1, execCommand{ 2025 Delay: 600, 2026 ExitCode: 0, 2027 }), 2028 RestartPolicy: &containerRestartPolicyAlways, 2029 }, 2030 }, 2031 Containers: []v1.Container{ 2032 { 2033 Name: regular1, 2034 Image: busyboxImage, 2035 Command: ExecCommand(regular1, execCommand{ 2036 Delay: 1, 2037 ExitCode: 0, 2038 }), 2039 }, 2040 }, 2041 }, 2042 } 2043 2044 preparePod(podSpec) 2045 var results containerOutputList 2046 2047 // regular container should exit at least once so we can get it's termination log 2048 // this test case is different from restartPolicy=Never 2049 ginkgo.It("should keep running a Pod continuously and produce log", func() { /* check the regular container restartCount > 0 */ 2050 client := e2epod.NewPodClient(f) 2051 podSpec = client.Create(context.TODO(), podSpec) 2052 2053 err := WaitForPodContainerRestartCount(context.TODO(), f.ClientSet, podSpec.Namespace, podSpec.Name, 0, 2, 2*time.Minute) 2054 framework.ExpectNoError(err) 2055 2056 podSpec, err := client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{}) 2057 framework.ExpectNoError(err) 2058 results = parseOutput(context.TODO(), f, podSpec) 2059 }) 2060 2061 ginkgo.It("should not restart a restartable init container", func() { 2062 framework.ExpectNoError(results.DoesntStartAfter(restartableInit1, regular1)) 2063 }) 2064 // this test case is different from restartPolicy=Never 2065 ginkgo.It("should start a regular container", func() { 2066 framework.ExpectNoError(results.HasRestarted(regular1)) 2067 }) 2068 }) 2069 2070 ginkgo.When("a restartable init container fails to start because of a bad image", ginkgo.Ordered, func() { 2071 2072 restartableInit1 := "restartable-init-1" 2073 regular1 := "regular-1" 2074 2075 podSpec := &v1.Pod{ 2076 ObjectMeta: metav1.ObjectMeta{ 2077 Name: "restartable-init-runs-with-pod", 2078 }, 2079 Spec: v1.PodSpec{ 2080 RestartPolicy: v1.RestartPolicyNever, 2081 InitContainers: []v1.Container{ 2082 { 2083 Name: restartableInit1, 2084 Image: imageutils.GetE2EImage(imageutils.InvalidRegistryImage), 2085 Command: ExecCommand(restartableInit1, execCommand{ 2086 Delay: 600, 2087 ExitCode: 0, 2088 }), 2089 RestartPolicy: &containerRestartPolicyAlways, 2090 }, 2091 }, 2092 Containers: []v1.Container{ 2093 { 2094 Name: regular1, 2095 Image: busyboxImage, 2096 Command: ExecCommand(regular1, execCommand{ 2097 Delay: 1, 2098 ExitCode: 0, 2099 }), 2100 }, 2101 }, 2102 }, 2103 } 2104 2105 preparePod(podSpec) 2106 var results containerOutputList 2107 2108 ginkgo.It("should continuously run Pod keeping it Pending and produce log", func() { 2109 client := e2epod.NewPodClient(f) 2110 podSpec = client.Create(context.TODO(), podSpec) 2111 2112 // restartable init container should be in image pull backoff 2113 err := WaitForPodInitContainerToFail(context.TODO(), f.ClientSet, podSpec.Namespace, podSpec.Name, 0, "ImagePullBackOff", f.Timeouts.PodStart) 2114 framework.ExpectNoError(err) 2115 2116 podSpec, err = client.Get(context.Background(), podSpec.Name, metav1.GetOptions{}) 2117 framework.ExpectNoError(err) 2118 results = parseOutput(context.TODO(), f, podSpec) 2119 }) 2120 ginkgo.It("should not start a restartable init container", func() { 2121 framework.ExpectNoError(results.DoesntStart(restartableInit1)) 2122 }) 2123 ginkgo.It("should not start a regular container", func() { 2124 framework.ExpectNoError(results.DoesntStart(regular1)) 2125 }) 2126 }) 2127 2128 // TODO: add a test case similar to one above, but with startup probe never succeeding 2129 2130 ginkgo.When("a restartable init container starts and exits with exit code 0 continuously", ginkgo.Ordered, func() { 2131 2132 restartableInit1 := "restartable-init-1" 2133 init1 := "init-1" 2134 regular1 := "regular-1" 2135 2136 podSpec := &v1.Pod{ 2137 ObjectMeta: metav1.ObjectMeta{ 2138 Name: "restartable-init-container-exit-0-continuously", 2139 }, 2140 Spec: v1.PodSpec{ 2141 RestartPolicy: v1.RestartPolicyAlways, 2142 InitContainers: []v1.Container{ 2143 { 2144 Name: restartableInit1, 2145 Image: busyboxImage, 2146 Command: ExecCommand(restartableInit1, execCommand{ 2147 Delay: 5, 2148 ExitCode: 0, 2149 }), 2150 RestartPolicy: &containerRestartPolicyAlways, 2151 }, 2152 { 2153 Name: init1, 2154 Image: busyboxImage, 2155 Command: ExecCommand(init1, execCommand{ 2156 Delay: 5, 2157 ExitCode: 0, 2158 }), 2159 }, 2160 }, 2161 Containers: []v1.Container{ 2162 { 2163 Name: regular1, 2164 Image: busyboxImage, 2165 Command: ExecCommand(regular1, execCommand{ 2166 Delay: 60, 2167 ExitCode: 0, 2168 }), 2169 }, 2170 }, 2171 }, 2172 } 2173 2174 preparePod(podSpec) 2175 var results containerOutputList 2176 2177 ginkgo.It("should keep running a Pod continuously and produce log", func() { /* check the regular container restartCount > 0 */ 2178 client := e2epod.NewPodClient(f) 2179 podSpec = client.Create(context.TODO(), podSpec) 2180 2181 err := WaitForPodContainerRestartCount(context.TODO(), f.ClientSet, podSpec.Namespace, podSpec.Name, 0, 1, 2*time.Minute) 2182 framework.ExpectNoError(err) 2183 2184 podSpec, err := client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{}) 2185 framework.ExpectNoError(err) 2186 results = parseOutput(context.TODO(), f, podSpec) 2187 }) 2188 ginkgo.It("should restart a restartable init container before the regular container started", func() { 2189 framework.ExpectNoError(results.StartsBefore(restartableInit1, regular1)) 2190 }) 2191 ginkgo.It("should restart a restartable init container after the regular container started", func() { 2192 framework.ExpectNoError(results.StartsBefore(regular1, restartableInit1)) 2193 }) 2194 ginkgo.It("should start a regular container", func() { 2195 framework.ExpectNoError(results.Starts(regular1)) 2196 }) 2197 }) 2198 2199 // this test case the same as for restartPolicy=Never 2200 ginkgo.When("a restartable init container starts and exits with exit code 1 continuously", ginkgo.Ordered, func() { 2201 2202 restartableInit1 := "restartable-init-1" 2203 init1 := "init-1" 2204 regular1 := "regular-1" 2205 2206 podSpec := &v1.Pod{ 2207 ObjectMeta: metav1.ObjectMeta{ 2208 Name: "restartable-init-container-exit-1-continuously", 2209 }, 2210 Spec: v1.PodSpec{ 2211 RestartPolicy: v1.RestartPolicyAlways, 2212 InitContainers: []v1.Container{ 2213 { 2214 Name: restartableInit1, 2215 Image: busyboxImage, 2216 Command: ExecCommand(restartableInit1, execCommand{ 2217 Delay: 5, 2218 ExitCode: 1, 2219 }), 2220 RestartPolicy: &containerRestartPolicyAlways, 2221 }, 2222 { 2223 Name: init1, 2224 Image: busyboxImage, 2225 Command: ExecCommand(init1, execCommand{ 2226 Delay: 5, 2227 ExitCode: 0, 2228 }), 2229 }, 2230 }, 2231 Containers: []v1.Container{ 2232 { 2233 Name: regular1, 2234 Image: busyboxImage, 2235 Command: ExecCommand(regular1, execCommand{ 2236 Delay: 60, 2237 ExitCode: 0, 2238 }), 2239 }, 2240 }, 2241 }, 2242 } 2243 2244 preparePod(podSpec) 2245 var results containerOutputList 2246 2247 ginkgo.It("should keep running a Pod continuously and produce log", func() { /* check the regular container restartCount > 0 */ 2248 client := e2epod.NewPodClient(f) 2249 podSpec = client.Create(context.TODO(), podSpec) 2250 2251 err := WaitForPodContainerRestartCount(context.TODO(), f.ClientSet, podSpec.Namespace, podSpec.Name, 0, 1, 2*time.Minute) 2252 framework.ExpectNoError(err) 2253 2254 podSpec, err := client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{}) 2255 framework.ExpectNoError(err) 2256 results = parseOutput(context.TODO(), f, podSpec) 2257 }) 2258 ginkgo.It("should restart a restartable init container before the regular container started", func() { 2259 framework.ExpectNoError(results.StartsBefore(restartableInit1, regular1)) 2260 }) 2261 ginkgo.It("should restart a restartable init container after the regular container started", func() { 2262 framework.ExpectNoError(results.StartsBefore(regular1, restartableInit1)) 2263 }) 2264 ginkgo.It("should start a regular container", func() { 2265 framework.ExpectNoError(results.Starts(regular1)) 2266 }) 2267 }) 2268 2269 ginkgo.When("an Init container before restartable init container continuously fails", ginkgo.Ordered, func() { 2270 2271 init1 := "init-1" 2272 restartableInit1 := "restartable-init-1" 2273 regular1 := "regular-1" 2274 2275 podSpec := &v1.Pod{ 2276 ObjectMeta: metav1.ObjectMeta{ 2277 Name: "init-container-fails-before-restartable-init-starts", 2278 }, 2279 Spec: v1.PodSpec{ 2280 RestartPolicy: v1.RestartPolicyAlways, 2281 InitContainers: []v1.Container{ 2282 { 2283 Name: init1, 2284 Image: busyboxImage, 2285 Command: ExecCommand(init1, execCommand{ 2286 Delay: 1, 2287 ExitCode: 1, 2288 }), 2289 }, 2290 { 2291 Name: restartableInit1, 2292 Image: busyboxImage, 2293 Command: ExecCommand(restartableInit1, execCommand{ 2294 Delay: 600, 2295 ExitCode: 0, 2296 }), 2297 RestartPolicy: &containerRestartPolicyAlways, 2298 }, 2299 }, 2300 Containers: []v1.Container{ 2301 { 2302 Name: regular1, 2303 Image: busyboxImage, 2304 Command: ExecCommand(regular1, execCommand{ 2305 Delay: 600, 2306 ExitCode: 0, 2307 }), 2308 }, 2309 }, 2310 }, 2311 } 2312 2313 preparePod(podSpec) 2314 var results containerOutputList 2315 2316 ginkgo.It("should continuously run Pod keeping it Pending", func() { 2317 client := e2epod.NewPodClient(f) 2318 podSpec = client.Create(context.TODO(), podSpec) 2319 2320 err := e2epod.WaitForPodCondition(context.TODO(), f.ClientSet, podSpec.Namespace, podSpec.Name, "pending and restarting 3 times", 5*time.Minute, func(pod *v1.Pod) (bool, error) { 2321 if pod.Status.Phase != v1.PodPending { 2322 return false, fmt.Errorf("pod should be in pending phase") 2323 } 2324 if len(pod.Status.InitContainerStatuses) < 1 { 2325 return false, nil 2326 } 2327 containerStatus := pod.Status.InitContainerStatuses[0] 2328 return containerStatus.RestartCount >= 3, nil 2329 }) 2330 framework.ExpectNoError(err) 2331 2332 podSpec, err := client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{}) 2333 framework.ExpectNoError(err) 2334 results = parseOutput(context.TODO(), f, podSpec) 2335 }) 2336 ginkgo.It("should have Init container restartCount greater than 0", func() { 2337 framework.ExpectNoError(results.HasRestarted(init1)) 2338 }) 2339 ginkgo.It("should not start restartable init container", func() { 2340 framework.ExpectNoError(results.DoesntStart(restartableInit1)) 2341 }) 2342 }) 2343 2344 ginkgo.When("an Init container after restartable init container fails", ginkgo.Ordered, func() { 2345 2346 init1 := "init-1" 2347 restartableInit1 := "restartable-init-1" 2348 regular1 := "regular-1" 2349 2350 podSpec := &v1.Pod{ 2351 ObjectMeta: metav1.ObjectMeta{ 2352 Name: "restartable-init-container-fails-before-init-container", 2353 }, 2354 Spec: v1.PodSpec{ 2355 RestartPolicy: v1.RestartPolicyAlways, 2356 InitContainers: []v1.Container{ 2357 { 2358 Name: restartableInit1, 2359 Image: busyboxImage, 2360 Command: ExecCommand(restartableInit1, execCommand{ 2361 Delay: 5, 2362 ExitCode: 1, 2363 }), 2364 RestartPolicy: &containerRestartPolicyAlways, 2365 }, 2366 { 2367 Name: init1, 2368 Image: busyboxImage, 2369 Command: ExecCommand(init1, execCommand{ 2370 Delay: 1, 2371 ExitCode: 1, 2372 }), 2373 }, 2374 }, 2375 Containers: []v1.Container{ 2376 { 2377 Name: regular1, 2378 Image: busyboxImage, 2379 Command: ExecCommand(regular1, execCommand{ 2380 Delay: 600, 2381 ExitCode: 0, 2382 }), 2383 }, 2384 }, 2385 }, 2386 } 2387 2388 preparePod(podSpec) 2389 var results containerOutputList 2390 2391 ginkgo.It("should continuously run Pod keeping it Pending", func() { 2392 client := e2epod.NewPodClient(f) 2393 podSpec = client.Create(context.TODO(), podSpec) 2394 2395 err := e2epod.WaitForPodCondition(context.TODO(), f.ClientSet, podSpec.Namespace, podSpec.Name, "pending and restarting 3 times", 5*time.Minute, func(pod *v1.Pod) (bool, error) { 2396 if pod.Status.Phase != v1.PodPending { 2397 return false, fmt.Errorf("pod should be in pending phase") 2398 } 2399 if len(pod.Status.InitContainerStatuses) < 1 { 2400 return false, nil 2401 } 2402 containerStatus := pod.Status.InitContainerStatuses[0] 2403 return containerStatus.RestartCount >= 3, nil 2404 }) 2405 framework.ExpectNoError(err) 2406 2407 podSpec, err := client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{}) 2408 framework.ExpectNoError(err) 2409 results = parseOutput(context.TODO(), f, podSpec) 2410 }) 2411 ginkgo.It("should have Init container restartCount greater than 0", func() { 2412 framework.ExpectNoError(results.HasRestarted(init1)) 2413 }) 2414 // TODO: how will we be able to test it if restartable init container will never fail and there will be no termination log? Or will be? 2415 ginkgo.It("should be running restartable init container and a failed Init container in parallel", func() { 2416 framework.ExpectNoError(results.RunTogether(restartableInit1, init1)) 2417 }) 2418 }) 2419 }) 2420 2421 ginkgo.It("should launch restartable init containers serially considering the startup probe", func() { 2422 2423 restartableInit1 := "restartable-init-1" 2424 restartableInit2 := "restartable-init-2" 2425 regular1 := "regular-1" 2426 2427 pod := &v1.Pod{ 2428 ObjectMeta: metav1.ObjectMeta{ 2429 Name: "restartable-init-containers-start-serially", 2430 }, 2431 Spec: v1.PodSpec{ 2432 RestartPolicy: v1.RestartPolicyNever, 2433 InitContainers: []v1.Container{ 2434 { 2435 Name: restartableInit1, 2436 Image: busyboxImage, 2437 Command: ExecCommand(restartableInit1, execCommand{ 2438 StartDelay: 10, 2439 Delay: 600, 2440 ExitCode: 0, 2441 }), 2442 StartupProbe: &v1.Probe{ 2443 ProbeHandler: v1.ProbeHandler{ 2444 Exec: &v1.ExecAction{ 2445 Command: []string{"test", "-f", "started"}, 2446 }, 2447 }, 2448 }, 2449 RestartPolicy: &containerRestartPolicyAlways, 2450 }, 2451 { 2452 Name: restartableInit2, 2453 Image: busyboxImage, 2454 Command: ExecCommand(restartableInit2, execCommand{ 2455 StartDelay: 10, 2456 Delay: 600, 2457 ExitCode: 0, 2458 }), 2459 StartupProbe: &v1.Probe{ 2460 ProbeHandler: v1.ProbeHandler{ 2461 Exec: &v1.ExecAction{ 2462 Command: []string{"test", "-f", "started"}, 2463 }, 2464 }, 2465 }, 2466 RestartPolicy: &containerRestartPolicyAlways, 2467 }, 2468 }, 2469 Containers: []v1.Container{ 2470 { 2471 Name: regular1, 2472 Image: busyboxImage, 2473 Command: ExecCommand(regular1, execCommand{ 2474 Delay: 1, 2475 ExitCode: 0, 2476 }), 2477 }, 2478 }, 2479 }, 2480 } 2481 2482 preparePod(pod) 2483 2484 client := e2epod.NewPodClient(f) 2485 pod = client.Create(context.TODO(), pod) 2486 2487 ginkgo.By("Waiting for the pod to finish") 2488 err := e2epod.WaitTimeoutForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, pod.Name, pod.Namespace, 5*time.Minute) 2489 framework.ExpectNoError(err) 2490 2491 pod, err = client.Get(context.TODO(), pod.Name, metav1.GetOptions{}) 2492 framework.ExpectNoError(err) 2493 results := parseOutput(context.TODO(), f, pod) 2494 2495 ginkgo.By("Analyzing results") 2496 framework.ExpectNoError(results.StartsBefore(restartableInit1, restartableInit2)) 2497 framework.ExpectNoError(results.StartsBefore(restartableInit2, regular1)) 2498 }) 2499 2500 ginkgo.It("should call the container's preStop hook and not launch next container if the restartable init container's startup probe fails", func() { 2501 2502 restartableInit1 := "restartable-init-1" 2503 regular1 := "regular-1" 2504 2505 pod := &v1.Pod{ 2506 ObjectMeta: metav1.ObjectMeta{ 2507 Name: "restartable-init-container-failed-startup", 2508 }, 2509 Spec: v1.PodSpec{ 2510 RestartPolicy: v1.RestartPolicyAlways, 2511 InitContainers: []v1.Container{ 2512 { 2513 Name: restartableInit1, 2514 Image: busyboxImage, 2515 Command: ExecCommand(restartableInit1, execCommand{ 2516 Delay: 600, 2517 TerminationSeconds: 15, 2518 ExitCode: 0, 2519 }), 2520 StartupProbe: &v1.Probe{ 2521 InitialDelaySeconds: 5, 2522 FailureThreshold: 1, 2523 ProbeHandler: v1.ProbeHandler{ 2524 Exec: &v1.ExecAction{ 2525 Command: []string{ 2526 "sh", 2527 "-c", 2528 "exit 1", 2529 }, 2530 }, 2531 }, 2532 }, 2533 Lifecycle: &v1.Lifecycle{ 2534 PreStop: &v1.LifecycleHandler{ 2535 Exec: &v1.ExecAction{ 2536 Command: ExecCommand(prefixedName(PreStopPrefix, restartableInit1), execCommand{ 2537 Delay: 1, 2538 ExitCode: 0, 2539 ContainerName: restartableInit1, 2540 }), 2541 }, 2542 }, 2543 }, 2544 RestartPolicy: &containerRestartPolicyAlways, 2545 }, 2546 }, 2547 Containers: []v1.Container{ 2548 { 2549 Name: regular1, 2550 Image: busyboxImage, 2551 Command: ExecCommand(regular1, execCommand{ 2552 Delay: 1, 2553 ExitCode: 0, 2554 }), 2555 }, 2556 }, 2557 }, 2558 } 2559 2560 preparePod(pod) 2561 2562 client := e2epod.NewPodClient(f) 2563 pod = client.Create(context.TODO(), pod) 2564 2565 ginkgo.By("Waiting for the restartable init container to restart") 2566 err := WaitForPodInitContainerRestartCount(context.TODO(), f.ClientSet, pod.Namespace, pod.Name, 0, 2, 2*time.Minute) 2567 framework.ExpectNoError(err) 2568 2569 pod, err = client.Get(context.TODO(), pod.Name, metav1.GetOptions{}) 2570 framework.ExpectNoError(err) 2571 2572 if pod.Status.Phase != v1.PodPending { 2573 framework.Failf("pod %q is not pending, it's %q", pod.Name, pod.Status.Phase) 2574 } 2575 2576 results := parseOutput(context.TODO(), f, pod) 2577 2578 ginkgo.By("Analyzing results") 2579 framework.ExpectNoError(results.RunTogether(restartableInit1, prefixedName(PreStopPrefix, restartableInit1))) 2580 framework.ExpectNoError(results.Starts(prefixedName(PreStopPrefix, restartableInit1))) 2581 framework.ExpectNoError(results.Exits(restartableInit1)) 2582 framework.ExpectNoError(results.DoesntStart(regular1)) 2583 }) 2584 2585 ginkgo.It("should call the container's preStop hook and start the next container if the restartable init container's liveness probe fails", func() { 2586 2587 restartableInit1 := "restartable-init-1" 2588 regular1 := "regular-1" 2589 2590 pod := &v1.Pod{ 2591 ObjectMeta: metav1.ObjectMeta{ 2592 Name: "restartable-init-container-failed-startup", 2593 }, 2594 Spec: v1.PodSpec{ 2595 RestartPolicy: v1.RestartPolicyAlways, 2596 InitContainers: []v1.Container{ 2597 { 2598 Name: restartableInit1, 2599 Image: busyboxImage, 2600 Command: ExecCommand(restartableInit1, execCommand{ 2601 Delay: 600, 2602 TerminationSeconds: 15, 2603 ExitCode: 0, 2604 }), 2605 LivenessProbe: &v1.Probe{ 2606 InitialDelaySeconds: 5, 2607 FailureThreshold: 1, 2608 ProbeHandler: v1.ProbeHandler{ 2609 Exec: &v1.ExecAction{ 2610 Command: []string{ 2611 "sh", 2612 "-c", 2613 "exit 1", 2614 }, 2615 }, 2616 }, 2617 }, 2618 Lifecycle: &v1.Lifecycle{ 2619 PreStop: &v1.LifecycleHandler{ 2620 Exec: &v1.ExecAction{ 2621 Command: ExecCommand(prefixedName(PreStopPrefix, restartableInit1), execCommand{ 2622 Delay: 1, 2623 ExitCode: 0, 2624 ContainerName: restartableInit1, 2625 }), 2626 }, 2627 }, 2628 }, 2629 RestartPolicy: &containerRestartPolicyAlways, 2630 }, 2631 }, 2632 Containers: []v1.Container{ 2633 { 2634 Name: regular1, 2635 Image: busyboxImage, 2636 Command: ExecCommand(regular1, execCommand{ 2637 Delay: 1, 2638 ExitCode: 0, 2639 }), 2640 }, 2641 }, 2642 }, 2643 } 2644 2645 preparePod(pod) 2646 2647 client := e2epod.NewPodClient(f) 2648 pod = client.Create(context.TODO(), pod) 2649 2650 ginkgo.By("Waiting for the restartable init container to restart") 2651 err := WaitForPodInitContainerRestartCount(context.TODO(), f.ClientSet, pod.Namespace, pod.Name, 0, 2, 2*time.Minute) 2652 framework.ExpectNoError(err) 2653 2654 err = WaitForPodContainerRestartCount(context.TODO(), f.ClientSet, pod.Namespace, pod.Name, 0, 1, 2*time.Minute) 2655 framework.ExpectNoError(err) 2656 2657 pod, err = client.Get(context.TODO(), pod.Name, metav1.GetOptions{}) 2658 framework.ExpectNoError(err) 2659 2660 results := parseOutput(context.TODO(), f, pod) 2661 2662 ginkgo.By("Analyzing results") 2663 framework.ExpectNoError(results.RunTogether(restartableInit1, prefixedName(PreStopPrefix, restartableInit1))) 2664 framework.ExpectNoError(results.Starts(prefixedName(PreStopPrefix, restartableInit1))) 2665 framework.ExpectNoError(results.Exits(restartableInit1)) 2666 framework.ExpectNoError(results.Starts(regular1)) 2667 }) 2668 2669 ginkgo.It("should terminate sidecars in reverse order after all main containers have exited", func() { 2670 restartableInit1 := "restartable-init-1" 2671 restartableInit2 := "restartable-init-2" 2672 restartableInit3 := "restartable-init-3" 2673 regular1 := "regular-1" 2674 2675 pod := &v1.Pod{ 2676 ObjectMeta: metav1.ObjectMeta{ 2677 Name: "serialize-termination", 2678 }, 2679 Spec: v1.PodSpec{ 2680 RestartPolicy: v1.RestartPolicyNever, 2681 InitContainers: []v1.Container{ 2682 { 2683 Name: restartableInit1, 2684 Image: busyboxImage, 2685 RestartPolicy: &containerRestartPolicyAlways, 2686 Command: ExecCommand(restartableInit1, execCommand{ 2687 Delay: 60, 2688 TerminationSeconds: 5, 2689 ExitCode: 0, 2690 }), 2691 }, 2692 { 2693 Name: restartableInit2, 2694 Image: busyboxImage, 2695 RestartPolicy: &containerRestartPolicyAlways, 2696 Command: ExecCommand(restartableInit2, execCommand{ 2697 Delay: 60, 2698 TerminationSeconds: 5, 2699 ExitCode: 0, 2700 }), 2701 }, 2702 { 2703 Name: restartableInit3, 2704 Image: busyboxImage, 2705 RestartPolicy: &containerRestartPolicyAlways, 2706 Command: ExecCommand(restartableInit3, execCommand{ 2707 Delay: 60, 2708 TerminationSeconds: 5, 2709 ExitCode: 0, 2710 }), 2711 }, 2712 }, 2713 Containers: []v1.Container{ 2714 { 2715 Name: regular1, 2716 Image: busyboxImage, 2717 Command: ExecCommand(regular1, execCommand{ 2718 Delay: 5, 2719 ExitCode: 0, 2720 }), 2721 }, 2722 }, 2723 }, 2724 } 2725 2726 preparePod(pod) 2727 2728 client := e2epod.NewPodClient(f) 2729 pod = client.Create(context.TODO(), pod) 2730 2731 err := e2epod.WaitTimeoutForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, pod.Name, pod.Namespace, 5*time.Minute) 2732 framework.ExpectNoError(err) 2733 2734 pod, err = client.Get(context.TODO(), pod.Name, metav1.GetOptions{}) 2735 framework.ExpectNoError(err) 2736 2737 results := parseOutput(context.TODO(), f, pod) 2738 2739 ginkgo.By("Analyzing results") 2740 framework.ExpectNoError(results.StartsBefore(restartableInit1, restartableInit2)) 2741 framework.ExpectNoError(results.StartsBefore(restartableInit1, restartableInit3)) 2742 framework.ExpectNoError(results.StartsBefore(restartableInit2, restartableInit3)) 2743 framework.ExpectNoError(results.StartsBefore(restartableInit1, regular1)) 2744 framework.ExpectNoError(results.StartsBefore(restartableInit2, regular1)) 2745 framework.ExpectNoError(results.StartsBefore(restartableInit3, regular1)) 2746 2747 // main containers exit first 2748 framework.ExpectNoError(results.ExitsBefore(regular1, restartableInit1)) 2749 framework.ExpectNoError(results.ExitsBefore(regular1, restartableInit2)) 2750 framework.ExpectNoError(results.ExitsBefore(regular1, restartableInit3)) 2751 // followed by sidecars in reverse order 2752 framework.ExpectNoError(results.ExitsBefore(restartableInit3, restartableInit2)) 2753 framework.ExpectNoError(results.ExitsBefore(restartableInit2, restartableInit1)) 2754 }) 2755 2756 ginkgo.It("should terminate sidecars simultaneously if prestop doesn't exit", func() { 2757 restartableInit1 := "restartable-init-1" 2758 restartableInit2 := "restartable-init-2" 2759 restartableInit3 := "restartable-init-3" 2760 regular1 := "regular-1" 2761 2762 makePrestop := func(containerName string) *v1.Lifecycle { 2763 return &v1.Lifecycle{ 2764 PreStop: &v1.LifecycleHandler{ 2765 Exec: &v1.ExecAction{ 2766 Command: ExecCommand(prefixedName(PreStopPrefix, containerName), execCommand{ 2767 ExitCode: 0, 2768 ContainerName: containerName, 2769 LoopForever: true, 2770 }), 2771 }, 2772 }, 2773 } 2774 } 2775 2776 pod := &v1.Pod{ 2777 ObjectMeta: metav1.ObjectMeta{ 2778 Name: "serialize-termination", 2779 }, 2780 Spec: v1.PodSpec{ 2781 RestartPolicy: v1.RestartPolicyNever, 2782 InitContainers: []v1.Container{ 2783 { 2784 Name: restartableInit1, 2785 Image: busyboxImage, 2786 RestartPolicy: &containerRestartPolicyAlways, 2787 Command: ExecCommand(restartableInit1, execCommand{ 2788 Delay: 60, 2789 TerminationSeconds: 5, 2790 ExitCode: 0, 2791 }), 2792 Lifecycle: makePrestop(restartableInit1), 2793 }, 2794 { 2795 Name: restartableInit2, 2796 Image: busyboxImage, 2797 RestartPolicy: &containerRestartPolicyAlways, 2798 Command: ExecCommand(restartableInit2, execCommand{ 2799 Delay: 60, 2800 TerminationSeconds: 5, 2801 ExitCode: 0, 2802 }), 2803 Lifecycle: makePrestop(restartableInit2), 2804 }, 2805 { 2806 Name: restartableInit3, 2807 Image: busyboxImage, 2808 RestartPolicy: &containerRestartPolicyAlways, 2809 Command: ExecCommand(restartableInit3, execCommand{ 2810 Delay: 60, 2811 TerminationSeconds: 5, 2812 ExitCode: 0, 2813 }), 2814 Lifecycle: makePrestop(restartableInit3), 2815 }, 2816 }, 2817 Containers: []v1.Container{ 2818 { 2819 Name: regular1, 2820 Image: busyboxImage, 2821 Command: ExecCommand(regular1, execCommand{ 2822 Delay: 5, 2823 ExitCode: 0, 2824 }), 2825 }, 2826 }, 2827 }, 2828 } 2829 2830 preparePod(pod) 2831 2832 client := e2epod.NewPodClient(f) 2833 pod = client.Create(context.TODO(), pod) 2834 2835 err := e2epod.WaitTimeoutForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, pod.Name, pod.Namespace, 5*time.Minute) 2836 framework.ExpectNoError(err) 2837 2838 pod, err = client.Get(context.TODO(), pod.Name, metav1.GetOptions{}) 2839 framework.ExpectNoError(err) 2840 2841 results := parseOutput(context.TODO(), f, pod) 2842 2843 ginkgo.By("Analyzing results") 2844 framework.ExpectNoError(results.StartsBefore(restartableInit1, restartableInit2)) 2845 framework.ExpectNoError(results.StartsBefore(restartableInit1, restartableInit3)) 2846 framework.ExpectNoError(results.StartsBefore(restartableInit2, restartableInit3)) 2847 framework.ExpectNoError(results.StartsBefore(restartableInit1, regular1)) 2848 framework.ExpectNoError(results.StartsBefore(restartableInit2, regular1)) 2849 framework.ExpectNoError(results.StartsBefore(restartableInit3, regular1)) 2850 2851 ps1, err := results.TimeOfStart(prefixedName(PreStopPrefix, restartableInit1)) 2852 framework.ExpectNoError(err) 2853 ps2, err := results.TimeOfStart(prefixedName(PreStopPrefix, restartableInit2)) 2854 framework.ExpectNoError(err) 2855 ps3, err := results.TimeOfStart(prefixedName(PreStopPrefix, restartableInit3)) 2856 framework.ExpectNoError(err) 2857 2858 ps1Last, err := results.TimeOfLastLoop(prefixedName(PreStopPrefix, restartableInit1)) 2859 framework.ExpectNoError(err) 2860 ps2Last, err := results.TimeOfLastLoop(prefixedName(PreStopPrefix, restartableInit2)) 2861 framework.ExpectNoError(err) 2862 ps3Last, err := results.TimeOfLastLoop(prefixedName(PreStopPrefix, restartableInit3)) 2863 framework.ExpectNoError(err) 2864 2865 const simulToleration = 500 // milliseconds 2866 // should all end together since they loop infinitely and exceed their grace period 2867 gomega.Expect(ps1Last-ps2Last).To(gomega.BeNumerically("~", 0, simulToleration), 2868 fmt.Sprintf("expected PostStart 1 & PostStart 2 to be killed at the same time, got %s", results)) 2869 gomega.Expect(ps1Last-ps3Last).To(gomega.BeNumerically("~", 0, simulToleration), 2870 fmt.Sprintf("expected PostStart 1 & PostStart 3 to be killed at the same time, got %s", results)) 2871 gomega.Expect(ps2Last-ps3Last).To(gomega.BeNumerically("~", 0, simulToleration), 2872 fmt.Sprintf("expected PostStart 2 & PostStart 3 to be killed at the same time, got %s", results)) 2873 2874 // 30 seconds + 2 second minimum grace for the SIGKILL 2875 const lifetimeToleration = 1000 // milliseconds 2876 gomega.Expect(ps1Last-ps1).To(gomega.BeNumerically("~", 32000, lifetimeToleration), 2877 fmt.Sprintf("expected PostStart 1 to live for ~32 seconds, got %s", results)) 2878 gomega.Expect(ps2Last-ps2).To(gomega.BeNumerically("~", 32000, lifetimeToleration), 2879 fmt.Sprintf("expected PostStart 2 to live for ~32 seconds, got %s", results)) 2880 gomega.Expect(ps3Last-ps3).To(gomega.BeNumerically("~", 32000, lifetimeToleration), 2881 fmt.Sprintf("expected PostStart 3 to live for ~32 seconds, got %s", results)) 2882 2883 }) 2884 2885 ginkgo.It("should call sidecar container PreStop hook simultaneously", func() { 2886 restartableInit1 := "restartable-init-1" 2887 restartableInit2 := "restartable-init-2" 2888 restartableInit3 := "restartable-init-3" 2889 regular1 := "regular-1" 2890 2891 makePrestop := func(containerName string) *v1.Lifecycle { 2892 return &v1.Lifecycle{ 2893 PreStop: &v1.LifecycleHandler{ 2894 Exec: &v1.ExecAction{ 2895 Command: ExecCommand(prefixedName(PreStopPrefix, containerName), execCommand{ 2896 Delay: 1, 2897 ExitCode: 0, 2898 ContainerName: containerName, 2899 }), 2900 }, 2901 }, 2902 } 2903 } 2904 2905 pod := &v1.Pod{ 2906 ObjectMeta: metav1.ObjectMeta{ 2907 Name: "serialize-termination-simul-prestop", 2908 }, 2909 Spec: v1.PodSpec{ 2910 RestartPolicy: v1.RestartPolicyNever, 2911 InitContainers: []v1.Container{ 2912 { 2913 Name: restartableInit1, 2914 Image: busyboxImage, 2915 RestartPolicy: &containerRestartPolicyAlways, 2916 Command: ExecCommand(restartableInit1, execCommand{ 2917 Delay: 60, 2918 TerminationSeconds: 5, 2919 ExitCode: 0, 2920 }), 2921 Lifecycle: makePrestop(restartableInit1), 2922 }, 2923 { 2924 Name: restartableInit2, 2925 Image: busyboxImage, 2926 RestartPolicy: &containerRestartPolicyAlways, 2927 Command: ExecCommand(restartableInit2, execCommand{ 2928 Delay: 60, 2929 TerminationSeconds: 5, 2930 ExitCode: 0, 2931 }), 2932 Lifecycle: makePrestop(restartableInit2), 2933 }, 2934 { 2935 Name: restartableInit3, 2936 Image: busyboxImage, 2937 RestartPolicy: &containerRestartPolicyAlways, 2938 Command: ExecCommand(restartableInit3, execCommand{ 2939 Delay: 60, 2940 TerminationSeconds: 5, 2941 ExitCode: 0, 2942 }), 2943 Lifecycle: makePrestop(restartableInit3), 2944 }, 2945 }, 2946 Containers: []v1.Container{ 2947 { 2948 Name: regular1, 2949 Image: busyboxImage, 2950 Command: ExecCommand(regular1, execCommand{ 2951 Delay: 5, 2952 ExitCode: 0, 2953 }), 2954 }, 2955 }, 2956 }, 2957 } 2958 2959 preparePod(pod) 2960 2961 client := e2epod.NewPodClient(f) 2962 pod = client.Create(context.TODO(), pod) 2963 2964 err := e2epod.WaitTimeoutForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, pod.Name, pod.Namespace, 5*time.Minute) 2965 framework.ExpectNoError(err) 2966 2967 pod, err = client.Get(context.TODO(), pod.Name, metav1.GetOptions{}) 2968 framework.ExpectNoError(err) 2969 2970 results := parseOutput(context.TODO(), f, pod) 2971 2972 ginkgo.By("Analyzing results") 2973 framework.ExpectNoError(results.StartsBefore(restartableInit1, restartableInit2)) 2974 framework.ExpectNoError(results.StartsBefore(restartableInit1, restartableInit3)) 2975 framework.ExpectNoError(results.StartsBefore(restartableInit2, restartableInit3)) 2976 framework.ExpectNoError(results.StartsBefore(restartableInit1, regular1)) 2977 framework.ExpectNoError(results.StartsBefore(restartableInit2, regular1)) 2978 framework.ExpectNoError(results.StartsBefore(restartableInit3, regular1)) 2979 2980 // main containers exit first 2981 framework.ExpectNoError(results.ExitsBefore(regular1, restartableInit1)) 2982 framework.ExpectNoError(results.ExitsBefore(regular1, restartableInit2)) 2983 framework.ExpectNoError(results.ExitsBefore(regular1, restartableInit3)) 2984 2985 // followed by sidecars in reverse order 2986 framework.ExpectNoError(results.ExitsBefore(restartableInit3, restartableInit2)) 2987 framework.ExpectNoError(results.ExitsBefore(restartableInit2, restartableInit1)) 2988 2989 // and the pre-stop hooks should have been called simultaneously 2990 ps1, err := results.TimeOfStart(prefixedName(PreStopPrefix, restartableInit1)) 2991 framework.ExpectNoError(err) 2992 ps2, err := results.TimeOfStart(prefixedName(PreStopPrefix, restartableInit2)) 2993 framework.ExpectNoError(err) 2994 ps3, err := results.TimeOfStart(prefixedName(PreStopPrefix, restartableInit3)) 2995 framework.ExpectNoError(err) 2996 2997 const toleration = 500 // milliseconds 2998 gomega.Expect(ps1-ps2).To(gomega.BeNumerically("~", 0, toleration), 2999 fmt.Sprintf("expected PostStart 1 & PostStart 2 to start at the same time, got %s", results)) 3000 gomega.Expect(ps1-ps3).To(gomega.BeNumerically("~", 0, toleration), 3001 fmt.Sprintf("expected PostStart 1 & PostStart 3 to start at the same time, got %s", results)) 3002 gomega.Expect(ps2-ps3).To(gomega.BeNumerically("~", 0, toleration), 3003 fmt.Sprintf("expected PostStart 2 & PostStart 3 to start at the same time, got %s", results)) 3004 }) 3005 3006 ginkgo.It("should not hang in termination if terminated during initialization", func() { 3007 startInit := "start-init" 3008 restartableInit1 := "restartable-init-1" 3009 restartableInit2 := "restartable-init-2" 3010 restartableInit3 := "restartable-init-3" 3011 regular1 := "regular-1" 3012 3013 makePrestop := func(containerName string) *v1.Lifecycle { 3014 return &v1.Lifecycle{ 3015 PreStop: &v1.LifecycleHandler{ 3016 Exec: &v1.ExecAction{ 3017 Command: ExecCommand(prefixedName(PreStopPrefix, containerName), execCommand{ 3018 Delay: 1, 3019 ExitCode: 0, 3020 ContainerName: containerName, 3021 }), 3022 }, 3023 }, 3024 } 3025 } 3026 3027 pod := &v1.Pod{ 3028 ObjectMeta: metav1.ObjectMeta{ 3029 Name: "dont-hang-if-terminated-in-init", 3030 }, 3031 Spec: v1.PodSpec{ 3032 RestartPolicy: v1.RestartPolicyNever, 3033 InitContainers: []v1.Container{ 3034 { 3035 Name: startInit, 3036 Image: busyboxImage, 3037 Command: ExecCommand(startInit, execCommand{ 3038 Delay: 300, 3039 TerminationSeconds: 0, 3040 ExitCode: 0, 3041 }), 3042 }, 3043 { 3044 Name: restartableInit1, 3045 Image: busyboxImage, 3046 RestartPolicy: &containerRestartPolicyAlways, 3047 Command: ExecCommand(restartableInit1, execCommand{ 3048 Delay: 60, 3049 TerminationSeconds: 5, 3050 ExitCode: 0, 3051 }), 3052 Lifecycle: makePrestop(restartableInit1), 3053 }, 3054 { 3055 Name: restartableInit2, 3056 Image: busyboxImage, 3057 RestartPolicy: &containerRestartPolicyAlways, 3058 Command: ExecCommand(restartableInit2, execCommand{ 3059 Delay: 60, 3060 TerminationSeconds: 5, 3061 ExitCode: 0, 3062 }), 3063 Lifecycle: makePrestop(restartableInit2), 3064 }, 3065 { 3066 Name: restartableInit3, 3067 Image: busyboxImage, 3068 RestartPolicy: &containerRestartPolicyAlways, 3069 Command: ExecCommand(restartableInit3, execCommand{ 3070 Delay: 60, 3071 TerminationSeconds: 5, 3072 ExitCode: 0, 3073 }), 3074 Lifecycle: makePrestop(restartableInit3), 3075 }, 3076 }, 3077 Containers: []v1.Container{ 3078 { 3079 Name: regular1, 3080 Image: busyboxImage, 3081 Command: ExecCommand(regular1, execCommand{ 3082 Delay: 5, 3083 ExitCode: 0, 3084 }), 3085 }, 3086 }, 3087 }, 3088 } 3089 3090 preparePod(pod) 3091 3092 client := e2epod.NewPodClient(f) 3093 pod = client.Create(context.TODO(), pod) 3094 3095 err := e2epod.WaitForPodCondition(context.TODO(), f.ClientSet, pod.Namespace, pod.Name, "pod pending and init running", 2*time.Minute, func(pod *v1.Pod) (bool, error) { 3096 if pod.Status.Phase != v1.PodPending { 3097 return false, fmt.Errorf("pod should be in pending phase") 3098 } 3099 if len(pod.Status.InitContainerStatuses) < 1 { 3100 return false, nil 3101 } 3102 containerStatus := pod.Status.InitContainerStatuses[0] 3103 return *containerStatus.Started && containerStatus.State.Running != nil, nil 3104 }) 3105 framework.ExpectNoError(err) 3106 3107 // the init container is running, so we stop the pod before the sidecars even start 3108 start := time.Now() 3109 grace := int64(3) 3110 ginkgo.By("deleting the pod") 3111 err = client.Delete(context.TODO(), pod.Name, metav1.DeleteOptions{GracePeriodSeconds: &grace}) 3112 framework.ExpectNoError(err) 3113 ginkgo.By("waiting for the pod to disappear") 3114 err = e2epod.WaitForPodNotFoundInNamespace(context.TODO(), f.ClientSet, pod.Name, pod.Namespace, 120*time.Second) 3115 framework.ExpectNoError(err) 3116 3117 buffer := int64(2) 3118 deleteTime := time.Since(start).Seconds() 3119 // should delete quickly and not try to start/wait on any sidecars since they never started 3120 gomega.Expect(deleteTime).To(gomega.BeNumerically("<", grace+buffer), fmt.Sprintf("should delete in < %d seconds, took %f", grace+buffer, deleteTime)) 3121 }) 3122 }) 3123 3124 var _ = SIGDescribe(nodefeature.SidecarContainers, framework.WithSerial(), "Containers Lifecycle", func() { 3125 f := framework.NewDefaultFramework("containers-lifecycle-test-serial") 3126 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged 3127 3128 ginkgo.It("should restart the containers in right order after the node reboot", func(ctx context.Context) { 3129 init1 := "init-1" 3130 restartableInit2 := "restartable-init-2" 3131 init3 := "init-3" 3132 regular1 := "regular-1" 3133 3134 podLabels := map[string]string{ 3135 "test": "containers-lifecycle-test-serial", 3136 "namespace": f.Namespace.Name, 3137 } 3138 pod := &v1.Pod{ 3139 ObjectMeta: metav1.ObjectMeta{ 3140 Name: "initialized-pod", 3141 Labels: podLabels, 3142 }, 3143 Spec: v1.PodSpec{ 3144 RestartPolicy: v1.RestartPolicyAlways, 3145 InitContainers: []v1.Container{ 3146 { 3147 Name: init1, 3148 Image: busyboxImage, 3149 Command: ExecCommand(init1, execCommand{ 3150 Delay: 5, 3151 ExitCode: 0, 3152 }), 3153 }, 3154 { 3155 Name: restartableInit2, 3156 Image: busyboxImage, 3157 Command: ExecCommand(restartableInit2, execCommand{ 3158 Delay: 300, 3159 ExitCode: 0, 3160 }), 3161 RestartPolicy: &containerRestartPolicyAlways, 3162 }, 3163 { 3164 Name: init3, 3165 Image: busyboxImage, 3166 Command: ExecCommand(init3, execCommand{ 3167 Delay: 5, 3168 ExitCode: 0, 3169 }), 3170 }, 3171 }, 3172 Containers: []v1.Container{ 3173 { 3174 Name: regular1, 3175 Image: busyboxImage, 3176 Command: ExecCommand(regular1, execCommand{ 3177 Delay: 300, 3178 ExitCode: 0, 3179 }), 3180 }, 3181 }, 3182 }, 3183 } 3184 preparePod(pod) 3185 3186 client := e2epod.NewPodClient(f) 3187 pod = client.Create(ctx, pod) 3188 ginkgo.By("Waiting for the pod to be initialized and run") 3189 err := e2epod.WaitForPodRunningInNamespace(ctx, f.ClientSet, pod) 3190 framework.ExpectNoError(err) 3191 3192 ginkgo.By("Getting the current pod sandbox ID") 3193 rs, _, err := getCRIClient() 3194 framework.ExpectNoError(err) 3195 3196 sandboxes, err := rs.ListPodSandbox(ctx, &runtimeapi.PodSandboxFilter{ 3197 LabelSelector: podLabels, 3198 }) 3199 framework.ExpectNoError(err) 3200 gomega.Expect(sandboxes).To(gomega.HaveLen(1)) 3201 podSandboxID := sandboxes[0].Id 3202 3203 ginkgo.By("Stopping the kubelet") 3204 restartKubelet := stopKubelet() 3205 gomega.Eventually(ctx, func() bool { 3206 return kubeletHealthCheck(kubeletHealthCheckURL) 3207 }, f.Timeouts.PodStart, f.Timeouts.Poll).Should(gomega.BeFalse()) 3208 3209 ginkgo.By("Stopping the pod sandbox to simulate the node reboot") 3210 err = rs.StopPodSandbox(ctx, podSandboxID) 3211 framework.ExpectNoError(err) 3212 3213 ginkgo.By("Restarting the kubelet") 3214 restartKubelet() 3215 gomega.Eventually(ctx, func() bool { 3216 return kubeletHealthCheck(kubeletHealthCheckURL) 3217 }, f.Timeouts.PodStart, f.Timeouts.Poll).Should(gomega.BeTrue()) 3218 3219 ginkgo.By("Waiting for the pod to be re-initialized and run") 3220 err = e2epod.WaitForPodCondition(ctx, f.ClientSet, pod.Namespace, pod.Name, "re-initialized", f.Timeouts.PodStart, func(pod *v1.Pod) (bool, error) { 3221 if pod.Status.ContainerStatuses[0].RestartCount < 1 { 3222 return false, nil 3223 } 3224 if pod.Status.Phase != v1.PodRunning { 3225 return false, nil 3226 } 3227 return true, nil 3228 }) 3229 framework.ExpectNoError(err) 3230 3231 ginkgo.By("Parsing results") 3232 pod, err = client.Get(ctx, pod.Name, metav1.GetOptions{}) 3233 framework.ExpectNoError(err) 3234 results := parseOutput(context.TODO(), f, pod) 3235 3236 ginkgo.By("Analyzing results") 3237 init1Started, err := results.FindIndex(init1, "Started", 0) 3238 framework.ExpectNoError(err) 3239 restartableInit2Started, err := results.FindIndex(restartableInit2, "Started", 0) 3240 framework.ExpectNoError(err) 3241 init3Started, err := results.FindIndex(init3, "Started", 0) 3242 framework.ExpectNoError(err) 3243 regular1Started, err := results.FindIndex(regular1, "Started", 0) 3244 framework.ExpectNoError(err) 3245 3246 init1Restarted, err := results.FindIndex(init1, "Started", init1Started+1) 3247 framework.ExpectNoError(err) 3248 restartableInit2Restarted, err := results.FindIndex(restartableInit2, "Started", restartableInit2Started+1) 3249 framework.ExpectNoError(err) 3250 init3Restarted, err := results.FindIndex(init3, "Started", init3Started+1) 3251 framework.ExpectNoError(err) 3252 regular1Restarted, err := results.FindIndex(regular1, "Started", regular1Started+1) 3253 framework.ExpectNoError(err) 3254 3255 framework.ExpectNoError(init1Started.IsBefore(restartableInit2Started)) 3256 framework.ExpectNoError(restartableInit2Started.IsBefore(init3Started)) 3257 framework.ExpectNoError(init3Started.IsBefore(regular1Started)) 3258 3259 framework.ExpectNoError(init1Restarted.IsBefore(restartableInit2Restarted)) 3260 framework.ExpectNoError(restartableInit2Restarted.IsBefore(init3Restarted)) 3261 framework.ExpectNoError(init3Restarted.IsBefore(regular1Restarted)) 3262 }) 3263 })