From 213c689cc18b48a0d0c6e777790d716fd55685c6 Mon Sep 17 00:00:00 2001 From: librelad Date: Wed, 19 Aug 2026 00:12:46 +0100 Subject: [PATCH] stalwart: one primitive for probing the admin listener MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit stalwart_wait_http hardcoded the /healthz/ prefix and returned a yes/no, so the admin-console check could not use it and grew its own copy of the docker exec curl line. Extract stalwart_http_code [max-time] and build both on it: the wait loop keeps its probe-name signature and its 3s timeout, the console check keeps its 5s and gets the status code back rather than a verdict, since 404 and no-reply-at-all need saying apart. Probe commands are byte-identical to before; no behaviour change. The upgrade verifier keeps its own copy on purpose — verifiers here are self-contained (see nextcloud's, which inlines the occ idiom rather than calling the install hook's wrapper) and should not drag a lifecycle file they have no other use for into an upgrade run. Co-Authored-By: Claude Opus 5 --- .../scripts/stalwart_install_hooks.sh | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/containers/stalwart/scripts/stalwart_install_hooks.sh b/containers/stalwart/scripts/stalwart_install_hooks.sh index 84e8f04..63ab50c 100644 --- a/containers/stalwart/scripts/stalwart_install_hooks.sh +++ b/containers/stalwart/scripts/stalwart_install_hooks.sh @@ -133,14 +133,31 @@ stalwart_install_post_setup() fi } +# Status code for one path on the admin listener, empty if the container did +# not answer at all. Empty and a code are different answers — "no reply" is a +# container/exec problem, while a code means the server replied and said no. +# +# curl runs INSIDE the container so this works whatever the port mapping does: +# private mode unbinds ports from the host, and a probe aimed at the host would +# then fail for reasons that have nothing to do with the server's health. +stalwart_http_code() +{ + local path="$1" max_time="${2:-5}" + runFileOp docker exec stalwart-service curl -fsS -o /dev/null -w '%{http_code}' \ + --max-time "$max_time" "http://localhost:8080${path}" 2>/dev/null | tr -d '\r' +} + # Wait for the admin HTTP listener. Used twice: once for the bootstrap listener # before we configure anything, once for the real one after the restart. +# +# Takes a healthz probe NAME (live/ready), not a path — the two callers are both +# waiting on a health probe, and only a 200 ends the wait. Anything that needs a +# different path, or the code rather than a yes/no, wants stalwart_http_code. stalwart_wait_http() { local probe="$1" tries="${2:-40}" i code for ((i = 0; i < tries; i++)); do - code=$(runFileOp docker exec stalwart-service curl -fsS -o /dev/null -w '%{http_code}' \ - --max-time 3 "http://localhost:8080/healthz/$probe" 2>/dev/null | tr -d '\r') + code=$(stalwart_http_code "/healthz/$probe" 3) [[ "$code" == "200" ]] && return 0 sleep 2 done @@ -386,8 +403,7 @@ stalwart_install_post_start() # turns "the panel is broken" into a one-line, fixable cause. isNotice "Checking the admin console (WebUI) responds…" local admin_code - admin_code=$(runFileOp docker exec stalwart-service curl -fsS -o /dev/null -w '%{http_code}' \ - --max-time 5 http://localhost:8080/admin 2>/dev/null | tr -d '\r') + admin_code=$(stalwart_http_code /admin) if [[ "$admin_code" == "404" ]]; then isError "The admin console is missing (/admin returns 404)." isNotice " Stalwart does not bundle the WebUI — it downloads it from"