Skip to content

Commit e5065ac

Browse files
committed
test: add/update tests related to postStart hook commands
Signed-off-by: Oleksii Kurinnyi <[email protected]>
1 parent 3026a0a commit e5065ac

File tree

8 files changed

+646
-48
lines changed

8 files changed

+646
-48
lines changed

pkg/library/container/container_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ func TestGetKubeContainersFromDevfile(t *testing.T) {
8787
t.Run(tt.Name, func(t *testing.T) {
8888
// sanity check that file is read correctly.
8989
assert.True(t, len(tt.Input.Components) > 0, "Input defines no components")
90-
gotPodAdditions, err := GetKubeContainersFromDevfile(tt.Input, nil, testImagePullPolicy, defaultResources)
90+
gotPodAdditions, err := GetKubeContainersFromDevfile(tt.Input, nil, testImagePullPolicy, defaultResources, nil)
9191
if tt.Output.ErrRegexp != nil && assert.Error(t, err) {
9292
assert.Regexp(t, *tt.Output.ErrRegexp, err.Error(), "Error message should match")
9393
} else {

pkg/library/lifecycle/poststart.go

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -76,23 +76,18 @@ func AddPostStartLifecycleHooks(wksp *dw.DevWorkspaceTemplateSpec, containers []
7676
return nil
7777
}
7878

79-
// processCommandsForPostStart builds a lifecycle handler that runs the provided command(s)
80-
// The command has the format
81-
//
82-
// exec:
83-
//
84-
// command:
85-
// - "/bin/sh"
86-
// - "-c"
87-
// - |
88-
// cd <workingDir>
89-
// <commandline>
90-
func processCommandsForPostStart(commands []dw.Command, postStartTimeout *int32) (*corev1.LifecycleHandler, error) {
79+
// buildUserScript takes a list of DevWorkspace commands and constructs a single
80+
// shell script string that executes them sequentially.
81+
func buildUserScript(commands []dw.Command) (string, error) {
9182
var commandScriptLines []string
9283
for _, command := range commands {
9384
execCmd := command.Exec
85+
if execCmd == nil {
86+
// Should be caught by earlier validation, but good to be safe
87+
return "", fmt.Errorf("exec command is nil for command ID %s", command.Id)
88+
}
9489
if len(execCmd.Env) > 0 {
95-
return nil, fmt.Errorf("env vars in postStart command %s are unsupported", command.Id)
90+
return "", fmt.Errorf("env vars in postStart command %s are unsupported", command.Id)
9691
}
9792
var singleCommandParts []string
9893
if execCmd.WorkingDir != "" {
@@ -107,17 +102,18 @@ func processCommandsForPostStart(commands []dw.Command, postStartTimeout *int32)
107102
commandScriptLines = append(commandScriptLines, strings.Join(singleCommandParts, " && "))
108103
}
109104
}
105+
return strings.Join(commandScriptLines, "\n"), nil
106+
}
110107

111-
originalUserScript := strings.Join(commandScriptLines, "\n")
112-
113-
scriptToExecute := "set -e\n" + originalUserScript
114-
escapedUserScript := strings.ReplaceAll(scriptToExecute, "'", `'\''`)
115-
116-
scriptWithTimeout := fmt.Sprintf(`
108+
// generateScriptWithTimeout wraps a given user script with timeout logic,
109+
// environment variable exports, and specific exit code handling.
110+
// The killAfterDurationSeconds is hardcoded to 5s within this generated script.
111+
func generateScriptWithTimeout(escapedUserScript string, timeoutSeconds int32) string {
112+
return fmt.Sprintf(`
117113
export POSTSTART_TIMEOUT_DURATION="%d"
118114
export POSTSTART_KILL_AFTER_DURATION="5"
119115
120-
echo "[postStart hook] Executing commands with timeout: ${POSTSTART_TIMEOUT_DURATION} s, kill after: ${POSTSTART_KILL_AFTER_DURATION} s" >&2
116+
echo "[postStart hook] Executing commands with timeout: ${POSTSTART_TIMEOUT_DURATION} seconds, kill after: ${POSTSTART_KILL_AFTER_DURATION} seconds" >&2
121117
122118
# Run the user's script under the 'timeout' utility.
123119
timeout --preserve-status --kill-after="${POSTSTART_KILL_AFTER_DURATION}" "${POSTSTART_TIMEOUT_DURATION}" /bin/sh -c '%s'
@@ -128,16 +124,38 @@ if [ $exit_code -eq 143 ]; then # 128 + 15 (SIGTERM)
128124
echo "[postStart hook] Commands terminated by SIGTERM (likely timed out after ${POSTSTART_TIMEOUT_DURATION}s). Exit code 143." >&2
129125
elif [ $exit_code -eq 137 ]; then # 128 + 9 (SIGKILL)
130126
echo "[postStart hook] Commands forcefully killed by SIGKILL (likely after --kill-after ${POSTSTART_KILL_AFTER_DURATION}s expired). Exit code 137." >&2
131-
elif [ $exit_code -ne 0 ]; then # Catches any other non-zero exit code, including 124
127+
elif [ $exit_code -ne 0 ]; then # Catches any other non-zero exit code
132128
echo "[postStart hook] Commands failed with exit code $exit_code." >&2
133129
else
134130
echo "[postStart hook] Commands completed successfully within the time limit." >&2
135131
fi
136132
137133
exit $exit_code
138-
`, *postStartTimeout, escapedUserScript)
134+
`, timeoutSeconds, escapedUserScript)
135+
}
136+
137+
// processCommandsForPostStart processes a list of DevWorkspace commands
138+
// and generates a corev1.LifecycleHandler for the PostStart lifecycle hook.
139+
func processCommandsForPostStart(commands []dw.Command, postStartTimeout *int32) (*corev1.LifecycleHandler, error) {
140+
if postStartTimeout == nil {
141+
// The 'timeout' command treats 0 as "no timeout", so it is disabled by default.
142+
defaultTimeout := int32(0)
143+
postStartTimeout = &defaultTimeout
144+
}
145+
146+
originalUserScript, err := buildUserScript(commands)
147+
if err != nil {
148+
return nil, fmt.Errorf("failed to build aggregated user script: %w", err)
149+
}
150+
151+
// The user script needs 'set -e' to ensure it exits on error.
152+
// This script is then passed to `sh -c '...'`, so single quotes within it must be escaped.
153+
scriptToExecute := "set -e\n" + originalUserScript
154+
escapedUserScriptForTimeoutWrapper := strings.ReplaceAll(scriptToExecute, "'", `'\''`)
155+
156+
fullScriptWithTimeout := generateScriptWithTimeout(escapedUserScriptForTimeoutWrapper, *postStartTimeout)
139157

140-
finalScriptForHook := fmt.Sprintf(redirectOutputFmt, scriptWithTimeout)
158+
finalScriptForHook := fmt.Sprintf(redirectOutputFmt, fullScriptWithTimeout)
141159

142160
handler := &corev1.LifecycleHandler{
143161
Exec: &corev1.ExecAction{

0 commit comments

Comments
 (0)