#!/usr/bin/env bash # # bootstrap.sh — the SINGLE Flux + SOPS bootstrap entrypoint for the dev cluster. # # Phase 1, T-1.4 · ADR-0004 (Flux) · ADR-0007 (SOPS+age) · R-GITOPS-01 · R-SEC-01. # ============================================================================= # *** AUTHORED, NOT RUN. The Infra worker's FINAL Ansible play invokes this *** # *** (interface contract, see ../README.md). Agents must not execute it. *** # ============================================================================= # # WHAT THIS DOES (Phase 1, bootstrap-source method — reconciles from the LOCAL repo, no remote # git host exists yet per spec docs/05 + CONTRIBUTING.md): # # 1. `flux install` — deploy the Flux controllers ONLY (source, kustomize, helm controllers). # We do NOT use `flux bootstrap git` in Phase 1 because that command commits # and OWNS its own gotk-sync GitRepository+Kustomization named `flux-system`. # Our entrypoints are ALREADY authored in clusters/base (flux-system.yaml = # the GitRepository; platform.yaml/apps.yaml = the Kustomizations). Letting # bootstrap regenerate them would DUPLICATE/conflict. `flux install` + # applying the authored clusters/dev overlay is the reconciling path. # 2. sops-age Secret — the operator installs the age PRIVATE key out-of-band BEFORE reconcile so # decryption works on first pass (../sops/age-key-secret.md). This script # only VERIFIES it exists; it never handles key material. # 3. `kubectl apply -k clusters/dev` — materialises the authored reconciliation graph: # GitRepository `flux-system` (source) -> Kustomization `platform` -> # Kustomization `apps` (dependsOn platform), with cluster-vars overlay # (R-ENV-02) and SOPS decryption enabled via the patches in ./patches/. # # PREREQUISITES the operator/PM resolves before this runs (see ../README.md "Handoff"): # - clusters/base/flux-system.yaml url placeholder is overridden — wire ./patches/flux-system- # source-url.yaml into clusters/dev/kustomization.yaml (or set the URL) so the source is real. # - ./patches/platform-sops-decryption.yaml + apps-sops-decryption.yaml are wired into # clusters/dev/kustomization.yaml `patches:` so decryption is enabled at reconcile. # - .sops.yaml (from ../sops) is installed at the repo root with the real age recipient. # # PHASE 6 (WS-GITOPS, later — NOT Phase 1): once Gitea is up, cut the source over with # `flux bootstrap git` against gitea.home.keithjackson.dev — see ../README.md "Phase-6 cutover". # set -euo pipefail # --- config (env-overridable) ------------------------------------------------ FLUX_ENV_PATH="${FLUX_ENV_PATH:-clusters/dev}" # dev is the ONLY Phase-1 build target FLUX_NAMESPACE="${FLUX_NAMESPACE:-flux-system}" SOPS_SECRET_NAME="${SOPS_SECRET_NAME:-sops-age}" script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" repo_root="$(git -C "${script_dir}" rev-parse --show-toplevel 2>/dev/null || cd "${script_dir}/../.." && pwd)" echo "==> Flux + SOPS bootstrap (dev) — path=${FLUX_ENV_PATH}" # --- 0. tooling present (from infra/devcontainer; versions pinned there) ------ for bin in flux kubectl; do command -v "$bin" >/dev/null 2>&1 || { echo "ERROR: '$bin' not found (use infra/devcontainer)." >&2; exit 1; } done # --- 1. install Flux controllers --------------------------------------------- echo "==> [1/3] flux install (controllers only)" flux install --namespace="${FLUX_NAMESPACE}" # --- 2. verify the out-of-band age key Secret exists ------------------------- # We DO NOT create it here (it holds the private key). The operator installs it per # ../sops/age-key-secret.md. Fail fast if missing so decryption can't silently no-op. echo "==> [2/3] verifying ${SOPS_SECRET_NAME} Secret exists in ${FLUX_NAMESPACE}" if ! kubectl -n "${FLUX_NAMESPACE}" get secret "${SOPS_SECRET_NAME}" >/dev/null 2>&1; then echo "ERROR: Secret ${FLUX_NAMESPACE}/${SOPS_SECRET_NAME} not found." >&2 echo " Install the age private key first: bootstrap/sops/age-key-secret.md" >&2 exit 1 fi # --- 3. apply the authored dev entrypoint ------------------------------------ echo "==> [3/3] kubectl apply -k ${FLUX_ENV_PATH}" kubectl apply -k "${repo_root}/${FLUX_ENV_PATH}" echo echo "==> Bootstrap applied. Watch reconciliation with (read-only):" echo " flux get kustomizations --watch" echo " flux get sources git -n ${FLUX_NAMESPACE}" echo " 'platform' should go Ready, then 'apps' (dependsOn platform)."