prole/etc/init_common_services.sh
chrisfu b03efa8f69 Kong API gateway, docker-import preload, OpenTofu graceful fallback, milestone fix
Kong API Gateway (replacing prole nginx):
- Add etc/init_kong.sh provisioning script (DB-less mode, prole-db namespace)
- Add kong-deployment.yaml and kong-service.yaml manifests
- Rewire ingress rules (svc/git/api.prole.org) to prole-db-kong:8000
- Update kustomization.yaml to reference kong manifests

PostgREST & DB Manager in prole-db namespace:
- Add etc/init_postgrest.sh and etc/init_db_manager.sh scripts
- Add postgrest/db-manager deployment and service manifests
- Add src/db-manager/ Node.js REST endpoint for backup triggers
- Default NAMESPACE changed to prole-db in both scripts

Docker image pre-load from PROLE_DATA/docker-import:
- Add _preload_docker_images() to init_common_services.sh
- Scan for .tar files exported by final_deployment.sh
- Import via k3d image import (k3d) or ctr (k3s) before deployments
- Increase rollout timeouts to 300s (configurable via ROLLOUT_TIMEOUT) in init_openbao.sh, init_opentofu.sh, init_garage_store.sh, init_registry.sh

OpenTofu password resolution fix:
- Add Kubernetes secret fallback in resolve_admin_password()
- Change hard exit 1 to graceful return 1 with warning
- Wrap call in if-guard so set -e doesn't abort the script chain

Milestone fix (init scripts not running):
- Add init_kong.sh, init_postgrest.sh, init_db_manager.sh to InitializationScriptsMilestone.execute() script list and arg branches
- Previously only actions.py had these; milestones.py was missing them

Installer integration:
- Add Kong/PostgREST/DB Manager to silent installer _step_init_scripts
- Add corresponding tabs and execution blocks in UI services.py
2026-02-22 00:57:49 -08:00

306 lines
10 KiB
Bash
Executable File

