fix: arm cron on agent tenant boxes
Agent tenants exist to run the cron-driven duty engine, and the engine's installer runs unprivileged — it can detect a missing cron but never apt-get it. Install cron with the shared toolbelt, then assert the effective state, not the package: crontab on PATH AND cron.service enabled and active, converging (unmask/enable/start) best-effort first. A masked daemon with the binary present is exactly the silent-inert box the issue reports, so the service asserts are the authority and either failing dies naming cron. staging-box stays exempt with the rest of the agent-assert block: no agent, no duty engine. Machine roles (bootstrap.sh) are deliberately not widened — the issue names tenants as the demonstrated gap. Closes #162 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
34ff1c8917
commit
29c4a0840b
3 changed files with 37 additions and 1 deletions
3
changelog.d/162.md
Normal file
3
changelog.d/162.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
### Fixed
|
||||
|
||||
- Agent tenant boxes ship cron — binary asserted, service enabled and active — so the duty engine can arm its timer (#162)
|
||||
|
|
@ -246,8 +246,11 @@ else
|
|||
# The shared agent toolbelt the templates carried, plus the definition's
|
||||
# APT_EXTRAS (claude-box's zsh rides there). Unquoted on purpose — it is a
|
||||
# word list, every word already vetted by the parser's package-name gate.
|
||||
# cron is toolbelt, not a template flavour: every agent tenant exists to
|
||||
# run the cron-driven duty engine, whose unprivileged installer can detect
|
||||
# a missing cron but never apt-get it (#162).
|
||||
# shellcheck disable=SC2086
|
||||
apt-get install -y -qq git gh curl ca-certificates gnupg ripgrep jq tmux age unzip build-essential $TPL_APT_EXTRAS
|
||||
apt-get install -y -qq git gh curl ca-certificates gnupg ripgrep jq tmux age unzip build-essential cron $TPL_APT_EXTRAS
|
||||
fi
|
||||
# Assert the effective toolbelt, not apt's exit code — tmux is the box#65
|
||||
# contract ('box tmux' runs tmux new-session inside every box) and gh is how
|
||||
|
|
@ -256,6 +259,24 @@ command -v tmux >/dev/null 2>&1 || die "tmux missing after package install — '
|
|||
if [ "$ROLE" != "staging-box" ]; then
|
||||
command -v gh >/dev/null 2>&1 || die "gh missing after package install"
|
||||
command -v git >/dev/null 2>&1 || die "git missing after package install"
|
||||
command -v crontab >/dev/null 2>&1 || die "crontab missing after package install — the duty engine arms itself with cron (#162)"
|
||||
# The binary on PATH is not the effective state — an image can ship crontab
|
||||
# with cron.service masked or stopped, and an unarmed timer is exactly the
|
||||
# silent-inert box #162 is about. Converge best-effort, then assert what
|
||||
# systemd actually reports; the assert is the authority. Enabling an
|
||||
# already-enabled unit is a no-op and no path here touches any crontab.
|
||||
# staging-box is exempt with the rest of this block: no agent, no engine.
|
||||
if ! systemctl is-enabled cron >/dev/null 2>&1; then
|
||||
systemctl unmask cron >/dev/null 2>&1 || true
|
||||
systemctl enable cron >/dev/null 2>&1 || true
|
||||
log "enabled cron.service"
|
||||
fi
|
||||
if ! systemctl is-active cron >/dev/null 2>&1; then
|
||||
systemctl start cron >/dev/null 2>&1 || true
|
||||
log "started cron.service"
|
||||
fi
|
||||
systemctl is-enabled cron >/dev/null 2>&1 || die "cron.service is not enabled after converge — the duty engine's timer never fires without it (#162)"
|
||||
systemctl is-active cron >/dev/null 2>&1 || die "cron.service is not active after converge — the duty engine's timer never fires without it (#162)"
|
||||
fi
|
||||
|
||||
# --- docker ------------------------------------------------------------------
|
||||
|
|
|
|||
12
test/cli.sh
12
test/cli.sh
|
|
@ -655,6 +655,18 @@ check "tenant: a crafted role name dies at the charset gate" 2 "invalid tenant r
|
|||
"$ROOT/commands/bootstrap-tenant.sh" 'UPPER-box'
|
||||
check "tenant: dockerd effective-state assert is present" 0 "" \
|
||||
grep -qF "docker info" "$ROOT/commands/bootstrap-tenant.sh"
|
||||
# The #162 contract, both halves: cron installs with the agent toolbelt (the
|
||||
# duty engine's unprivileged installer can never apt-get it), and PATH is not
|
||||
# the effective state — the service must be asserted enabled AND active, or a
|
||||
# masked daemon leaves every tenant crontab silently inert.
|
||||
check "tenant: cron rides the agent toolbelt install" 0 "" \
|
||||
grep -qE '^ *apt-get install .* cron ' "$ROOT/commands/bootstrap-tenant.sh"
|
||||
check "tenant: crontab toolbelt assert is present" 0 "" \
|
||||
grep -qF "command -v crontab" "$ROOT/commands/bootstrap-tenant.sh"
|
||||
check "tenant: cron.service enabled assert is present" 0 "" \
|
||||
grep -qF "systemctl is-enabled cron" "$ROOT/commands/bootstrap-tenant.sh"
|
||||
check "tenant: cron.service active assert is present" 0 "" \
|
||||
grep -qF "systemctl is-active cron" "$ROOT/commands/bootstrap-tenant.sh"
|
||||
# The machine-role traits die with the tenant story, never "unknown flag" — an
|
||||
# operator coming from the machine families needs the boundary, not a shrug.
|
||||
check "tenant: trait flags die with the tenant story" 2 "have no traits" \
|
||||
|
|
|
|||
Loading…
Reference in a new issue