stalwart: one primitive for probing the admin listener

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 <path> [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 <noreply@anthropic.com>
This commit is contained in:
librelad 2026-08-19 00:12:46 +01:00
parent 27fea7aa17
commit 213c689cc1

View File

@ -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"