fix(bootstrap): stop telling operators to run bare roles; pin the migration
Two review findings from the bot round on this stack.
BLOCKING (codex-bot, claude-bot -- both, independently). bootstrap-tenant.sh
emits the staging guest's tailnet-join next step at the end of a converge
("box shell -> sudo rig bootstrap workload"), repeats it in usage, and two of
its refusals recite the old machine-role list. Fixed here rather than on the
stacked tenant PR because THIS is the branch that removes the `workload` role
-- shipping it alone would print a next step naming a role that no longer
exists.
None of those four sites is code that ACCEPTS a role, which is why the rename
missed them, and is also what makes them the worse failure. A stale flag dies
immediately with a usage error. A stale next-step is copy-pasted by a human
onto a DIFFERENT box, minutes after the run that printed it reported success,
and dies there with no thread back to the cause.
So test/cli.sh sweeps every shipped script under bin/ and commands/ for
`rig bootstrap <pre-#76 name>` rather than pinning the four known sites: the
next instance of this class will be somewhere else. Proven non-vacuous --
reintroducing the bare `workload` next-step turns the suite red (412/1),
restoring it turns it green (413/0).
NON-BLOCKING (claude-bot). The migration story was documented and untested:
every marker fixture was renamed alongside the code, so nothing asserted what
a real pre-rename box does. A `role=control-plane` fixture now pins both
halves of the promise -- such a box WARNS on the coolify verbs (its marker no
longer names a role that exists) and is never REFUSED. Both halves matter: a
rename that turned this into a refusal would break the exact boxes the
CHANGELOG promises keep working, on the command that installs the control
plane.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
1845468765
commit
00f6351e28
3 changed files with 44 additions and 5 deletions
|
|
@ -49,6 +49,15 @@ on the way to cutting its first release, and this file starts there.
|
||||||
branch until it is re-bootstrapped. That check has always been advisory and
|
branch until it is re-bootstrapped. That check has always been advisory and
|
||||||
never a gate, so the run still proceeds and the warning names the repair.
|
never a gate, so the run still proceeds and the warning names the repair.
|
||||||
|
|
||||||
|
The rename also reaches every string that *tells an operator to run a role*,
|
||||||
|
not just the code that accepts one — `bootstrap-tenant.sh` emits the staging
|
||||||
|
guest's tailnet-join next step (`sudo rig bootstrap workload-server`), and
|
||||||
|
two of its refusals recite the machine-role list. A stale next-step is worse
|
||||||
|
than a stale flag: it fails when someone copy-pastes it, on a different box,
|
||||||
|
minutes after the run that printed it reported success. `test/cli.sh` sweeps
|
||||||
|
every shipped script for pre-rename role names rather than pinning the known
|
||||||
|
sites, because the next instance of this will be somewhere else.
|
||||||
|
|
||||||
`dev-server` is `class=human`, which reads like a contradiction and is not:
|
`dev-server` is `class=human`, which reads like a contradiction and is not:
|
||||||
the suffix names the family, the class names the root-SSH door policy, and
|
the suffix names the family, the class names the root-SSH door policy, and
|
||||||
operators enter a dev box as themselves so `close-root` shuts its door. The
|
operators enter a dev box as themselves so `close-root` shuts its door. The
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
# Creds-free BY CONTRACT: box auto-runs these at mint ('box exec … rig
|
# Creds-free BY CONTRACT: box auto-runs these at mint ('box exec … rig
|
||||||
# bootstrap claude'), so every path here is non-interactive and nothing joins
|
# bootstrap claude'), so every path here is non-interactive and nothing joins
|
||||||
# or admits — no tailnet, no keys, no prompts. staging's tailnet join stays
|
# or admits — no tailnet, no keys, no prompts. staging's tailnet join stays
|
||||||
# operator-run ('rig bootstrap workload' through 'box shell'), exactly the
|
# operator-run ('rig bootstrap workload-server' through 'box shell'), exactly the
|
||||||
# creds split box#69 designed.
|
# creds split box#69 designed.
|
||||||
# Convergent: safe to re-run; a second run changes nothing.
|
# Convergent: safe to re-run; a second run changes nothing.
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
@ -43,7 +43,7 @@ tenant on top, and re-runs converge an existing box to a new spec.
|
||||||
hardening. The tailnet workload join is deliberately
|
hardening. The tailnet workload join is deliberately
|
||||||
NOT here — it holds a credential, so it stays
|
NOT here — it holds a credential, so it stays
|
||||||
operator-run: `box shell` → `sudo rig bootstrap
|
operator-run: `box shell` → `sudo rig bootstrap
|
||||||
workload` with a tagged pre-auth key.
|
workload-server` with a tagged pre-auth key.
|
||||||
|
|
||||||
--user <name> the tenant user the box seed created (default: the
|
--user <name> the tenant user the box seed created (default: the
|
||||||
role's name; staging defaults to `ops`)
|
role's name; staging defaults to `ops`)
|
||||||
|
|
@ -77,10 +77,10 @@ while [ $# -gt 0 ]; do
|
||||||
# from the box seed, and the one trait-shaped thing a staging guest
|
# from the box seed, and the one trait-shaped thing a staging guest
|
||||||
# eventually does (join the tailnet as a workload) is deliberately not
|
# eventually does (join the tailnet as a workload) is deliberately not
|
||||||
# here: it holds a credential, so it stays operator-run.
|
# here: it holds a credential, so it stays operator-run.
|
||||||
die "tenant roles have no traits: $1 belongs to the machine roles (control-plane|workload|runner|dev|workstation|custom). A tenant box's shape comes from its seed; staging's tailnet join is operator-run via 'rig bootstrap workload'." 2 ;;
|
die "tenant roles have no traits: $1 belongs to the machine roles (control-plane-server|workload-server|runner-server|staging-server|dev-server|workstation|custom). A tenant box's shape comes from its seed; staging's tailnet join is operator-run via 'rig bootstrap workload-server'. The METAL that hosts these guests is 'rig bootstrap staging-server'." 2 ;;
|
||||||
--ts-tag)
|
--ts-tag)
|
||||||
[ $# -ge 2 ] && shift
|
[ $# -ge 2 ] && shift
|
||||||
die "--ts-tag is gone and tenant roles never join the tailnet anyway. staging's join is operator-run via 'rig bootstrap workload', where the tag comes from the pre-auth key." 2 ;;
|
die "--ts-tag is gone and tenant roles never join the tailnet anyway. staging's join is operator-run via 'rig bootstrap workload-server', where the tag comes from the pre-auth key." 2 ;;
|
||||||
*) die "unknown flag: $1" 2 ;;
|
*) die "unknown flag: $1" 2 ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
@ -384,7 +384,7 @@ fi
|
||||||
|
|
||||||
log "done — tenant ${ROLE}, user ${TENANT_USER}"
|
log "done — tenant ${ROLE}, user ${TENANT_USER}"
|
||||||
if [ "$ROLE" = "staging" ]; then
|
if [ "$ROLE" = "staging" ]; then
|
||||||
log "next (operator-run, holds a credential): box shell → sudo rig bootstrap workload --hostname <name> with a tagged pre-auth key"
|
log "next (operator-run, holds a credential): box shell → sudo rig bootstrap workload-server --hostname <name> with a tagged pre-auth key"
|
||||||
else
|
else
|
||||||
log "next: creds stay with the operator — ${CLI} authenticates through its own interactive login when a human decides"
|
log "next: creds stay with the operator — ${CLI} authenticates through its own interactive login when a human decides"
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
30
test/cli.sh
30
test/cli.sh
|
|
@ -93,6 +93,19 @@ check "bootstrap: workstation keeps its bare name" 2 "unset TS_AUTHKEY" \
|
||||||
env TS_AUTHKEY=x "$ROOT/commands/bootstrap.sh" workstation
|
env TS_AUTHKEY=x "$ROOT/commands/bootstrap.sh" workstation
|
||||||
check "bootstrap: custom keeps its bare name" 2 "--hostname" \
|
check "bootstrap: custom keeps its bare name" 2 "--hostname" \
|
||||||
"$ROOT/commands/bootstrap.sh" custom --class server --host no --join authkey
|
"$ROOT/commands/bootstrap.sh" custom --class server --host no --join authkey
|
||||||
|
# NOTHING may still TELL an operator to run a pre-#76 role. The rename is a
|
||||||
|
# hard cut, so a next-step string, a usage line or a refusal that still recites
|
||||||
|
# a bare role name is a command that fails when someone copy-pastes it — and it
|
||||||
|
# fails later and further from the cause than a broken flag would, because it
|
||||||
|
# fails on a different box, minutes after this run reported success. The tenant
|
||||||
|
# script is the one that emits the staging guest's workload-join next step, so
|
||||||
|
# it is where this bites first (caught in review on #80, fixed here where the
|
||||||
|
# rename actually happens). Swept across every shipped script rather than
|
||||||
|
# asserted at the one known site: the next instance of this will be somewhere
|
||||||
|
# else, and a site-specific check would not see it.
|
||||||
|
check "roles: no shipped script tells an operator to run a pre-#76 role name" 1 "" \
|
||||||
|
grep -rnE "rig bootstrap (control-plane|workload|runner|dev)( |'|\"|$)" \
|
||||||
|
"$ROOT/bin/rig" "$ROOT/commands/"
|
||||||
# --- traits: roles are presets, every trait individually settable (#26) -----
|
# --- traits: roles are presets, every trait individually settable (#26) -----
|
||||||
check "bootstrap: unknown role still exits 2" 2 "unknown role" "$ROOT/commands/bootstrap.sh" potato
|
check "bootstrap: unknown role still exits 2" 2 "unknown role" "$ROOT/commands/bootstrap.sh" potato
|
||||||
check "bootstrap: bad --class value exits 2" 2 "human|server" "$ROOT/commands/bootstrap.sh" workload-server --class potato
|
check "bootstrap: bad --class value exits 2" 2 "human|server" "$ROOT/commands/bootstrap.sh" workload-server --class potato
|
||||||
|
|
@ -624,6 +637,12 @@ MARKER_FIX="$(mktemp -d)"
|
||||||
printf 'role=workload-server class=server host=no join=authkey\n' > "$MARKER_FIX/workload"
|
printf 'role=workload-server class=server host=no join=authkey\n' > "$MARKER_FIX/workload"
|
||||||
printf 'role=control-plane-server class=server host=no join=authkey\n' > "$MARKER_FIX/control-plane"
|
printf 'role=control-plane-server class=server host=no join=authkey\n' > "$MARKER_FIX/control-plane"
|
||||||
printf 'role=control-plane-server\n' > "$MARKER_FIX/bare-control-plane"
|
printf 'role=control-plane-server\n' > "$MARKER_FIX/bare-control-plane"
|
||||||
|
# A PRE-#76 marker, verbatim as a real box bootstrapped before the rename
|
||||||
|
# carries it. This is the one fixture that must keep its old spelling: the
|
||||||
|
# CHANGELOG promises such a box takes the warning branch and keeps working,
|
||||||
|
# and until this existed nothing asserted it — every other fixture here was
|
||||||
|
# renamed with the code, so the migration story was documented and untested.
|
||||||
|
printf 'role=control-plane class=server host=no join=authkey\n' > "$MARKER_FIX/pre-rename-cp"
|
||||||
if [ "$(id -u)" -ne 0 ]; then
|
if [ "$(id -u)" -ne 0 ]; then
|
||||||
check "coolify: warns on a non-control-plane marker" 0 "1" \
|
check "coolify: warns on a non-control-plane marker" 0 "1" \
|
||||||
marker_warns "$MARKER_FIX/workload" "$ROOT/commands/coolify-install.sh" --version 4.1.2
|
marker_warns "$MARKER_FIX/workload" "$ROOT/commands/coolify-install.sh" --version 4.1.2
|
||||||
|
|
@ -635,6 +654,17 @@ if [ "$(id -u)" -ne 0 ]; then
|
||||||
marker_warns "$MARKER_FIX/bare-control-plane" "$ROOT/commands/coolify-install.sh" --version 4.1.2
|
marker_warns "$MARKER_FIX/bare-control-plane" "$ROOT/commands/coolify-install.sh" --version 4.1.2
|
||||||
check "coolify: absent marker stays silent (advisory, not a gate)" 0 "0" \
|
check "coolify: absent marker stays silent (advisory, not a gate)" 0 "0" \
|
||||||
marker_warns "$MARKER_FIX/absent" "$ROOT/commands/coolify-install.sh" --version 4.1.2
|
marker_warns "$MARKER_FIX/absent" "$ROOT/commands/coolify-install.sh" --version 4.1.2
|
||||||
|
# The migration story, pinned in both halves: a pre-#76 control plane WARNS
|
||||||
|
# (its marker no longer names a role that exists) but is never refused. Both
|
||||||
|
# halves matter — a rename that turned this into a refusal would break the
|
||||||
|
# exact boxes the CHANGELOG promises keep working, and it would do it on the
|
||||||
|
# command that installs the control plane.
|
||||||
|
check "coolify: a PRE-#76 'role=control-plane' marker warns (migration)" 0 "1" \
|
||||||
|
marker_warns "$MARKER_FIX/pre-rename-cp" "$ROOT/commands/coolify-install.sh" --version 4.1.2
|
||||||
|
check "coolify: ...and is still never refused" 1 "must run as root" \
|
||||||
|
env RIG_ROLE_MARKER="$MARKER_FIX/pre-rename-cp" "$ROOT/commands/coolify-install.sh" --version 4.1.2
|
||||||
|
check "coolify backup: a PRE-#76 'role=control-plane' marker warns (migration)" 0 "1" \
|
||||||
|
marker_warns "$MARKER_FIX/pre-rename-cp" "$ROOT/commands/coolify-backup-install.sh"
|
||||||
# The warning must stay a warning: the run proceeds past it and stops at the
|
# The warning must stay a warning: the run proceeds past it and stops at the
|
||||||
# root check (exit 1), never turned into a marker refusal.
|
# root check (exit 1), never turned into a marker refusal.
|
||||||
check "coolify: the marker warns but never refuses" 1 "must run as root" \
|
check "coolify: the marker warns but never refuses" 1 "must run as root" \
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue