From d92a712ee4c0e04826ad5b49a8e8b58fc0f25e51 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Mon, 19 Oct 2026 20:35:21 +0200 Subject: [PATCH] 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) --- scripts/CHANGELOG.md | 14 +++++++ scripts/felhom-host-install.sh | 23 ++++++++++-- scripts/hostinstall-mode-harness.sh | 57 +++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 3 deletions(-) diff --git a/scripts/CHANGELOG.md b/scripts/CHANGELOG.md index ba9c9e32..de1f1110 100644 --- a/scripts/CHANGELOG.md +++ b/scripts/CHANGELOG.md @@ -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 diff --git a/scripts/felhom-host-install.sh b/scripts/felhom-host-install.sh index 889d20cd..970109fd 100644 --- a/scripts/felhom-host-install.sh +++ b/scripts/felhom-host-install.sh @@ -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") diff --git a/scripts/hostinstall-mode-harness.sh b/scripts/hostinstall-mode-harness.sh index c7f3abeb..ddd7490f 100644 --- a/scripts/hostinstall-mode-harness.sh +++ b/scripts/hostinstall-mode-harness.sh @@ -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() { # → $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.