#!/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 < Deploys common infrastructure services (ArgoCD, OpenTofu, Garage, OpenBao, Kong, Cert-Manager) 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|verify) 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 _verify_common_services() { local status_script="$SCRIPT_DIR/status_common_services.sh" if [[ ! -x "$status_script" ]]; then echo "ERROR: status_common_services.sh not found at $status_script" >&2 return 2 fi local args=(-n "$NS") if [[ "$ENABLE_KERBEROS" == "1" ]]; then args+=(-k) fi echo "Verifying common services in namespace '$NS' ..." "$status_script" "${args[@]}" } # Short-circuit verify before any deploy/reload/preload work if [[ "$ACTION" == "verify" ]]; then _verify_common_services exit $? fi # --------------------------------------------------------------------------- # Pre-import container images from $PROLE_DATA/docker-import/*.tar # --------------------------------------------------------------------------- # final_deployment.sh exports images as # $PROLE_DATA/docker-import/.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}" echo " Importing all images into k3d cluster '$cluster_name' (bulk) ..." local -a tar_array=() while IFS= read -r line; do [[ -n "$line" ]] && tar_array+=("$line") done <<< "$tar_files" if [[ ${#tar_array[@]} -gt 0 ]]; then if k3d image import "${tar_array[@]}" -c "$cluster_name" >/dev/null 2>&1; then echo " [OK] Bulk import of ${#tar_array[@]} images successful." else echo " [WARN] Bulk import failed; falling back to sequential." >&2 for tar_file in "${tar_array[@]}"; 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 fi fi ;; k3s) # For k3s running from macOS: load tarballs into Docker, then push to # the in-cluster registry:2. Falls back to Ansible ctr import if the # registry push fails. local registry="${LOCAL_REGISTRY:-}" if [[ -z "$registry" ]]; then # Derive from k3s server URL local _srv="${PROLE_K3S_SERVER:-}" if [[ -n "$_srv" ]]; then _srv="${_srv#https://}"; _srv="${_srv#http://}"; _srv="${_srv%%/*}"; _srv="${_srv%%:*}" [[ -n "$_srv" ]] && registry="${_srv}:5000" fi fi local tar_file while IFS= read -r tar_file; do local base_name base_name=$(basename "$tar_file" .tar) echo " Loading $(basename "$tar_file") into local Docker ..." local loaded_image="" loaded_image=$(docker load -i "$tar_file" 2>/dev/null | sed -n 's/^Loaded image: //p' || true) if [[ -z "$loaded_image" ]]; then # Try to derive image name from tarball filename (safe_name convention) loaded_image="${base_name//_//}" loaded_image="${loaded_image%/*}:${loaded_image##*/}" fi if [[ -n "$registry" && -n "$loaded_image" ]]; then local remote_tag="${registry}/${loaded_image}" docker tag "$loaded_image" "$remote_tag" 2>/dev/null || true echo " Pushing $remote_tag to registry ..." if docker push "$remote_tag" 2>/dev/null; then echo " [OK] $(basename "$tar_file") → $remote_tag" continue fi echo " [WARN] Registry push failed; image will be pulled at deploy time." >&2 else echo " [WARN] No registry configured; 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 OPENTOFU_NAME=${OPENTOFU_NAME:-opentofu} OPENBAO_NAME=${OPENBAO_NAME:-openbao} 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} 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" "$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 for old_ns in $(collect_other_namespaces "$NS" "$OPENBAO_NAME" deployment service statefulset); do echo "Found OpenBao in namespace '$old_ns'; removing before deploy to '$NS' ..." if [ -x "$SCRIPT_DIR/init_openbao.sh" ]; then "$SCRIPT_DIR/init_openbao.sh" -n "$old_ns" stop || true else kubectl delete -n "$old_ns" deploy "$OPENBAO_NAME" --ignore-not-found >/dev/null 2>&1 || true kubectl delete -n "$old_ns" statefulset "$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 fi done } echo "Deploying common services (namespace=$NS, action=$ACTION)" rc=0 case "$ACTION" in start|update|reload|initialize|restart|status) migrate_common_services ;; esac 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_openbao.sh" ]; then OPENBAO_NAMESPACE="$NS" SERVICE_NAMESPACE="$NS" \ "$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_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"