#!/usr/bin/env bash
set -u
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# shellcheck disable=SC1090
source "$SCRIPT_DIR/prole_cfg.sh"
usage() {
cat <<EOF
Usage: init_common_services.sh [-n|--namespace NS] [-k|--kerberos] <update|start|status>
Deploys common infrastructure services (ArgoCD, OpenBao, OpenTofu, Garage)
into the given Kubernetes namespace. Use -k to include the Kerberos/KDC service.
EOF
}
NS=""
ACTION="update"
ENABLE_KERBEROS=0
while [ $# -gt 0 ]; do
case "$1" in
-m|--mode)
shift
prole_set_mode "${1:-}"
shift
;;
-m=*|--mode=*)
prole_set_mode "${1#*=}"
shift
;;
-n|--namespace)
shift
NS="${1:-}"
shift
;;
-n=*|--namespace=*)
NS="${1#*=}"
shift
;;
-k|--kerberos)
ENABLE_KERBEROS=1
shift
;;
update|start|status)
ACTION="$1"
shift
;;
-h|--help)
usage
exit 0
;;
*)
usage
exit 2
;;
esac
done
if [ -z "$NS" ]; then
NS="${SERVICE_NAMESPACE:-${NAMESPACE:-}}"
fi
if [ -z "$NS" ]; then
NS="default"
fi
ARGOCD_NS="${ARGOCD_NAMESPACE:-argocd}"
REGISTRY_NS="${REGISTRY_NAMESPACE:-default}"
prole_ensure_kubeconfig >/dev/null 2>&1 || true
# ---------------------------------------------------------------------------
# Pre-import container images from $PROLE_DATA/docker-import/*.tar
# ---------------------------------------------------------------------------
# final_deployment.sh exports images as
# $PROLE_DATA/docker-import/<safe_name>.tar
# where safe_name = image ref with / and : replaced by _.
# Loading these tars into the local container runtime before the sub-scripts
# deploy pods avoids slow image pulls from remote registries.
# ---------------------------------------------------------------------------
_resolve_prole_data() {
local d="${PROLE_DATA:-}"
if [[ -z "$d" ]]; then
if [[ -f "$SCRIPT_DIR/../env.sh" ]]; then
# shellcheck disable=SC1090
source "$SCRIPT_DIR/../env.sh"
d="${PROLE_DATA:-}"
fi
fi
echo "${d:-}"
}
_preload_docker_images() {
local prole_data
prole_data=$(_resolve_prole_data)
local import_dir="${DOCKER_IMPORT_DIR:-${prole_data:+${prole_data}/docker-import}}"
if [[ -z "$import_dir" || ! -d "$import_dir" ]]; then
echo "[SKIP] No docker-import directory found; images will be pulled from registries."
return 0
fi
local tar_files
tar_files=$(find "$import_dir" -maxdepth 1 -name '*.tar' -type f 2>/dev/null || true)
if [[ -z "$tar_files" ]]; then
echo "[SKIP] docker-import directory exists but contains no .tar files."
return 0
fi
local mode="${PROLE_MODE:-k3d}"
echo "Pre-loading container images from $import_dir (mode=$mode) ..."
case "$mode" in
k3d)
if ! command -v k3d >/dev/null 2>&1; then
echo "WARN: k3d not available; skipping docker-import pre-load." >&2
return 0
fi
local cluster_name="${K3D_CLUSTER_NAME:-prole-dev-cluster}"
local tar_file
while IFS= read -r tar_file; do
echo " Importing $(basename "$tar_file") into k3d cluster '$cluster_name' ..."
if k3d image import "$tar_file" -c "$cluster_name" >/dev/null 2>&1; then
echo " [OK] $(basename "$tar_file")"
else
echo " [WARN] Failed to import $(basename "$tar_file"); image will be pulled at deploy time." >&2
fi
done <<< "$tar_files"
;;
k3s)
# For k3s we import directly via ctr on the local node (assumes this
# script runs on a k3s server node or has access to the containerd socket).
local ctr_sock="/run/k3s/containerd/containerd.sock"
if [[ ! -S "$ctr_sock" ]]; then
echo "WARN: k3s containerd socket not found at $ctr_sock; skipping docker-import pre-load." >&2
return 0
fi
local tar_file
while IFS= read -r tar_file; do
echo " Importing $(basename "$tar_file") into k3s containerd ..."
if ctr -n k8s.io -a "$ctr_sock" images import --no-unpack "$tar_file" >/dev/null 2>&1; then
echo " [OK] $(basename "$tar_file")"
else
echo " [WARN] Failed to import $(basename "$tar_file"); image will be pulled at deploy time." >&2
fi
done <<< "$tar_files"
;;
*)
echo "WARN: Unknown mode '$mode'; skipping docker-import pre-load." >&2
;;
esac
echo "Pre-load complete."
}
case "$ACTION" in
start|update|reload|initialize|restart)
_preload_docker_images
;;
esac
if [ -x "$SCRIPT_DIR/init_service_layer.sh" ]; then
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
"$SCRIPT_DIR/init_service_layer.sh" -n "$NS" -k "$ACTION"
else
"$SCRIPT_DIR/init_service_layer.sh" -n "$NS" "$ACTION"
fi
exit $?
fi
if [[ -n "${COMMON_SERVICES_INIT_LOG:-}" ]]; then
mkdir -p "$(dirname "$COMMON_SERVICES_INIT_LOG")"
exec > >(tee -a "$COMMON_SERVICES_INIT_LOG") 2>&1
fi
OPENBAO_NAME=${OPENBAO_NAME:-openbao}
OPENTOFU_NAME=${OPENTOFU_NAME:-opentofu}
ARGOCD_SERVER_NAME=${ARGOCD_SERVER_NAME:-argocd-server}
GARAGE_NAME=${GARAGE_NAME:-garage}
OPENTOFU_CONFIGMAP=${OPENTOFU_CONFIGMAP:-opentofu-nginx}
OPENTOFU_SECRET=${OPENTOFU_SECRET:-opentofu-admin}
OPENBAO_KRB5_CONFIGMAP=${OPENBAO_KRB5_CONFIGMAP:-prole-krb5-conf}
GARAGE_CONFIGMAP=${GARAGE_CONFIGMAP:-garage-config}
GARAGE_SECRET_NAME=${GARAGE_SECRET_NAME:-garage-secrets}
find_namespaces() {
local kind="$1"
local name="$2"
kubectl get "$kind" -A --no-headers 2>/dev/null | awk -v n="$name" '$2==n {print $1}' | sort -u
}
collect_other_namespaces() {
local target="$1"
shift
local name="$1"
shift
local kinds=("$@")
local found=""
local kind
for kind in "${kinds[@]}"; do
found+=$(find_namespaces "$kind" "$name")
found+=$'\n'
done
printf '%s\n' "$found" | awk -v target="$target" 'NF && $1 != target {print $1}' | sort -u
}
migrate_common_services() {
local old_ns
for old_ns in $(collect_other_namespaces "$NS" "$OPENBAO_NAME" statefulset deployment service); do
echo "Found OpenBao in namespace '$old_ns'; removing before deploy to '$NS' ..."
kubectl delete -n "$old_ns" statefulset "$OPENBAO_NAME" --ignore-not-found >/dev/null 2>&1 || true
kubectl delete -n "$old_ns" deploy "$OPENBAO_NAME" --ignore-not-found >/dev/null 2>&1 || true
kubectl delete -n "$old_ns" svc "$OPENBAO_NAME" --ignore-not-found >/dev/null 2>&1 || true
kubectl delete -n "$old_ns" configmap "$OPENBAO_KRB5_CONFIGMAP" --ignore-not-found >/dev/null 2>&1 || true
done
for old_ns in $(collect_other_namespaces "$NS" "$OPENTOFU_NAME" deployment service); do
echo "Found OpenTofu in namespace '$old_ns'; removing before deploy to '$NS' ..."
if [ -x "$SCRIPT_DIR/init_opentofu.sh" ]; then
"$SCRIPT_DIR/init_opentofu.sh" -n "$old_ns" stop || true
else
kubectl delete -n "$old_ns" deploy "$OPENTOFU_NAME" --ignore-not-found >/dev/null 2>&1 || true
kubectl delete -n "$old_ns" svc "$OPENTOFU_NAME" --ignore-not-found >/dev/null 2>&1 || true
fi
kubectl delete -n "$old_ns" configmap "$OPENTOFU_CONFIGMAP" --ignore-not-found >/dev/null 2>&1 || true
kubectl delete -n "$old_ns" secret "$OPENTOFU_SECRET" --ignore-not-found >/dev/null 2>&1 || true
done
for old_ns in $(collect_other_namespaces "$ARGOCD_NS" "$ARGOCD_SERVER_NAME" deployment service); do
echo "Found ArgoCD in namespace '$old_ns'; removing before deploy to '$ARGOCD_NS' ..."
if [ -x "$SCRIPT_DIR/init_registry.sh" ]; then
REGISTRY_NAMESPACE="$REGISTRY_NS" "$SCRIPT_DIR/init_registry.sh" -n "$old_ns" stop || true
else
kubectl delete -n "$old_ns" deploy "$ARGOCD_SERVER_NAME" --ignore-not-found >/dev/null 2>&1 || true
kubectl delete -n "$old_ns" svc "$ARGOCD_SERVER_NAME" --ignore-not-found >/dev/null 2>&1 || true
fi
done
for old_ns in $(collect_other_namespaces "$NS" "$GARAGE_NAME" statefulset service); do
echo "Found Garage in namespace '$old_ns'; removing before deploy to '$NS' ..."
kubectl delete -n "$old_ns" statefulset "$GARAGE_NAME" --ignore-not-found >/dev/null 2>&1 || true
kubectl delete -n "$old_ns" svc "$GARAGE_NAME" --ignore-not-found >/dev/null 2>&1 || true
kubectl delete -n "$old_ns" configmap "$GARAGE_CONFIGMAP" --ignore-not-found >/dev/null 2>&1 || true
kubectl delete -n "$old_ns" secret "$GARAGE_SECRET_NAME" --ignore-not-found >/dev/null 2>&1 || true
done
}
echo "Deploying common services (namespace=$NS, action=$ACTION)"
rc=0
case "$ACTION" in
start|update|reload|initialize|restart)
migrate_common_services
;;
esac
if [ -x "$SCRIPT_DIR/init_openbao.sh" ]; then
ROLLOUT_TIMEOUT="${ROLLOUT_TIMEOUT:-300s}" "$SCRIPT_DIR/init_openbao.sh" -n "$NS" "$ACTION" || rc=$?
else
echo "WARN: init_openbao.sh not found; skipping OpenBao."
fi
if [ -x "$SCRIPT_DIR/init_opentofu.sh" ]; then
ROLLOUT_TIMEOUT="${ROLLOUT_TIMEOUT:-300s}" "$SCRIPT_DIR/init_opentofu.sh" -n "$NS" "$ACTION" || rc=$?
else
echo "WARN: init_opentofu.sh not found; skipping OpenTofu."
fi
if [ -x "$SCRIPT_DIR/init_registry.sh" ]; then
ARGOCD_NAMESPACE="$ARGOCD_NS" REGISTRY_NAMESPACE="$REGISTRY_NS" \
"$SCRIPT_DIR/init_registry.sh" -n "$ARGOCD_NS" --registry-namespace "$REGISTRY_NS" "$ACTION" || rc=$?
else
echo "WARN: init_registry.sh not found; ArgoCD deploy skipped."
fi
if [ -x "$SCRIPT_DIR/init_garage_store.sh" ]; then
garage_action="$ACTION"
case "$garage_action" in
update|reload|initialize) garage_action="start" ;;
esac
NAMESPACE="$NS" SERVICE_NAMESPACE="$NS" GARAGE_NAMESPACE="$NS" ROLLOUT_TIMEOUT="${ROLLOUT_TIMEOUT:-300s}" \
"$SCRIPT_DIR/init_garage_store.sh" "$garage_action" || rc=$?
else
echo "WARN: init_garage_store.sh not found; garage deploy skipped."
fi
if [[ "$ENABLE_KERBEROS" == "1" ]]; then
if [ -x "$SCRIPT_DIR/init_kdc.sh" ]; then
kdc_action="$ACTION"
case "$kdc_action" in
stop) kdc_action="cleanup" ;;
status) kdc_action="status" ;;
*) kdc_action="update" ;;
esac
SERVICE_NAMESPACE="$NS" PROLE_KDC_NAMESPACE="$NS" \
"$SCRIPT_DIR/init_kdc.sh" "$kdc_action" || rc=$?
else
echo "WARN: init_kdc.sh not found; kerberos deploy skipped."
fi
fi
exit "$rc"