Merge pull request #127221 from toVersus/test/restartable-init-termination

[Sidecar Containers] Expand test coverage for Node E2E tests on pod termination behavior
This commit is contained in:
Kubernetes Prow Robot 2024-10-10 02:48:23 +01:00 committed by GitHub
commit 582dcc2aca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -50,6 +50,28 @@ func prefixedName(namePrefix string, name string) string {
return fmt.Sprintf("%s-%s", namePrefix, name)
}
type podTerminationContainerStatus struct {
exitCode int32
reason string
}
func expectPodTerminationContainerStatuses(statuses []v1.ContainerStatus, to map[string]podTerminationContainerStatus) {
ginkgo.GinkgoHelper()
if len(statuses) != len(to) {
ginkgo.Fail(fmt.Sprintf("mismatched lengths in pod termination container statuses. got %d, expected %d", len(statuses), len(to)))
}
for _, status := range statuses {
expected, ok := to[status.Name]
if !ok {
ginkgo.Fail(fmt.Sprintf("container %q not found in expected pod termination container statuses", status.Name))
}
gomega.Expect(status.State.Terminated).NotTo(gomega.BeNil())
gomega.Expect(status.State.Terminated.ExitCode).To(gomega.Equal(expected.exitCode))
gomega.Expect(status.State.Terminated.Reason).To(gomega.Equal(expected.reason))
}
}
var _ = SIGDescribe(framework.WithNodeConformance(), "Containers Lifecycle", func() {
f := framework.NewDefaultFramework("containers-lifecycle-test")
addAfterEachForCleaningUpPods(f)
@ -3115,6 +3137,12 @@ var _ = SIGDescribe(nodefeature.SidecarContainers, "Containers Lifecycle", func(
pod, err = client.Get(context.TODO(), pod.Name, metav1.GetOptions{})
framework.ExpectNoError(err)
expectPodTerminationContainerStatuses(pod.Status.InitContainerStatuses, map[string]podTerminationContainerStatus{
restartableInit1: {exitCode: int32(0), reason: "Completed"},
restartableInit2: {exitCode: int32(0), reason: "Completed"},
restartableInit3: {exitCode: int32(0), reason: "Completed"},
})
results := parseOutput(context.TODO(), f, pod)
ginkgo.By("Analyzing results")
@ -3221,6 +3249,13 @@ var _ = SIGDescribe(nodefeature.SidecarContainers, "Containers Lifecycle", func(
pod, err = client.Get(context.TODO(), pod.Name, metav1.GetOptions{})
framework.ExpectNoError(err)
// all restartable init containers are sigkilled with exit code 137
expectPodTerminationContainerStatuses(pod.Status.InitContainerStatuses, map[string]podTerminationContainerStatus{
restartableInit1: {exitCode: int32(137), reason: "Error"},
restartableInit2: {exitCode: int32(137), reason: "Error"},
restartableInit3: {exitCode: int32(137), reason: "Error"},
})
results := parseOutput(context.TODO(), f, pod)
ginkgo.By("Analyzing results")
@ -3352,6 +3387,12 @@ var _ = SIGDescribe(nodefeature.SidecarContainers, "Containers Lifecycle", func(
pod, err = client.Get(context.TODO(), pod.Name, metav1.GetOptions{})
framework.ExpectNoError(err)
expectPodTerminationContainerStatuses(pod.Status.InitContainerStatuses, map[string]podTerminationContainerStatus{
restartableInit1: {exitCode: int32(0), reason: "Completed"},
restartableInit2: {exitCode: int32(0), reason: "Completed"},
restartableInit3: {exitCode: int32(0), reason: "Completed"},
})
results := parseOutput(context.TODO(), f, pod)
ginkgo.By("Analyzing results")
@ -3610,6 +3651,654 @@ var _ = SIGDescribe(nodefeature.SidecarContainers, "Containers Lifecycle", func(
framework.ExpectNoError(err, "the pod should be deleted before its terminationGracePeriodSeconds if the restartalbe init containers get termination signal correctly")
})
})
ginkgo.When("The restartable init containers exit with non-zero exit code", func() {
ginkgo.It("should mark pod as succeeded if any of the restartable init containers have terminated with non-zero exit code", func(ctx context.Context) {
restartableInit1 := "restartable-init-1"
restartableInit2 := "restartable-init-2"
restartableInit3 := "restartable-init-3"
regular1 := "regular-1"
podTerminationGracePeriodSeconds := int64(30)
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "restartable-init-terminated-with-non-zero-exit-code",
Finalizers: []string{testFinalizer},
},
Spec: v1.PodSpec{
RestartPolicy: v1.RestartPolicyNever,
TerminationGracePeriodSeconds: ptr.To(podTerminationGracePeriodSeconds),
InitContainers: []v1.Container{
{
Name: restartableInit1,
Image: busyboxImage,
RestartPolicy: &containerRestartPolicyAlways,
Command: ExecCommand(restartableInit1, execCommand{
Delay: 600,
TerminationSeconds: 1,
ExitCode: 0,
}),
},
{
Name: restartableInit2,
Image: busyboxImage,
RestartPolicy: &containerRestartPolicyAlways,
Command: ExecCommand(restartableInit2, execCommand{
Delay: 600,
TerminationSeconds: 1,
ExitCode: 1,
}),
},
{
Name: restartableInit3,
Image: busyboxImage,
RestartPolicy: &containerRestartPolicyAlways,
Command: ExecCommand(restartableInit3, execCommand{
Delay: 600,
TerminationSeconds: 1,
ExitCode: 0,
}),
},
},
Containers: []v1.Container{
{
Name: regular1,
Image: busyboxImage,
Command: ExecCommand(regular1, execCommand{
Delay: 600,
TerminationSeconds: 1,
ExitCode: 0,
}),
},
},
},
}
preparePod(pod)
ginkgo.By("Creating the pod with finalizer")
client := e2epod.NewPodClient(f)
pod = client.Create(ctx, pod)
defer client.RemoveFinalizer(ctx, pod.Name, testFinalizer)
ginkgo.By("Waiting for the pod to be initialized and run")
err := e2epod.WaitForPodRunningInNamespace(ctx, f.ClientSet, pod)
framework.ExpectNoError(err)
ginkgo.By("Deleting the pod")
err = client.Delete(ctx, pod.Name, metav1.DeleteOptions{GracePeriodSeconds: &podTerminationGracePeriodSeconds})
framework.ExpectNoError(err)
ginkgo.By(fmt.Sprintf("Waiting for the pod (%s/%s) to be transitioned into the Succeeded phase", pod.Namespace, pod.Name))
err = e2epod.WaitForPodSuccessInNamespace(ctx, f.ClientSet, pod.Name, f.Namespace.Name)
framework.ExpectNoError(err, "Failed to await for the pod to be terminated: %q", pod.Name)
ginkgo.By(fmt.Sprintf("Fetch the end state of the pod (%s/%s)", pod.Namespace, pod.Name))
pod, err = client.Get(ctx, pod.Name, metav1.GetOptions{})
framework.ExpectNoError(err, "Failed to fetch the end state of the pod: %q", pod.Name)
// regular container is gracefully terminated
expectPodTerminationContainerStatuses(pod.Status.ContainerStatuses, map[string]podTerminationContainerStatus{
regular1: {exitCode: int32(0), reason: "Completed"},
})
// restartable-init-2 that terminated with non-zero exit code is marked as error
expectPodTerminationContainerStatuses(pod.Status.InitContainerStatuses, map[string]podTerminationContainerStatus{
restartableInit1: {exitCode: int32(0), reason: "Completed"},
restartableInit2: {exitCode: int32(1), reason: "Error"},
restartableInit3: {exitCode: int32(0), reason: "Completed"},
})
})
})
ginkgo.When("The restartable init containers exit with non-zero exit code by prestop hook", func() {
ginkgo.It("should mark pod as succeeded if any of the restartable init containers have terminated with non-zero exit code by prestop hook", func(ctx context.Context) {
restartableInit1 := "restartable-init-1"
restartableInit2 := "restartable-init-2"
restartableInit3 := "restartable-init-3"
regular1 := "regular-1"
podTerminationGracePeriodSeconds := int64(30)
makePrestop := func(containerName string, exitCode int) *v1.Lifecycle {
return &v1.Lifecycle{
PreStop: &v1.LifecycleHandler{
Exec: &v1.ExecAction{
Command: ExecCommand(containerName, execCommand{
Delay: 1,
ExitCode: exitCode,
}),
},
},
}
}
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "restartable-init-terminated-with-non-zero-exit-code",
Finalizers: []string{testFinalizer},
},
Spec: v1.PodSpec{
RestartPolicy: v1.RestartPolicyNever,
TerminationGracePeriodSeconds: ptr.To(podTerminationGracePeriodSeconds),
InitContainers: []v1.Container{
{
Name: restartableInit1,
Image: busyboxImage,
RestartPolicy: &containerRestartPolicyAlways,
Command: ExecCommand(restartableInit1, execCommand{
Delay: 600,
TerminationSeconds: 1,
ExitCode: 0,
}),
Lifecycle: makePrestop(restartableInit1, 0),
},
{
Name: restartableInit2,
Image: busyboxImage,
RestartPolicy: &containerRestartPolicyAlways,
Command: ExecCommand(restartableInit2, execCommand{
Delay: 600,
TerminationSeconds: 1,
ExitCode: 0,
}),
Lifecycle: makePrestop(restartableInit2, 1),
},
{
Name: restartableInit3,
Image: busyboxImage,
RestartPolicy: &containerRestartPolicyAlways,
Command: ExecCommand(restartableInit3, execCommand{
Delay: 600,
TerminationSeconds: 1,
ExitCode: 0,
}),
Lifecycle: makePrestop(restartableInit3, 0),
},
},
Containers: []v1.Container{
{
Name: regular1,
Image: busyboxImage,
Command: ExecCommand(regular1, execCommand{
Delay: 600,
TerminationSeconds: 1,
ExitCode: 0,
}),
},
},
},
}
preparePod(pod)
ginkgo.By("Creating the pod with finalizer")
client := e2epod.NewPodClient(f)
pod = client.Create(ctx, pod)
defer client.RemoveFinalizer(ctx, pod.Name, testFinalizer)
ginkgo.By("Waiting for the pod to be initialized and run")
err := e2epod.WaitForPodRunningInNamespace(ctx, f.ClientSet, pod)
framework.ExpectNoError(err)
ginkgo.By("Deleting the pod")
err = client.Delete(ctx, pod.Name, metav1.DeleteOptions{GracePeriodSeconds: &podTerminationGracePeriodSeconds})
framework.ExpectNoError(err)
ginkgo.By(fmt.Sprintf("Waiting for the pod (%s/%s) to be transitioned into the Succeeded phase", pod.Namespace, pod.Name))
err = e2epod.WaitForPodSuccessInNamespace(ctx, f.ClientSet, pod.Name, f.Namespace.Name)
framework.ExpectNoError(err, "Failed to await for the pod to be terminated: %q", pod.Name)
ginkgo.By(fmt.Sprintf("Fetch the end state of the pod (%s/%s)", pod.Namespace, pod.Name))
pod, err = client.Get(ctx, pod.Name, metav1.GetOptions{})
framework.ExpectNoError(err, "Failed to fetch the end state of the pod: %q", pod.Name)
// regular container is gracefully terminated
expectPodTerminationContainerStatuses(pod.Status.ContainerStatuses, map[string]podTerminationContainerStatus{
regular1: {exitCode: int32(0), reason: "Completed"},
})
// restartable init containers are marked as completed if their prestop hooks are failed
expectPodTerminationContainerStatuses(pod.Status.InitContainerStatuses, map[string]podTerminationContainerStatus{
restartableInit1: {exitCode: int32(0), reason: "Completed"},
restartableInit2: {exitCode: int32(0), reason: "Completed"},
restartableInit3: {exitCode: int32(0), reason: "Completed"},
})
})
})
ginkgo.When("The regular container has exceeded its termination grace period seconds", func() {
ginkgo.It("should mark pod as failed if regular container has exceeded its termination grace period seconds", func(ctx context.Context) {
restartableInit1 := "restartable-init-1"
restartableInit2 := "restartable-init-2"
restartableInit3 := "restartable-init-3"
regular1 := "regular-1"
podTerminationGracePeriodSeconds := int64(5)
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "regular-exceeded-termination-grace-period",
Finalizers: []string{testFinalizer},
},
Spec: v1.PodSpec{
RestartPolicy: v1.RestartPolicyNever,
TerminationGracePeriodSeconds: ptr.To(podTerminationGracePeriodSeconds),
InitContainers: []v1.Container{
{
Name: restartableInit1,
Image: busyboxImage,
RestartPolicy: &containerRestartPolicyAlways,
Command: ExecCommand(restartableInit1, execCommand{
Delay: 600,
TerminationSeconds: 20,
ExitCode: 0,
}),
},
{
Name: restartableInit2,
Image: busyboxImage,
RestartPolicy: &containerRestartPolicyAlways,
Command: ExecCommand(restartableInit2, execCommand{
Delay: 600,
// SIGKILL won't be sent because it only gets triggered 2 seconds after SIGTERM.
TerminationSeconds: 1,
ExitCode: 0,
}),
},
{
Name: restartableInit3,
Image: busyboxImage,
RestartPolicy: &containerRestartPolicyAlways,
Command: ExecCommand(restartableInit3, execCommand{
Delay: 600,
TerminationSeconds: 20,
ExitCode: 0,
}),
},
},
Containers: []v1.Container{
{
Name: regular1,
Image: busyboxImage,
Command: ExecCommand(regular1, execCommand{
Delay: 600,
TerminationSeconds: 20,
ExitCode: 0,
}),
},
},
},
}
preparePod(pod)
ginkgo.By("Creating the pod with finalizer")
client := e2epod.NewPodClient(f)
pod = client.Create(ctx, pod)
defer client.RemoveFinalizer(ctx, pod.Name, testFinalizer)
ginkgo.By("Waiting for the pod to be initialized and run")
err := e2epod.WaitForPodRunningInNamespace(ctx, f.ClientSet, pod)
framework.ExpectNoError(err)
ginkgo.By("Deleting the pod")
err = client.Delete(ctx, pod.Name, metav1.DeleteOptions{GracePeriodSeconds: &podTerminationGracePeriodSeconds})
framework.ExpectNoError(err)
ginkgo.By(fmt.Sprintf("Waiting for the pod (%s/%s) to be transitioned into the Failed phase", pod.Namespace, pod.Name))
err = e2epod.WaitForPodTerminatedInNamespace(ctx, f.ClientSet, pod.Name, "", f.Namespace.Name)
framework.ExpectNoError(err, "Failed to await for the pod to be terminated: %q", pod.Name)
ginkgo.By(fmt.Sprintf("Fetch the end state of the pod (%s/%s)", pod.Namespace, pod.Name))
pod, err = client.Get(ctx, pod.Name, metav1.GetOptions{})
framework.ExpectNoError(err, "Failed to fetch the end state of the pod: %q", pod.Name)
// regular container that exceeds its termination grace period seconds is sigkilled with exit code 137
expectPodTerminationContainerStatuses(pod.Status.ContainerStatuses, map[string]podTerminationContainerStatus{
regular1: {exitCode: int32(137), reason: "Error"},
})
// restartable-init-2 is gracefully terminated within 2 seconds after receiving SIGTERM.
// The other containers that exceed 2 seconds after receiving SIGTERM are sigkilled with exit code 137.
expectPodTerminationContainerStatuses(pod.Status.InitContainerStatuses, map[string]podTerminationContainerStatus{
restartableInit1: {exitCode: int32(137), reason: "Error"},
restartableInit2: {exitCode: int32(0), reason: "Completed"},
restartableInit3: {exitCode: int32(137), reason: "Error"},
})
})
})
ginkgo.When("The restartable init containers have exceeded its termination grace period seconds", func() {
ginkgo.It("should mark pod as succeeded if any of the restartable init containers have exceeded its termination grace period seconds", func(ctx context.Context) {
restartableInit1 := "restartable-init-1"
restartableInit2 := "restartable-init-2"
restartableInit3 := "restartable-init-3"
regular1 := "regular-1"
podTerminationGracePeriodSeconds := int64(5)
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "restartable-init-exceeded-termination-grace-period",
Finalizers: []string{testFinalizer},
},
Spec: v1.PodSpec{
RestartPolicy: v1.RestartPolicyNever,
TerminationGracePeriodSeconds: ptr.To(podTerminationGracePeriodSeconds),
InitContainers: []v1.Container{
{
Name: restartableInit1,
Image: busyboxImage,
RestartPolicy: &containerRestartPolicyAlways,
Command: ExecCommand(restartableInit1, execCommand{
Delay: 600,
TerminationSeconds: 1,
ExitCode: 0,
}),
},
{
Name: restartableInit2,
Image: busyboxImage,
RestartPolicy: &containerRestartPolicyAlways,
Command: ExecCommand(restartableInit2, execCommand{
Delay: 600,
TerminationSeconds: 20,
ExitCode: 0,
}),
},
{
Name: restartableInit3,
Image: busyboxImage,
RestartPolicy: &containerRestartPolicyAlways,
Command: ExecCommand(restartableInit3, execCommand{
Delay: 600,
TerminationSeconds: 1,
ExitCode: 0,
}),
},
},
Containers: []v1.Container{
{
Name: regular1,
Image: busyboxImage,
Command: ExecCommand(regular1, execCommand{
Delay: 600,
TerminationSeconds: 1,
ExitCode: 0,
}),
},
},
},
}
preparePod(pod)
ginkgo.By("Creating the pod with finalizer")
client := e2epod.NewPodClient(f)
pod = client.Create(ctx, pod)
defer client.RemoveFinalizer(ctx, pod.Name, testFinalizer)
ginkgo.By("Waiting for the pod to be initialized and run")
err := e2epod.WaitForPodRunningInNamespace(ctx, f.ClientSet, pod)
framework.ExpectNoError(err)
ginkgo.By("Deleting the pod")
err = client.Delete(ctx, pod.Name, metav1.DeleteOptions{GracePeriodSeconds: &podTerminationGracePeriodSeconds})
framework.ExpectNoError(err)
ginkgo.By(fmt.Sprintf("Waiting for the pod (%s/%s) to be transitioned into the Succeeded phase", pod.Namespace, pod.Name))
err = e2epod.WaitForPodSuccessInNamespace(ctx, f.ClientSet, pod.Name, f.Namespace.Name)
framework.ExpectNoError(err, "Failed to await for the pod to be terminated: %q", pod.Name)
ginkgo.By(fmt.Sprintf("Fetch the end state of the pod (%s/%s)", pod.Namespace, pod.Name))
pod, err = client.Get(ctx, pod.Name, metav1.GetOptions{})
framework.ExpectNoError(err, "Failed to fetch the end state of the pod: %q", pod.Name)
// regular container is gracefully terminated
expectPodTerminationContainerStatuses(pod.Status.ContainerStatuses, map[string]podTerminationContainerStatus{
regular1: {exitCode: int32(0), reason: "Completed"},
})
// restartable-init-2 that exceeds its termination grace period seconds is sigkilled with exit code 137.
// The other containers are gracefully terminated within 2 seconds after receiving SIGTERM
expectPodTerminationContainerStatuses(pod.Status.InitContainerStatuses, map[string]podTerminationContainerStatus{
restartableInit1: {exitCode: int32(0), reason: "Completed"},
restartableInit2: {exitCode: int32(137), reason: "Error"},
restartableInit3: {exitCode: int32(0), reason: "Completed"},
})
})
})
ginkgo.When("The regular containers have exceeded its termination grace period seconds by prestop hook", func() {
ginkgo.It("should mark pod as failed if any of the prestop hook in regular container has exceeded its termination grace period seconds", func(ctx context.Context) {
restartableInit1 := "restartable-init-1"
restartableInit2 := "restartable-init-2"
restartableInit3 := "restartable-init-3"
regular1 := "regular-1"
podTerminationGracePeriodSeconds := int64(5)
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "regular-prestop-exceeded-termination-grace-period",
Finalizers: []string{testFinalizer},
},
Spec: v1.PodSpec{
RestartPolicy: v1.RestartPolicyNever,
TerminationGracePeriodSeconds: ptr.To(podTerminationGracePeriodSeconds),
InitContainers: []v1.Container{
{
Name: restartableInit1,
Image: busyboxImage,
RestartPolicy: &containerRestartPolicyAlways,
Command: ExecCommand(restartableInit1, execCommand{
Delay: 600,
TerminationSeconds: 1,
ExitCode: 0,
}),
},
{
Name: restartableInit2,
Image: busyboxImage,
RestartPolicy: &containerRestartPolicyAlways,
Command: ExecCommand(restartableInit2, execCommand{
Delay: 600,
TerminationSeconds: 20,
ExitCode: 0,
}),
},
{
Name: restartableInit3,
Image: busyboxImage,
RestartPolicy: &containerRestartPolicyAlways,
Command: ExecCommand(restartableInit3, execCommand{
Delay: 600,
TerminationSeconds: 1,
ExitCode: 0,
}),
},
},
Containers: []v1.Container{
{
Name: regular1,
Image: busyboxImage,
Command: ExecCommand(regular1, execCommand{
Delay: 600,
TerminationSeconds: 20,
ExitCode: 0,
}),
Lifecycle: &v1.Lifecycle{
PreStop: &v1.LifecycleHandler{
Exec: &v1.ExecAction{
Command: ExecCommand(regular1, execCommand{
Delay: 20,
ExitCode: 0,
}),
},
},
},
},
},
},
}
preparePod(pod)
ginkgo.By("Creating the pod with finalizer")
client := e2epod.NewPodClient(f)
pod = client.Create(ctx, pod)
defer client.RemoveFinalizer(ctx, pod.Name, testFinalizer)
ginkgo.By("Waiting for the pod to be initialized and run")
err := e2epod.WaitForPodRunningInNamespace(ctx, f.ClientSet, pod)
framework.ExpectNoError(err)
ginkgo.By("Deleting the pod")
err = client.Delete(ctx, pod.Name, metav1.DeleteOptions{GracePeriodSeconds: &podTerminationGracePeriodSeconds})
framework.ExpectNoError(err)
ginkgo.By(fmt.Sprintf("Waiting for the pod (%s/%s) to be transitioned into the Failed phase", pod.Namespace, pod.Name))
err = e2epod.WaitForPodTerminatedInNamespace(ctx, f.ClientSet, pod.Name, "", f.Namespace.Name)
framework.ExpectNoError(err, "Failed to await for the pod to be terminated: %q", pod.Name)
ginkgo.By(fmt.Sprintf("Fetch the end state of the pod (%s/%s)", pod.Namespace, pod.Name))
pod, err = client.Get(ctx, pod.Name, metav1.GetOptions{})
framework.ExpectNoError(err, "Failed to fetch the end state of the pod: %q", pod.Name)
// regular container that exceeds its termination grace period seconds is sigkilled with exit code 137
expectPodTerminationContainerStatuses(pod.Status.ContainerStatuses, map[string]podTerminationContainerStatus{
regular1: {exitCode: int32(137), reason: "Error"},
})
// restartable-init-2 that exceed 2 seconds after receiving SIGTERM is sigkilled with exit code 137.
// The other containers are gracefully terminated within 2 seconds after receiving SIGTERM
expectPodTerminationContainerStatuses(pod.Status.InitContainerStatuses, map[string]podTerminationContainerStatus{
restartableInit1: {exitCode: int32(0), reason: "Completed"},
restartableInit2: {exitCode: int32(137), reason: "Error"},
restartableInit3: {exitCode: int32(0), reason: "Completed"},
})
})
})
ginkgo.When("The restartable init containers have exceeded its termination grace period seconds by prestop hook", func() {
ginkgo.It("should mark pod as succeeded if any of the prestop hook in restartable init containers have exceeded its termination grace period seconds", func(ctx context.Context) {
restartableInit1 := "restartable-init-1"
restartableInit2 := "restartable-init-2"
restartableInit3 := "restartable-init-3"
regular1 := "regular-1"
podTerminationGracePeriodSeconds := int64(5)
makePrestop := func(containerName string, delay int) *v1.Lifecycle {
return &v1.Lifecycle{
PreStop: &v1.LifecycleHandler{
Exec: &v1.ExecAction{
Command: ExecCommand(containerName, execCommand{
Delay: delay,
ExitCode: 0,
}),
},
},
}
}
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "restartable-init-prestop-exceeded-termination-grace-period",
Finalizers: []string{testFinalizer},
},
Spec: v1.PodSpec{
RestartPolicy: v1.RestartPolicyNever,
TerminationGracePeriodSeconds: ptr.To(podTerminationGracePeriodSeconds),
InitContainers: []v1.Container{
{
Name: restartableInit1,
Image: busyboxImage,
RestartPolicy: &containerRestartPolicyAlways,
Command: ExecCommand(restartableInit1, execCommand{
Delay: 600,
TerminationSeconds: 1,
ExitCode: 0,
}),
Lifecycle: makePrestop(restartableInit1, 1),
},
{
Name: restartableInit2,
Image: busyboxImage,
RestartPolicy: &containerRestartPolicyAlways,
Command: ExecCommand(restartableInit2, execCommand{
Delay: 600,
TerminationSeconds: 20,
ExitCode: 0,
}),
Lifecycle: makePrestop(restartableInit1, 30),
},
{
Name: restartableInit3,
Image: busyboxImage,
RestartPolicy: &containerRestartPolicyAlways,
Command: ExecCommand(restartableInit3, execCommand{
Delay: 600,
TerminationSeconds: 1,
ExitCode: 0,
}),
Lifecycle: makePrestop(restartableInit1, 1),
},
},
Containers: []v1.Container{
{
Name: regular1,
Image: busyboxImage,
Command: ExecCommand(regular1, execCommand{
Delay: 600,
TerminationSeconds: 1,
ExitCode: 0,
}),
},
},
},
}
preparePod(pod)
ginkgo.By("Creating the pod with finalizer")
client := e2epod.NewPodClient(f)
pod = client.Create(ctx, pod)
defer client.RemoveFinalizer(ctx, pod.Name, testFinalizer)
ginkgo.By("Waiting for the pod to be initialized and run")
err := e2epod.WaitForPodRunningInNamespace(ctx, f.ClientSet, pod)
framework.ExpectNoError(err)
ginkgo.By("Deleting the pod")
err = client.Delete(ctx, pod.Name, metav1.DeleteOptions{GracePeriodSeconds: &podTerminationGracePeriodSeconds})
framework.ExpectNoError(err)
ginkgo.By(fmt.Sprintf("Waiting for the pod (%s/%s) to be transitioned into the Succeeded phase", pod.Namespace, pod.Name))
err = e2epod.WaitForPodSuccessInNamespace(ctx, f.ClientSet, pod.Name, f.Namespace.Name)
framework.ExpectNoError(err, "Failed to await for the pod to be terminated: %q", pod.Name)
ginkgo.By(fmt.Sprintf("Fetch the end state of the pod (%s/%s)", pod.Namespace, pod.Name))
pod, err = client.Get(ctx, pod.Name, metav1.GetOptions{})
framework.ExpectNoError(err, "Failed to fetch the end state of the pod: %q", pod.Name)
// regular container is gracefully terminated
expectPodTerminationContainerStatuses(pod.Status.ContainerStatuses, map[string]podTerminationContainerStatus{
regular1: {exitCode: int32(0), reason: "Completed"},
})
// restartable-init-2 that exceed its termination grace period seconds by prestop hook is sigkilled
// with exit code 137.
// The other containers are gracefully terminated within their termination grace period seconds
expectPodTerminationContainerStatuses(pod.Status.InitContainerStatuses, map[string]podTerminationContainerStatus{
restartableInit1: {exitCode: int32(0), reason: "Completed"},
restartableInit2: {exitCode: int32(137), reason: "Error"},
restartableInit3: {exitCode: int32(0), reason: "Completed"},
})
})
})
})
})