mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-30 06:54:01 +00:00
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:
commit
582dcc2aca
@ -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"},
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user