Skip to content

Commit fa4b8f3

Browse files
authored
Merge pull request kubernetes#125935 from gjkim42/fix-125880
Terminate restartable init containers ignoring not-started containers
2 parents 2a372a9 + a03affa commit fa4b8f3

File tree

2 files changed

+111
-2
lines changed

2 files changed

+111
-2
lines changed

pkg/kubelet/kuberuntime/kuberuntime_termination_order.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ func newTerminationOrdering(pod *v1.Pod, runningContainerNames []string) *termin
5656
to.terminated[c.Name] = channel
5757
mainContainerChannels = append(mainContainerChannels, channel)
5858

59-
// if its not a running container, pre-close the channel so nothing waits on it
59+
// if it's not a running container, pre-close the channel so nothing
60+
// waits on it
6061
if _, isRunning := runningContainers[c.Name]; !isRunning {
6162
close(channel)
6263
}
@@ -67,7 +68,14 @@ func newTerminationOrdering(pod *v1.Pod, runningContainerNames []string) *termin
6768
// get the init containers in reverse order
6869
ic := pod.Spec.InitContainers[len(pod.Spec.InitContainers)-i-1]
6970

70-
to.terminated[ic.Name] = make(chan struct{})
71+
channel := make(chan struct{})
72+
to.terminated[ic.Name] = channel
73+
74+
// if it's not a running container, pre-close the channel so nothing
75+
// waits on it
76+
if _, isRunning := runningContainers[ic.Name]; !isRunning {
77+
close(channel)
78+
}
7179

7280
if types.IsRestartableInitContainer(&ic) {
7381
// sidecars need to wait for all main containers to exit

test/e2e_node/container_lifecycle_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3118,6 +3118,107 @@ var _ = SIGDescribe(nodefeature.SidecarContainers, "Containers Lifecycle", func(
31183118
// should delete quickly and not try to start/wait on any sidecars since they never started
31193119
gomega.Expect(deleteTime).To(gomega.BeNumerically("<", grace+buffer), fmt.Sprintf("should delete in < %d seconds, took %f", grace+buffer, deleteTime))
31203120
})
3121+
3122+
f.It("should terminate restartable init containers gracefully if there is a non-started restartable init container", func(ctx context.Context) {
3123+
init1 := "init-1"
3124+
restartableInit2 := "restartable-init-2"
3125+
restartableInit3 := "restartable-init-3"
3126+
regular1 := "regular-1"
3127+
3128+
podTerminationGracePeriodSeconds := int64(180)
3129+
containerTerminationSeconds := 1
3130+
3131+
pod := &v1.Pod{
3132+
ObjectMeta: metav1.ObjectMeta{
3133+
Name: "terminate-restartable-init-gracefully",
3134+
},
3135+
Spec: v1.PodSpec{
3136+
TerminationGracePeriodSeconds: &podTerminationGracePeriodSeconds,
3137+
RestartPolicy: v1.RestartPolicyNever,
3138+
InitContainers: []v1.Container{
3139+
{
3140+
Name: init1,
3141+
Image: busyboxImage,
3142+
Command: ExecCommand(init1, execCommand{
3143+
Delay: 1,
3144+
TerminationSeconds: 5,
3145+
ExitCode: 0,
3146+
}),
3147+
},
3148+
{
3149+
Name: restartableInit2,
3150+
Image: busyboxImage,
3151+
Command: ExecCommand(restartableInit2, execCommand{
3152+
Delay: 600,
3153+
TerminationSeconds: containerTerminationSeconds,
3154+
ExitCode: 0,
3155+
}),
3156+
StartupProbe: &v1.Probe{
3157+
FailureThreshold: 600,
3158+
ProbeHandler: v1.ProbeHandler{
3159+
Exec: &v1.ExecAction{
3160+
Command: []string{"false"},
3161+
},
3162+
},
3163+
},
3164+
RestartPolicy: &containerRestartPolicyAlways,
3165+
},
3166+
{
3167+
Name: restartableInit3,
3168+
Image: busyboxImage,
3169+
Command: ExecCommand(restartableInit3, execCommand{
3170+
Delay: 600,
3171+
TerminationSeconds: 1,
3172+
ExitCode: 0,
3173+
}),
3174+
RestartPolicy: &containerRestartPolicyAlways,
3175+
},
3176+
},
3177+
Containers: []v1.Container{
3178+
{
3179+
Name: regular1,
3180+
Image: busyboxImage,
3181+
Command: ExecCommand(regular1, execCommand{
3182+
Delay: 600,
3183+
TerminationSeconds: 1,
3184+
ExitCode: 0,
3185+
}),
3186+
},
3187+
},
3188+
},
3189+
}
3190+
3191+
preparePod(pod)
3192+
3193+
client := e2epod.NewPodClient(f)
3194+
pod = client.Create(ctx, pod)
3195+
3196+
err := e2epod.WaitForPodCondition(ctx, f.ClientSet, pod.Namespace, pod.Name, "the second init container is running but not started", 2*time.Minute, func(pod *v1.Pod) (bool, error) {
3197+
if pod.Status.Phase != v1.PodPending {
3198+
return false, fmt.Errorf("pod should be in pending phase")
3199+
}
3200+
if len(pod.Status.InitContainerStatuses) != 3 {
3201+
return false, fmt.Errorf("pod should have the same number of statuses as init containers")
3202+
}
3203+
containerStatus := pod.Status.InitContainerStatuses[1]
3204+
return containerStatus.State.Running != nil &&
3205+
(containerStatus.Started == nil || *containerStatus.Started == false), nil
3206+
})
3207+
framework.ExpectNoError(err)
3208+
3209+
ginkgo.By("Deleting the pod")
3210+
err = client.Delete(ctx, pod.Name, metav1.DeleteOptions{GracePeriodSeconds: &podTerminationGracePeriodSeconds})
3211+
framework.ExpectNoError(err)
3212+
3213+
ginkgo.By("Waiting for the pod to terminate gracefully before its terminationGracePeriodSeconds")
3214+
err = e2epod.WaitForPodNotFoundInNamespace(ctx, f.ClientSet, pod.Name, pod.Namespace,
3215+
// The duration should be less than the pod's
3216+
// terminationGracePeriodSeconds while adding a buffer(60s) to the
3217+
// container termination seconds(1s) to account for the time it
3218+
// takes to delete the pod.
3219+
time.Duration(containerTerminationSeconds+60)*time.Second)
3220+
framework.ExpectNoError(err, "the pod should be deleted before its terminationGracePeriodSeconds if the restartalbe init containers get termination signal correctly")
3221+
})
31213222
})
31223223

31233224
var _ = SIGDescribe(nodefeature.SidecarContainers, framework.WithSerial(), "Containers Lifecycle", func() {

0 commit comments

Comments
 (0)