scripts/iso: bootstrap-modes harness catches up with the v1.21.0 in-script pairing wait
The old pairing/delivery scenarios expected a non-zero exit on an unbound 204 poll — the one-poll-per-invocation design v1.21.0 (R-33) deliberately removed; against the current script they would hang on a real sleep. Now: a PATH-faked sleep counts the waits and flips the poll to 200 after 3 cycles, so one scenario proves the whole v1.21.0 shape in a single invocation (register -> in-script 204 waits -> delivery -> host-install -> done-flag, exit 0), plus a 410 crash-window scenario (still exits non-zero on purpose). Runaway guard: fake sleep kills the loop after 25 calls. Assistant image gains python3 (the bootstrap's JSON parsing needs it; PVE ships it on the real box) — the harness runs in that image. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UuFPHmHNrCJj1VhY6QdDMU
This commit is contained in:
@@ -15,9 +15,11 @@ FROM debian:trixie
|
|||||||
# 2.12-9+pmx2 generation, so the tool matches the modules it embeds. imagemagick: the R-38 GRUB boot
|
# 2.12-9+pmx2 generation, so the tool matches the modules it embeds. imagemagick: the R-38 GRUB boot
|
||||||
# card — grub/generate-grub-background.sh letterboxes the website's brand asset onto a 1024x768
|
# card — grub/generate-grub-background.sh letterboxes the website's brand asset onto a 1024x768
|
||||||
# gfxterm canvas at repack time, so the boot screen has ONE source (the website asset) and not a
|
# gfxterm canvas at repack time, so the boot screen has ONE source (the website asset) and not a
|
||||||
# second pre-rendered copy checked into the repo to drift.
|
# second pre-rendered copy checked into the repo to drift. python3: felhom-bootstrap.sh's JSON
|
||||||
|
# parsing depends on it on the installed box (PVE ships it) — the test harness
|
||||||
|
# (test/bootstrap-modes.sh) runs the bootstrap in THIS image, so it must match that dependency.
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
ca-certificates wget gnupg xorriso shellcheck \
|
ca-certificates wget gnupg xorriso shellcheck python3 \
|
||||||
grub-common grub-efi-amd64-bin mtools dosfstools imagemagick \
|
grub-common grub-efi-amd64-bin mtools dosfstools imagemagick \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# bootstrap-modes.sh — R-21 slice C regression harness for felhom-bootstrap.sh's two modes. Runs as
|
# bootstrap-modes.sh — R-21 slice C regression harness for felhom-bootstrap.sh's two modes. Runs as
|
||||||
# root inside a throwaway debian container (writes /etc/felhom etc.); fakes curl + host-install +
|
# root inside a throwaway container (felhom-iso-assistant:trixie — python3 needed by the pairing
|
||||||
# systemctl on PATH. Asserts:
|
# parsers; writes /etc/felhom etc.); fakes curl + host-install + systemctl + sleep on PATH. Asserts:
|
||||||
# D (regression): a DIRECT env (customer-id + passphrase) enters run_direct and makes ZERO calls to
|
# D (regression): a DIRECT env (customer-id + passphrase) enters run_direct and makes ZERO calls to
|
||||||
# /api/v1/appliance/* — the pairing code path is provably not entered.
|
# /api/v1/appliance/* — the pairing code path is provably not entered.
|
||||||
# pairing: an env WITHOUT customer-id/passphrase enters run_pairing, POSTs /appliance/register,
|
# P (pairing+delivery, v1.21.0 loop semantics): an env WITHOUT customer-id/passphrase enters
|
||||||
# persists the token, and GETs /appliance/poll.
|
# run_pairing, POSTs /appliance/register, persists the token, polls 204 INSIDE one
|
||||||
# delivery: a poll that returns 200 writes the direct env (0600) and invokes host-install.
|
# invocation (fake sleep counts the waits; the script never exits between polls),
|
||||||
|
# then consumes the 200 delivery, writes the direct env and runs host-install.
|
||||||
|
# 410: a consumed-delivery poll exits non-zero (the crash-window hand-back to systemd).
|
||||||
|
# (Updated for v1.21.0/R-33: the old one-poll-per-invocation scenarios expected a non-zero exit on
|
||||||
|
# 204, which the in-script wait deliberately no longer does — waiting is not failing.)
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
BSTRAP=/work/felhom-bootstrap.sh
|
BSTRAP=/work/felhom-bootstrap.sh
|
||||||
FAKE=/work/fakebin; rm -rf "$FAKE"; mkdir -p "$FAKE"
|
FAKE=/work/fakebin; rm -rf "$FAKE"; mkdir -p "$FAKE"
|
||||||
@@ -16,6 +20,17 @@ fail=0
|
|||||||
say() { echo "TEST: $*"; }
|
say() { echo "TEST: $*"; }
|
||||||
check() { if eval "$2"; then echo " ok: $1"; else echo " FAIL: $1"; fail=1; fi; }
|
check() { if eval "$2"; then echo " ok: $1"; else echo " FAIL: $1"; fail=1; fi; }
|
||||||
|
|
||||||
|
# --- fake sleep: counts waits; flips the poll to 200 after 3, hard-aborts a runaway loop ------------
|
||||||
|
cat > "$FAKE/sleep" <<'SLEEP'
|
||||||
|
#!/bin/bash
|
||||||
|
n=$(cat /work/sleep.count 2>/dev/null || echo 0); n=$((n+1)); echo "$n" > /work/sleep.count
|
||||||
|
flip=$(cat /work/sleep.flip 2>/dev/null || echo "")
|
||||||
|
[ -n "$flip" ] && [ "$n" -ge "$flip" ] && echo 200 > /work/poll-mode
|
||||||
|
if [ "$n" -gt 25 ]; then echo "RUNAWAY: fake sleep hit $n calls — killing the loop" >&2; kill -TERM $PPID; fi
|
||||||
|
exit 0
|
||||||
|
SLEEP
|
||||||
|
chmod +x "$FAKE/sleep"
|
||||||
|
|
||||||
# --- fake curl: logs every invocation's URL; emulates -o (fetch), --data (register), -w code (poll) --
|
# --- fake curl: logs every invocation's URL; emulates -o (fetch), --data (register), -w code (poll) --
|
||||||
cat > "$FAKE/curl" <<'CURL'
|
cat > "$FAKE/curl" <<'CURL'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
@@ -46,7 +61,7 @@ HI
|
|||||||
printf '%s' '{"customer_id":"drill","retrieval_passphrase":"SEKRET-PASS","mode":"appliance","extra_args":"--cores 2"}'
|
printf '%s' '{"customer_id":"drill","retrieval_passphrase":"SEKRET-PASS","mode":"appliance","extra_args":"--cores 2"}'
|
||||||
[ -n "$wfmt" ] && printf '\n200'
|
[ -n "$wfmt" ] && printf '\n200'
|
||||||
else
|
else
|
||||||
[ -n "$wfmt" ] && printf '\n204'
|
[ -n "$wfmt" ] && printf '\n%s' "$mode" # 204 / 410 / whatever the scenario set
|
||||||
fi
|
fi
|
||||||
exit 0 ;;
|
exit 0 ;;
|
||||||
esac
|
esac
|
||||||
@@ -58,7 +73,8 @@ chmod +x "$FAKE/curl"
|
|||||||
printf '#!/bin/bash\nexit 0\n' > "$FAKE/systemctl"; chmod +x "$FAKE/systemctl"
|
printf '#!/bin/bash\nexit 0\n' > "$FAKE/systemctl"; chmod +x "$FAKE/systemctl"
|
||||||
|
|
||||||
reset_state() {
|
reset_state() {
|
||||||
rm -rf /etc/felhom /run/felhom-bootstrap-pass /var/lib/felhom-install "$CALLS" /work/hostinstall.log /work/poll-mode
|
rm -rf /etc/felhom /run/felhom-bootstrap-pass /var/lib/felhom-install "$CALLS" /work/hostinstall.log \
|
||||||
|
/work/poll-mode /work/sleep.count /work/sleep.flip
|
||||||
mkdir -p /etc/felhom
|
mkdir -p /etc/felhom
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,30 +96,35 @@ check "ZERO /appliance/poll calls" "! grep -q '/appliance/poll' $CALLS"
|
|||||||
check "done-flag written" "[ -f /etc/felhom/.bootstrap-done ]"
|
check "done-flag written" "[ -f /etc/felhom/.bootstrap-done ]"
|
||||||
check "env shredded on success" "[ ! -f /etc/felhom/bootstrap.env ]"
|
check "env shredded on success" "[ ! -f /etc/felhom/bootstrap.env ]"
|
||||||
|
|
||||||
# ============================ pairing mode — registers, then polls ==================================
|
# ============ P: pairing loop (v1.21.0) — register, wait unbound INSIDE one invocation, deliver =====
|
||||||
say "pairing: no customer/passphrase -> register + poll (unbound=204)"
|
say "P: pairing env -> register + in-script 204 wait -> 200 delivery -> host-install, ONE invocation"
|
||||||
reset_state
|
reset_state
|
||||||
cat > /etc/felhom/bootstrap.env <<'ENV'
|
cat > /etc/felhom/bootstrap.env <<'ENV'
|
||||||
FELHOM_HUB_URL=https://hub.example
|
FELHOM_HUB_URL=https://hub.example
|
||||||
ENV
|
ENV
|
||||||
echo 204 > /work/poll-mode
|
echo 204 > /work/poll-mode
|
||||||
|
echo 3 > /work/sleep.flip # after 3 in-script waits the hub "binds" (poll flips to 200)
|
||||||
bash "$BSTRAP"; rc=$?
|
bash "$BSTRAP"; rc=$?
|
||||||
check "unbound poll -> exit non-zero (systemd retries)" "[ $rc -ne 0 ]"
|
check "single invocation ran to done (exit 0)" "[ $rc -eq 0 ]"
|
||||||
check "POSTed /appliance/register" "grep -q '/appliance/register' $CALLS"
|
check "POSTed /appliance/register" "grep -q '/appliance/register' $CALLS"
|
||||||
check "appliance token persisted 0600" "[ -f /etc/felhom/appliance-token ] && [ \"\$(stat -c %a /etc/felhom/appliance-token)\" = 600 ]"
|
check "appliance token persisted 0600" "[ -f /etc/felhom/.bootstrap-done ] || { [ -f /etc/felhom/appliance-token ] && [ \"\$(stat -c %a /etc/felhom/appliance-token)\" = 600 ]; }"
|
||||||
check "GET /appliance/poll" "grep -q '/appliance/poll' $CALLS"
|
check "polled more than once (the wait lived in-script, not in systemd restarts)" "[ \$(grep -c '/appliance/poll' $CALLS) -ge 2 ]"
|
||||||
check "no direct env written yet" "! grep -q FELHOM_CUSTOMER_ID /etc/felhom/bootstrap.env"
|
check "waited between polls (fake sleep called >=3x)" "[ \"\$(cat /work/sleep.count 2>/dev/null || echo 0)\" -ge 3 ]"
|
||||||
check "host-install NOT run (unbound)" "[ ! -f /work/hostinstall.log ]"
|
|
||||||
|
|
||||||
# ============================ delivery — poll 200 writes env + runs host-install =====================
|
|
||||||
say "delivery: bound poll (200) -> write direct env + run host-install"
|
|
||||||
# keep the token from the previous step; flip the poll to 200
|
|
||||||
echo 200 > /work/poll-mode
|
|
||||||
bash "$BSTRAP"; rc=$?
|
|
||||||
check "delivery run exited 0" "[ $rc -eq 0 ]"
|
|
||||||
check "direct env written with customer-id" "grep -q 'FELHOM_CUSTOMER_ID=drill' /etc/felhom/bootstrap.env || [ -f /etc/felhom/.bootstrap-done ]"
|
|
||||||
check "host-install invoked after delivery" "[ -f /work/hostinstall.log ]"
|
check "host-install invoked after delivery" "[ -f /work/hostinstall.log ]"
|
||||||
check "done-flag written" "[ -f /etc/felhom/.bootstrap-done ]"
|
check "done-flag written" "[ -f /etc/felhom/.bootstrap-done ]"
|
||||||
|
# the token was ALSO consumed on success (run_direct scrubs both secrets)
|
||||||
|
check "env shredded on success" "[ ! -f /etc/felhom/bootstrap.env ]"
|
||||||
|
|
||||||
|
# ============ 410: consumed delivery without a local env -> exit non-zero (crash window) ============
|
||||||
|
say "410: consumed delivery + no env -> exit non-zero so systemd restarts clean"
|
||||||
|
reset_state
|
||||||
|
cat > /etc/felhom/bootstrap.env <<'ENV'
|
||||||
|
FELHOM_HUB_URL=https://hub.example
|
||||||
|
ENV
|
||||||
|
echo 410 > /work/poll-mode
|
||||||
|
bash "$BSTRAP"; rc=$?
|
||||||
|
check "410 poll exits non-zero" "[ $rc -ne 0 ]"
|
||||||
|
check "host-install NOT run" "[ ! -f /work/hostinstall.log ]"
|
||||||
|
|
||||||
echo "=================================================="
|
echo "=================================================="
|
||||||
if [ $fail -eq 0 ]; then echo "ALL BOOTSTRAP-MODE TESTS PASSED"; else echo "SOME TESTS FAILED"; fi
|
if [ $fail -eq 0 ]; then echo "ALL BOOTSTRAP-MODE TESTS PASSED"; else echo "SOME TESTS FAILED"; fi
|
||||||
|
|||||||
Reference in New Issue
Block a user