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  })