Merge pull request #156 from codex-bot-andresmgsl/build/152-machine-role-template
feat: add registry-backed machine roles
This commit is contained in:
commit
2af3b24fd8
6 changed files with 234 additions and 20 deletions
28
README.md
28
README.md
|
|
@ -401,6 +401,34 @@ unattended VM-host appliance) — and `workstation` is the machine at the keyboa
|
|||
end of all the SSH connections: `root-door=closed`, `join=login`, entering the
|
||||
tailnet as *your* device rather than the fleet's.
|
||||
|
||||
### Machine-role templates
|
||||
|
||||
Machine presets can also live in the
|
||||
[heavy-duty/rig-templates](https://github.com/heavy-duty/rig-templates)
|
||||
registry. A `*-server` directory is a fleet-machine definition; the exact
|
||||
name `workstation` is the deliberate suffix-less exception. Its
|
||||
`template.env` contains exactly the three traits bootstrap's built-in table
|
||||
uses:
|
||||
|
||||
```dotenv
|
||||
ROOT_DOOR="open" # open|closed
|
||||
HOST="no" # yes|no
|
||||
JOIN="authkey" # authkey|login
|
||||
```
|
||||
|
||||
An `install.sh` is optional. When present, bootstrap runs it as root,
|
||||
non-interactively, from the definition directory with `RIG_ROLE` set, after
|
||||
the tailnet join, host setup, role marker prerequisites, and operator
|
||||
convergence. A nonzero exit fails bootstrap and names the role and registry
|
||||
source. The definition owns idempotence, just as bootstrap does.
|
||||
|
||||
Built-in roles and `custom` take precedence over registry names. Any other
|
||||
non-tenant role is looked up in the resolved registry; the same three source
|
||||
knobs below apply, including `RIG_TEMPLATES_DIR` for an offline local
|
||||
definition. Pin reviewed registry content into rig's tree before using it on
|
||||
fleet machines: an optional machine `install.sh` executes as root on metal,
|
||||
and an override is the operator explicitly choosing a different trust root.
|
||||
|
||||
### `rig bootstrap <role>-box` — the box tenants
|
||||
|
||||
Run as root, **inside** a [box](https://github.com/heavy-duty/box)-minted
|
||||
|
|
|
|||
3
changelog.d/152.md
Normal file
3
changelog.d/152.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
### Added
|
||||
|
||||
- Machine-role templates can declare bootstrap traits and an optional final root install hook (#152)
|
||||
|
|
@ -12,6 +12,8 @@ HERE="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)"
|
|||
. "$HERE/lib/users-config.sh" # parse_users_file — the --users PRE-FLIGHT only
|
||||
# shellcheck source=SCRIPTDIR/lib/manifest.sh
|
||||
. "$HERE/lib/manifest.sh" # manifest_stamp — provenance, written beside the marker
|
||||
# shellcheck source=SCRIPTDIR/lib/templates.sh
|
||||
. "$HERE/lib/templates.sh" # registry-backed machine-role definitions
|
||||
# The users lib is sourced for validation, never for convergence: `users apply`
|
||||
# stays the single owner of what a users file DOES to a box (#51). Bootstrap
|
||||
# borrows the parser so a typo'd users file is caught in the same breath as a
|
||||
|
|
@ -119,6 +121,7 @@ EOF
|
|||
|
||||
# --- args (validated before the root check, so errors are testable) ---------
|
||||
ROLE="${1:-}"
|
||||
MACHINE_TEMPLATE_DIR=""
|
||||
case "$ROLE" in
|
||||
--undo)
|
||||
shift
|
||||
|
|
@ -136,7 +139,20 @@ case "$ROLE" in
|
|||
exec "$HERE/bootstrap-tenant.sh" "$@" ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
"") usage >&2; die "role required (control-plane-server|workload-server|runner-server|staging-server|dev-server|workstation|custom — or a '-box' tenant role from the template registry, e.g. claude-box)" 2 ;;
|
||||
*) die "unknown role: $ROLE (want control-plane-server|workload-server|runner-server|staging-server|dev-server|workstation|custom — or a '-box' tenant role from the template registry, e.g. claude-box)" 2 ;;
|
||||
*)
|
||||
shift
|
||||
templates_resolve || exit 2
|
||||
trap '[ -n "$TEMPLATES_TMP" ] && rm -rf "$TEMPLATES_TMP"' EXIT
|
||||
MACHINE_TEMPLATE_DIR="$REGISTRY_DIR/$ROLE"
|
||||
if [[ ! "$ROLE" =~ ^[a-z][a-z0-9-]*-server$ ]] \
|
||||
|| [ "$(template_family "$ROLE" 2>/dev/null || true)" != "machine" ] \
|
||||
|| [ ! -f "$MACHINE_TEMPLATE_DIR/template.env" ]; then
|
||||
MACHINE_ROLES="$(templates_machine_roles "$REGISTRY_DIR" | paste -sd'|' -)"
|
||||
[ -n "$MACHINE_ROLES" ] || MACHINE_ROLES="none"
|
||||
die "unknown role: $ROLE (want control-plane-server|workload-server|runner-server|staging-server|dev-server|workstation|custom; machine roles from $(templates_source_desc): $MACHINE_ROLES; or a '-box' tenant role)" 2
|
||||
fi
|
||||
machine_template_parse_env "$MACHINE_TEMPLATE_DIR/template.env" \
|
||||
|| die "invalid machine role $ROLE from $(templates_source_desc)" 2 ;;
|
||||
esac
|
||||
|
||||
# Role→traits map — the single place a role's shape is declared (issue #26).
|
||||
|
|
@ -156,6 +172,7 @@ case "$ROLE" in
|
|||
dev-server) ROOT_DOOR=closed HOST=yes JOIN=authkey ;;
|
||||
workstation) ROOT_DOOR=closed HOST=yes JOIN=login ;;
|
||||
custom) ;;
|
||||
*) ROOT_DOOR="$TPL_ROOT_DOOR" HOST="$TPL_HOST" JOIN="$TPL_JOIN" ;;
|
||||
esac
|
||||
|
||||
# custom has no hostname default: a made-up name on a made-up shape helps nobody.
|
||||
|
|
@ -784,6 +801,17 @@ if [ -n "$USERS_FILE" ]; then
|
|||
"$HERE/users-apply.sh" --file "$USERS_FILE"
|
||||
fi
|
||||
|
||||
# A registry machine's optional install is the final convergence phase: after
|
||||
# join, host setup, the marker prerequisites, and operators. It inherits the
|
||||
# caller environment, adds only the selected role, and runs from its definition
|
||||
# directory. Definitions own idempotence, like bootstrap itself.
|
||||
if [ -n "$MACHINE_TEMPLATE_DIR" ] && [ -e "$MACHINE_TEMPLATE_DIR/install.sh" ]; then
|
||||
log "running install hook for ${ROLE} from $(templates_source_desc)"
|
||||
if ! (cd "$MACHINE_TEMPLATE_DIR" && RIG_ROLE="$ROLE" bash ./install.sh); then
|
||||
die "install hook failed for role $ROLE from $(templates_source_desc)"
|
||||
fi
|
||||
fi
|
||||
|
||||
log "done — role ${ROLE}, hostname ${TS_HOSTNAME}"
|
||||
if [ "$ROLE" = "control-plane-server" ]; then
|
||||
log "next: rig coolify install --version <pin>"
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ RIG_TEMPLATES_PIN=be749f7fd1ff8dd7c2359bbce7fd6abd3f403eb0
|
|||
# KEY="value" — nothing else. Parsed by regex, never sourced.
|
||||
TEMPLATE_KEYS_REQUIRED=(USER CONTEXT_PATH CLI_NAME PATH_LINE)
|
||||
TEMPLATE_KEYS_OPTIONAL=(CLI_SRC NEEDS_NODE APT_EXTRAS)
|
||||
MACHINE_KEYS_REQUIRED=(ROOT_DOOR HOST JOIN)
|
||||
|
||||
# templates_source_desc — where the resolved registry came from, for error
|
||||
# messages and logs: a misconfigured RIG_TEMPLATES_REPO must be visible in
|
||||
|
|
@ -128,6 +129,26 @@ templates_roles() {
|
|||
done
|
||||
}
|
||||
|
||||
# template_family <role> — directory names are the registry's family tag.
|
||||
# workstation is the one intentional suffix-less machine role (#152 / epic D5).
|
||||
template_family() {
|
||||
case "$1" in
|
||||
*-box) printf 'tenant\n' ;;
|
||||
*-server|workstation) printf 'machine\n' ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# templates_machine_roles <registry-dir> — only machine definitions, for the
|
||||
# machine bootstrap's unknown-role refusal.
|
||||
templates_machine_roles() {
|
||||
local role
|
||||
while IFS= read -r role; do
|
||||
[ "$(template_family "$role" 2>/dev/null || true)" = "machine" ] || continue
|
||||
printf '%s\n' "$role"
|
||||
done < <(templates_roles "$1")
|
||||
}
|
||||
|
||||
# template_parse_env <template.env> — parse against the allowlist. Sets
|
||||
# TPL_USER, TPL_CONTEXT_PATH, TPL_CLI_NAME, TPL_CLI_SRC, TPL_PATH_LINE,
|
||||
# TPL_NEEDS_NODE (default no), TPL_APT_EXTRAS. Every refusal names the
|
||||
|
|
@ -206,6 +227,62 @@ template_parse_env() {
|
|||
done
|
||||
}
|
||||
|
||||
# machine_template_parse_env <template.env> — the fleet-machine traits schema.
|
||||
# The globals match bootstrap's table columns so a definition becomes a table
|
||||
# row without changing any downstream trait behavior.
|
||||
# shellcheck disable=SC2034
|
||||
machine_template_parse_env() {
|
||||
local file="$1" line key val n=0 seen=" " k ok
|
||||
TPL_ROOT_DOOR="" TPL_HOST="" TPL_JOIN=""
|
||||
[ -f "$file" ] || { printf 'template.env missing: %s\n' "$file" >&2; return 1; }
|
||||
while IFS= read -r line || [ -n "$line" ]; do
|
||||
n=$((n+1))
|
||||
case "$line" in ''|'#'*) continue ;; esac
|
||||
if [[ ! "$line" =~ ^([A-Z_]+)=\"(.*)\"$ ]]; then
|
||||
printf 'template.env:%d: not KEY="value": %s\n' "$n" "$line" >&2
|
||||
return 1
|
||||
fi
|
||||
key="${BASH_REMATCH[1]}" val="${BASH_REMATCH[2]}"
|
||||
ok=""
|
||||
for k in "${MACHINE_KEYS_REQUIRED[@]}"; do
|
||||
[ "$key" = "$k" ] && ok=1
|
||||
done
|
||||
[ -n "$ok" ] || {
|
||||
printf 'template.env:%d: unknown key: %s (allowed: %s)\n' \
|
||||
"$n" "$key" "${MACHINE_KEYS_REQUIRED[*]}" >&2
|
||||
return 1
|
||||
}
|
||||
case "$seen" in *" $key "*)
|
||||
printf 'template.env:%d: duplicate key: %s\n' "$n" "$key" >&2
|
||||
return 1 ;;
|
||||
esac
|
||||
seen="$seen$key "
|
||||
case "$key" in
|
||||
ROOT_DOOR) TPL_ROOT_DOOR="$val" ;;
|
||||
HOST) TPL_HOST="$val" ;;
|
||||
JOIN) TPL_JOIN="$val" ;;
|
||||
esac
|
||||
done < "$file"
|
||||
for k in "${MACHINE_KEYS_REQUIRED[@]}"; do
|
||||
case "$seen" in *" $k "*) ;; *)
|
||||
printf 'template.env: missing required key: %s\n' "$k" >&2
|
||||
return 1 ;;
|
||||
esac
|
||||
done
|
||||
case "$TPL_ROOT_DOOR" in
|
||||
open|closed) ;;
|
||||
*) printf 'template.env: ROOT_DOOR: want open or closed, got: %s\n' "$TPL_ROOT_DOOR" >&2; return 1 ;;
|
||||
esac
|
||||
case "$TPL_HOST" in
|
||||
yes|no) ;;
|
||||
*) printf 'template.env: HOST: want yes or no, got: %s\n' "$TPL_HOST" >&2; return 1 ;;
|
||||
esac
|
||||
case "$TPL_JOIN" in
|
||||
authkey|login) ;;
|
||||
*) printf 'template.env: JOIN: want authkey or login, got: %s\n' "$TPL_JOIN" >&2; return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# render_tenant_context <role> <creds.md> — the agent-context file's
|
||||
# content, on stdout: the one file every agent reads before touching
|
||||
# anything. The skeleton is MECHANISM and lives here once — the box#80 guard
|
||||
|
|
@ -248,19 +325,32 @@ EOF
|
|||
# protects the registry, the mint-time parse protects a mint served through
|
||||
# RIG_TEMPLATES_REPO/_DIR that CI never saw.
|
||||
template_lint() {
|
||||
local dir="${1%/}" role
|
||||
local dir="${1%/}" role family
|
||||
role="$(basename "$dir")"
|
||||
[ -d "$dir" ] || { printf '%s: not a directory\n' "$dir" >&2; return 1; }
|
||||
case "$role" in
|
||||
*-box|*-server) ;;
|
||||
*) printf '%s: role directories carry a family suffix (-box for box tenants, -server for fleet machines — rig#76)\n' "$role" >&2; return 1 ;;
|
||||
esac
|
||||
template_parse_env "$dir/template.env" || return 1
|
||||
[ -s "$dir/install.sh" ] \
|
||||
|| { printf '%s: install.sh missing or empty\n' "$role" >&2; return 1; }
|
||||
head -n1 "$dir/install.sh" | grep -q '^#!' \
|
||||
|| { printf '%s: install.sh has no shebang\n' "$role" >&2; return 1; }
|
||||
grep -q '[^[:space:]]' "$dir/creds.md" 2>/dev/null \
|
||||
|| { printf '%s: creds.md missing or blank (the context renderer splices it in — a blank paragraph would ship a context file with a hole)\n' "$role" >&2; return 1; }
|
||||
family="$(template_family "$role" 2>/dev/null || true)"
|
||||
[ -n "$family" ] || {
|
||||
printf '%s: role directories carry a family suffix (-box for box tenants, -server for fleet machines — rig#76; workstation is #152 machine carve-out)\n' "$role" >&2
|
||||
return 1
|
||||
}
|
||||
if [ "$family" = "tenant" ]; then
|
||||
template_parse_env "$dir/template.env" || return 1
|
||||
[ -s "$dir/install.sh" ] \
|
||||
|| { printf '%s: install.sh missing or empty\n' "$role" >&2; return 1; }
|
||||
head -n1 "$dir/install.sh" | grep -q '^#!' \
|
||||
|| { printf '%s: install.sh has no shebang\n' "$role" >&2; return 1; }
|
||||
grep -q '[^[:space:]]' "$dir/creds.md" 2>/dev/null \
|
||||
|| { printf '%s: creds.md missing or blank (the context renderer splices it in — a blank paragraph would ship a context file with a hole)\n' "$role" >&2; return 1; }
|
||||
else
|
||||
machine_template_parse_env "$dir/template.env" || return 1
|
||||
[ ! -e "$dir/creds.md" ] \
|
||||
|| { printf '%s: creds.md is not allowed for machine roles (machines render no tenant context)\n' "$role" >&2; return 1; }
|
||||
if [ -e "$dir/install.sh" ]; then
|
||||
[ -s "$dir/install.sh" ] \
|
||||
|| { printf '%s: install.sh is empty\n' "$role" >&2; return 1; }
|
||||
head -n1 "$dir/install.sh" | grep -q '^#!' \
|
||||
|| { printf '%s: install.sh has no shebang\n' "$role" >&2; return 1; }
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
# rig template-lint <role-dir>... — is this a valid tenant-role definition?
|
||||
# rig template-lint <role-dir>... — is this a valid role definition?
|
||||
#
|
||||
# rig defines what a valid template is (the schema lives in
|
||||
# lib/templates.sh, beside the mint-time parser that enforces it); the
|
||||
|
|
@ -23,12 +23,15 @@ usage() {
|
|||
cat <<'EOF'
|
||||
usage: rig template-lint <role-dir>...
|
||||
|
||||
Validate tenant-role definitions (the heavy-duty/rig-templates shape):
|
||||
each <role-dir> must carry a family-suffixed name (rig#76), a template.env
|
||||
that parses against the allowlist (KEY="value" only — the file is data,
|
||||
never sourced), an install.sh with a shebang, and a non-blank creds.md.
|
||||
Every refusal names the failing key or file. Exits non-zero if any
|
||||
definition fails; nothing is written.
|
||||
Validate role definitions (the heavy-duty/rig-templates shape).
|
||||
|
||||
Tenant roles use a *-box directory, tenant template.env schema, a shebang
|
||||
install.sh, and non-blank creds.md. Machine roles use a *-server directory
|
||||
(or exact name workstation), the ROOT_DOOR/HOST/JOIN schema, no creds.md,
|
||||
and an optional install.sh which must be non-empty and carry a shebang.
|
||||
template.env is parsed as KEY="value" data and never sourced. Every refusal
|
||||
names the failing key or file. Exits non-zero if any definition fails;
|
||||
nothing is written.
|
||||
EOF
|
||||
}
|
||||
|
||||
|
|
|
|||
62
test/cli.sh
62
test/cli.sh
|
|
@ -613,6 +613,22 @@ mkdir -p "$TPL_FIX/badnode-box"
|
|||
printf 'USER="x"\nCONTEXT_PATH=".x/A.md"\nCLI_NAME="x"\nPATH_LINE="p"\nNEEDS_NODE="maybe"\n' > "$TPL_FIX/badnode-box/template.env"
|
||||
mkdir -p "$TPL_FIX/badapt-box"
|
||||
printf 'USER="x"\nCONTEXT_PATH=".x/A.md"\nCLI_NAME="x"\nPATH_LINE="p"\nAPT_EXTRAS="zsh -o"\n' > "$TPL_FIX/badapt-box/template.env"
|
||||
mkdir -p "$TPL_FIX/scratch-server"
|
||||
printf 'ROOT_DOOR="closed"\nHOST="no"\nJOIN="login"\n' > "$TPL_FIX/scratch-server/template.env"
|
||||
mkdir -p "$TPL_FIX/workstation"
|
||||
printf 'ROOT_DOOR="closed"\nHOST="yes"\nJOIN="authkey"\n' > "$TPL_FIX/workstation/template.env"
|
||||
mkdir -p "$TPL_FIX/baddoor-server"
|
||||
printf 'ROOT_DOOR="ajar"\nHOST="no"\nJOIN="authkey"\n' > "$TPL_FIX/baddoor-server/template.env"
|
||||
mkdir -p "$TPL_FIX/tenantkeys-server"
|
||||
cp "$TPL_FIX/scratch-box/template.env" "$TPL_FIX/tenantkeys-server/template.env"
|
||||
mkdir -p "$TPL_FIX/machinekeys-box"
|
||||
cp "$TPL_FIX/scratch-server/template.env" "$TPL_FIX/machinekeys-box/template.env"
|
||||
mkdir -p "$TPL_FIX/creds-server"
|
||||
cp "$TPL_FIX/scratch-server/template.env" "$TPL_FIX/creds-server/template.env"
|
||||
printf 'not used\n' > "$TPL_FIX/creds-server/creds.md"
|
||||
mkdir -p "$TPL_FIX/noshebang-server"
|
||||
cp "$TPL_FIX/scratch-server/template.env" "$TPL_FIX/noshebang-server/template.env"
|
||||
printf 'exit 0\n' > "$TPL_FIX/noshebang-server/install.sh"
|
||||
|
||||
# THE HARD CUT, tenant half (#76). The pre-rename names are gone and must fail
|
||||
# as UNKNOWN — asserted per name, because an alias left in for one tenant is the
|
||||
|
|
@ -653,6 +669,28 @@ check "bootstrap: tenant roles dispatch through bootstrap.sh" 0 "Box TENANT role
|
|||
check "bootstrap: an unheard-of '-box' role still dispatches (zero code changes)" 0 "Box TENANT roles" \
|
||||
"$ROOT/commands/bootstrap.sh" scratch-box --help
|
||||
|
||||
# Machine roles use the same resolved registry but remain table-compatible:
|
||||
# loading happens before flag parsing, so an explicit flag overrides the
|
||||
# definition exactly as it overrides a built-in row.
|
||||
check "machine template: traits load from the local registry" 2 "join=login" \
|
||||
env RIG_TEMPLATES_DIR="$TPL_FIX" TS_AUTHKEY=x \
|
||||
"$ROOT/commands/bootstrap.sh" scratch-server --no-users
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
check "machine template: a flag overrides the loaded trait" 1 "must run as root" \
|
||||
env RIG_TEMPLATES_DIR="$TPL_FIX" TS_AUTHKEY=x \
|
||||
"$ROOT/commands/bootstrap.sh" scratch-server --no-users --join authkey
|
||||
fi
|
||||
check "machine template: invalid ROOT_DOOR is refused by key" 2 "ROOT_DOOR" \
|
||||
env RIG_TEMPLATES_DIR="$TPL_FIX" \
|
||||
"$ROOT/commands/bootstrap.sh" baddoor-server --no-users
|
||||
check "machine template: unknown role lists machine definitions" 2 "scratch-server" \
|
||||
env RIG_TEMPLATES_DIR="$TPL_FIX" "$ROOT/commands/bootstrap.sh" absent-server
|
||||
check "machine template: unknown role names the resolved source" 2 "RIG_TEMPLATES_DIR" \
|
||||
env RIG_TEMPLATES_DIR="$TPL_FIX" "$ROOT/commands/bootstrap.sh" absent-server
|
||||
check "machine template: a registry role cannot shadow a built-in" 2 "unset TS_AUTHKEY" \
|
||||
env RIG_TEMPLATES_DIR="$TPL_FIX" TS_AUTHKEY=x \
|
||||
"$ROOT/commands/bootstrap.sh" workstation --no-users
|
||||
|
||||
# The tenant marker guard (#83), against marker FIXTURES (never the harness
|
||||
# machine's real /etc/rig/role): converging a tenant onto a machine-role box or a
|
||||
# VM host (host=yes) refuses for every tenant — and names the staging PAIR,
|
||||
|
|
@ -844,6 +882,30 @@ cp "$TPL_FIX/scratch-box/template.env" "$TPL_FIX/scratch-box/creds.md" "$TPL_FIX
|
|||
printf 'exit 0\n' > "$TPL_FIX/noshebang-box/install.sh"
|
||||
check "template-lint: an install.sh without a shebang is refused" 1 "no shebang" \
|
||||
"$ROOT/commands/template-lint.sh" "$TPL_FIX/noshebang-box"
|
||||
check "template-lint: a traits-only machine definition passes" 0 "OK: " \
|
||||
"$ROOT/commands/template-lint.sh" "$TPL_FIX/scratch-server"
|
||||
check "template-lint: workstation is the machine-family carve-out" 0 "OK: " \
|
||||
"$ROOT/commands/template-lint.sh" "$TPL_FIX/workstation"
|
||||
check "template-lint: machine roles refuse tenant keys" 1 "unknown key: USER" \
|
||||
"$ROOT/commands/template-lint.sh" "$TPL_FIX/tenantkeys-server"
|
||||
check "template-lint: tenant roles refuse machine keys" 1 "unknown key: ROOT_DOOR" \
|
||||
"$ROOT/commands/template-lint.sh" "$TPL_FIX/machinekeys-box"
|
||||
check "template-lint: machine roles refuse creds.md" 1 "creds.md is not allowed" \
|
||||
"$ROOT/commands/template-lint.sh" "$TPL_FIX/creds-server"
|
||||
check "template-lint: machine install.sh requires a shebang" 1 "no shebang" \
|
||||
"$ROOT/commands/template-lint.sh" "$TPL_FIX/noshebang-server"
|
||||
# The install is deliberately after the users phase and its wrapper names both
|
||||
# role and source. Dynamic execution belongs to the root integration path; the
|
||||
# non-root offline harness pins the safety ordering and failure contract.
|
||||
machine_hook_at="$(grep -n 'running install hook for' "$ROOT/commands/bootstrap.sh" | head -n1 | cut -d: -f1)"
|
||||
check "machine template: install hook is bootstrap's last convergence phase" 0 "" \
|
||||
test "${users_apply_at:-999999}" -lt "${machine_hook_at:-0}"
|
||||
# shellcheck disable=SC2016
|
||||
check "machine template: install failure names role and source" 0 "" \
|
||||
grep -qF 'install hook failed for role $ROLE from $(templates_source_desc)' "$ROOT/commands/bootstrap.sh"
|
||||
# shellcheck disable=SC2016
|
||||
check "machine template: install runs from its definition with RIG_ROLE" 0 "" \
|
||||
grep -qF 'cd "$MACHINE_TEMPLATE_DIR" && RIG_ROLE="$ROLE" bash ./install.sh' "$ROOT/commands/bootstrap.sh"
|
||||
rm -rf "$TPL_FIX" "$TPL_WORK"
|
||||
|
||||
# Creds-free BY CONSTRUCTION, provable by absence (box#69's grep-refusal
|
||||
|
|
|
|||
Loading…
Reference in a new issue