Newer
Older
openstack-caracal-dc-dc / scripts / preflight.sh
#!/usr/bin/env bash
# scripts/preflight.sh [repo-root]
#
# THE single pre-deploy gate (DOCFIX-073). Before this existed the gate surface
# was six artifacts the operator had to remember independently (pre-flight-checks
# + its three delegates + provider-bundle-check + standup dry-run) -- the same
# fragmentation that let review-bundle.py rot into noise. This runs every
# stage-1 (pre-add-model) gate in dependency order with sectioned output and
# worst-exit aggregation, then prints the stage-2 gates it CANNOT run yet.
#
# Mutates NOTHING. Stages:
#   P1 repo lint            scripts/repo-lint.sh          (static hygiene)
#   P2 bundle invariants    scripts/provider-bundle-check.py
#   P3 channel assert       scripts/channel_assert.py     (needs charmhub; WARN if offline)
#   P4 live pre-flight      scripts/pre-flight-checks.sh  (needs MAAS; overlay/VIP/planes/nodes)
#   P5 credential matrix    scripts/creds-matrix.py       (D-137 ruling 1; tier 1 STATIC)
#   P6 reminders            stage-2 gates: juju-spaces-check.sh (AFTER add-model),
#                           osd-blank-check.sh (sudo), 'juju deploy --dry-run' (phase-01 1.2)
#
# Exit: 0 all pass | 1 any FAIL (do NOT deploy) | 2 warnings only (review, then decide).
# ASCII + LF.
set -uo pipefail
REPO="${1:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
cd "$REPO" || { echo "FAIL: cannot cd to repo root $REPO"; exit 1; }
WORST=0
note() { local rc=$1; [ "$rc" -eq 1 ] && WORST=1; [ "$rc" -eq 2 ] && [ "$WORST" -ne 1 ] && WORST=2; }

echo "================ P1: repo lint ================"
bash scripts/repo-lint.sh "$REPO"; note $?

echo "================ P2: bundle invariants ================"
python3 scripts/provider-bundle-check.py bundle.yaml; note $?

echo "================ P3: channel assert (charmhub) ================"
python3 scripts/channel_assert.py bundle.yaml; note $?

echo "================ P4: live pre-flight (MAAS/overlay/nodes) ================"
if [ -x scripts/pre-flight-checks.sh ] || [ -f scripts/pre-flight-checks.sh ]; then
  bash scripts/pre-flight-checks.sh; note $?
else
  echo "  [FAIL] scripts/pre-flight-checks.sh missing"; note 1
fi

echo "================ P5: credential matrix (D-137 tier 1) ================"
# D-137 ruling 1: enforcement is BLOCKING IN PREFLIGHT. Advisory was rejected as the
# posture that demonstrably failed -- `creds-audit` read CLEAN on 2026-07-15 while four
# region-VM secrets sat undeclared, and `admin.pass` surfaced 12 days later only because
# someone looked. Tier 1 is wired here because it is OFFLINE and its findings ARE the three
# ruled failure classes (expected-but-absent / undeclared / per-DC-asymmetric). Tier 2
# needs --remote/--privileged and a caller-supplied --pending-stage, so it stays out of an
# unattended gate; wiring it is a separate decision.
#
# EXPECTED TO FAIL TODAY, BY DESIGN. The register is red on real defects (SEC-021's
# declaration gaps, the SEC-020 ruling-5 identity conflation, the per-DC power-key
# asymmetry). That is this gate working, not misconfigured: a deploy should not proceed
# over an unreconciled credential estate. Going green is remediation, gated per row --
# do NOT silence it by deleting matrix rows or by demoting this to a warning.
# FAIL CLOSED if the checker is absent, mirroring P4. Without this guard a missing file
# makes python3 exit 2, which `note` maps to WARN -- so DELETING the gate would downgrade
# it to a warning instead of stopping the deploy. A gate that vanishes must never read as
# a pass (harness T9 encodes this).
if [ -f scripts/creds-matrix.py ]; then
  python3 scripts/creds-matrix.py; note $?
else
  echo "  [FAIL] scripts/creds-matrix.py missing"; note 1
fi

echo "================ P6: stage-2 reminders (NOT run here) ================"
echo "  - after 'juju add-model': bash scripts/juju-spaces-check.sh"
echo "  - with sudo:              bash scripts/osd-blank-check.sh"
echo "  - phase-01 Step 1.2:      juju deploy --dry-run (plan: 50 apps / 97 relations)"

case "$WORST" in
  0) echo; echo "PREFLIGHT: PASS -- clear to add-model / deploy" ;;
  2) echo; echo "PREFLIGHT: WARN -- review warnings, then decide" ;;
  *) echo; echo "PREFLIGHT: FAIL -- do NOT deploy" ;;
esac
exit "$WORST"