mirror of
https://github.com/dredx/prole.git
synced 2026-09-23 10:13:58 +00:00
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>
759 lines
22 KiB
Bash
759 lines
22 KiB
Bash
#!/usr/bin/env bash
|
|
# Standard include for Prole etc scripts.
|
|
# - Loads environment from env.sh (if available)
|
|
# - Loads values from PROLE_CONF/prole.cfg (INI-style)
|
|
# - Does not override already-set env vars
|
|
|
|
if [[ "${_PROLE_CFG_LOADED:-}" == "1" ]]; then
|
|
return 0 2>/dev/null || true
|
|
fi
|
|
_PROLE_CFG_LOADED=1
|
|
|
|
_prole_cfg_script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
_prole_cfg_home_guess=$(cd "$_prole_cfg_script_dir/.." && pwd)
|
|
|
|
# 1. Try to load env.sh to establish base PROLE_HOME/PROLE_CONF
|
|
if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then
|
|
# shellcheck disable=SC1090
|
|
source "$PROLE_HOME/env.sh"
|
|
elif [[ -f "$HOME/.prole/env.sh" ]]; then
|
|
# shellcheck disable=SC1090
|
|
source "$HOME/.prole/env.sh"
|
|
elif [[ -f "$_prole_cfg_home_guess/env.sh" ]]; then
|
|
# shellcheck disable=SC1090
|
|
source "$_prole_cfg_home_guess/env.sh"
|
|
fi
|
|
|
|
_prole_trim() {
|
|
local s="$1"
|
|
s="${s#"${s%%[![:space:]]*}"}"
|
|
s="${s%"${s##*[![:space:]]}"}"
|
|
printf '%s' "$s"
|
|
}
|
|
|
|
_prole_cfg_expand() {
|
|
local value="$1"
|
|
local out="" rest="$value"
|
|
local prefix token suffix var repl
|
|
if [[ -z "$value" ]]; then
|
|
printf '%s' "$value"
|
|
return 0
|
|
fi
|
|
case "$value" in
|
|
'${OPENBAO:'*|'${PROLE_SECRET:'*)
|
|
printf '%s' "$value"
|
|
return 0
|
|
;;
|
|
esac
|
|
while [[ "$rest" =~ ^([^$]*)(\$[A-Za-z_][A-Za-z0-9_]*|\$\{[A-Za-z_][A-Za-z0-9_]*\})(.*)$ ]]; do
|
|
prefix="${BASH_REMATCH[1]}"
|
|
token="${BASH_REMATCH[2]}"
|
|
suffix="${BASH_REMATCH[3]}"
|
|
var="${token#\$}"
|
|
var="${var#\{}"
|
|
var="${var%\}}"
|
|
if [[ -n "${!var+x}" ]]; then
|
|
repl="${!var}"
|
|
else
|
|
repl="$token"
|
|
fi
|
|
out+="${prefix}${repl}"
|
|
rest="$suffix"
|
|
done
|
|
out+="$rest"
|
|
printf '%s' "$out"
|
|
}
|
|
|
|
_prole_host_from_url() {
|
|
local val="${1:-}"
|
|
val="${val#http://}"
|
|
val="${val#https://}"
|
|
val="${val%%/*}"
|
|
val="${val%%:*}"
|
|
printf '%s' "$val"
|
|
}
|
|
|
|
prole_is_in_cluster() {
|
|
[[ -n "${KUBERNETES_SERVICE_HOST:-}" && -f /var/run/secrets/kubernetes.io/serviceaccount/token ]]
|
|
}
|
|
|
|
_prole_local_registry_enabled() {
|
|
local raw="${PROLE_ENABLE_LOCAL_REGISTRY:-${ENABLE_LOCAL_REGISTRY:-}}"
|
|
if [[ -n "$raw" ]]; then
|
|
_prole_bool_true "$raw" && return 0
|
|
return 1
|
|
fi
|
|
local mode=""
|
|
if command -v prole_normalize_mode >/dev/null 2>&1; then
|
|
mode=$(prole_normalize_mode "${PROLE_MODE:-${DEPLOYMENT_MODE:-${CLUSTER_ENV:-}}}")
|
|
else
|
|
mode="${PROLE_MODE:-${DEPLOYMENT_MODE:-${CLUSTER_ENV:-}}}"
|
|
fi
|
|
[[ "$mode" == "k3d" || "$mode" == "k3s" ]]
|
|
}
|
|
|
|
_prole_bool_true() {
|
|
case "${1:-}" in
|
|
1|true|TRUE|True|yes|YES|Yes|on|ON|On) return 0 ;;
|
|
esac
|
|
return 1
|
|
}
|
|
|
|
_prole_kubeconfig_mark_insecure() {
|
|
local cfg="${1:-${KUBECONFIG:-}}"
|
|
[[ -z "$cfg" || ! -f "$cfg" ]] && return 1
|
|
command -v kubectl >/dev/null 2>&1 || return 1
|
|
local clusters
|
|
clusters=$(KUBECONFIG="$cfg" kubectl config get-clusters 2>/dev/null | awk 'NR>1 {print $1}' || true)
|
|
[[ -z "$clusters" ]] && return 1
|
|
local c
|
|
for c in $clusters; do
|
|
KUBECONFIG="$cfg" kubectl config set-cluster "$c" --insecure-skip-tls-verify=true >/dev/null 2>&1 || true
|
|
done
|
|
return 0
|
|
}
|
|
|
|
_PROLE_CFG_SET_VARS="|"
|
|
|
|
_prole_cfg_set_default() {
|
|
local key="$1" value="$2" token="|$key|"
|
|
# Only set if currently empty OR if we were the ones who set it from config previously
|
|
if [[ -z "${!key:-}" || "$_PROLE_CFG_SET_VARS" == *"$token"* ]]; then
|
|
printf -v "$key" '%s' "$value"
|
|
export "$key"
|
|
if [[ "$_PROLE_CFG_SET_VARS" != *"$token"* ]]; then
|
|
_PROLE_CFG_SET_VARS="${_PROLE_CFG_SET_VARS}${key}|"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
_prole_read_cfg() {
|
|
local cfg="$1" line key value
|
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
line="$(_prole_trim "$line")"
|
|
[[ -z "$line" ]] && continue
|
|
case "$line" in
|
|
\#*|\;*|\[*\])
|
|
continue
|
|
;;
|
|
esac
|
|
if [[ "$line" == *"="* ]]; then
|
|
key="$(_prole_trim "${line%%=*}")"
|
|
value="$(_prole_trim "${line#*=}")"
|
|
[[ -z "$key" ]] && continue
|
|
# Replace dots with underscores for shell compatibility
|
|
key="${key//./_}"
|
|
value="$(_prole_cfg_expand "$value")"
|
|
_prole_cfg_set_default "$key" "$value"
|
|
fi
|
|
done <"$cfg"
|
|
}
|
|
|
|
_prole_cfg_extract_key() {
|
|
local cfg="$1" key="$2"
|
|
local value
|
|
value=$(awk -F= -v k="$key" '
|
|
/^[[:space:]]*;/ {next}
|
|
/^[[:space:]]*#/ {next}
|
|
/^[[:space:]]*\\[/ {next}
|
|
$1 ~ "^[[:space:]]*" k "[[:space:]]*$" {
|
|
v=$2
|
|
sub(/^[[:space:]]+/, "", v)
|
|
sub(/[[:space:]]+$/, "", v)
|
|
val=v
|
|
}
|
|
END { print val }
|
|
' "$cfg" 2>/dev/null || true)
|
|
value="$(_prole_cfg_expand "$value")"
|
|
printf '%s' "$value"
|
|
}
|
|
|
|
prole_normalize_mode() {
|
|
local s
|
|
s=$(printf '%s' "${1:-}" | tr 'A-Z' 'a-z')
|
|
case "$s" in
|
|
dev|k3d|k3d-*) echo "k3d"; return 0 ;;
|
|
service|k3s|k3s-*) echo "k3s"; return 0 ;;
|
|
prod|production|k8s|k8s-*) echo "k8s"; return 0 ;;
|
|
prole-dev-cluster|k3d-prole-dev-cluster) echo "k3d"; return 0 ;;
|
|
knoe-dev-cluster|k3d-knoe-dev-cluster) echo "k3d"; return 0 ;;
|
|
prole-service-cluster) echo "k3s"; return 0 ;;
|
|
prole-prod-cluster) echo "k8s"; return 0 ;;
|
|
esac
|
|
echo "$s"
|
|
}
|
|
|
|
prole_set_mode() {
|
|
local raw="$1"
|
|
if [[ -z "$raw" ]]; then
|
|
echo "ERROR: --mode requires a value (k3d, k3s, k8s)" >&2
|
|
exit 2
|
|
fi
|
|
local norm
|
|
norm=$(prole_normalize_mode "$raw")
|
|
case "$norm" in
|
|
k3d|k3s|k8s)
|
|
PROLE_MODE="$norm"
|
|
export PROLE_MODE
|
|
;;
|
|
*)
|
|
echo "ERROR: Unsupported mode '$raw' (use k3d, k3s, or k8s)" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
}
|
|
|
|
prole_render_manifest() {
|
|
local src="$1"
|
|
if [[ "${PROLE_MODE:-}" != "k3d" ]]; then
|
|
cat "$src"
|
|
return 0
|
|
fi
|
|
local renderer=""
|
|
if [[ -n "${PROLE_SERVICE:-}" && -f "$PROLE_SERVICE/render_manifest.py" ]]; then
|
|
renderer="$PROLE_SERVICE/render_manifest.py"
|
|
elif [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/etc/render_manifest.py" ]]; then
|
|
renderer="$PROLE_HOME/etc/render_manifest.py"
|
|
fi
|
|
if [[ -n "$renderer" ]]; then
|
|
python3 "$renderer" "$src"
|
|
return $?
|
|
fi
|
|
cat "$src"
|
|
}
|
|
|
|
_prole_cfg_file=""
|
|
if [[ -n "${PROLE_CONF:-}" && -f "$PROLE_CONF/prole.cfg" ]]; then
|
|
_prole_cfg_file="$PROLE_CONF/prole.cfg"
|
|
elif [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/conf/prole.cfg" ]]; then
|
|
_prole_cfg_file="$PROLE_HOME/conf/prole.cfg"
|
|
elif [[ -f "$_prole_cfg_home_guess/conf/prole.cfg" ]]; then
|
|
_prole_cfg_file="$_prole_cfg_home_guess/conf/prole.cfg"
|
|
fi
|
|
|
|
if [[ -n "$_prole_cfg_file" ]]; then
|
|
if [[ -z "${PROLE_CONF:-}" ]]; then
|
|
PROLE_CONF=$(cd "$(dirname "$_prole_cfg_file")" && pwd)
|
|
export PROLE_CONF
|
|
fi
|
|
_prole_read_cfg "$_prole_cfg_file"
|
|
|
|
if [[ -z "${KERBEROS_ENABLED:-}" && -n "${ENABLED:-}" ]]; then
|
|
KERBEROS_ENABLED="$ENABLED"
|
|
export KERBEROS_ENABLED
|
|
fi
|
|
|
|
# Always prefer namespace from prole.cfg (single source of truth).
|
|
_cfg_ns=$(_prole_cfg_extract_key "$_prole_cfg_file" "NAMESPACE")
|
|
if [[ -n "$_cfg_ns" ]]; then
|
|
export NAMESPACE="$_cfg_ns"
|
|
fi
|
|
_cfg_sns=$(_prole_cfg_extract_key "$_prole_cfg_file" "SERVICE_NAMESPACE")
|
|
if [[ -n "$_cfg_sns" ]]; then
|
|
export SERVICE_NAMESPACE="$_cfg_sns"
|
|
fi
|
|
unset _cfg_ns _cfg_sns
|
|
fi
|
|
|
|
if [[ -z "${PROLE_HOME:-}" && -d "$_prole_cfg_home_guess" ]]; then
|
|
PROLE_HOME="$_prole_cfg_home_guess"
|
|
export PROLE_HOME
|
|
fi
|
|
|
|
# Final check for critical variables
|
|
if [[ -z "${PROLE_SERVICE:-}" && -n "${PROLE_HOME:-}" ]]; then
|
|
export PROLE_SERVICE="$PROLE_HOME/etc"
|
|
fi
|
|
|
|
# Ensure NAMESPACE is set, defaulting to 'default' if not in config/env
|
|
if [[ -z "${NAMESPACE:-}" ]]; then
|
|
# Try to detect from current context if not set anywhere
|
|
NAMESPACE=$(kubectl config view --minify --output 'jsonpath={..namespace}' 2>/dev/null)
|
|
fi
|
|
export NAMESPACE=${NAMESPACE:-default}
|
|
if [[ -z "${SERVICE_NAMESPACE:-}" ]]; then
|
|
SERVICE_NAMESPACE="$NAMESPACE"
|
|
export SERVICE_NAMESPACE
|
|
fi
|
|
|
|
if [[ -z "${PROLE_MODE:-}" ]]; then
|
|
if [[ -n "${DEPLOYMENT_MODE:-}" ]]; then
|
|
_prole_mode_guess=$(prole_normalize_mode "$DEPLOYMENT_MODE")
|
|
case "$_prole_mode_guess" in
|
|
k3d|k3s|k8s) PROLE_MODE="$_prole_mode_guess"; export PROLE_MODE ;;
|
|
esac
|
|
unset _prole_mode_guess
|
|
elif [[ -n "${CLUSTER_ENV:-}" ]]; then
|
|
_prole_mode_guess=$(prole_normalize_mode "$CLUSTER_ENV")
|
|
case "$_prole_mode_guess" in
|
|
k3d|k3s|k8s) PROLE_MODE="$_prole_mode_guess"; export PROLE_MODE ;;
|
|
esac
|
|
unset _prole_mode_guess
|
|
fi
|
|
fi
|
|
|
|
if [[ -n "${NAMESPACE:-}" ]]; then
|
|
_prole_mode_resolved=$(prole_normalize_mode "${PROLE_MODE:-}")
|
|
case "$_prole_mode_resolved" in
|
|
k3d)
|
|
if [[ -n "${LOCAL_REGISTRY_INTERNAL:-}" ]]; then
|
|
if [[ "${LOCAL_REGISTRY_INTERNAL}" == *.localhost:5000 ]]; then
|
|
LOCAL_REGISTRY_INTERNAL="${LOCAL_REGISTRY_INTERNAL%.localhost:5000}:5000"
|
|
export LOCAL_REGISTRY_INTERNAL
|
|
elif [[ "${LOCAL_REGISTRY_INTERNAL}" == *.localhost ]]; then
|
|
LOCAL_REGISTRY_INTERNAL="${LOCAL_REGISTRY_INTERNAL%.localhost}"
|
|
export LOCAL_REGISTRY_INTERNAL
|
|
fi
|
|
elif _prole_local_registry_enabled; then
|
|
LOCAL_REGISTRY_INTERNAL="k3d-prole-registry:5000"
|
|
export LOCAL_REGISTRY_INTERNAL
|
|
fi
|
|
;;
|
|
esac
|
|
case "$_prole_mode_resolved" in
|
|
k3s|k8s)
|
|
_prole_registry_ns="${SERVICE_NAMESPACE:-${NAMESPACE}}"
|
|
if [[ -n "${LOCAL_REGISTRY_INTERNAL:-}" ]]; then
|
|
:
|
|
elif _prole_local_registry_enabled; then
|
|
_prole_registry_host="$(_prole_host_from_url "${PROLE_K3S_SERVER:-${K3S_SERVER_URL:-}}")"
|
|
if [[ -n "${_prole_registry_host:-}" ]]; then
|
|
LOCAL_REGISTRY_INTERNAL="${_prole_registry_host}:5000"
|
|
export LOCAL_REGISTRY_INTERNAL
|
|
elif [[ -n "${_prole_registry_ns:-}" ]]; then
|
|
LOCAL_REGISTRY_INTERNAL="registry.${_prole_registry_ns}.svc.cluster.local:5000"
|
|
export LOCAL_REGISTRY_INTERNAL
|
|
fi
|
|
unset _prole_registry_host
|
|
fi
|
|
unset _prole_registry_ns
|
|
;;
|
|
esac
|
|
unset _prole_mode_resolved
|
|
fi
|
|
|
|
_prole_is_secret_ref() {
|
|
case "${1:-}" in
|
|
'${PROLE_SECRET:'*|'${OPENBAO:'*) return 0 ;;
|
|
esac
|
|
return 1
|
|
}
|
|
|
|
_prole_is_openbao_ref() {
|
|
case "${1:-}" in
|
|
'${OPENBAO:'*) return 0 ;;
|
|
esac
|
|
return 1
|
|
}
|
|
|
|
_prole_decrypt_prole_secret() {
|
|
local value="${1:-}"
|
|
case "$value" in
|
|
'${PROLE_SECRET:'*) ;;
|
|
*) printf '%s' "$value"; return 0 ;;
|
|
esac
|
|
python3 - "$value" <<'PY'
|
|
import base64
|
|
import getpass
|
|
import platform
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
PROLE_SECRET_PREFIX = "${PROLE_SECRET:"
|
|
PROLE_SECRET_SUFFIX = "}"
|
|
PROLE_SECRET_VERSION = "v1"
|
|
PROLE_SECRET_SERVICE = "prole-installer"
|
|
PROLE_SECRET_KEY_FILE = Path.home() / ".prole" / "secrets" / "installer.key"
|
|
|
|
def get_keychain_key(service: str, account: str) -> bytes:
|
|
try:
|
|
res = subprocess.run(
|
|
["security", "find-generic-password", "-a", account, "-s", service, "-w"],
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
if res.returncode == 0 and res.stdout.strip():
|
|
return base64.urlsafe_b64decode(res.stdout.strip().encode("utf-8"))
|
|
except Exception:
|
|
pass
|
|
return b""
|
|
|
|
def get_file_key(path: Path) -> bytes:
|
|
if not path.exists():
|
|
return b""
|
|
raw = path.read_text().strip()
|
|
try:
|
|
return base64.urlsafe_b64decode(raw.encode("utf-8"))
|
|
except Exception:
|
|
return b""
|
|
|
|
def get_secret_key() -> bytes:
|
|
system = platform.system()
|
|
account = getpass.getuser() or "prole"
|
|
if system == "Darwin":
|
|
return get_keychain_key(PROLE_SECRET_SERVICE, account)
|
|
return get_file_key(PROLE_SECRET_KEY_FILE)
|
|
|
|
def decrypt_prole_secret(value: str) -> str:
|
|
if not (value.startswith(PROLE_SECRET_PREFIX) and value.endswith(PROLE_SECRET_SUFFIX)):
|
|
return value
|
|
inner = value[len(PROLE_SECRET_PREFIX):-len(PROLE_SECRET_SUFFIX)]
|
|
parts = inner.split(":")
|
|
if len(parts) != 3 or parts[0] != PROLE_SECRET_VERSION:
|
|
return ""
|
|
key = get_secret_key()
|
|
if not key:
|
|
return ""
|
|
try:
|
|
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
nonce = base64.urlsafe_b64decode(parts[1].encode("utf-8"))
|
|
ciphertext = base64.urlsafe_b64decode(parts[2].encode("utf-8"))
|
|
aesgcm = AESGCM(key)
|
|
return aesgcm.decrypt(nonce, ciphertext, None).decode("utf-8")
|
|
except Exception:
|
|
return ""
|
|
|
|
value = sys.argv[1]
|
|
out = decrypt_prole_secret(value)
|
|
print(out)
|
|
PY
|
|
}
|
|
|
|
_prole_resolve_openbao_ref() {
|
|
local value="${1:-}"
|
|
case "$value" in
|
|
'${OPENBAO:'*) ;;
|
|
*) printf '%s' "$value"; return 0 ;;
|
|
esac
|
|
python3 - "$value" <<'PY'
|
|
import json
|
|
import os
|
|
import sys
|
|
import urllib.request
|
|
|
|
OPENBAO_PREFIX = "${OPENBAO:"
|
|
OPENBAO_SUFFIX = "}"
|
|
|
|
def resolve_openbao_ref(value: str) -> str:
|
|
if not (value.startswith(OPENBAO_PREFIX) and value.endswith(OPENBAO_SUFFIX)):
|
|
return value
|
|
inner = value[len(OPENBAO_PREFIX):-len(OPENBAO_SUFFIX)]
|
|
if "#" not in inner:
|
|
return ""
|
|
path, key = inner.split("#", 1)
|
|
if not path or not key:
|
|
return ""
|
|
mount = "kv"
|
|
secret_path = path
|
|
if "/" in path:
|
|
mount, secret_path = path.split("/", 1)
|
|
|
|
token = os.environ.get("OPENBAO_ROOT_TOKEN", "")
|
|
if not token:
|
|
prole_service = os.environ.get("PROLE_SERVICE", "")
|
|
if prole_service:
|
|
token_path = os.path.join(prole_service, "secrets", "openbao-root-token")
|
|
try:
|
|
if os.path.exists(token_path):
|
|
token = open(token_path, "r", encoding="utf-8").read().strip()
|
|
except Exception:
|
|
token = ""
|
|
if not token:
|
|
return ""
|
|
|
|
url = os.environ.get("PROLE_OPENBAO_URL")
|
|
if not url:
|
|
for p in ["8200", "8200"]:
|
|
try:
|
|
with urllib.request.urlopen(f"http://127.0.0.1:{p}/v1/sys/health", timeout=0.5) as r:
|
|
if r.getcode() == 200:
|
|
url = f"http://127.0.0.1:{p}"
|
|
break
|
|
except Exception:
|
|
pass
|
|
if not url:
|
|
url = "http://127.0.0.1:8200"
|
|
url = url.rstrip("/")
|
|
try:
|
|
req = urllib.request.Request(f"{url}/v1/{mount}/data/{secret_path}")
|
|
req.add_header("X-Vault-Token", token)
|
|
with urllib.request.urlopen(req, timeout=4) as resp:
|
|
payload = json.loads(resp.read().decode("utf-8"))
|
|
return payload.get("data", {}).get("data", {}).get(key, "") or ""
|
|
except Exception:
|
|
return ""
|
|
|
|
value = sys.argv[1]
|
|
print(resolve_openbao_ref(value))
|
|
PY
|
|
}
|
|
|
|
_prole_usable_dir() {
|
|
local dir="$1"
|
|
if [[ -z "$dir" ]]; then return 1; fi
|
|
if [[ -w "$dir" ]] || [[ ! -e "$dir" && -w "$(dirname "$dir" 2>/dev/null)" ]]; then
|
|
printf '%s' "$dir"
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
_prole_kubeconfig_path() {
|
|
local base=""
|
|
# Try PROLE_SERVICE, then PROLE_HOME, then guess
|
|
local candidates=("${PROLE_SERVICE:-}/secrets" "${PROLE_HOME:-}" "${_prole_cfg_home_guess:-}")
|
|
for b in "${candidates[@]}"; do
|
|
if [[ -z "$b" ]]; then continue; fi
|
|
if _prole_usable_dir "$b" >/dev/null; then
|
|
base="$b"
|
|
break
|
|
fi
|
|
done
|
|
|
|
# Fallback to ~/.prole if everything else failed
|
|
if [[ -z "$base" ]]; then
|
|
base="$HOME/.prole/secrets"
|
|
mkdir -p "$base" 2>/dev/null || true
|
|
fi
|
|
|
|
if [[ "$base" == */secrets ]]; then
|
|
printf '%s\n' "${base}/k3s.kubeconfig"
|
|
else
|
|
printf '%s\n' "${base}/prole-k3s.kubeconfig"
|
|
fi
|
|
}
|
|
|
|
_prole_token_is_bearer() {
|
|
local token="$1"
|
|
[[ -z "$token" ]] && return 1
|
|
# K3s node tokens include '::' and are not valid API bearer tokens.
|
|
[[ "$token" == *"::"* ]] && return 1
|
|
# JWT-style tokens contain two dots.
|
|
case "$token" in
|
|
*.*.*) return 0 ;;
|
|
esac
|
|
# Bootstrap tokens are usually id.secret with a dot separator.
|
|
if [[ "$token" == *.* ]]; then
|
|
local id="${token%%.*}"
|
|
local secret="${token#*.}"
|
|
if [[ ${#id} -ge 6 && ${#secret} -ge 16 ]]; then
|
|
return 0
|
|
fi
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
_prole_kubeconfig_from_token() {
|
|
local server token ns
|
|
server="${PROLE_K3S_SERVER:-${K3S_SERVER_URL:-}}"
|
|
token="${PROLE_K3S_TOKEN:-${K3S_TOKEN:-}}"
|
|
ns="${NAMESPACE:-default}"
|
|
if _prole_is_secret_ref "$token"; then
|
|
return 1
|
|
fi
|
|
if [[ -z "$server" || -z "$token" ]]; then
|
|
return 1
|
|
fi
|
|
if ! _prole_token_is_bearer "$token"; then
|
|
return 1
|
|
fi
|
|
if [[ "$server" != http* ]]; then
|
|
server="https://$server"
|
|
fi
|
|
local cfg_path
|
|
cfg_path="$(_prole_kubeconfig_path)" || return 1
|
|
# Never overwrite a kubeconfig that uses client-certificate auth
|
|
if [[ -f "$cfg_path" ]] && grep -q "client-certificate-data" "$cfg_path" 2>/dev/null; then
|
|
export KUBECONFIG="$cfg_path"
|
|
return 0
|
|
fi
|
|
mkdir -p "$(dirname "$cfg_path")"
|
|
cat >"$cfg_path" <<EOF
|
|
apiVersion: v1
|
|
kind: Config
|
|
clusters:
|
|
- name: prole-k3s
|
|
cluster:
|
|
server: ${server}
|
|
insecure-skip-tls-verify: true
|
|
users:
|
|
- name: prole-k3s
|
|
user:
|
|
token: ${token}
|
|
contexts:
|
|
- name: prole-k3s
|
|
context:
|
|
cluster: prole-k3s
|
|
user: prole-k3s
|
|
namespace: ${ns}
|
|
current-context: prole-k3s
|
|
EOF
|
|
chmod 600 "$cfg_path" 2>/dev/null || true
|
|
export KUBECONFIG="$cfg_path"
|
|
return 0
|
|
}
|
|
|
|
prole_ensure_kubeconfig() {
|
|
if [[ "${PROLE_MODE:-}" == "k3d" ]]; then
|
|
return 0
|
|
fi
|
|
local _prole_insecure_flag
|
|
_prole_insecure_flag="${PROLE_K3S_SKIP_TLS_VERIFY:-${K3S_SKIP_TLS_VERIFY:-${PROLE_K3S_INSECURE:-${K3S_INSECURE:-}}}}"
|
|
_prole_kubeconfig_maybe_insecure() {
|
|
if _prole_bool_true "${_prole_insecure_flag:-}"; then
|
|
_prole_kubeconfig_mark_insecure "${KUBECONFIG:-}" >/dev/null 2>&1 || true
|
|
fi
|
|
}
|
|
# Prefer an existing valid kubeconfig (e.g. Ansible-fetched with client certs)
|
|
# over generating a new token-based one from prole.cfg.
|
|
if [[ -n "${KUBECONFIG:-}" && -f "$KUBECONFIG" ]]; then
|
|
_prole_kubeconfig_maybe_insecure
|
|
return 0
|
|
fi
|
|
if [[ -n "${PROLE_SERVICE:-}" && -f "$PROLE_SERVICE/secrets/k3s.kubeconfig" ]]; then
|
|
export KUBECONFIG="$PROLE_SERVICE/secrets/k3s.kubeconfig"
|
|
_prole_kubeconfig_maybe_insecure
|
|
return 0
|
|
fi
|
|
if [[ -n "${PROLE_K3S_KUBECONFIG:-}" && -f "$PROLE_K3S_KUBECONFIG" ]]; then
|
|
export KUBECONFIG="$PROLE_K3S_KUBECONFIG"
|
|
_prole_kubeconfig_maybe_insecure
|
|
return 0
|
|
fi
|
|
if [[ -n "${PROLE_KUBECONFIG:-}" && -f "$PROLE_KUBECONFIG" ]]; then
|
|
export KUBECONFIG="$PROLE_KUBECONFIG"
|
|
_prole_kubeconfig_maybe_insecure
|
|
return 0
|
|
fi
|
|
if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/prole-k3s.kubeconfig" ]]; then
|
|
export KUBECONFIG="$PROLE_HOME/prole-k3s.kubeconfig"
|
|
_prole_kubeconfig_maybe_insecure
|
|
return 0
|
|
fi
|
|
if [[ -f "$_prole_cfg_home_guess/prole-k3s.kubeconfig" ]]; then
|
|
export KUBECONFIG="$_prole_cfg_home_guess/prole-k3s.kubeconfig"
|
|
_prole_kubeconfig_maybe_insecure
|
|
return 0
|
|
fi
|
|
if [[ -r "/etc/rancher/k3s/k3s.yaml" ]]; then
|
|
export KUBECONFIG="/etc/rancher/k3s/k3s.yaml"
|
|
_prole_kubeconfig_maybe_insecure
|
|
return 0
|
|
fi
|
|
_prole_kubeconfig_from_token
|
|
_prole_kubeconfig_maybe_insecure
|
|
}
|
|
|
|
if _prole_is_secret_ref "${PROLE_K3S_TOKEN:-}"; then
|
|
if _prole_is_openbao_ref "${PROLE_K3S_TOKEN:-}"; then
|
|
_decoded=$(_prole_resolve_openbao_ref "${PROLE_K3S_TOKEN:-}")
|
|
else
|
|
_decoded=$(_prole_decrypt_prole_secret "${PROLE_K3S_TOKEN:-}")
|
|
fi
|
|
if [[ -n "$_decoded" ]]; then
|
|
PROLE_K3S_TOKEN="$_decoded"
|
|
export PROLE_K3S_TOKEN
|
|
else
|
|
unset PROLE_K3S_TOKEN
|
|
fi
|
|
fi
|
|
if _prole_is_secret_ref "${K3S_TOKEN:-}"; then
|
|
if _prole_is_openbao_ref "${K3S_TOKEN:-}"; then
|
|
_decoded=$(_prole_resolve_openbao_ref "${K3S_TOKEN:-}")
|
|
else
|
|
_decoded=$(_prole_decrypt_prole_secret "${K3S_TOKEN:-}")
|
|
fi
|
|
if [[ -n "$_decoded" ]]; then
|
|
K3S_TOKEN="$_decoded"
|
|
export K3S_TOKEN
|
|
else
|
|
unset K3S_TOKEN
|
|
fi
|
|
fi
|
|
|
|
if [[ -z "${KUBECONFIG:-}" ]]; then
|
|
_prole_mode_guess=""
|
|
if command -v prole_normalize_mode >/dev/null 2>&1; then
|
|
_prole_mode_guess=$(prole_normalize_mode "${PROLE_MODE:-${DEPLOYMENT_MODE:-${CLUSTER_ENV:-}}}")
|
|
else
|
|
_prole_mode_guess="${PROLE_MODE:-${DEPLOYMENT_MODE:-${CLUSTER_ENV:-}}}"
|
|
fi
|
|
if [[ "$_prole_mode_guess" == "k3s" ]]; then
|
|
prole_ensure_kubeconfig >/dev/null 2>&1 || true
|
|
fi
|
|
unset _prole_mode_guess
|
|
fi
|
|
|
|
_prole_child_ns="${PROLE_NAMESPACE:-${NAMESPACE:-}}"
|
|
_prole_child_id=""
|
|
if [[ -n "${PROLE_CHILD_ID:-}" ]]; then
|
|
_prole_child_id="${PROLE_CHILD_ID}"
|
|
elif [[ -n "$_prole_child_ns" && "$_prole_child_ns" =~ ^prole-db-([A-Za-z0-9]+)$ ]]; then
|
|
_prole_child_id=$(printf '%s' "${BASH_REMATCH[1]}" | tr '[:lower:]' '[:upper:]')
|
|
fi
|
|
|
|
_prole_parent_realm="${PROLE_PARENT_REALM:-${REALM:-${KRB5_REALM:-${kerberos_config_realm:-}}}}"
|
|
if [[ -n "$_prole_parent_realm" ]]; then
|
|
_prole_parent_realm=$(printf '%s' "$_prole_parent_realm" | tr '[:lower:]' '[:upper:]')
|
|
if [[ -z "${PROLE_PARENT_REALM:-}" ]]; then
|
|
PROLE_PARENT_REALM="$_prole_parent_realm"
|
|
export PROLE_PARENT_REALM
|
|
fi
|
|
fi
|
|
|
|
if [[ -n "$_prole_child_id" ]]; then
|
|
PROLE_CHILD_ID="${PROLE_CHILD_ID:-$_prole_child_id}"
|
|
export PROLE_CHILD_ID
|
|
if [[ -n "${PROLE_CHILD_REALM:-}" ]]; then
|
|
:
|
|
elif [[ -n "$_prole_parent_realm" ]]; then
|
|
if [[ "$_prole_parent_realm" == "${PROLE_CHILD_ID}."* ]]; then
|
|
PROLE_CHILD_REALM="$_prole_parent_realm"
|
|
if [[ -z "${PROLE_PARENT_REALM:-}" ]]; then
|
|
PROLE_PARENT_REALM="${_prole_parent_realm#${PROLE_CHILD_ID}.}"
|
|
export PROLE_PARENT_REALM
|
|
fi
|
|
else
|
|
PROLE_CHILD_REALM="${PROLE_CHILD_ID}.${_prole_parent_realm}"
|
|
fi
|
|
fi
|
|
|
|
if [[ -n "${PROLE_CHILD_REALM:-}" ]]; then
|
|
export PROLE_CHILD_REALM
|
|
PROLE_CHILD_WORKGROUP="${PROLE_CHILD_WORKGROUP:-${PROLE_CHILD_ID}}"
|
|
PROLE_CHILD_NETBIOS_NAME="${PROLE_CHILD_NETBIOS_NAME:-${PROLE_CHILD_ID}}"
|
|
PROLE_CHILD_SERVER_STRING="${PROLE_CHILD_SERVER_STRING:-${PROLE_CHILD_REALM} AD DC}"
|
|
export PROLE_CHILD_WORKGROUP PROLE_CHILD_NETBIOS_NAME PROLE_CHILD_SERVER_STRING
|
|
fi
|
|
fi
|
|
|
|
if [[ -z "${PROLE_USE_CHILD_REALM:-}" && -n "${PROLE_CHILD_REALM:-}" ]]; then
|
|
PROLE_USE_CHILD_REALM=1
|
|
export PROLE_USE_CHILD_REALM
|
|
fi
|
|
|
|
if _prole_bool_true "${PROLE_USE_CHILD_REALM:-}" && [[ -n "${PROLE_CHILD_REALM:-}" ]]; then
|
|
if [[ -z "${KRB5_REALM:-}" || ( -n "${PROLE_PARENT_REALM:-}" && "${KRB5_REALM}" == "${PROLE_PARENT_REALM}" ) ]]; then
|
|
KRB5_REALM="${PROLE_CHILD_REALM}"
|
|
export KRB5_REALM
|
|
fi
|
|
if [[ -z "${REALM:-}" || ( -n "${PROLE_PARENT_REALM:-}" && "${REALM}" == "${PROLE_PARENT_REALM}" ) ]]; then
|
|
REALM="${PROLE_CHILD_REALM}"
|
|
export REALM
|
|
fi
|
|
fi
|
|
|
|
prole_register_port_forward() {
|
|
local id="$1" ns="$2" target="$3" hostPort="$4" servicePort="$5"
|
|
local addr="${6:-0.0.0.0}"
|
|
local proto="${7:-TCP}"
|
|
local desc="${8:-}"
|
|
echo "PORT_FORWARD_MAPPING: id=$id;namespace=$ns;target=$target;address=$addr;hostPort=$hostPort;servicePort=$servicePort;protocol=$proto;description=$desc"
|
|
}
|
|
|
|
unset _prole_cfg_script_dir
|
|
unset _prole_cfg_home_guess
|
|
unset _prole_cfg_file
|
|
unset _PROLE_CFG_SET_VARS
|