From f5bae45ec92898353fae0dfba6eaf0301510c86f Mon Sep 17 00:00:00 2001 From: Alex Stanfield <13949480+chaptersix@users.noreply.github.com> Date: Thu, 7 May 2026 14:52:10 -0500 Subject: [PATCH] fix: admin-tools container ignores SIGTERM until kill deadline (#10187) ## Summary - The admin-tools container CMD runs `sleep infinity` in the foreground, which blocks the shell from processing signal traps - SIGTERM is never handled, so the container hangs until the kubelet termination deadline before being force-killed - Background the sleep and use `wait` as the foreground command instead -- `wait` is a shell builtin that gets interrupted by signals, allowing the trap handler to exit immediately ## Test plan Run the following to compare signal handling before and after: ```bash #!/usr/bin/env bash set -euo pipefail STOP_TIMEOUT=5 echo "=== OLD: sleep infinity in foreground (should hang for ${STOP_TIMEOUT}s) ===" docker run -d --name admin-tools-old alpine:latest \ sh -c "trap exit INT HUP TERM; sleep infinity" >/dev/null sleep 1 echo "Container running. Sending SIGTERM..." start=$(date +%s) docker stop --timeout "$STOP_TIMEOUT" admin-tools-old >/dev/null elapsed=$(( $(date +%s) - start )) echo "Stopped in ${elapsed}s (expected: ${STOP_TIMEOUT}s -- signal was ignored)" docker rm admin-tools-old >/dev/null echo "" echo "=== NEW: sleep infinity & wait (should exit immediately) ===" docker run -d --name admin-tools-new alpine:latest \ sh -c "trap exit INT HUP TERM; sleep infinity & wait" >/dev/null sleep 1 echo "Container running. Sending SIGTERM..." start=$(date +%s) docker stop --timeout "$STOP_TIMEOUT" admin-tools-new >/dev/null elapsed=$(( $(date +%s) - start )) echo "Stopped in ${elapsed}s (expected: 0s -- signal was handled)" docker rm admin-tools-new >/dev/null ``` Expected output: ``` === OLD: sleep infinity in foreground (should hang for 5s) === Container running. Sending SIGTERM... Stopped in 5s (expected: 5s -- signal was ignored) === NEW: sleep infinity & wait (should exit immediately) === Container running. Sending SIGTERM... Stopped in 0s (expected: 0s -- signal was handled) ``` --- docker/targets/admin-tools.Dockerfile | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/docker/targets/admin-tools.Dockerfile b/docker/targets/admin-tools.Dockerfile index f821ee2985..e958917c29 100644 --- a/docker/targets/admin-tools.Dockerfile +++ b/docker/targets/admin-tools.Dockerfile @@ -29,4 +29,22 @@ COPY ./build/temporal/schema /etc/temporal/schema USER temporal -CMD ["sh", "-c", "trap exit INT HUP TERM; sleep infinity"] +# Keep the container running idle so users can exec into it for admin tasks. +# +# trap exit INT HUP TERM +# Register a signal handler so that when the shell receives SIGINT, SIGHUP, +# or SIGTERM it runs "exit" instead of the default PID 1 behavior (ignore). +# +# sleep infinity & +# Start a never-ending process to keep the container alive. It runs in the +# background ("&") so the shell remains the foreground process. +# +# wait +# Block the shell until background jobs finish. Unlike a foreground "sleep", +# "wait" is a shell builtin that gets interrupted when a signal arrives, +# giving the shell a chance to run the trap handler and exit immediately. +# +# Without the "& wait" pattern, the shell is blocked on the foreground sleep and +# never processes signals, causing the container to hang until the kubelet +# termination deadline before being force-killed. +CMD ["sh", "-c", "trap exit INT HUP TERM; sleep infinity & wait"]