prole/etc/init_opentofu.sh
chrisfu f2c9012cce Refactor installation and initialization logic, and expand test coverage
- install.py: Major update including configuration variable expansion, improved k3s/k3d handling, and enhanced installation logic.

- etc/ scripts: Significant refactoring of initialization scripts (Kerberos, Port Forwards, Garage Store, etc.).

- Port Forwards: Transitioned from XML to port-mappings.conf for managing kubectl port-forwards.

- Status Reporting: Improved status checking for common services.

- Infrastructure: Updated Ansible inventory and rsyslog role configurations.

- Tests: Added a comprehensive suite of tests for 'etc' initialization scripts in prole/tests/etc/.

- Documentation: Added prole-db-documentation-mcp-architecture.md.

- General: Updated Dockerfiles and various helper scripts.
2026-02-13 21:36:39 -08:00

213 lines
5.6 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# init_opentofu.sh
# Purpose:
# - Deploy OpenTofu control plane into Kubernetes
# - Configure admin access using the Prole DB root password
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# Load environment and config via prole_cfg.sh
# shellcheck disable=SC1090
source "$SCRIPT_DIR/prole_cfg.sh"
if [[ -z "${PROLE_SERVICE:-}" ]]; then
echo "ERROR: PROLE_SERVICE is not defined. Provide PROLE_HOME/env.sh or ~/.prole/env.sh" >&2
exit 1
fi
ACTION=""
NAMESPACE_OVERRIDE=""
OPENTOFU_NAME=${OPENTOFU_NAME:-opentofu}
OPENTOFU_ADMIN_USER=${OPENTOFU_ADMIN_USER:-admin}
if [[ -d "$SCRIPT_DIR/../k8s/opentofu" ]]; then
OPENTOFU_MANIFEST_DIR="$SCRIPT_DIR/../k8s/opentofu"
elif [[ -n "${PROLE_HOME:-}" && -d "$PROLE_HOME/k8s/opentofu" ]]; then
OPENTOFU_MANIFEST_DIR="$PROLE_HOME/k8s/opentofu"
else
OPENTOFU_MANIFEST_DIR="$SCRIPT_DIR/../k8s/opentofu"
fi
while [[ $# -gt 0 ]]; do
case "$1" in
-m|--mode)
shift
prole_set_mode "${1:-}"
;;
-m=*|--mode=*)
prole_set_mode "${1#*=}"
;;
-n|--namespace)
shift
if [[ -z "${1:-}" ]]; then
echo "ERROR: -n/--namespace requires a value" >&2
exit 2
fi
NAMESPACE_OVERRIDE="$1"
;;
-n=*|--namespace=*)
NAMESPACE_OVERRIDE="${1#*=}"
;;
start|stop|status|restart|initialize|update|reload)
ACTION="$1"
;;
--)
shift
break
;;
*)
if [[ -z "$ACTION" && "$1" != -* ]]; then
ACTION="$1"
else
echo "Unknown argument: $1" >&2
exit 2
fi
;;
esac
shift
done
if [[ -n "$NAMESPACE_OVERRIDE" ]]; then
NAMESPACE="$NAMESPACE_OVERRIDE"
export NAMESPACE
fi
if [[ -z "${NAMESPACE:-}" ]]; then
NAMESPACE="default"
export NAMESPACE
fi
OPENTOFU_NAMESPACE=${OPENTOFU_NAMESPACE:-${SERVICE_NAMESPACE:-${NAMESPACE:-default}}}
OPENTOFU_SECRET_NAMESPACE=${OPENTOFU_SECRET_NAMESPACE:-${NAMESPACE:-default}}
OPENTOFU_OPENBAO_NAMESPACE=${OPENTOFU_OPENBAO_NAMESPACE:-${SERVICE_NAMESPACE:-${OPENTOFU_NAMESPACE}}}
ensure_tools() {
for t in kubectl openssl curl jq; do
command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; }
done
}
ensure_namespace() {
if ! kubectl get namespace "$OPENTOFU_NAMESPACE" >/dev/null 2>&1; then
echo "Creating namespace '$OPENTOFU_NAMESPACE' ..."
kubectl create namespace "$OPENTOFU_NAMESPACE" >/dev/null 2>&1 || true
fi
}
openbao_url() {
if [[ -n "${PROLE_OPENBAO_URL:-}" ]]; then
echo "$PROLE_OPENBAO_URL"
elif curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then
echo "http://127.0.0.1:18200"
else
echo "http://openbao.${OPENTOFU_OPENBAO_NAMESPACE}.svc.cluster.local:8200"
fi
}
openbao_token() {
if [[ -f "$PROLE_SERVICE/secrets/openbao-root-token" ]]; then
cat "$PROLE_SERVICE/secrets/openbao-root-token"
else
echo "${OPENBAO_ROOT_TOKEN:-}"
fi
}
fetch_openbao_secret() {
local path="$1"
local key="$2"
local token url
token=$(openbao_token)
url=$(openbao_url)
if [[ -z "$token" ]]; then
echo ""
return 0
fi
curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/$path" | jq -r ".data.data.\"$key\"" || echo ""
}
is_anchor() {
case "${1:-}" in
'${OPENBAO:'*|'${PROLE_SECRET:'*) return 0 ;;
*) return 1 ;;
esac
}
resolve_admin_password() {
if [[ -z "${OPENTOFU_ADMIN_PASSWORD:-}" ]]; then
OPENTOFU_ADMIN_PASSWORD="${DB_PASSWORD:-}"
fi
if [[ -z "${OPENTOFU_ADMIN_PASSWORD:-}" ]] || is_anchor "${OPENTOFU_ADMIN_PASSWORD:-}"; then
local fetched
fetched=$(fetch_openbao_secret "prole/${OPENTOFU_SECRET_NAMESPACE:-default}/db" "password")
if [[ -n "$fetched" && "$fetched" != "null" ]]; then
OPENTOFU_ADMIN_PASSWORD="$fetched"
fi
fi
if [[ -z "${OPENTOFU_ADMIN_PASSWORD:-}" ]]; then
echo "ERROR: OPENTOFU_ADMIN_PASSWORD/DB_PASSWORD is missing." >&2
exit 1
fi
}
ensure_opentofu_secret() {
local tmp
tmp=$(mktemp)
local hash
hash=$(printf "%s" "$OPENTOFU_ADMIN_PASSWORD" | openssl passwd -apr1 -stdin)
printf "%s:%s\n" "$OPENTOFU_ADMIN_USER" "$hash" >"$tmp"
kubectl create secret generic opentofu-admin \
-n "$OPENTOFU_NAMESPACE" \
--from-literal=admin_password="$OPENTOFU_ADMIN_PASSWORD" \
--from-file=auth="$tmp" \
--dry-run=client -o yaml | kubectl apply -f - >/dev/null
rm -f "$tmp"
}
apply_k8s() {
echo "Applying OpenTofu manifest to namespace '$OPENTOFU_NAMESPACE' ..."
prole_render_manifest "$OPENTOFU_MANIFEST_DIR/deployment.yaml" | kubectl apply -n "$OPENTOFU_NAMESPACE" -f -
kubectl rollout status deploy/$OPENTOFU_NAME -n "$OPENTOFU_NAMESPACE" --timeout=120s || true
}
delete_k8s() {
echo "Removing OpenTofu resources from namespace '$OPENTOFU_NAMESPACE' ..."
kubectl delete -n "$OPENTOFU_NAMESPACE" -f "$OPENTOFU_MANIFEST_DIR/deployment.yaml" --ignore-not-found
kubectl delete -n "$OPENTOFU_NAMESPACE" secret opentofu-admin --ignore-not-found || true
}
status_k8s() {
kubectl -n "$OPENTOFU_NAMESPACE" get deploy "$OPENTOFU_NAME" 2>/dev/null || true
kubectl -n "$OPENTOFU_NAMESPACE" get svc "$OPENTOFU_NAME" 2>/dev/null || true
}
case "${ACTION:-}" in
start|initialize|update|reload|restart)
ensure_tools
ensure_namespace
resolve_admin_password
ensure_opentofu_secret
apply_k8s
prole_register_port_forward "opentofu" "${OPENTOFU_NAMESPACE:-default}" "svc/opentofu" "8080" "8080" "0.0.0.0" "TCP" "OpenTofu"
;;
stop)
ensure_tools
delete_k8s
;;
status)
ensure_tools
status_k8s
;;
*)
echo "Usage: $0 [start|stop|status|restart|initialize|update|reload] [-n namespace] [--mode k3d|k3s|k8s]" >&2
exit 1
;;
esac