From ac7a43855d020a1b1c3f6c94b770c4e0986c0e61 Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Sat, 3 Oct 2026 08:04:03 +0000 Subject: [PATCH] feat(controller-peers-env-fallback): pin the generated xceadmin wrapper to its own deployment (task 5.1) Co-Authored-By: Claude Opus 5.5 (1M context) --- .../controller-peers-env-fallback/tasks.md | 2 +- src/tools/deploy-ha.sh | 9 +++ src/tools/verify-controller-peers-env.sh | 62 +++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/.kiro/specs/controller-peers-env-fallback/tasks.md b/.kiro/specs/controller-peers-env-fallback/tasks.md index f4b8b6bc..d2b1c361 100644 --- a/.kiro/specs/controller-peers-env-fallback/tasks.md +++ b/.kiro/specs/controller-peers-env-fallback/tasks.md @@ -96,7 +96,7 @@ - _Depends: 4.1, 4.2_ - [ ] 5. Integration -- [ ] 5.1 Pin the generated xceadmin wrapper to its own deployment +- [x] 5.1 Pin the generated xceadmin wrapper to its own deployment - Add the wrapper case to the regression script first and see it fail: generate a deployment on free high ports into a scratch directory, run a copy of the generated wrapper whose final line prints the two variables, with a bogus value of the new variable in the caller's environment, and expect the deployment's own `ctrl-N=host:port` list and first candidate; fail with a message naming the xceadmin build when the wheel is missing - In the deploy script's generated wrapper, export the new variable set to the deployment's full peers list next to the existing legacy export, and extend the comment above it to say why - Done when, with the xceadmin wheel built (`/xce-build xceadmin`), the wrapper case in the regression script passes diff --git a/src/tools/deploy-ha.sh b/src/tools/deploy-ha.sh index e569c261..b1622f1e 100755 --- a/src/tools/deploy-ha.sh +++ b/src/tools/deploy-ha.sh @@ -686,10 +686,19 @@ if [ -n "$XCEADMIN_WHEEL" ]; then # connecting to the wrong port with no override given). An explicit # `--controller ...` on the command line still wins (click's own # envvar-fallback precedence, xceadmin/cli.py). + # + # XCE_CONTROLLER_PEERS is pinned too, to this deploy's full $PEERS_STR: + # xceadmin reads it AHEAD of XCEADMIN_CONTROLLER, so an ambient value + # left in the caller's shell for a different cluster would otherwise + # silently redirect this deployment's wrapper there despite the legacy + # export above. Exporting the whole list (not just the first candidate) + # also lets the wrapper fail over across every controller of this + # deployment. cat > "$TOP/scripts/xceadmin" << EOF #!$GEN_BASH export PYTHONPATH="$TOP/xceadmin-site:$PYTHONPATH" export XCEADMIN_CONTROLLER="${PEER_ARRAY[0]}" +export XCE_CONTROLLER_PEERS="$PEERS_STR" exec $(command -v python3) -m xceadmin.cli "\$@" EOF chmod +x "$TOP/scripts/xceadmin" diff --git a/src/tools/verify-controller-peers-env.sh b/src/tools/verify-controller-peers-env.sh index bdd4cf73..4e32ab57 100755 --- a/src/tools/verify-controller-peers-env.sh +++ b/src/tools/verify-controller-peers-env.sh @@ -884,6 +884,68 @@ case_harness_help_names_variable() { add_case case_harness_help_names_variable \ "harness: help set documents XCE_CONTROLLER_PEERS and the opt-out (1.5, 1.7)" +# ── deploy-ha.sh's generated xceadmin wrapper (task 5.1, Req 4) ───────────── +# Generation only: deploy-ha.sh writes certs, cluster.bin and scripts into its +# top dir and starts nothing (starting is start-all.sh's job, never run here). +# It runs under `nix develop`, like the skill scripts that call it, because it +# needs openssl and the PYTHONPATH that `xceadmin config build` imports from. +# The ports only have to be free: deploy-ha.sh refuses ports already in use. +DEPLOY_HA="$SCRIPT_DIR/deploy-ha.sh" + +# free_port_base: print P such that P, P+1 and P+2 have no listener. +free_port_base() { + local base k busy + for _ in $(seq 1 50); do + base=$(( 40000 + RANDOM % 20000 )) + busy=0 + for k in 0 1 2; do + if port_listening $(( base + k )); then busy=1; break; fi + done + if [ "$busy" -eq 0 ]; then echo "$base"; return 0; fi + done + return 1 +} + +case_wrapper_pins_own_deployment() { + local d top base p1 p2 p3 wheel want_peers want_first + wheel="$(compgen -G "$BUILD_DIR/xceadmin/dist/xceadmin-*.whl" | head -1 || true)" + if [ -z "$wheel" ]; then + red " no xceadmin wheel under $BUILD_DIR/xceadmin/dist, so deploy-ha.sh generates no wrapper -- build it with: /xce-build xceadmin --incremental" + return 1 + fi + command -v nix >/dev/null 2>&1 || { red " nix not found: deploy-ha.sh runs under nix develop"; return 1; } + base="$(free_port_base)" || { red " no three free consecutive ports found in 40000-59999"; return 1; } + p1="localhost:$base"; p2="localhost:$(( base + 1 ))"; p3="localhost:$(( base + 2 ))" + want_peers="ctrl-1=$p1,ctrl-2=$p2,ctrl-3=$p3" + want_first="$p1" + + d="$(new_scratch wrapper-pins)" + top="$d/top" + run_to_completion "$d" 300 -u XCE_CONTROLLER_PEERS \ + nix develop "$SRC_DIR" --command bash "$DEPLOY_HA" "$CONTROLLER_BIN" "$top" "$p1,$p2,$p3" + assert_rc 0 || { _show_tail "$d/stdout"; _show_tail "$d/stderr"; return 1; } + if [ ! -f "$top/scripts/xceadmin" ]; then + red " deploy-ha.sh generated no $top/scripts/xceadmin (wheel $wheel not picked up?) -- build it with: /xce-build xceadmin --incremental" + _show_tail "$d/stdout" + return 1 + fi + + # The wrapper with its final exec replaced by a print of both variables. + sed 's/^exec .*/printenv XCE_CONTROLLER_PEERS XCEADMIN_CONTROLLER/' \ + "$top/scripts/xceadmin" >"$d/wrapper-probe" + assert_grep "$d/wrapper-probe" '^printenv XCE_CONTROLLER_PEERS XCEADMIN_CONTROLLER$' "probe copy of the wrapper" || return 1 + + run_to_completion "$d" 10 XCE_CONTROLLER_PEERS=bogus:1 XCEADMIN_CONTROLLER=bogus:2 \ + bash "$d/wrapper-probe" + assert_rc 0 || { _show_tail "$d/stderr"; return 1; } + sed -n 1p "$d/stdout" >"$d/line1" + sed -n 2p "$d/stdout" >"$d/line2" + assert_grep "$d/line1" "^${want_peers}\$" "wrapper XCE_CONTROLLER_PEERS (line 1 of $d/stdout)" || return 1 + assert_grep "$d/line2" "^${want_first}\$" "wrapper XCEADMIN_CONTROLLER (line 2 of $d/stdout)" || return 1 +} +add_case case_wrapper_pins_own_deployment \ + "deploy-ha: generated xceadmin wrapper exports its own peers list over an ambient XCE_CONTROLLER_PEERS (4.1, 4.2)" + # ════════════════════════════════════════════════════════════════════════════ # main