mirror of
https://github.com/dredx/prole.git
synced 2026-09-23 12:03:59 +00:00
Filed in response to the 2026-04-28 14:00 UTC backup outage. An
`install.sh --mode k3d` run with the shell pointed at GKE silently
overwrote the GKE cluster's GCS-backed ObjectStore + ScheduledBackup
with k3d-mode defaults; Garage filled up and CNPG backups failed for
hours before the next manual check. The class of bug is "config says
target cluster A, shell context says target cluster B, installer
proceeds against B without warning."
New shared bash helper at etc/preflight_kubecontext.sh with two
functions:
- verify_kubecontext_matches_config <cfg-path>
Strict gate. Reads [Global] APP_CLUSTER_KUBECONTEXT from the
config and exits 1 if `kubectl config current-context` differs.
Skipped silently when the config has no baked APP_CLUSTER_KUBECONTEXT
(e.g. fresh k3d.cfg) or when there's no live current-context.
- print_kubecontext_notice
Informational. Prints what's about to be inherited so the user
can abort before the TUI launches if it looks wrong. Never fails.
Wiring:
- deploy.sh sources the helper and calls the strict gate against
${PROLE_DEPLOY_CFG:-conf/gke.cfg} before invoking Python.
Unattended path -> hard refusal on mismatch.
- install.sh sources the helper and calls the informational notice
(gated on not-`--min`) right after entering the local-checkout
branch. The TUI is interactive, so the strict mode-aware gate is
a follow-up once the welcome screen records a mode in
state.inputs.
Bypass for deliberate cross-cluster maintenance:
KNOE_SKIP_KUBECONTEXT_GUARD=true ./deploy.sh
End-to-end verified:
- deploy.sh with current=cnpg-0, gke.cfg=app-0 -> exit 1, clear msg
- deploy.sh with KNOE_SKIP_...=true -> bypasses, prints
"skipping check"
- install.sh --min -> notice skipped
- install.sh (no flag) and install.sh --silent -> notice printed
Doc updates:
- CLAUDE.md §"Env-contamination warning" rewritten to describe the
live guard (was a forward-looking TODO).
- CLAUDE.md drift table row R4 removed; "Closed 2026-05-01" line added.
- docs/TODO.md queue item #1 archived to Done; R4 dropped from the
reality-vs-intent table. Queue numbering retained (no #1 placeholder)
so the docs/plans/junie/<NN>-...md filenames still match.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
127 lines
4.8 KiB
Bash
Executable File
127 lines
4.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Thin launch vehicle for the Knoe installer.
|
||
#
|
||
# Local execution (from a repo checkout):
|
||
# ./install.sh [args...]
|
||
#
|
||
# Bootstrap via curl (first-time install):
|
||
# curl -fsSL https://knoey.com/install.sh | bash
|
||
#
|
||
# ---------------------------------------------------------------------------
|
||
# Dual-cluster GKE architecture (prod / k8s mode)
|
||
# ---------------------------------------------------------------------------
|
||
# knoe-dev-0 — Autopilot cluster: app workloads (GitLab, platform), service mesh knoe-0
|
||
# knoe-cnpg-0 — Standard GKE cluster: CloudNative-PG only (rw/ro/r pod separation)
|
||
# 3 nodes × e2-standard-2, node pool: cnpg-db-pool
|
||
#
|
||
# GCP quota requirements (project: plenary-truck-485623-p7, region: us-west3):
|
||
# pd-standard : node boot disks (3 × 50 GB = 150 GB — does NOT count against pd-ssd quota)
|
||
# pd-ssd : PGDATA PVCs (3 × 50 Gi = 150 Gi)
|
||
# WAL PVCs (3 × 50 Gi = 150 Gi)
|
||
# Total pd-ssd = 300 Gi (requires 300 GB pd-ssd regional quota)
|
||
#
|
||
# To patch an existing knoe-cnpg-0 cluster with wrong boot disk type:
|
||
# ./scripts/patch_clusters.sh # dry-run first: DRY_RUN=true ./scripts/patch_clusters.sh
|
||
# ---------------------------------------------------------------------------
|
||
|
||
set -euo pipefail
|
||
|
||
# Repo URL used only during bootstrap (curl-pipe) installs.
|
||
KNOE_REPO_URL="${KNOE_REPO_URL:-https://gitlab.knoey.com/knoe/knoe.git}"
|
||
# Default install directory for bootstrap installs.
|
||
_INSTALL_DIR="${KNOE_HOME:-$HOME/knoe}"
|
||
|
||
_check_gcp_tools() {
|
||
# Skip GCP tool check if --min flag is present
|
||
for arg in "$@"; do
|
||
if [[ "$arg" == "--min" ]]; then
|
||
return 0
|
||
fi
|
||
done
|
||
|
||
local missing=0
|
||
if ! command -v gcloud >/dev/null 2>&1; then
|
||
echo "Error: gcloud CLI is required for GKE cluster management." >&2
|
||
echo " Install: https://cloud.google.com/sdk/docs/install" >&2
|
||
missing=1
|
||
fi
|
||
if ! command -v kubectl >/dev/null 2>&1; then
|
||
echo "Error: kubectl is required for Kubernetes cluster operations." >&2
|
||
echo " Install: https://kubernetes.io/docs/tasks/tools/" >&2
|
||
missing=1
|
||
fi
|
||
if [[ "$missing" -ne 0 ]]; then
|
||
echo "" >&2
|
||
echo "Install missing tools and re-run install.sh." >&2
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
_bootstrap() {
|
||
echo "==> Bootstrapping Knoe installer..."
|
||
command -v git >/dev/null 2>&1 || { echo "Error: git is required." >&2; exit 1; }
|
||
command -v python3 >/dev/null 2>&1 || { echo "Error: python3 is required." >&2; exit 1; }
|
||
|
||
if [[ -d "${_INSTALL_DIR}/.git" ]]; then
|
||
echo "==> Updating existing checkout at ${_INSTALL_DIR} ..."
|
||
git -C "${_INSTALL_DIR}" pull --ff-only
|
||
else
|
||
echo "==> Cloning to ${_INSTALL_DIR} ..."
|
||
git clone "${KNOE_REPO_URL}" "${_INSTALL_DIR}"
|
||
fi
|
||
|
||
exec "${_INSTALL_DIR}/install.sh" "$@"
|
||
}
|
||
|
||
# Resolve script directory; empty when running via curl pipe.
|
||
_src="${BASH_SOURCE[0]:-}"
|
||
_script_dir="$(cd "$(dirname "${_src:-/}")" 2>/dev/null && pwd || echo "")"
|
||
|
||
if [[ -n "${_script_dir}" && -d "${_script_dir}/knoe" ]]; then
|
||
# Local checkout: delegate to the Python installer module.
|
||
cd "${_script_dir}"
|
||
|
||
# kubectl-context notice. install.sh defers mode selection to the Python
|
||
# TUI, so we can't strictly verify here — but printing the inherited
|
||
# context up-front gives the user a chance to abort before the TUI
|
||
# launches if it looks wrong (e.g. shell on GKE while planning a k3d
|
||
# install). Filed against docs/TODO.md queue item #1 (drift R4); the
|
||
# mode-aware strict guard runs in deploy.sh (which knows its config) and
|
||
# should also run Python-side once the welcome screen records a mode.
|
||
if [[ -f "${_script_dir}/etc/preflight_kubecontext.sh" ]]; then
|
||
_is_min=0
|
||
for arg in "$@"; do
|
||
if [[ "$arg" == "--min" ]]; then _is_min=1; break; fi
|
||
done
|
||
if [[ "$_is_min" -eq 0 ]]; then
|
||
# shellcheck source=etc/preflight_kubecontext.sh
|
||
source "${_script_dir}/etc/preflight_kubecontext.sh"
|
||
print_kubecontext_notice
|
||
fi
|
||
unset _is_min
|
||
fi
|
||
|
||
# 1Password preflight — sign in, create 'knoey' vault, ensure 'administrator' item.
|
||
# Skipped in --min mode (init_1password.sh handles the flag check itself).
|
||
if [[ -f "${_script_dir}/etc/init_1password.sh" ]]; then
|
||
bash "${_script_dir}/etc/init_1password.sh" "$@" || true
|
||
fi
|
||
|
||
# If a built binary exists, prefer it.
|
||
if [[ -x "dist/knoe" ]]; then
|
||
exec "./dist/knoe" "$@"
|
||
fi
|
||
|
||
# Prefer the venv Python at KNOE_HOME (or the repo root) when available;
|
||
# it carries all knoe_requirements.txt dependencies.
|
||
_VENV_PYTHON="${KNOE_HOME:-${_script_dir}}/bin/python3"
|
||
_check_gcp_tools "$@"
|
||
if [[ -x "${_VENV_PYTHON}" ]]; then
|
||
exec "${_VENV_PYTHON}" -m knoe.ui.screens "$@"
|
||
fi
|
||
exec python3 -m knoe.ui.screens "$@"
|
||
else
|
||
# No local checkout found — bootstrap from the remote repo.
|
||
_bootstrap "$@"
|
||
fi
|