felhom-host-install.sh 1.35.0 (not published): the hub's live-box 409 is worded in EN and HU at the preflight, host-enroll and the controller.yaml fetch, with harness cases (R-1167)
gates / gates (push) Failing after 7m16s

This commit is contained in:
2026-10-19 20:35:21 +02:00
parent 5f31da04bc
commit d92a712ee4
3 changed files with 91 additions and 3 deletions
+14
View File
@@ -1,3 +1,17 @@
## 2026-10-19 — felhom-host-install.sh 1.35.0 (NOT published): the hub's „live box" 409 is worded (R-1167)
Written for the night brief of 2026-10-19, Part 6; **not published** (publishing is a tag `installer-v1.35.0` plus the
website manifest's ref — the operator's word). Since hub 0.161.0 (R-1159) a re-run against a customer whose box is
live gets 409 from the passphrase calls; the installer printed „unexpected hub status 409 on config preflight". Now
`die_live_box` says it in English and Hungarian: this customer already has a live box, which uses its own key; the
operator deletes the old host on the hub first (Hosts page); then run the installer again. Wired at all three
passphrase calls: the preflight `GET /config`, `POST /host-enroll`, and the controller.yaml fetch for the git
credential (`resolve_git_creds`, which now reads the status instead of `curl -f`). The installer had no Hungarian text
before (the brief said „as it already does"; it did not) — the HU lines are new. Harness cases (static + behaviour: the
REAL `resolve_git_creds` + `die_live_box` extracted from the script, against a local fake hub on 127.0.0.1): every call
maps 409; 409 → the EN + HU sentence, never „hub status 409"; 200 → the git user is still read. Red-proved on a
mutated copy (the 409 arm removed → both 409 cases FAIL with „hub status 409").
## 2026-10-19 — the website's git-syncs read Gitea in-cluster and retry forever (`manifests/webpage.yaml`, gate 6)
On 2026-10-19 09:24 the public path to Gitea failed once; git-sync v4.4.0 (`--max-failures` defaults to 0) exited
+20 -3
View File
@@ -194,7 +194,7 @@
set -euo pipefail
SCRIPT_VERSION="1.34.0" # the SINGLE version source (F-1): -h and the run banners follow it.
SCRIPT_VERSION="1.35.0" # the SINGLE version source (F-1): -h and the run banners follow it.
# The hub used to carry a copy for its Setup tab; R-94 DELETED it
# (2026-08-02) because the hub cannot know which version a box runs —
# the Setup command fetches this script at run time. scripts/
@@ -227,6 +227,15 @@ log_success() { echo -e "${GREEN}[OK]${NC} $1"; }
log_skip() { echo -e "${CYAN}[SKIP]${NC} $1"; }
log_dry() { echo -e "${CYAN}[DRY-RUN]${NC} $1"; }
die() { log_error "$1"; exit 1; }
# R-1167 (1.35.0): since hub 0.161.0 (R-1159) the hub answers 409 when this customer's box is LIVE — its controller
# pulls its config with its own key, so the household passphrase gives no keys any more. Say what to do, EN and HU.
# Pinned by the harness case R-1167 (every passphrase call maps 409 here; the text names the delete and the re-run).
die_live_box() { # $1 = the hub call that answered 409
die "the hub answered 409 on $1: this customer already has a live box, which uses its own key.
The operator deletes the old host on the hub first (Hosts page); then run this installer again.
HU: a hub 409-cel válaszolt ($1): ennek az ügyfélnek már van élő gépe, amely a saját kulcsát használja.
Előbb az üzemeltető törli a régi gépet a hubon (Hosts oldal), aztán futtasd újra ezt a telepítőt."
}
#-------------------------------------------------------------------------------
# Defaults
@@ -476,9 +485,15 @@ resolve_artifacts() {
# hub-vouched sha256 + visudo/sh -n validation, never from transport auth). Loud warn, never die:
# a customer config without a git token must not block day-0 while the anonymous path works.
resolve_git_creds() {
local yaml
yaml=$(curl -fsS "$HUB_URL/api/v1/config/$CUSTOMER_ID" -H "X-Retrieval-Password: $PASSPHRASE") \
local yaml resp code
resp=$(curl -sS -w $'\n%{http_code}' "$HUB_URL/api/v1/config/$CUSTOMER_ID" -H "X-Retrieval-Password: $PASSPHRASE") \
|| die "controller.yaml fetch failed (for the git fetch credential)"
code=$(tail -n1 <<<"$resp"); yaml=$(sed '$d' <<<"$resp")
case "$code" in
200) ;;
409) die_live_box "the controller.yaml fetch (GET /config)" ;;
*) die "controller.yaml fetch failed (hub status $code, for the git fetch credential)" ;;
esac
GIT_USER=$(awk '/^[^[:space:]#]/{ingit=($1=="git:")} ingit&&$1=="username:"{print $2}' <<<"$yaml" | head -1)
GIT_TOKEN=$(awk '/^[^[:space:]#]/{ingit=($1=="git:")} ingit&&$1=="token:"{print $2}' <<<"$yaml" | head -1)
# strip any surrounding quotes
@@ -2319,6 +2334,7 @@ step_preflight() {
200) log_success " customer '$CUSTOMER_ID' exists + passphrase valid" ;;
401) die "passphrase REJECTED (401) for customer '$CUSTOMER_ID'" ;;
404) die "customer '$CUSTOMER_ID' not found in hub (404) — create it in the hub first" ;;
409) die_live_box "the config preflight (GET /config)" ;;
*) die "unexpected hub status $code on config preflight" ;;
esac
@@ -2557,6 +2573,7 @@ step_enroll() {
200) log_success " host REUSED (idempotent — existing credential)" ;;
401) die "host-enroll 401 (passphrase) — should have been caught in preflight" ;;
404) die "host-enroll 404 (unknown customer)" ;;
409) die_live_box "host-enroll (POST /host-enroll)" ;;
*) die "host-enroll unexpected $code: $body" ;;
esac
HOST_ID=$(python3 -c "import json,sys;print(json.loads(sys.argv[1])['host_id'])" "$body")
+57
View File
@@ -288,6 +288,63 @@ else
verdict FAIL "GL6-ANON empty-cred anonymous-fetch fallback (warn-not-die + conditional auth)"
fi
# R-1167 (1.35.0): the hub's 409 (a LIVE box — since hub 0.161.0 the passphrase gives no keys) is worded, never
# „unexpected 409". Static: every passphrase call (preflight, host-enroll, the controller.yaml fetch) maps 409 to
# die_live_box. Behaviour: the REAL resolve_git_creds + die_live_box, extracted from the script, against a local fake
# hub — 409 → exit nonzero with the delete-first sentence in EN and HU; 200 → the git user is read (no regression).
# Red-proof (2026-10-19): with the 409 arm removed from resolve_git_creds the behaviour case FAILs („hub status 409").
r1167_static=true
for want in '409) die_live_box "the config preflight' '409) die_live_box "host-enroll' '409) die_live_box "the controller.yaml fetch'; do
grep -qF "$want" "$SCRIPT" || { r1167_static=false; r1167_why="missing: $want"; }
done
if $r1167_static; then
verdict PASS "R-1167 every passphrase call maps 409 to die_live_box"
else
verdict FAIL "R-1167 every passphrase call maps 409 to die_live_box" "$r1167_why"
fi
r1167_fns="$WORK/r1167-fns.sh"
{
echo 'log_error() { echo "ERROR: $*"; }; log_warn() { echo "WARN: $*"; }'
sed -n '/^die() /p; /^die_live_box() {/,/^}/p; /^resolve_git_creds() {/,/^}/p' "$SCRIPT"
} > "$r1167_fns"
cat > "$WORK/r1167-hub.py" <<'PY'
import http.server, sys, threading
code = int(sys.argv[1])
class H(http.server.BaseHTTPRequestHandler):
def do_GET(self):
body = b"git:\n username: felhom-ci\n token: \"\"\n" if code == 200 else b"Conflict: this customer already has a live box\n"
self.send_response(code); self.send_header("Content-Length", str(len(body))); self.end_headers(); self.wfile.write(body)
def log_message(self, *a): pass
srv = http.server.HTTPServer(("127.0.0.1", 0), H)
print(srv.server_address[1], flush=True)
srv.serve_forever()
PY
r1167_run() { # <hub status> → $out/$rc of resolve_git_creds against the fake hub
local port pid
python3 "$WORK/r1167-hub.py" "$1" > "$WORK/r1167-port" 2>/dev/null & pid=$!
for _ in 1 2 3 4 5 6 7 8 9 10; do port=$(head -1 "$WORK/r1167-port" 2>/dev/null); [[ -n "$port" ]] && break; sleep 0.2; done
set +e
out=$(HUB_URL="http://127.0.0.1:$port" CUSTOMER_ID=t PASSPHRASE=x bash -c ". '$r1167_fns'; resolve_git_creds; echo \"GIT_USER=\$GIT_USER\"" 2>&1)
rc=$?
set -e
kill "$pid" 2>/dev/null || true; wait "$pid" 2>/dev/null || true
[[ -n "$port" ]]
}
if ! grep -q '^resolve_git_creds() {' "$r1167_fns" || ! grep -q '^die_live_box() {' "$r1167_fns"; then
verdict FAIL "R-1167 live-box 409 is worded (EN + HU)" "could not extract resolve_git_creds/die_live_box from the script"
elif ! r1167_run 409; then
verdict SKIP "R-1167 live-box 409 is worded (EN + HU)" "the local fake hub did not start"
elif [[ $rc -ne 0 && "$out" == *"The operator deletes the old host on the hub first"* && "$out" == *"HU: a hub 409-cel"* && "$out" != *"hub status 409"* ]]; then
verdict PASS "R-1167 live-box 409 is worded (EN + HU)"
else
verdict FAIL "R-1167 live-box 409 is worded (EN + HU)" "rc=$rc; got: $(echo "$out" | tr '\n' ' ' | cut -c1-200)"
fi
if r1167_run 200 && [[ $rc -eq 0 && "$out" == *"GIT_USER=felhom-ci"* ]]; then
verdict PASS "R-1167 a 200 still reads the git credential"
else
verdict FAIL "R-1167 a 200 still reads the git credential" "rc=$rc; got: $(echo "$out" | tr '\n' ' ' | cut -c1-200)"
fi
# GL6-F4 (v1.11.3): the producer steps token/enroll/grows must run UNCONDITIONALLY (not behind
# `should_skip … ||`) so --resume repopulates the in-memory outputs later steps consume; and the
# resume block must re-derive GOLDEN_VOLID from the local archive when golden already completed.