Skip to content

Commit 6e832c5

Browse files
authored
Add support for explicitly setting lifecycle hooks (#120)
1 parent 8c859a2 commit 6e832c5

File tree

4 files changed

+214
-30
lines changed

4 files changed

+214
-30
lines changed

charts/k8s-service/templates/_deployment_spec.tpl

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,15 +224,26 @@ spec:
224224
resources:
225225
{{ toYaml .Values.containerResources | indent 12 }}
226226
227-
{{- if gt (int .Values.shutdownDelay) 0 }}
228-
# Include a preStop hook with a shutdown delay for eventual consistency reasons.
229-
# See https://blog.gruntwork.io/delaying-shutdown-to-wait-for-pod-deletion-propagation-445f779a8304
227+
{{- if or .Values.lifecycleHooks.enabled (gt (int .Values.shutdownDelay) 0) }}
230228
lifecycle:
229+
{{- if and .Values.lifecycleHooks.enabled .Values.lifecycleHooks.postStart }}
230+
postStart:
231+
{{ toYaml .Values.lifecycleHooks.postStart | indent 14 }}
232+
{{- end }}
233+
234+
{{- if and .Values.lifecycleHooks.enabled .Values.lifecycleHooks.preStop }}
235+
preStop:
236+
{{ toYaml .Values.lifecycleHooks.preStop | indent 14 }}
237+
{{- else if gt (int .Values.shutdownDelay) 0 }}
238+
# Include a preStop hook with a shutdown delay for eventual consistency reasons.
239+
# See https://blog.gruntwork.io/delaying-shutdown-to-wait-for-pod-deletion-propagation-445f779a8304
231240
preStop:
232241
exec:
233242
command:
234243
- sleep
235244
- "{{ int .Values.shutdownDelay }}"
245+
{{- end }}
246+
236247
{{- end }}
237248
238249
{{- /* START ENV VAR LOGIC */ -}}

charts/k8s-service/values.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,33 @@ podSecurityContext: {}
132132
# consistency reasons. You can read more about why you might want to do this in
133133
# https://blog.gruntwork.io/delaying-shutdown-to-wait-for-pod-deletion-propagation-445f779a8304
134134
# You can disable this behavior by setting this value to 0.
135+
# NOTE: this conflicts with lifecycleHooks.preStop
135136
shutdownDelay: 5
136137

138+
# lifecycleHooks configures container lifecycle hooks on the Pod so you can run arbitrary commands after the
139+
# container starts (postStart) or before the container stops.
140+
# Refer to https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/ for more information on container
141+
# lifecycles.
142+
#
143+
# EXAMPLE:
144+
#
145+
# lifecycleHooks:
146+
# enabled: true
147+
# postStart:
148+
# exec:
149+
# command:
150+
# - echo
151+
# - "Run after starting container"
152+
# preStop:
153+
# exec:
154+
# command:
155+
# - echo
156+
# - "Run before stopping container"
157+
#
158+
# NOTE: the preStop hook conflicts with shutdownDelay
159+
lifecycleHooks:
160+
enabled: false
161+
137162
# sideCarContainers specifies any additional containers that should be deployed as side cars to the main application
138163
# container. This will be included in the Deployment container spec so that it will be included in the application Pod.
139164
# This is a nested map, where the first map key is used to name the container, with the nested map being injected as the
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
//go:build all || tpl
2+
// +build all tpl
3+
4+
// NOTE: We use build flags to differentiate between template tests and integration tests so that you can conveniently
5+
// run just the template tests. See the test README for more information.
6+
7+
package test
8+
9+
import (
10+
"testing"
11+
12+
"github.com/stretchr/testify/assert"
13+
"github.com/stretchr/testify/require"
14+
)
15+
16+
// Test that setting shutdownDelay to 0 will disable the preStop hook
17+
func TestK8SServiceShutdownDelayZeroDisablesPreStopHook(t *testing.T) {
18+
t.Parallel()
19+
20+
deployment := renderK8SServiceDeploymentWithSetValues(t, map[string]string{"shutdownDelay": "0"})
21+
22+
renderedPodContainers := deployment.Spec.Template.Spec.Containers
23+
require.Equal(t, len(renderedPodContainers), 1)
24+
appContainer := renderedPodContainers[0]
25+
assert.Nil(t, appContainer.Lifecycle)
26+
}
27+
28+
// Test that setting shutdownDelay to something greater than 0 will include a preStop hook
29+
func TestK8SServiceNonZeroShutdownDelayIncludesPreStopHook(t *testing.T) {
30+
t.Parallel()
31+
32+
deployment := renderK8SServiceDeploymentWithSetValues(t, map[string]string{"shutdownDelay": "5"})
33+
34+
renderedPodContainers := deployment.Spec.Template.Spec.Containers
35+
require.Equal(t, len(renderedPodContainers), 1)
36+
appContainer := renderedPodContainers[0]
37+
require.NotNil(t, appContainer.Lifecycle)
38+
require.NotNil(t, appContainer.Lifecycle.PreStop)
39+
require.NotNil(t, appContainer.Lifecycle.PreStop.Exec)
40+
require.Equal(t, appContainer.Lifecycle.PreStop.Exec.Command, []string{"sleep", "5"})
41+
}
42+
43+
func TestK8SServiceDeploymentAddingOnlyPostStartLifecycleHooks(t *testing.T) {
44+
t.Parallel()
45+
46+
deployment := renderK8SServiceDeploymentWithSetValues(
47+
t,
48+
map[string]string{
49+
// Disable shutdown delay to ensure it doesn't enable preStop hooks.
50+
"shutdownDelay": "0",
51+
52+
"lifecycleHooks.enabled": "true",
53+
"lifecycleHooks.postStart.exec.command[0]": "echo",
54+
"lifecycleHooks.postStart.exec.command[1]": "run after start",
55+
},
56+
)
57+
58+
renderedPodContainers := deployment.Spec.Template.Spec.Containers
59+
require.Equal(t, len(renderedPodContainers), 1)
60+
appContainer := renderedPodContainers[0]
61+
require.NotNil(t, appContainer.Lifecycle)
62+
63+
assert.Nil(t, appContainer.Lifecycle.PreStop)
64+
65+
require.NotNil(t, appContainer.Lifecycle.PostStart)
66+
require.NotNil(t, appContainer.Lifecycle.PostStart.Exec)
67+
require.Equal(t, appContainer.Lifecycle.PostStart.Exec.Command, []string{"echo", "run after start"})
68+
}
69+
70+
func TestK8SServiceDeploymentAddingOnlyPreStopLifecycleHooks(t *testing.T) {
71+
t.Parallel()
72+
73+
deployment := renderK8SServiceDeploymentWithSetValues(
74+
t,
75+
map[string]string{
76+
// Disable shutdown delay to ensure it doesn't enable preStop hooks.
77+
"shutdownDelay": "0",
78+
79+
"lifecycleHooks.enabled": "true",
80+
"lifecycleHooks.preStop.exec.command[0]": "echo",
81+
"lifecycleHooks.preStop.exec.command[1]": "run before stop",
82+
},
83+
)
84+
85+
renderedPodContainers := deployment.Spec.Template.Spec.Containers
86+
require.Equal(t, len(renderedPodContainers), 1)
87+
appContainer := renderedPodContainers[0]
88+
require.NotNil(t, appContainer.Lifecycle)
89+
90+
assert.Nil(t, appContainer.Lifecycle.PostStart)
91+
92+
require.NotNil(t, appContainer.Lifecycle.PreStop)
93+
require.NotNil(t, appContainer.Lifecycle.PreStop.Exec)
94+
require.Equal(t, appContainer.Lifecycle.PreStop.Exec.Command, []string{"echo", "run before stop"})
95+
}
96+
97+
func TestK8SServiceDeploymentAddingBothLifecycleHooks(t *testing.T) {
98+
t.Parallel()
99+
100+
deployment := renderK8SServiceDeploymentWithSetValues(
101+
t,
102+
map[string]string{
103+
// Disable shutdown delay to ensure it doesn't enable preStop hooks.
104+
"shutdownDelay": "0",
105+
106+
"lifecycleHooks.enabled": "true",
107+
"lifecycleHooks.postStart.exec.command[0]": "echo",
108+
"lifecycleHooks.postStart.exec.command[1]": "run after start",
109+
"lifecycleHooks.preStop.exec.command[0]": "echo",
110+
"lifecycleHooks.preStop.exec.command[1]": "run before stop",
111+
},
112+
)
113+
114+
renderedPodContainers := deployment.Spec.Template.Spec.Containers
115+
require.Equal(t, len(renderedPodContainers), 1)
116+
appContainer := renderedPodContainers[0]
117+
require.NotNil(t, appContainer.Lifecycle)
118+
119+
require.NotNil(t, appContainer.Lifecycle.PostStart)
120+
require.NotNil(t, appContainer.Lifecycle.PostStart.Exec)
121+
require.Equal(t, appContainer.Lifecycle.PostStart.Exec.Command, []string{"echo", "run after start"})
122+
123+
require.NotNil(t, appContainer.Lifecycle.PreStop)
124+
require.NotNil(t, appContainer.Lifecycle.PreStop.Exec)
125+
require.Equal(t, appContainer.Lifecycle.PreStop.Exec.Command, []string{"echo", "run before stop"})
126+
}
127+
128+
func TestK8SServiceDeploymentPreferExplicitPreStopOverShutdownDelay(t *testing.T) {
129+
t.Parallel()
130+
131+
deployment := renderK8SServiceDeploymentWithSetValues(
132+
t,
133+
map[string]string{
134+
"shutdownDelay": "5",
135+
"lifecycleHooks.enabled": "true",
136+
"lifecycleHooks.preStop.exec.command[0]": "echo",
137+
"lifecycleHooks.preStop.exec.command[1]": "run before stop",
138+
},
139+
)
140+
141+
renderedPodContainers := deployment.Spec.Template.Spec.Containers
142+
require.Equal(t, len(renderedPodContainers), 1)
143+
appContainer := renderedPodContainers[0]
144+
require.NotNil(t, appContainer.Lifecycle)
145+
146+
assert.Nil(t, appContainer.Lifecycle.PostStart)
147+
148+
require.NotNil(t, appContainer.Lifecycle.PreStop)
149+
require.NotNil(t, appContainer.Lifecycle.PreStop.Exec)
150+
require.Equal(t, appContainer.Lifecycle.PreStop.Exec.Command, []string{"echo", "run before stop"})
151+
}
152+
153+
func TestK8SServiceDeploymentEnabledFalseDisablesLifecycleHooksEvenWhenAddingBoth(t *testing.T) {
154+
t.Parallel()
155+
156+
deployment := renderK8SServiceDeploymentWithSetValues(
157+
t,
158+
map[string]string{
159+
// Disable shutdown delay to ensure it doesn't enable preStop hooks.
160+
"shutdownDelay": "0",
161+
162+
"lifecycleHooks.enabled": "false",
163+
"lifecycleHooks.postStart.exec.command[0]": "echo",
164+
"lifecycleHooks.postStart.exec.command[1]": "run after start",
165+
"lifecycleHooks.preStop.exec.command[0]": "echo",
166+
"lifecycleHooks.preStop.exec.command[1]": "run before stop",
167+
},
168+
)
169+
170+
renderedPodContainers := deployment.Spec.Template.Spec.Containers
171+
require.Equal(t, len(renderedPodContainers), 1)
172+
appContainer := renderedPodContainers[0]
173+
require.Nil(t, appContainer.Lifecycle)
174+
}

test/k8s_service_template_test.go

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//go:build all || tpl
12
// +build all tpl
23

34
// NOTE: We use build flags to differentiate between template tests and integration tests so that you can conveniently
@@ -228,33 +229,6 @@ func TestK8SServiceMultipleImagePullSecrets(t *testing.T) {
228229
assert.Equal(t, renderedImagePullSecrets[1].Name, "gcr-registry-key")
229230
}
230231

231-
// Test that setting shutdownDelay to 0 will disable the preStop hook
232-
func TestK8SServiceShutdownDelayZeroDisablesPreStopHook(t *testing.T) {
233-
t.Parallel()
234-
235-
deployment := renderK8SServiceDeploymentWithSetValues(t, map[string]string{"shutdownDelay": "0"})
236-
237-
renderedPodContainers := deployment.Spec.Template.Spec.Containers
238-
require.Equal(t, len(renderedPodContainers), 1)
239-
appContainer := renderedPodContainers[0]
240-
assert.Nil(t, appContainer.Lifecycle)
241-
}
242-
243-
// Test that setting shutdownDelay to something greater than 0 will include a preStop hook
244-
func TestK8SServiceNonZeroShutdownDelayIncludesPreStopHook(t *testing.T) {
245-
t.Parallel()
246-
247-
deployment := renderK8SServiceDeploymentWithSetValues(t, map[string]string{"shutdownDelay": "5"})
248-
249-
renderedPodContainers := deployment.Spec.Template.Spec.Containers
250-
require.Equal(t, len(renderedPodContainers), 1)
251-
appContainer := renderedPodContainers[0]
252-
require.NotNil(t, appContainer.Lifecycle)
253-
require.NotNil(t, appContainer.Lifecycle.PreStop)
254-
require.NotNil(t, appContainer.Lifecycle.PreStop.Exec)
255-
require.Equal(t, appContainer.Lifecycle.PreStop.Exec.Command, []string{"sleep", "5"})
256-
}
257-
258232
// Test that setting additionalPaths on ingress add paths after service path
259233
func TestK8SServiceIngressAdditionalPathsAfterMainServicePath(t *testing.T) {
260234
t.Parallel()

0 commit comments

Comments
 (0)