fix(bootstrap): fail the nano install when the unit does not stay up

install_nano_service printed "enabled and started" straight after
systemctl restart, which returns as soon as the process is forked. An
upgrade that keeps an old felis.toml the new binary rejects (an
[[auth_source]] without a prefix, say) left the unit crash-looping in
auto-restart while the installer reported success, and every login
through the proxy failed.

The install now waits two seconds and asks systemctl is-active. A unit
that exited is in "activating (auto-restart)", which is-active does not
count as active; on real systemd a unit whose process exits 1 under
Restart=on-failure reads activating/auto-restart and is-active returns
non-zero, while a running one reads active/running and returns 0. On
failure the install prints the unit's last 20 journal lines and stops.
This also surfaces a nano unit locked out of an existing 0700 /etc/felis.

The harness runs the extracted function with systemctl stubbed both
ways. Without the check, the dead-unit cases fail.
This commit is contained in:
flyemoji committed 2026-09-22 13:05:42 +09:00
1 parent 26f685be0e
commit a0f54df2a6
2 files changed
+39

No files matched your search

+7
View File
@@ -2379,6 +2379,13 @@ EOF
# restart, not `enable --now`: on a re-run the service is already active and --now would
# leave the OLD binary running against the NEW unit. Converge means converge.
systemctl restart felis-nano
# restart returns as soon as the process is forked. A config the new binary rejects, or a
# file it cannot open, only shows once it has exited and the unit sits in auto-restart.
sleep 2
if ! systemctl is-active --quiet felis-nano; then
journalctl -u felis-nano -n 20 --no-pager || true
die "felis-nano did not stay up; its last log lines are above"
fi
ok "felis-nano.service enabled and started (listen ${FELIS_NANO_LISTEN})"
}
+32
View File
@@ -206,6 +206,38 @@ else
echo "SKIP directory modes: this filesystem ignores chmod"
fi
# --- install_nano_service reports a unit that dies at once ------------------------------
# A config the new binary rejects leaves the unit in auto-restart; the install must say so
# instead of printing "started" over a proxy whose every login now fails.
iblock="$(awk '/^install_nano_service\(\) \{/,/^}/' "$BS")"
[ -n "$iblock" ] || { echo "FAIL: no install_nano_service found in $BS"; exit 1; }
[ "$(printf '%s\n' "$iblock" | wc -l)" -lt 50 ] \
|| { echo "FAIL: the extracted block is not the function -- did its closing brace move?"; exit 1; }
run_nano_service() { # exit status systemctl is-active reports
ACTIVE="$1" NANO_SERVICE="$sdir/felis-nano.service" HOST_BIN=/usr/local/bin/felis \
STATE_DIR=/etc/felis FELIS_NANO_LISTEN=127.0.0.1:25580 bash -c '
die() { printf "DIE: %s\n" "$*"; exit 1; }
ok() { printf "OK: %s\n" "$*"; }
sleep() { :; }
systemctl() { if [ "$1" = is-active ]; then return "$ACTIVE"; fi; }
journalctl() { printf "JOURNAL: config: needs prefix\n"; }
'"$iblock"'
install_nano_service'
}
out="$(run_nano_service 3)"
expect "a unit that dies at once fails the install" "DIE: felis-nano did not stay up" "$out"
expect "the failure shows the unit's own log" "JOURNAL: config: needs prefix" "$out"
case "$out" in
*"OK: felis-nano.service"*) echo "FAIL a dead unit must not be reported as started"; fails=$((fails + 1)) ;;
*) echo "PASS a dead unit is not reported as started" ;;
esac
out="$(run_nano_service 0)"
expect "a unit that stays up is reported as started" "OK: felis-nano.service enabled and started" "$out"
# ---------------------------------------------------------------------------------------
if [ "$fails" -eq 0 ]; then
echo "ALL PASS"