feat: add guarded bootstrap undo
This commit is contained in:
parent
9cc7568ca5
commit
8b4a55a712
4 changed files with 61 additions and 2 deletions
|
|
@ -13,6 +13,7 @@ on the way to cutting its first release, and this file starts there.
|
||||||
- `rig platform` prints a stable machine `ID`, derived from `/etc/machine-id`, never the raw value (#95)
|
- `rig platform` prints a stable machine `ID`, derived from `/etc/machine-id`, never the raw value (#95)
|
||||||
- Platform, drill, docs and labels changes receive dedicated scope labels (#119)
|
- Platform, drill, docs and labels changes receive dedicated scope labels (#119)
|
||||||
- CI drills the install lifecycle against a real tree — install from the checkout, converge to an empty diff, uninstall to proven absence (#106)
|
- CI drills the install lifecycle against a real tree — install from the checkout, converge to an empty diff, uninstall to proven absence (#106)
|
||||||
|
- `rig bootstrap --undo` removes only a tailnet join rig can prove it made (#63)
|
||||||
- `kimi-box` joins the box tenant roles — the Kimi CLI agent guest (#109)
|
- `kimi-box` joins the box tenant roles — the Kimi CLI agent guest (#109)
|
||||||
- The `changelog-armed` guard returns, version-keyed (#112, ceremony#13)
|
- The `changelog-armed` guard returns, version-keyed (#112, ceremony#13)
|
||||||
- The `.ceremony/` doctrine mirror, verified by `docs-sync` on every PR (#112, ceremony#19)
|
- The `.ceremony/` doctrine mirror, verified by `docs-sync` on every PR (#112, ceremony#19)
|
||||||
|
|
|
||||||
4
bin/rig
4
bin/rig
|
|
@ -33,6 +33,10 @@ commands:
|
||||||
tenant roles); custom and workstation take none. join=login
|
tenant roles); custom and workstation take none. join=login
|
||||||
(workstation) needs no key: interactive login, node must come up
|
(workstation) needs no key: interactive login, node must come up
|
||||||
untagged. Run as root.
|
untagged. Run as root.
|
||||||
|
bootstrap --undo
|
||||||
|
Leave the tailnet only when /etc/rig/role proves rig performed the
|
||||||
|
join, then remove that marker. Refuses for pre-existing or old unknown
|
||||||
|
joins, and while a GitHub runner is installed. Run as root.
|
||||||
bootstrap <claude-box|codex-box|grok-box|kimi-box|staging-box> [--user <name>]
|
bootstrap <claude-box|codex-box|grok-box|kimi-box|staging-box> [--user <name>]
|
||||||
The box TENANT roles: converge a box-minted guest. The '-box' suffix
|
The box TENANT roles: converge a box-minted guest. The '-box' suffix
|
||||||
names the family (a guest, vs the '-server' machine roles above).
|
names the family (a guest, vs the '-server' machine roles above).
|
||||||
|
|
|
||||||
43
commands/bootstrap-undo.sh
Executable file
43
commands/bootstrap-undo.sh
Executable file
|
|
@ -0,0 +1,43 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# rig bootstrap --undo — remove only off-box state rig can prove it created.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
log() { printf 'rig-bootstrap: %s\n' "$*"; }
|
||||||
|
die() { printf 'rig-bootstrap: ERROR: %s\n' "$*" >&2; exit 1; }
|
||||||
|
|
||||||
|
MARKER="${RIG_ROLE_MARKER:-/etc/rig/role}"
|
||||||
|
RUNNER_DIR="${RIG_RUNNER_DIR:-/home/github-runner/actions-runner}"
|
||||||
|
|
||||||
|
[ "$(id -u)" -eq 0 ] || die "must run as root"
|
||||||
|
[ -e "$MARKER" ] || die "no /etc/rig/role marker — refusing to touch the tailnet"
|
||||||
|
|
||||||
|
if [ -e "$RUNNER_DIR/.runner" ]; then
|
||||||
|
die "a GitHub runner is installed — run 'rig runner remove' first so undo does not leave a ghost runner in the repository"
|
||||||
|
fi
|
||||||
|
|
||||||
|
join_by=""
|
||||||
|
while IFS= read -r field; do
|
||||||
|
case "$field" in
|
||||||
|
join-by=*) join_by="${field#join-by=}" ;;
|
||||||
|
esac
|
||||||
|
done < <(tr '[:space:]' '\n' < "$MARKER")
|
||||||
|
|
||||||
|
case "$join_by" in
|
||||||
|
rig) ;;
|
||||||
|
preexisting)
|
||||||
|
die "the tailnet join predates this bootstrap run (join-by=preexisting), so rig will not remove state it did not create; run 'tailscale logout' by hand if that is intended" ;;
|
||||||
|
"")
|
||||||
|
die "the role marker predates join-by provenance, so rig cannot prove it made this tailnet join and will not remove it; re-run bootstrap to write a current marker, or run 'tailscale logout' by hand" ;;
|
||||||
|
*)
|
||||||
|
die "the role marker has unknown join-by=$join_by, so rig cannot prove it made this tailnet join and will not remove it; run 'tailscale logout' by hand if that is intended" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# The same back-out/keep law as first-join verification: logout is earned only
|
||||||
|
# when the marker proves rig performed the join. Preserve the marker on failure
|
||||||
|
# so the operation remains retryable and never reports a half-undone machine.
|
||||||
|
if ! tailscale logout; then
|
||||||
|
die "tailscale logout failed; role marker kept so 'rig bootstrap --undo' can be retried"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f -- "$MARKER"
|
||||||
|
log "tailnet join removed; role marker removed"
|
||||||
|
|
@ -32,6 +32,9 @@ usage: rig bootstrap <control-plane-server|workload-server|runner-server|
|
||||||
rig bootstrap <claude-box|codex-box|grok-box|kimi-box|staging-box> [--user <name>]
|
rig bootstrap <claude-box|codex-box|grok-box|kimi-box|staging-box> [--user <name>]
|
||||||
(the box TENANT roles — see their own --help; they take
|
(the box TENANT roles — see their own --help; they take
|
||||||
no --users, see below)
|
no --users, see below)
|
||||||
|
rig bootstrap --undo
|
||||||
|
leave the tailnet only when the role marker proves rig
|
||||||
|
performed the join, then remove the role marker
|
||||||
|
|
||||||
--users the users file this box's operators come from — REQUIRED. It is
|
--users the users file this box's operators come from — REQUIRED. It is
|
||||||
applied as bootstrap's last phase, exactly as `rig users apply
|
applied as bootstrap's last phase, exactly as `rig users apply
|
||||||
|
|
@ -114,6 +117,10 @@ EOF
|
||||||
# --- args (validated before the root check, so errors are testable) ---------
|
# --- args (validated before the root check, so errors are testable) ---------
|
||||||
ROLE="${1:-}"
|
ROLE="${1:-}"
|
||||||
case "$ROLE" in
|
case "$ROLE" in
|
||||||
|
--undo)
|
||||||
|
shift
|
||||||
|
[ $# -eq 0 ] || die "bootstrap --undo takes no arguments" 2
|
||||||
|
exec "$HERE/bootstrap-undo.sh" ;;
|
||||||
control-plane-server|workload-server|runner-server|staging-server|dev-server|workstation|custom) shift ;;
|
control-plane-server|workload-server|runner-server|staging-server|dev-server|workstation|custom) shift ;;
|
||||||
claude-box|codex-box|grok-box|kimi-box|staging-box)
|
claude-box|codex-box|grok-box|kimi-box|staging-box)
|
||||||
# The box TENANT roles (#31) are a different family — guests a box mints,
|
# The box TENANT roles (#31) are a different family — guests a box mints,
|
||||||
|
|
@ -534,6 +541,7 @@ if ! command -v tailscale >/dev/null 2>&1; then
|
||||||
log "installing tailscale"
|
log "installing tailscale"
|
||||||
curl -fsSL https://tailscale.com/install.sh | sh
|
curl -fsSL https://tailscale.com/install.sh | sh
|
||||||
fi
|
fi
|
||||||
|
JOIN_BY=preexisting
|
||||||
if tailscale status >/dev/null 2>&1; then
|
if tailscale status >/dev/null 2>&1; then
|
||||||
log "tailnet already joined; skipping tailscale up (no pre-auth key needed)"
|
log "tailnet already joined; skipping tailscale up (no pre-auth key needed)"
|
||||||
# ...but skipping `tailscale up` also skipped --hostname, so the TAILNET name
|
# ...but skipping `tailscale up` also skipped --hostname, so the TAILNET name
|
||||||
|
|
@ -574,6 +582,7 @@ elif [ "$JOIN" = "login" ]; then
|
||||||
log "joining tailnet as ${TS_HOSTNAME} (interactive login; follow the URL tailscale prints)"
|
log "joining tailnet as ${TS_HOSTNAME} (interactive login; follow the URL tailscale prints)"
|
||||||
tailscale up --hostname="$TS_HOSTNAME"
|
tailscale up --hostname="$TS_HOSTNAME"
|
||||||
verify_user_owned back-out
|
verify_user_owned back-out
|
||||||
|
JOIN_BY=rig
|
||||||
else
|
else
|
||||||
# env override, else prompt; never touches disk. The prompt only fires on a
|
# env override, else prompt; never touches disk. The prompt only fires on a
|
||||||
# tty: with no terminal, a bare `read` exits non-zero and `set -e` would end
|
# tty: with no terminal, a bare `read` exits non-zero and `set -e` would end
|
||||||
|
|
@ -594,6 +603,7 @@ else
|
||||||
log "joining tailnet as ${TS_HOSTNAME} (tag comes from the pre-auth key)"
|
log "joining tailnet as ${TS_HOSTNAME} (tag comes from the pre-auth key)"
|
||||||
tailscale up --authkey="$TS_AUTHKEY" --hostname="$TS_HOSTNAME"
|
tailscale up --authkey="$TS_AUTHKEY" --hostname="$TS_HOSTNAME"
|
||||||
verify_effective_tag back-out
|
verify_effective_tag back-out
|
||||||
|
JOIN_BY=rig
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- role marker --------------------------------------------------------------
|
# --- role marker --------------------------------------------------------------
|
||||||
|
|
@ -611,11 +621,12 @@ fi
|
||||||
# those exist in the field by the thousand and nothing will rewrite them.
|
# those exist in the field by the thousand and nothing will rewrite them.
|
||||||
MARKER=/etc/rig/role
|
MARKER=/etc/rig/role
|
||||||
MARKER_TMP="$(mktemp)"
|
MARKER_TMP="$(mktemp)"
|
||||||
printf 'role=%s root-door=%s host=%s join=%s\n' "$ROLE" "$ROOT_DOOR" "$HOST" "$JOIN" > "$MARKER_TMP"
|
printf 'role=%s root-door=%s host=%s join=%s join-by=%s\n' \
|
||||||
|
"$ROLE" "$ROOT_DOOR" "$HOST" "$JOIN" "$JOIN_BY" > "$MARKER_TMP"
|
||||||
if ! cmp -s "$MARKER_TMP" "$MARKER" 2>/dev/null; then
|
if ! cmp -s "$MARKER_TMP" "$MARKER" 2>/dev/null; then
|
||||||
mkdir -p /etc/rig
|
mkdir -p /etc/rig
|
||||||
install -m 0644 "$MARKER_TMP" "$MARKER"
|
install -m 0644 "$MARKER_TMP" "$MARKER"
|
||||||
log "role marker written: role=$ROLE root-door=$ROOT_DOOR host=$HOST join=$JOIN"
|
log "role marker written: role=$ROLE root-door=$ROOT_DOOR host=$HOST join=$JOIN join-by=$JOIN_BY"
|
||||||
else
|
else
|
||||||
log "role marker already current"
|
log "role marker already current"
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue