prole/etc/repair_pipeline.sh
chrisfu 5618b662dd Remove prole-db-manager; simplify deployment via prole-authority; fix pg18 downgrade & cluster name
Summary:
Removed the prole-db-manager microservice and simplified deployment to use
prole-authority as the internal management and authorization point. Fixed two
blocking bugs that prevented silent install from completing on knoe-dev-cluster.

Removed: prole-db-manager
- Deleted db-manager-deployment.yaml and db-manager-service.yaml from opentofu manifests
- Deleted src/db-manager/ (Dockerfile, server.js, package.json, tests)
- Removed prole-db-manager port-forward mapping from installer/core/env.py
- Removed init_db_manager.sh from Initialization Scripts (milestones.py, actions.py)
- Removed init_certmgr.sh and init_db_manager.sh tabs from services screen (services.py)
- Removed live k8s Deployment/Service from knoe-dev-cluster

Fixed: PostgreSQL version downgrade error (pg17 -> pg18)
- Created conf/postgresql/.version with value 18
- Updated k8s/prole/prole-db.yaml and prole-db-recovery.yaml.tpl imageName to prole-db:18-089
- Fixed _init_database_options_state() to restore saved version_type from prole.cfg
  so db_version_type defaults to v18 (pg18) instead of silently reverting to pg17
- Added database_options.* keys to _collect_input_snapshot() in cfg.py so
  distribution, version_type, and all extension toggles persist to prole.cfg

Fixed: Cluster name inconsistency
- Removed stale prole-dev-cluster references; all scripts now use knoe-dev-cluster
- Added knoe-dev-cluster to mode-detection case in etc/prole_cfg.sh

Config: conf/prole.cfg
- Set kerberos_config.enabled = False, KERBEROS_AUTO_ENABLED = False
- Added database_options.distribution = percona, version_type = v18
- Added all 13 extension flags set to True (postgis, pgvector, pgcrypto, pgaudit,
  pg_repack, pg_stat_statements, pg_buffercache, pg_freespacemap, pgrowlocks,
  postgres_fdw, dblink, pg_stat_monitor, pgbadger)

Verification:
./install.py -s -l -v -c conf/prole.cfg completed successfully.
CNPG deployed prole-db:18-089 to knoe-dev-cluster; all milestones passed.

Co-authored-by: Junie <junie@jetbrains.com>
2026-03-01 20:40:44 -08:00

436 lines
12 KiB
Bash
Executable File

#!/usr/bin/env bash
set -uo pipefail
# repair_pipeline.sh
# Purpose:
# - Validate cluster readiness and common service health
# - Repair or re-deploy safe-to-recreate services when anomalies are detected
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# shellcheck disable=SC1090
source "$SCRIPT_DIR/prole_cfg.sh"
ACTION="repair"
NAMESPACE_OVERRIDE=""
DB_NAMESPACE_OVERRIDE=""
usage() {
cat <<'USAGE'
Usage: repair_pipeline.sh [-n|--namespace NS] [--db-namespace NS] [-m|--mode MODE] [repair]
Runs a best-effort repair pass across common services and cluster add-ons.
USAGE
}
while [[ $# -gt 0 ]]; do
case "$1" in
-m|--mode)
shift
prole_set_mode "${1:-}"
;;
-m=*|--mode=*)
prole_set_mode "${1#*=}"
;;
-n|--namespace)
shift
NAMESPACE_OVERRIDE="${1:-}"
;;
--db-namespace)
shift
DB_NAMESPACE_OVERRIDE="${1:-}"
;;
--db-namespace=*)
DB_NAMESPACE_OVERRIDE="${1#*=}"
;;
-n=*|--namespace=*)
NAMESPACE_OVERRIDE="${1#*=}"
;;
repair)
ACTION="repair"
;;
-h|--help)
usage
exit 0
;;
*)
usage
exit 2
;;
esac
shift
done
BASE_NAMESPACE="${NAMESPACE:-}"
if [[ -n "$NAMESPACE_OVERRIDE" ]]; then
SERVICE_NAMESPACE="$NAMESPACE_OVERRIDE"
elif [[ -n "${SERVICE_NAMESPACE:-}" ]]; then
SERVICE_NAMESPACE="$SERVICE_NAMESPACE"
elif [[ -n "${NAMESPACE:-}" ]]; then
SERVICE_NAMESPACE="$NAMESPACE"
else
SERVICE_NAMESPACE="default"
fi
if [[ -n "$DB_NAMESPACE_OVERRIDE" ]]; then
DB_NAMESPACE="$DB_NAMESPACE_OVERRIDE"
elif [[ -n "${PROLE_DB_NAMESPACE:-}" ]]; then
DB_NAMESPACE="$PROLE_DB_NAMESPACE"
elif [[ -n "$BASE_NAMESPACE" ]]; then
DB_NAMESPACE="$BASE_NAMESPACE"
else
DB_NAMESPACE="$SERVICE_NAMESPACE"
fi
ARGOCD_NAMESPACE="${ARGOCD_NAMESPACE:-argocd}"
export SERVICE_NAMESPACE
export DB_NAMESPACE
export NAMESPACE="$SERVICE_NAMESPACE"
log() { printf '%s\n' "$*"; }
warn() { printf 'WARN: %s\n' "$*" >&2; }
err() { printf 'ERROR: %s\n' "$*" >&2; }
have() { command -v "$1" >/dev/null 2>&1; }
run_with_timeout() {
local timeout_s="$1"
shift
local cmd=( "$@" )
"${cmd[@]}" &
local pid=$!
local start=$SECONDS
while kill -0 "$pid" >/dev/null 2>&1; do
if (( SECONDS - start > timeout_s )); then
warn "Command timed out after ${timeout_s}s: ${cmd[*]}"
kill "$pid" >/dev/null 2>&1 || true
return 124
fi
sleep 2
done
wait "$pid"
return $?
}
ensure_kubectl() {
have kubectl || { err "kubectl not found"; exit 1; }
}
detect_mode() {
local mode
mode="${PROLE_MODE:-}"
if have prole_normalize_mode; then
mode="$(prole_normalize_mode "$mode")"
fi
printf '%s' "$mode"
}
ensure_kubeconfig_if_needed() {
local mode
mode="$(detect_mode)"
if [[ "$mode" == "k3s" || -n "${PROLE_K3S_SERVER:-}" || -n "${K3S_SERVER_URL:-}" ]]; then
prole_ensure_kubeconfig >/dev/null 2>&1 || true
fi
}
cluster_ready() {
ensure_kubectl
if ! kubectl cluster-info >/dev/null 2>&1; then
return 1
fi
return 0
}
k3d_cluster_ready() {
local cluster="${K3D_CLUSTER:-knoe-dev-cluster}"
if have k3d; then
if k3d cluster list --no-headers 2>/dev/null | grep -q "^${cluster}[[:space:]]"; then
return 0
fi
return 1
fi
return 0
}
resource_exists() {
local kind="$1" name="$2" ns="$3"
kubectl -n "$ns" get "$kind" "$name" >/dev/null 2>&1
}
resource_ready() {
local kind="$1" name="$2" ns="$3"
local desired ready
if ! resource_exists "$kind" "$name" "$ns"; then
return 2
fi
desired=$(kubectl -n "$ns" get "$kind" "$name" -o jsonpath='{.spec.replicas}' 2>/dev/null || echo "")
ready=$(kubectl -n "$ns" get "$kind" "$name" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo "")
desired=${desired:-1}
ready=${ready:-0}
if [[ "$ready" -ge "$desired" && "$desired" -gt 0 ]]; then
return 0
fi
return 1
}
authority_context_exists() {
local base
if [[ -n "${PROLE_HOME:-}" ]]; then
if [[ -d "$PROLE_HOME/authority" || -d "$PROLE_HOME/prole/authority" ]]; then
return 0
fi
fi
base="$SCRIPT_DIR/.."
if [[ -d "$base/authority" || -d "$base/prole/authority" ]]; then
return 0
fi
return 1
}
repair_dashboard() {
local ns="kubernetes-dashboard"
if ! kubectl get ns "$ns" >/dev/null 2>&1; then
warn "Dashboard namespace missing; installing via Helm"
if have helm; then
helm repo add kubernetes-dashboard https://kubernetes.github.io/dashboard/ >/dev/null 2>&1 || true
if ! run_with_timeout 120 bash -c 'helm repo update >/dev/null 2>&1'; then
warn "Helm repo update timed out; continuing with cached index"
fi
if ! run_with_timeout 300 helm upgrade --install kubernetes-dashboard kubernetes-dashboard/kubernetes-dashboard \
--create-namespace --namespace "$ns" \
--set kong.image.tag=3.8 --set kong.image.repository=docker.io/library/kong; then
warn "Helm install timed out or failed"
fi
else
warn "helm not found; cannot install kubernetes-dashboard"
return 1
fi
fi
local kong_pods
kong_pods=$(kubectl -n "$ns" get pods --no-headers 2>/dev/null | awk '{print $1}' | grep -E 'kong' || true)
if [[ -z "$kong_pods" ]]; then
warn "Dashboard Kong pod missing; re-applying Helm release"
if have helm; then
if ! run_with_timeout 300 helm upgrade --install kubernetes-dashboard kubernetes-dashboard/kubernetes-dashboard \
--create-namespace --namespace "$ns" \
--set kong.image.tag=3.8 --set kong.image.repository=docker.io/library/kong; then
warn "Helm install timed out or failed"
fi
else
warn "helm not found; cannot reinstall kubernetes-dashboard"
return 1
fi
else
local unhealthy
unhealthy=$(kubectl -n "$ns" get pods --no-headers 2>/dev/null | grep -E 'kong' | awk '$3 != "Running" {print $1}' || true)
if [[ -n "$unhealthy" ]]; then
warn "Dashboard Kong pod unhealthy; deleting pods for restart"
kubectl -n "$ns" delete pod $unhealthy --wait=false >/dev/null 2>&1 || true
fi
fi
}
repair_argocd() {
local ns="$1"
local need_fix=0
local items=(
"deploy argocd-server"
"deploy argocd-repo-server"
"deploy argocd-dex-server"
"deploy argocd-applicationset-controller"
"deploy argocd-notifications-controller"
"deploy argocd-redis"
"statefulset argocd-application-controller"
)
local item kind name status
for item in "${items[@]}"; do
kind="${item%% *}"
name="${item##* }"
resource_ready "$kind" "$name" "$ns"; status=$?
if [[ "$status" -ne 0 ]]; then
need_fix=1
break
fi
done
if [[ "$need_fix" -eq 1 ]]; then
warn "ArgoCD not ready; re-deploying"
REGISTRY_NAMESPACE="${REGISTRY_NAMESPACE:-default}" \
"$SCRIPT_DIR/init_registry.sh" -n "$ns" --registry-namespace "${REGISTRY_NAMESPACE:-default}" stop || true
REGISTRY_NAMESPACE="${REGISTRY_NAMESPACE:-default}" \
"$SCRIPT_DIR/init_registry.sh" -n "$ns" --registry-namespace "${REGISTRY_NAMESPACE:-default}" update || true
else
log "ArgoCD: OK"
fi
}
repair_openbao() {
local ns="$1"
local status
resource_ready deploy openbao "$ns"; status=$?
if [[ "$status" -eq 2 ]]; then
resource_ready statefulset openbao "$ns"; status=$?
fi
if [[ "$status" -ne 0 ]]; then
warn "OpenBao not ready; restarting pods and re-applying"
kubectl -n "$ns" delete pod -l app=openbao --wait=false >/dev/null 2>&1 || true
"$SCRIPT_DIR/init_openbao.sh" -n "$ns" update || true
else
log "OpenBao: OK"
fi
}
repair_opentofu() {
local ns="$1"
local status
resource_ready deploy opentofu "$ns"; status=$?
if [[ "$status" -ne 0 ]]; then
warn "OpenTofu not ready; re-deploying"
kubectl -n "$ns" delete deploy opentofu --ignore-not-found >/dev/null 2>&1 || true
"$SCRIPT_DIR/init_opentofu.sh" -n "$ns" update || true
else
log "OpenTofu: OK"
fi
}
repair_garage() {
local ns="$1"
local status
resource_ready statefulset garage "$ns"; status=$?
if [[ "$status" -ne 0 ]]; then
warn "Garage not ready; restarting pods (PVCs preserved)"
kubectl -n "$ns" delete pod -l app=garage --wait=false >/dev/null 2>&1 || true
"$SCRIPT_DIR/init_garage_store.sh" start || true
else
log "Garage: OK"
fi
}
repair_cnpg() {
local ns="$1"
local cluster="${CNPG_CLUSTER_NAME:-prole-db}"
if ! kubectl -n "$ns" get cluster "$cluster" >/dev/null 2>&1; then
warn "CNPG cluster '$cluster' not found in namespace '$ns'"
return 0
fi
local total ready bad
total=$(kubectl -n "$ns" get pods -l "cnpg.io/cluster=$cluster" --no-headers 2>/dev/null | wc -l | tr -d ' ')
if [[ "$total" -eq 0 ]]; then
warn "CNPG cluster '$cluster' has no pods; skipping rollout"
return 0
fi
ready=$(kubectl -n "$ns" get pods -l "cnpg.io/cluster=$cluster" --no-headers 2>/dev/null | awk '$2 ~ /^([0-9]+)\/\\1$/ {c++} END{print c+0}')
bad=$(kubectl -n "$ns" get pods -l "cnpg.io/cluster=$cluster" --no-headers 2>/dev/null | awk '$3 ~ /CrashLoopBackOff|Error|ImagePullBackOff|ErrImagePull/ {c++} END{print c+0}')
if [[ "$ready" -lt "$total" || "$bad" -gt 0 ]]; then
warn "CNPG pods unhealthy ($ready/$total ready, $bad bad); running rollout"
run_with_timeout 360 env NAMESPACE="$ns" "$SCRIPT_DIR/init_cloudnative_pg.sh" rollout || true
else
log "CNPG: OK"
fi
}
repair_barman_plugin() {
local ns="cnpg-system"
if ! kubectl -n "$ns" get deploy barman-cloud >/dev/null 2>&1; then
warn "Barman Cloud plugin missing; reinstalling"
run_with_timeout 300 env NAMESPACE="$DB_NAMESPACE" "$SCRIPT_DIR/init_cloudnative_pg.sh" install-barman-plugin || true
return 0
fi
local ready
ready=$(kubectl -n "$ns" get deploy barman-cloud -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo "0")
if [[ -z "$ready" || "$ready" == "0" ]]; then
warn "Barman Cloud plugin not ready; reinstalling and restarting"
run_with_timeout 300 env NAMESPACE="$DB_NAMESPACE" "$SCRIPT_DIR/init_cloudnative_pg.sh" install-barman-plugin || true
kubectl -n "$ns" rollout restart deploy/barman-cloud >/dev/null 2>&1 || true
else
log "Barman Cloud plugin: OK"
fi
}
repair_kdc() {
local ns="$1"
local status
if ! authority_context_exists; then
warn "Authority Docker context missing; skipping KDC rebuild"
return 0
fi
resource_ready deploy auth "$ns"; status=$?
if [[ "$status" -ne 0 ]]; then
warn "KDC (auth) not ready; re-deploying"
kubectl -n "$ns" delete deploy auth --ignore-not-found >/dev/null 2>&1 || true
kubectl -n "$ns" delete svc auth --ignore-not-found >/dev/null 2>&1 || true
"$SCRIPT_DIR/init_kdc.sh" update || true
else
log "KDC (auth): OK"
fi
}
case "${ACTION}" in
repair)
ensure_kubectl
ensure_kubeconfig_if_needed
log "== Repair Pipeline =="
log "Namespace: $NAMESPACE"
if ! cluster_ready; then
err "Cluster not reachable; aborting repair"
exit 2
fi
if [[ "$(detect_mode)" == "k3d" ]]; then
if ! k3d_cluster_ready; then
err "k3d cluster not running"
exit 2
fi
fi
log "-- Dashboard (Kong) --"
repair_dashboard || true
log "-- ArgoCD --"
repair_argocd "$ARGOCD_NAMESPACE"
log "-- OpenBao --"
repair_openbao "$SERVICE_NAMESPACE"
log "-- OpenTofu --"
repair_opentofu "$SERVICE_NAMESPACE"
log "-- Garage --"
repair_garage "$SERVICE_NAMESPACE"
if [[ "${KERBEROS_ENABLED:-}" == "0" || "${KERBEROS_ENABLED:-}" == "false" || "${KERBEROS_ENABLED:-}" == "False" ]]; then
log "KDC (auth): skipped (Kerberos disabled)"
elif [[ "${PROLE_KDC_ENABLED:-1}" != "0" ]]; then
log "-- KDC (auth) --"
repair_kdc "$SERVICE_NAMESPACE"
else
log "KDC (auth): skipped (disabled)"
fi
log "-- Barman Cloud Plugin --"
repair_barman_plugin
log "-- CNPG --"
repair_cnpg "$DB_NAMESPACE"
log "-- Common Services Status --"
if [[ "${KERBEROS_ENABLED:-}" == "1" || "${KERBEROS_ENABLED:-}" == "true" || "${KERBEROS_ENABLED:-}" == "True" ]]; then
"$SCRIPT_DIR/status_common_services.sh" -n "$SERVICE_NAMESPACE" -k || true
else
"$SCRIPT_DIR/status_common_services.sh" -n "$SERVICE_NAMESPACE" || true
fi
log "Repair pipeline complete."
;;
*)
usage
exit 2
;;
esac