forked from heavy-duty/rig
review r1: staging tolerates only the workload guest; dockerd must answer; one CLI capture
- The staging marker tolerance now says what the docs meant: class=server with host=no only. A non-server machine (class=human via custom) refuses with its own message instead of dying later inside harden_sshd with server-specific advice. Fixture pins the refusal. - The docker converge asserts the DAEMON answers (docker info, bounded 30s settle), not just the client binary — grep-pinned. - The agent-CLI version check is one capture serving assert and log; emptiness is the failure signal (head exits 0, a pipeline status can't be). - Harness gains the codex login-flow context grep alongside claude/grok. Verified: test/cli.sh 244/0, shellcheck -x clean, live container e2e (staging round 1 + convergence round 2, dockerd answering). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
e40e223ecb
commit
1cef6ed751
2 changed files with 38 additions and 7 deletions
|
|
@ -100,9 +100,12 @@ done
|
||||||
# Names the staging rename out loud — before #31, `staging` was the VM-host
|
# Names the staging rename out loud — before #31, `staging` was the VM-host
|
||||||
# PRESET; that shape is now spelled through the traits.
|
# PRESET; that shape is now spelled through the traits.
|
||||||
# - class= (agent tenants) → refuse: an agent box is never a tailnet machine.
|
# - class= (agent tenants) → refuse: an agent box is never a tailnet machine.
|
||||||
# - class= with host=no (staging only) → PROCEED, and leave the marker alone:
|
# - class=server with host=no (staging only) → PROCEED, and leave the marker
|
||||||
# that is the staging guest AFTER its operator-run workload join, and
|
# alone: that is the staging guest AFTER its operator-run workload join, and
|
||||||
# re-converging docker+hardening on it is exactly what convergence is for.
|
# re-converging docker+hardening on it is exactly what convergence is for.
|
||||||
|
# ONLY that shape — any other class (say class=human, via `custom`) is a
|
||||||
|
# machine rig built on purpose, and staging hardening it with server rules
|
||||||
|
# would die with server-specific messaging on a box that was never one.
|
||||||
MARKER_PATH="${RIG_ROLE_MARKER:-/etc/rig/role}"
|
MARKER_PATH="${RIG_ROLE_MARKER:-/etc/rig/role}"
|
||||||
EXISTING_MARKER="$(read_role_marker "$MARKER_PATH")"
|
EXISTING_MARKER="$(read_role_marker "$MARKER_PATH")"
|
||||||
case "$EXISTING_MARKER" in
|
case "$EXISTING_MARKER" in
|
||||||
|
|
@ -111,7 +114,12 @@ case "$EXISTING_MARKER" in
|
||||||
*class=*)
|
*class=*)
|
||||||
if [ "$ROLE" != "staging" ]; then
|
if [ "$ROLE" != "staging" ]; then
|
||||||
die "this box already carries a machine role (${EXISTING_MARKER}) — the agent tenants converge box guests, never tailnet machines. If this really is a guest, remove ${MARKER_PATH} and re-run."
|
die "this box already carries a machine role (${EXISTING_MARKER}) — the agent tenants converge box guests, never tailnet machines. If this really is a guest, remove ${MARKER_PATH} and re-run."
|
||||||
fi ;;
|
fi
|
||||||
|
case "$EXISTING_MARKER" in
|
||||||
|
*class=server*) ;;
|
||||||
|
*)
|
||||||
|
die "this box carries a non-server machine role (${EXISTING_MARKER}) — staging tolerates only the workload-joined guest (class=server host=no). If this really is a staging guest, remove ${MARKER_PATH} and re-run." ;;
|
||||||
|
esac ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
[ "$(id -u)" -eq 0 ] || die "must run as root"
|
[ "$(id -u)" -eq 0 ] || die "must run as root"
|
||||||
|
|
@ -185,6 +193,16 @@ else
|
||||||
log "docker already installed"
|
log "docker already installed"
|
||||||
fi
|
fi
|
||||||
docker --version >/dev/null 2>&1 || die "docker installed but 'docker --version' does not answer"
|
docker --version >/dev/null 2>&1 || die "docker installed but 'docker --version' does not answer"
|
||||||
|
# The client answering is not the effective state — a dead dockerd would still
|
||||||
|
# pass it. Ask the daemon, with a bounded settle for the freshly-installed case
|
||||||
|
# (get.docker.com starts it, but not instantaneously on a slow guest).
|
||||||
|
docker_up=""
|
||||||
|
for _ in 1 2 3 4 5 6; do
|
||||||
|
if docker info >/dev/null 2>&1; then docker_up=1; break; fi
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
[ -n "$docker_up" ] || die "dockerd does not answer 'docker info' after 30s — the daemon is not running; check 'systemctl status docker' (or the container's init) before re-running"
|
||||||
|
log "dockerd answering"
|
||||||
if getent group docker >/dev/null 2>&1; then
|
if getent group docker >/dev/null 2>&1; then
|
||||||
if id -nG "$TENANT_USER" | tr ' ' '\n' | grep -qx docker; then
|
if id -nG "$TENANT_USER" | tr ' ' '\n' | grep -qx docker; then
|
||||||
log "${TENANT_USER} already in the docker group"
|
log "${TENANT_USER} already in the docker group"
|
||||||
|
|
@ -260,9 +278,11 @@ esac
|
||||||
if [ -n "$CLI" ]; then
|
if [ -n "$CLI" ]; then
|
||||||
[ -e "$CLI_SRC" ] || die "the ${CLI} installer produced no ${CLI_SRC} — upstream layout changed?"
|
[ -e "$CLI_SRC" ] || die "the ${CLI} installer produced no ${CLI_SRC} — upstream layout changed?"
|
||||||
ln -sf "$CLI_SRC" "/usr/local/bin/$CLI"
|
ln -sf "$CLI_SRC" "/usr/local/bin/$CLI"
|
||||||
runuser -l "$TENANT_USER" -c "$CLI --version" >/dev/null 2>&1 \
|
# One capture serves both the assert and the log line; emptiness IS the
|
||||||
|| die "'$CLI --version' does not answer for ${TENANT_USER} — the CLI landed but cannot run; check /usr/local/bin/$CLI and its target"
|
# failure signal (head exits 0 regardless, so a pipeline status can't be).
|
||||||
log "${CLI} CLI on the system PATH and answering ($(runuser -l "$TENANT_USER" -c "$CLI --version" 2>/dev/null | head -n1))"
|
CLI_VER="$(runuser -l "$TENANT_USER" -c "$CLI --version" 2>/dev/null | head -n1)"
|
||||||
|
[ -n "$CLI_VER" ] || die "'$CLI --version' does not answer for ${TENANT_USER} — the CLI landed but cannot run; check /usr/local/bin/$CLI and its target"
|
||||||
|
log "${CLI} CLI on the system PATH and answering (${CLI_VER})"
|
||||||
|
|
||||||
# The interactive-shell PATH exports the templates carried, converged as
|
# The interactive-shell PATH exports the templates carried, converged as
|
||||||
# literal rc lines (written once, never duplicated). Single quotes are the
|
# literal rc lines (written once, never duplicated). Single quotes are the
|
||||||
|
|
|
||||||
13
test/cli.sh
13
test/cli.sh
|
|
@ -209,6 +209,11 @@ check "tenant: unknown role exits 2" 2 "unknown tenant role" "$ROOT/commands/
|
||||||
check "tenant: unknown flag exits 2" 2 "unknown flag" "$ROOT/commands/bootstrap-tenant.sh" claude --nope
|
check "tenant: unknown flag exits 2" 2 "unknown flag" "$ROOT/commands/bootstrap-tenant.sh" claude --nope
|
||||||
check "tenant: --user needs value" 2 "needs a value" "$ROOT/commands/bootstrap-tenant.sh" claude --user
|
check "tenant: --user needs value" 2 "needs a value" "$ROOT/commands/bootstrap-tenant.sh" claude --user
|
||||||
check "tenant: bad --user charset exits 2" 2 "invalid user" "$ROOT/commands/bootstrap-tenant.sh" claude --user 'fo|o'
|
check "tenant: bad --user charset exits 2" 2 "invalid user" "$ROOT/commands/bootstrap-tenant.sh" claude --user 'fo|o'
|
||||||
|
# The docker converge asserts the DAEMON answers, not just the client binary —
|
||||||
|
# a dead dockerd passing `docker --version` is the "linked but cannot run"
|
||||||
|
# scar in daemon form. Grep-pinned so the assert cannot ship deleted.
|
||||||
|
check "tenant: dockerd effective-state assert is present" 0 "" \
|
||||||
|
grep -qF "docker info" "$ROOT/commands/bootstrap-tenant.sh"
|
||||||
# The machine-role traits die with the tenant story, never "unknown flag" — an
|
# The machine-role traits die with the tenant story, never "unknown flag" — an
|
||||||
# operator reaching for --hostname must learn where the trait family went.
|
# operator reaching for --hostname must learn where the trait family went.
|
||||||
check "tenant: trait flags die with the tenant story" 2 "have no traits" \
|
check "tenant: trait flags die with the tenant story" 2 "have no traits" \
|
||||||
|
|
@ -224,12 +229,17 @@ check "bootstrap: tenant roles dispatch through bootstrap.sh" 0 "claude|codex|gr
|
||||||
# VM host (host=yes) refuses for every tenant — and names the staging rename,
|
# VM host (host=yes) refuses for every tenant — and names the staging rename,
|
||||||
# because a pre-#31 staging HOST re-running its old command is exactly who
|
# because a pre-#31 staging HOST re-running its old command is exactly who
|
||||||
# lands here. An agent tenant refuses ANY machine-role box; staging tolerates
|
# lands here. An agent tenant refuses ANY machine-role box; staging tolerates
|
||||||
# a class= marker with host=no — that is the staging guest after its
|
# ONLY class=server with host=no — that is the staging guest after its
|
||||||
# operator-run workload join, and re-converging it is what convergence is for.
|
# operator-run workload join, and re-converging it is what convergence is for.
|
||||||
|
# A non-server machine (class=human via custom) is NOT that guest, and server
|
||||||
|
# hardening would die at it with server-specific messaging — refuse instead.
|
||||||
TEN_FIX="$(mktemp -d)"
|
TEN_FIX="$(mktemp -d)"
|
||||||
printf 'role=dev class=human host=yes join=authkey\n' > "$TEN_FIX/host"
|
printf 'role=dev class=human host=yes join=authkey\n' > "$TEN_FIX/host"
|
||||||
printf 'role=workload class=server host=no join=authkey\n' > "$TEN_FIX/machine"
|
printf 'role=workload class=server host=no join=authkey\n' > "$TEN_FIX/machine"
|
||||||
|
printf 'role=custom class=human host=no join=login\n' > "$TEN_FIX/human"
|
||||||
printf 'role=claude tenant=yes host=no\n' > "$TEN_FIX/tenant"
|
printf 'role=claude tenant=yes host=no\n' > "$TEN_FIX/tenant"
|
||||||
|
check "tenant: staging refuses a non-server machine box" 1 "non-server machine role" \
|
||||||
|
env RIG_ROLE_MARKER="$TEN_FIX/human" "$ROOT/commands/bootstrap-tenant.sh" staging
|
||||||
check "tenant: refuses a host=yes box (a VM host is never a guest)" 1 "hosts VMs" \
|
check "tenant: refuses a host=yes box (a VM host is never a guest)" 1 "hosts VMs" \
|
||||||
env RIG_ROLE_MARKER="$TEN_FIX/host" "$ROOT/commands/bootstrap-tenant.sh" claude
|
env RIG_ROLE_MARKER="$TEN_FIX/host" "$ROOT/commands/bootstrap-tenant.sh" claude
|
||||||
check "tenant: the host refusal names the old staging preset's new spelling" 1 "custom --class server --host yes" \
|
check "tenant: the host refusal names the old staging preset's new spelling" 1 "custom --class server --host yes" \
|
||||||
|
|
@ -279,6 +289,7 @@ check "tenant context: the guard says whose host this is not" 0 "not a host you
|
||||||
check "tenant context: the guard cites box#80" 0 "box#80" tctx claude
|
check "tenant context: the guard cites box#80" 0 "box#80" tctx claude
|
||||||
check "tenant context: the creds-free contract is stated" 0 "Creds-free by default" tctx claude
|
check "tenant context: the creds-free contract is stated" 0 "Creds-free by default" tctx claude
|
||||||
check "tenant context: claude names /login as the operator's flow" 0 "/login" tctx claude
|
check "tenant context: claude names /login as the operator's flow" 0 "/login" tctx claude
|
||||||
|
check "tenant context: codex names its login flow" 0 "login flow (\`codex\`)" tctx codex
|
||||||
check "tenant context: grok names its login flow" 0 "grok login" tctx grok
|
check "tenant context: grok names its login flow" 0 "grok login" tctx grok
|
||||||
check "tenant context: staging renders nothing (no agent lives there)" 1 "" tctx staging
|
check "tenant context: staging renders nothing (no agent lives there)" 1 "" tctx staging
|
||||||
# Creds-free BY CONSTRUCTION, provable by absence (box#69's grep-refusal
|
# Creds-free BY CONSTRUCTION, provable by absence (box#69's grep-refusal
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue