Skip to content

Commit 8fb5b6e

Browse files
committed
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.
1 parent 0e14098 commit 8fb5b6e

File tree

2 files changed

+147
-2
lines changed

2 files changed

+147
-2
lines changed

test/e2e_node/container_lifecycle_pod_construction.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ type execCommand struct {
3838
StartDelay int
3939
// Delay is how long the container should delay before exiting
4040
Delay int
41+
// TerminationSeconds is the time it takes for the container before
42+
// terminating if it catches SIGTERM.
43+
TerminationSeconds int
4144
}
4245

4346
// 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 {
5760
fmt.Fprintf(&cmd, "cat %s >> /dev/termination-log; ", containerLog)
5861

5962
fmt.Fprintf(&cmd, "echo %s '%s Starting %d' | tee -a %s >> /dev/termination-log; ", timeCmd, name, c.StartDelay, containerLog)
63+
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)
64+
fmt.Fprintf(&cmd, "trap _term TERM; ")
6065
if c.StartDelay != 0 {
61-
fmt.Fprintf(&cmd, "sleep %d; ", c.StartDelay)
66+
fmt.Fprint(&cmd, sleepCommand(c.StartDelay))
6267
}
6368
// You can check started file to see if the container has started
6469
fmt.Fprintf(&cmd, "touch started; ")
6570
fmt.Fprintf(&cmd, "echo %s '%s Started' | tee -a %s >> /dev/termination-log; ", timeCmd, name, containerLog)
6671
fmt.Fprintf(&cmd, "echo %s '%s Delaying %d' | tee -a %s >> /dev/termination-log; ", timeCmd, name, c.Delay, containerLog)
6772
if c.Delay != 0 {
68-
fmt.Fprintf(&cmd, "sleep %d; ", c.Delay)
73+
fmt.Fprint(&cmd, sleepCommand(c.Delay))
6974
}
7075
fmt.Fprintf(&cmd, "echo %s '%s Exiting' | tee -a %s >> /dev/termination-log; ", timeCmd, name, containerLog)
7176
fmt.Fprintf(&cmd, "exit %d", c.ExitCode)
7277
return []string{"sh", "-c", cmd.String()}
7378
}
7479

