Files
computer-playbook/roles/sys-svc-compose/files/shell/swarm/service_converged.sh
kevinveenbirkenbachandClaude Opus 5 dc54e6d83f fix(swarm): stop the shell helpers from swallowing their own failures
Every one of these call sites ended in `|| true`, so a failure left no trace
and the surrounding script reported a clean run.

service_converged.sh needed the `|| true` for a reason that had nothing to do
with the command: under `set -o pipefail`, `head -10` closes the pipe and awk
dies of SIGPIPE, which pipefail then reports as the pipeline's status. That
made a genuine `container service ps` failure indistinguishable from a normal
truncation. Capping inside awk (`n++ < 10`) removes the SIGPIPE, so the command
status can be tested on its own and a real failure now prints why. give_up and
report_failed_tasks carried a byte-identical copy of that block, so the fix
lifts it into one report_tasks helper rather than applying it twice.

The swarm-clean scripts keep going after a failed step on purpose, since the
host layer below them clears what survives. They now say what did not work
before continuing, instead of continuing silently.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 13:20:26 +02:00

53 lines
1.5 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
: "${SERVICE:?SERVICE env var is required}"
fail() {
echo "${SERVICE}: $1" >&2
exit 1
}
report_tasks() {
local ps rows
if ! ps=$(timeout 15 container service ps "$SERVICE" --no-trunc \
--format '{{.Name}} {{.Node}} {{.DesiredState}} {{.CurrentState}} error={{.Error}}' 2>&1); then
printf '%s: service ps failed: %s\n' "${SERVICE}" "${ps}" >&2
return 0
fi
rows="$(printf '%s\n' "${ps}" | awk '!/error=$/ && n++ < 10')"
[ -n "${rows}" ] || return 0
printf '%s\n' "${rows}" >&2
}
give_up() {
echo "${SERVICE}: $1" >&2
report_tasks
exit 2
}
report_failed_tasks() {
echo "${SERVICE}: $1" >&2
report_tasks
exit 1
}
state=$(timeout 15 container service inspect "$SERVICE" \
--format '{{.UpdateStatus.State}}' 2>/dev/null) || state=""
case "$state" in
"" | "<no value>" | completed) ;;
rollback_started | rollback_completed) give_up "update was rolled back (UpdateStatus.State=${state}); the running spec is the previous one, not the desired one" ;;
paused | rollback_paused) give_up "update latched (UpdateStatus.State=${state}); it cannot leave this state on its own" ;;
*) fail "update in progress (UpdateStatus.State=${state})" ;;
esac
states=$(timeout 15 container service ps "$SERVICE" \
--filter desired-state=running \
--format '{{.CurrentState}}') || fail "service ps failed or timed out"
if [ -z "$states" ]; then
fail "no task carries desired-state=running"
fi
if grep -qvE '^Running' <<<"$states"; then
report_failed_tasks "tasks not running yet"
fi