node-e2e: Add container lifecycle e2e tests for preStop hook

This ensures that the container's pre-stop hook is invoked if the
startup or liveness probe fails.
This commit is contained in:
Gunju Kim 2023-07-09 13:25:35 +09:00
parent 0e14098333
commit 8fb5b6eb4c
No known key found for this signature in database
GPG Key ID: 9300A528F3F0DAB7
2 changed files with 147 additions and 2 deletions

View File

@ -38,6 +38,9 @@ type execCommand struct {
StartDelay int
// Delay is how long the container should delay before exiting
Delay int
// TerminationSeconds is the time it takes for the container before
// terminating if it catches SIGTERM.
TerminationSeconds int
}
// ExecCommand returns the command to execute in the container that implements execCommand and logs activities to a container
@ -57,21 +60,30 @@ func ExecCommand(name string, c execCommand) []string {
fmt.Fprintf(&cmd, "cat %s >> /dev/termination-log; ", containerLog)
fmt.Fprintf(&cmd, "echo %s '%s Starting %d' | tee -a %s >> /dev/termination-log; ", timeCmd, name, c.StartDelay, containerLog)
fmt.Fprintf(&cmd, "_term() { sleep %d; echo %s '%s Exiting' | tee -a %s >> /dev/termination-log; exit %d; }; ", c.TerminationSeconds, timeCmd, name, containerLog, c.ExitCode)
fmt.Fprintf(&cmd, "trap _term TERM; ")
if c.StartDelay != 0 {
fmt.Fprintf(&cmd, "sleep %d; ", c.StartDelay)
fmt.Fprint(&cmd, sleepCommand(c.StartDelay))
}
// You can check started file to see if the container has started
fmt.Fprintf(&cmd, "touch started; ")
fmt.Fprintf(&cmd, "echo %s '%s Started' | tee -a %s >> /dev/termination-log; ", timeCmd, name, containerLog)
fmt.Fprintf(&cmd, "echo %s '%s Delaying %d' | tee -a %s >> /dev/termination-log; ", timeCmd, name, c.Delay, containerLog)
if c.Delay != 0 {
fmt.Fprintf(&cmd, "sleep %d; ", c.Delay)
fmt.Fprint(&cmd, sleepCommand(c.Delay))
}
fmt.Fprintf(&cmd, "echo %s '%s Exiting' | tee -a %s >> /dev/termination-log; ", timeCmd, name, containerLog)
fmt.Fprintf(&cmd, "exit %d", c.ExitCode)
return []string{"sh", "-c", cmd.String()}
}
// sleepCommand returns a command that sleeps for the given number of seconds
// in background and waits for it to finish so that the parent process can
// handle signals.
func sleepCommand(seconds int) string {
return fmt.Sprintf("exec sleep %d & wait $!; ", seconds)
}
type containerOutput struct {
// time the message was seen to the nearest second
timestamp time.Time

View File

@ -33,6 +33,7 @@ import (
const (
PostStartPrefix = "PostStart"
PreStopPrefix = "PreStop"
)
var containerRestartPolicyAlways = v1.ContainerRestartPolicyAlways
@ -596,6 +597,138 @@ var _ = SIGDescribe("[NodeConformance] Containers Lifecycle ", func() {
framework.ExpectNoError(err)
})
})
ginkgo.It("should call the container's preStop hook and terminate it if its startup probe fails", func() {
regular1 := "regular-1"
podSpec := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
},
Spec: v1.PodSpec{
RestartPolicy: v1.RestartPolicyNever,
Containers: []v1.Container{
{
Name: regular1,
Image: busyboxImage,
Command: ExecCommand(regular1, execCommand{
Delay: 100,
TerminationSeconds: 15,
ExitCode: 0,
}),
StartupProbe: &v1.Probe{
ProbeHandler: v1.ProbeHandler{
Exec: &v1.ExecAction{
Command: []string{
"sh",
"-c",
"exit 1",
},
},
},
InitialDelaySeconds: 10,
FailureThreshold: 1,
},
Lifecycle: &v1.Lifecycle{
PreStop: &v1.LifecycleHandler{
Exec: &v1.ExecAction{
Command: ExecCommand(prefixedName(PreStopPrefix, regular1), execCommand{
Delay: 1,
ExitCode: 0,
}),
},
},
},
},
},
},
}
preparePod(podSpec)
client := e2epod.NewPodClient(f)
podSpec = client.Create(context.TODO(), podSpec)
ginkgo.By("Waiting for the pod to complete")
err := e2epod.WaitForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, podSpec.Name, podSpec.Namespace)
framework.ExpectNoError(err)
ginkgo.By("Parsing results")
podSpec, err = client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{})
framework.ExpectNoError(err)
results := parseOutput(podSpec)
ginkgo.By("Analyzing results")
framework.ExpectNoError(results.RunTogether(regular1, prefixedName(PreStopPrefix, regular1)))
framework.ExpectNoError(results.Starts(prefixedName(PreStopPrefix, regular1)))
framework.ExpectNoError(results.Exits(regular1))
})
ginkgo.It("should call the container's preStop hook and terminate it if its liveness probe fails", func() {
regular1 := "regular-1"
podSpec := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
},
Spec: v1.PodSpec{
RestartPolicy: v1.RestartPolicyNever,
Containers: []v1.Container{
{
Name: regular1,
Image: busyboxImage,
Command: ExecCommand(regular1, execCommand{
Delay: 100,
TerminationSeconds: 15,
ExitCode: 0,
}),
LivenessProbe: &v1.Probe{
ProbeHandler: v1.ProbeHandler{
Exec: &v1.ExecAction{
Command: []string{
"sh",
"-c",
"exit 1",
},
},
},
InitialDelaySeconds: 10,
FailureThreshold: 1,
},
Lifecycle: &v1.Lifecycle{
PreStop: &v1.LifecycleHandler{
Exec: &v1.ExecAction{
Command: ExecCommand(prefixedName(PreStopPrefix, regular1), execCommand{
Delay: 1,
ExitCode: 0,
}),
},
},
},
},
},
},
}
preparePod(podSpec)
client := e2epod.NewPodClient(f)
podSpec = client.Create(context.TODO(), podSpec)
ginkgo.By("Waiting for the pod to complete")
err := e2epod.WaitForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, podSpec.Name, podSpec.Namespace)
framework.ExpectNoError(err)
ginkgo.By("Parsing results")
podSpec, err = client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{})
framework.ExpectNoError(err)
results := parseOutput(podSpec)
ginkgo.By("Analyzing results")
framework.ExpectNoError(results.RunTogether(regular1, prefixedName(PreStopPrefix, regular1)))
framework.ExpectNoError(results.Starts(prefixedName(PreStopPrefix, regular1)))
framework.ExpectNoError(results.Exits(regular1))
})
})
var _ = SIGDescribe("[NodeAlphaFeature:SidecarContainers] Containers Lifecycle ", func() {