80+
// sleepCommand returns a command that sleeps for the given number of seconds
81+
// in background and waits for it to finish so that the parent process can
82+
// handle signals.
83+
func sleepCommand(seconds int) string {
84+
return fmt.Sprintf("exec sleep %d & wait $!; ", seconds)
85+
}
86+
7587
type containerOutput struct {
7688
// time the message was seen to the nearest second
7789
timestamp time.Time

test/e2e_node/container_lifecycle_test.go

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333

3434
const (
3535
PostStartPrefix = "PostStart"
36+
PreStopPrefix = "PreStop"
3637
)
3738

3839
var containerRestartPolicyAlways = v1.ContainerRestartPolicyAlways
@@ -596,6 +597,138 @@ var _ = SIGDescribe("[NodeConformance] Containers Lifecycle ", func() {
596597
framework.ExpectNoError(err)
597598
})
598599
})
600+
601+
ginkgo.It("should call the container's preStop hook and terminate it if its startup probe fails", func() {
602+
regular1 := "regular-1"
603+
604+
podSpec := &v1.Pod{
605+
ObjectMeta: metav1.ObjectMeta{
606+
Name: "test-pod",
607+
},
608+
Spec: v1.PodSpec{
609+
RestartPolicy: v1.RestartPolicyNever,
610+
Containers: []v1.Container{
611+
{
612+
Name: regular1,
613+
Image: busyboxImage,
614+
Command: ExecCommand(regular1, execCommand{
615+
Delay: 100,
616+
TerminationSeconds: 15,
617+
ExitCode: 0,
618+
}),
619+
StartupProbe: &v1.Probe{
620+
ProbeHandler: v1.ProbeHandler{
621+
Exec: &v1.ExecAction{
622+
Command: []string{
623+
"sh",
624+
"-c",
625+
"exit 1",
626+
},
627+
},
628+
},
629+
InitialDelaySeconds: 10,
630+
FailureThreshold: 1,
631+
},
632+
Lifecycle: &v1.Lifecycle{
633+
PreStop: &v1.LifecycleHandler{
634+
Exec: &v1.ExecAction{
635+
Command: ExecCommand(prefixedName(PreStopPrefix, regular1), execCommand{
636+
Delay: 1,
637+
ExitCode: 0,
638+
}),
639+
},
640+
},
641+
},
642+
},
643+
},
644+
},
645+
}
646+
647+
preparePod(podSpec)
648+
649+
client := e2epod.NewPodClient(f)
650+
podSpec = client.Create(context.TODO(), podSpec)
651+
652+
ginkgo.By("Waiting for the pod to complete")
653+
err := e2epod.WaitForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, podSpec.Name, podSpec.Namespace)
654+
framework.ExpectNoError(err)
655+
656+
ginkgo.By("Parsing results")
657+
podSpec, err = client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{})
658+
framework.ExpectNoError(err)
659+
results := parseOutput(podSpec)
660+
661+
ginkgo.By("Analyzing results")
662+
framework.ExpectNoError(results.RunTogether(regular1, prefixedName(PreStopPrefix, regular1)))
663+
framework.ExpectNoError(results.Starts(prefixedName(PreStopPrefix, regular1)))
664+
framework.ExpectNoError(results.Exits(regular1))
665+
})
666+
667+
ginkgo.It("should call the container's preStop hook and terminate it if its liveness probe fails", func() {
668+
regular1 := "regular-1"
669+
670+
podSpec := &v1.Pod{
671+
ObjectMeta: metav1.ObjectMeta{
672+
Name: "test-pod",
673+
},
674+
Spec: v1.PodSpec{
675+
RestartPolicy: v1.RestartPolicyNever,
676+
Containers: []v1.Container{
677+
{
678+
Name: regular1,
679+
Image: busyboxImage,
680+
Command: ExecCommand(regular1, execCommand{
681+
Delay: 100,
682+
TerminationSeconds: 15,
683+
ExitCode: 0,
684+
}),
685+
LivenessProbe: &v1.Probe{
686+
ProbeHandler: v1.ProbeHandler{
687+
Exec: &v1.ExecAction{
688+
Command: []string{
689+
"sh",
690+
"-c",
691+
"exit 1",
692+
},
693+
},
694+
},
695+
InitialDelaySeconds: 10,
696+
FailureThreshold: 1,
697+
},
698+
Lifecycle: &v1.Lifecycle{
699+
PreStop: &v1.LifecycleHandler{
700+
Exec: &v1.ExecAction{
701+
Command: ExecCommand(prefixedName(PreStopPrefix, regular1), execCommand{
702+
Delay: 1,
703+
ExitCode: 0,
704+
}),
705+
},
706+
},
707+
},
708+
},
709+
},
710+
},
711+
}
712+
713+
preparePod(podSpec)
714+
715+
client := e2epod.NewPodClient(f)
716+
podSpec = client.Create(context.TODO(), podSpec)
717+
718+
ginkgo.By("Waiting for the pod to complete")
719+
err := e2epod.WaitForPodNoLongerRunningInNamespace(context.TODO(), f.ClientSet, podSpec.Name, podSpec.Namespace)
720+
framework.ExpectNoError(err)
721+
722+
ginkgo.By("Parsing results")
723+
podSpec, err = client.Get(context.TODO(), podSpec.Name, metav1.GetOptions{})
724+
framework.ExpectNoError(err)
725+
results := parseOutput(podSpec)
726+
727+
ginkgo.By("Analyzing results")
728+
framework.ExpectNoError(results.RunTogether(regular1, prefixedName(PreStopPrefix, regular1)))
729+
framework.ExpectNoError(results.Starts(prefixedName(PreStopPrefix, regular1)))
730+
framework.ExpectNoError(results.Exits(regular1))
731+
})
599732
})
600733

601734
var _ = SIGDescribe("[NodeAlphaFeature:SidecarContainers] Containers Lifecycle ", func() {

0 commit comments

Comments
 (0)