fix: [nomad-step-2] S2-fix-F — wire tools/vault-seed-<svc>.sh into bin/disinto --with <svc> (#928)
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/nomad-validate Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/pr/nomad-validate Pipeline was successful
ci/woodpecker/pr/smoke-init Pipeline was successful

`tools/vault-seed-forgejo.sh` existed and worked, but `bin/disinto init
--backend=nomad --with forgejo` never invoked it, so a fresh LXC with an
empty Vault hit `Template Missing: vault.read(kv/data/disinto/shared/
forgejo)` and the forgejo alloc timed out inside deploy.sh's 240s
healthy_deadline — operator had to run the seeder + `nomad alloc
restart` by hand to recover.

In `_disinto_init_nomad`, after `vault-import.sh` (or its skip branch)
and before `deploy.sh`, iterate `--with <svc>` and auto-invoke
`tools/vault-seed-<svc>.sh` when the file exists + is executable.
Services without a seeder are silently skipped — Step 3+ services
(woodpecker, chat, etc.) can ship their own seeder without touching
`bin/disinto`. VAULT_ADDR is passed explicitly because cluster-up.sh
writes the profile.d export during this same init run (current shell
hasn't sourced it yet) and `vault-seed-forgejo.sh` — unlike its
sibling vault-* scripts — requires the caller to set VAULT_ADDR
instead of defaulting it via `_hvault_default_env`. Mirror the loop in
the --dry-run plan so the operator-visible plan matches the real run.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude 2026-04-16 22:00:13 +00:00
parent bc3f10aff5
commit 5e83ecc2ef
2 changed files with 80 additions and 1 deletions

View file

@ -783,9 +783,29 @@ _disinto_init_nomad() {
fi fi
if [ -n "$with_services" ]; then if [ -n "$with_services" ]; then
# Vault seed plan (S2.6, #928): one line per service whose
# tools/vault-seed-<svc>.sh ships. Services without a seeder are
# silently skipped — the real-run loop below mirrors this,
# making `--with woodpecker` in Step 3 auto-invoke
# tools/vault-seed-woodpecker.sh once that file lands without
# any further change to bin/disinto.
local seed_hdr_printed=false
local IFS=','
for svc in $with_services; do
svc=$(echo "$svc" | xargs) # trim whitespace
local seed_script="${FACTORY_ROOT}/tools/vault-seed-${svc}.sh"
if [ -x "$seed_script" ]; then
if [ "$seed_hdr_printed" = false ]; then
echo "── Vault seed dry-run ─────────────────────────────────"
seed_hdr_printed=true
fi
echo "[seed] [dry-run] ${seed_script} --dry-run"
fi
done
[ "$seed_hdr_printed" = true ] && echo ""
echo "── Deploy services dry-run ────────────────────────────" echo "── Deploy services dry-run ────────────────────────────"
echo "[deploy] services to deploy: ${with_services}" echo "[deploy] services to deploy: ${with_services}"
local IFS=','
for svc in $with_services; do for svc in $with_services; do
svc=$(echo "$svc" | xargs) # trim whitespace svc=$(echo "$svc" | xargs) # trim whitespace
# Validate known services first # Validate known services first
@ -893,6 +913,43 @@ _disinto_init_nomad() {
echo "[import] no --import-env/--import-sops — skipping; set them or seed kv/disinto/* manually before deploying secret-dependent services" echo "[import] no --import-env/--import-sops — skipping; set them or seed kv/disinto/* manually before deploying secret-dependent services"
fi fi
# Seed Vault for services that ship their own seeder (S2.6, #928).
# Convention: tools/vault-seed-<svc>.sh — auto-invoked when --with <svc>
# is requested. Runs AFTER vault-import so that real imported values
# win over generated seeds when both are present; each seeder is
# idempotent on a per-key basis (see vault-seed-forgejo.sh's
# "missing → generate, present → unchanged" contract), so re-running
# init does not rotate existing keys. Services without a seeder are
# silently skipped — keeps this loop forward-compatible with Step 3+
# services that may ship their own seeder without touching bin/disinto.
#
# VAULT_ADDR is passed explicitly because cluster-up.sh writes the
# profile.d export *during* this same init run, so the current shell
# hasn't sourced it yet; sibling vault-* scripts (engines/policies/
# auth/import) default VAULT_ADDR internally via _hvault_default_env,
# but vault-seed-forgejo.sh requires the caller to set it.
if [ -n "$with_services" ]; then
local vault_addr="${VAULT_ADDR:-http://127.0.0.1:8200}"
local IFS=','
for svc in $with_services; do
svc=$(echo "$svc" | xargs) # trim whitespace
local seed_script="${FACTORY_ROOT}/tools/vault-seed-${svc}.sh"
if [ -x "$seed_script" ]; then
echo ""
echo "── Seeding Vault for ${svc} ───────────────────────────"
if [ "$(id -u)" -eq 0 ]; then
VAULT_ADDR="$vault_addr" "$seed_script" || exit $?
else
if ! command -v sudo >/dev/null 2>&1; then
echo "Error: vault-seed-${svc}.sh must run as root and sudo is not installed" >&2
exit 1
fi
sudo -n "VAULT_ADDR=$vault_addr" -- "$seed_script" || exit $?
fi
fi
done
fi
# Deploy services if requested # Deploy services if requested
if [ -n "$with_services" ]; then if [ -n "$with_services" ]; then
echo "" echo ""

View file

@ -155,6 +155,28 @@ setup_file() {
[[ "$output" == *"[deploy] dry-run complete"* ]] [[ "$output" == *"[deploy] dry-run complete"* ]]
} }
# S2.6 / #928 — every --with <svc> that ships tools/vault-seed-<svc>.sh
# must auto-invoke the seeder before deploy.sh runs. forgejo is the
# only service with a seeder today, so the dry-run plan must include
# its seed line when --with forgejo is set. The seed block must also
# appear BEFORE the deploy block (seeded secrets must exist before
# nomad reads the template stanza) — pinned here by scanning output
# order. Services without a seeder (e.g. unknown hypothetical future
# ones) are silently skipped by the loop convention.
@test "disinto init --backend=nomad --with forgejo --dry-run prints seed plan before deploy plan" {
run "$DISINTO_BIN" init placeholder/repo --backend=nomad --with forgejo --dry-run
[ "$status" -eq 0 ]
[[ "$output" == *"Vault seed dry-run"* ]]
[[ "$output" == *"tools/vault-seed-forgejo.sh --dry-run"* ]]
# Order: seed header must appear before deploy header.
local seed_line deploy_line
seed_line=$(echo "$output" | grep -n "Vault seed dry-run" | head -1 | cut -d: -f1)
deploy_line=$(echo "$output" | grep -n "Deploy services dry-run" | head -1 | cut -d: -f1)
[ -n "$seed_line" ]
[ -n "$deploy_line" ]
[ "$seed_line" -lt "$deploy_line" ]
}
@test "disinto init --backend=nomad --with forgejo,forgejo --dry-run handles comma-separated services" { @test "disinto init --backend=nomad --with forgejo,forgejo --dry-run handles comma-separated services" {
run "$DISINTO_BIN" init placeholder/repo --backend=nomad --with forgejo,forgejo --dry-run run "$DISINTO_BIN" init placeholder/repo --backend=nomad --with forgejo,forgejo --dry-run
[ "$status" -eq 0 ] [ "$status" -eq 0 ]