Refactor shell scripts for manifest rendering via prole_render_manifest function, introduce dynamic port forwarding configuration, and improve handling for k3d compatibility.

This commit is contained in:
chrisfu 2026-02-06 21:22:26 -08:00
parent 63e6f18b5b
commit 42ec9bb7ab
10 changed files with 145 additions and 18 deletions

1
.gitignore vendored
View File

@ -41,6 +41,7 @@
/prole/backup/
# Secrets and local config
*.kubeconfig
etc/secrets/
prole/conf/
*-password.txt

View File

@ -3,6 +3,6 @@
<mapping id="dashboard" namespace="kubernetes-dashboard" target="svc/kubernetes-dashboard-kong-proxy" address="127.0.0.1" hostPort="8443" servicePort="443" protocol="TCP" description="Kubernetes Dashboard (https://127.0.0.1:8443)"/>
<mapping id="prometheus" namespace="default" target="svc/prometheus-community-kube-prometheus" address="127.0.0.1" hostPort="9090" servicePort="9090" protocol="TCP" description="Prometheus UI (http://127.0.0.1:9090)"/>
<mapping id="grafana" namespace="${NAMESPACE}" target="svc/grafana-prole" address="0.0.0.0" hostPort="3000" servicePort="80" protocol="TCP" description="Grafana UI (http://127.0.0.1:3000)"/>
<mapping id="postgres" namespace="${NAMESPACE}" target="svc/prole-db-rw" address="0.0.0.0" hostPort="5432" servicePort="5432" protocol="TCP" description="PostgreSQL (primary) (127.0.0.1:5432)"/>
<mapping id="postgres" namespace="${NAMESPACE}" target="svc/prole-db-rw" address="0.0.0.0" hostPort="5432" servicePort="5432" protocol="TCP" description="PostgreSQL (primary) (0.0.0.0:5432)"/>
<mapping id="openbao" namespace="${NAMESPACE}" target="svc/openbao" address="127.0.0.1" hostPort="8200" servicePort="8200" protocol="TCP" description="OpenBao UI (http://127.0.0.1:8200)"/>
</portMappings>

View File

@ -324,12 +324,28 @@ apply_barman_objectstore_if_present() {
return 0
fi
if kubectl get crd objectstores.barmancloud.cnpg.io >/dev/null 2>&1; then
kubectl apply -n "$NAMESPACE" -f "$BARMAN_OBJECTSTORE_MANIFEST"
prole_render_manifest "$BARMAN_OBJECTSTORE_MANIFEST" | kubectl apply -n "$NAMESPACE" -f -
else
echo "WARN: Barman Cloud ObjectStore CRD not found; skipping $BARMAN_OBJECTSTORE_MANIFEST."
fi
}
apply_prole_manifest_file() {
local file="$1"
local output=""
if output=$(prole_render_manifest "$file" | kubectl apply -n "$NAMESPACE" -f - 2>&1); then
printf '%s\n' "$output"
return 0
fi
if [[ "${PROLE_MODE:-}" == "k3d" && "$(basename "$file")" == "garage-statefulset.yaml" ]] \
&& echo "$output" | grep -q "updates to statefulset spec"; then
echo "WARN: Garage StatefulSet immutable in k3d; skipping apply."
return 0
fi
echo "$output" >&2
return 1
}
ensure_prole_stack_resources() {
echo "Applying CloudNative-PG cluster and related resources ..."
ensure_grafana_admin_secret
@ -346,7 +362,7 @@ ensure_prole_stack_resources() {
continue
;;
esac
kubectl apply -n "$NAMESPACE" -f "$file"
apply_prole_manifest_file "$file"
done
apply_barman_objectstore_if_present
apply_cnpg_cluster_manifest "$CNPG_MANIFEST_OVERRIDE"
@ -359,7 +375,7 @@ ensure_prole_stack_resources() {
continue
;;
esac
kubectl apply -n "$NAMESPACE" -f "$file"
apply_prole_manifest_file "$file"
done
apply_barman_objectstore_if_present
apply_cnpg_cluster_manifest "$CNPG_MANIFEST"
@ -390,7 +406,7 @@ apply_cnpg_cluster_manifest() {
local i out
for ((i=1; i<=attempts; i++)); do
if out=$(kubectl apply -n "$NAMESPACE" -f "$manifest" 2>&1); then
if out=$(prole_render_manifest "$manifest" | kubectl apply -n "$NAMESPACE" -f - 2>&1); then
printf '%s\n' "$out"
return 0
fi
@ -864,7 +880,7 @@ case "$ACTION" in
exit 1
fi
echo "Starting CloudNative-PG cluster from $CNPG_MANIFEST in namespace $NAMESPACE..."
kubectl apply -n "$NAMESPACE" -f "$CNPG_MANIFEST"
prole_render_manifest "$CNPG_MANIFEST" | kubectl apply -n "$NAMESPACE" -f -
;;
stop)
ensure_tools

View File

@ -97,7 +97,18 @@ ensure_secrets() {
apply_manifests() {
for f in "${GARAGE_FILES[@]}"; do
if [[ -f "$f" ]]; then
kubectl apply -n "$NAMESPACE" -f "$f"
local output=""
if output=$(prole_render_manifest "$f" | kubectl apply -n "$NAMESPACE" -f - 2>&1); then
printf '%s\n' "$output"
else
if [[ "${PROLE_MODE:-}" == "k3d" && "$(basename "$f")" == "garage-statefulset.yaml" ]] \
&& echo "$output" | grep -q "updates to statefulset spec"; then
echo "WARN: Garage StatefulSet immutable in k3d; skipping apply."
continue
fi
echo "$output" >&2
exit 1
fi
else
echo "ERROR: Missing manifest: $f" >&2
exit 1
@ -134,6 +145,26 @@ ensure_container_command() {
fi
}
pin_garage_node_k3d() {
if [[ "${PROLE_MODE:-}" != "k3d" ]]; then
return 0
fi
if ! kubectl get statefulset "$GARAGE_NAME" -n "$NAMESPACE" >/dev/null 2>&1; then
return 0
fi
local target=""
target=$(kubectl get nodes -l node-role.kubernetes.io/master -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true)
if [[ -z "$target" ]]; then
target=$(kubectl get nodes -l node-role.kubernetes.io/control-plane -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true)
fi
if [[ -z "$target" ]]; then
return 0
fi
echo "Pinning Garage StatefulSet to node '$target' for k3d exec access ..."
kubectl patch statefulset "$GARAGE_NAME" -n "$NAMESPACE" --type merge \
-p "{\"spec\":{\"template\":{\"spec\":{\"nodeSelector\":{\"kubernetes.io/hostname\":\"$target\"}}}}}" >/dev/null || true
}
restart_statefulset() {
if kubectl get statefulset "$GARAGE_NAME" -n "$NAMESPACE" >/dev/null 2>&1; then
echo "Restarting Garage StatefulSet to pick up config changes ..."
@ -271,6 +302,7 @@ case "$ACTION" in
echo "Applying Garage manifests in namespace '$NAMESPACE'..."
apply_manifests
ensure_container_command
pin_garage_node_k3d
restart_statefulset
wait_ready
init_layout
@ -287,6 +319,7 @@ case "$ACTION" in
echo "Re-applying Garage manifests in namespace '$NAMESPACE'..."
apply_manifests
ensure_container_command
pin_garage_node_k3d
restart_statefulset
wait_ready
init_layout

View File

@ -296,10 +296,10 @@ EOF
apply_k8s() {
echo "Applying OpenBao manifest to namespace '$NAMESPACE' ..."
if [[ -f "$SCRIPT_DIR/../k8s/prole/openbao-statefulset.yaml" ]]; then
kubectl apply -n "$NAMESPACE" -f "$SCRIPT_DIR/../k8s/prole/openbao-statefulset.yaml"
kubectl apply -n "$NAMESPACE" -f "$SCRIPT_DIR/../k8s/prole/openbao-service.yaml"
prole_render_manifest "$SCRIPT_DIR/../k8s/prole/openbao-statefulset.yaml" | kubectl apply -n "$NAMESPACE" -f -
prole_render_manifest "$SCRIPT_DIR/../k8s/prole/openbao-service.yaml" | kubectl apply -n "$NAMESPACE" -f -
else
kubectl apply -n "$NAMESPACE" -f "$OPENBAO_MANIFEST_DIR/deployment.yaml"
prole_render_manifest "$OPENBAO_MANIFEST_DIR/deployment.yaml" | kubectl apply -n "$NAMESPACE" -f -
fi
echo "Applying Kerberos ConfigMap (external realm) to namespace '$NAMESPACE' ..."
kubectl apply -n "$NAMESPACE" -f "$OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml"
@ -685,7 +685,7 @@ case "$ACTION" in
# Read password from stdin if provided
db_pass=""
if [[ ! -t 0 ]]; then
read -r db_pass
read -r db_pass || true
fi
ensure_admin_keypair

View File

@ -169,7 +169,7 @@ ensure_opentofu_secret() {
apply_k8s() {
echo "Applying OpenTofu manifest to namespace '$NAMESPACE' ..."
kubectl apply -n "$NAMESPACE" -f "$OPENTOFU_MANIFEST_DIR/deployment.yaml"
prole_render_manifest "$OPENTOFU_MANIFEST_DIR/deployment.yaml" | kubectl apply -n "$NAMESPACE" -f -
kubectl rollout status deploy/$OPENTOFU_NAME -n "$NAMESPACE" --timeout=120s || true
}

View File

@ -13,7 +13,15 @@ source "$SCRIPT_DIR/prole_cfg.sh"
PROLE_HOME="${PROLE_HOME:-/Users/chrisfu/dev/prole}"
VERBOSE=0
CONFIG_FILE="$PROLE_HOME/conf/port-mappings.properties"
CONFIG_FILE=""
CONFIG_FILE_SOURCE="auto"
PORT_FORWARD_ENABLED="${PORT_FORWARD_ENABLED:-}"
PORT_FORWARD_K3D_ENABLED="${PORT_FORWARD_K3D_ENABLED:-}"
PORT_FORWARD_K3S_ENABLED="${PORT_FORWARD_K3S_ENABLED:-}"
PORT_FORWARD_CONFIG_FILE="${PORT_FORWARD_CONFIG_FILE:-}"
PORT_FORWARD_CONFIG_FILE_K3D="${PORT_FORWARD_CONFIG_FILE_K3D:-}"
PORT_FORWARD_CONFIG_FILE_K3S="${PORT_FORWARD_CONFIG_FILE_K3S:-}"
usage() {
cat <<EOF
@ -21,7 +29,7 @@ Usage:
$PROG [-v|--verbose] [-f|--force] [-c|--config-file=FILE] <start|stop|restart|status> [component]
Options:
-c, --config-file=FILE Path to local-ports.properties (XML)
-c, --config-file=FILE Path to port-mappings.properties (XML)
-v, --verbose Verbose output
-f, --force Force: kill existing processes blocking ports
@ -85,6 +93,41 @@ is_truthy() {
return 1
}
resolve_config_file() {
if [ -n "${PORT_FORWARD_CONFIG_FILE:-}" ]; then
printf '%s' "$PORT_FORWARD_CONFIG_FILE"
return 0
fi
case "${PROLE_MODE:-}" in
k3s)
if [ -n "${PORT_FORWARD_CONFIG_FILE_K3S:-}" ]; then
printf '%s' "$PORT_FORWARD_CONFIG_FILE_K3S"
return 0
fi
;;
k3d|"")
if [ -n "${PORT_FORWARD_CONFIG_FILE_K3D:-}" ]; then
printf '%s' "$PORT_FORWARD_CONFIG_FILE_K3D"
return 0
fi
;;
esac
printf '%s' "$PROLE_HOME/conf/port-mappings.properties"
}
port_forward_enabled_for_mode() {
local val=""
case "${PROLE_MODE:-}" in
k3s) val="${PORT_FORWARD_K3S_ENABLED:-${PORT_FORWARD_ENABLED:-}}" ;;
k3d|"") val="${PORT_FORWARD_K3D_ENABLED:-${PORT_FORWARD_ENABLED:-}}" ;;
*) val="${PORT_FORWARD_ENABLED:-}" ;;
esac
if [ -z "$val" ]; then
return 0
fi
is_truthy "$val"
}
port_in_use() {
local port="$1"
if have lsof; then
@ -630,6 +673,10 @@ scan_for_collisions() {
}
do_start() {
if ! port_forward_enabled_for_mode; then
log "Port forwards disabled for mode '${PROLE_MODE:-auto}'."
return 0
fi
validate_env
if is_local_mode; then
# Preflight: require Docker daemon and k3d (if applicable)
@ -651,6 +698,10 @@ do_stop() {
}
do_restart() {
if ! port_forward_enabled_for_mode; then
log "Port forwards disabled for mode '${PROLE_MODE:-auto}'."
return 0
fi
validate_env
if is_local_mode; then
# Preflight: require Docker daemon and k3d (if applicable)
@ -758,10 +809,12 @@ while [ $# -gt 0 ]; do
shift
[ $# -gt 0 ] || { err "-c requires a file path"; usage; exit 2; }
CONFIG_FILE="$1"
CONFIG_FILE_SOURCE="cli"
shift
;;
--config-file=*)
CONFIG_FILE="${1#*=}"
CONFIG_FILE_SOURCE="cli"
shift
;;
-h|--help)
@ -780,6 +833,10 @@ while [ $# -gt 0 ]; do
esac
done
if [ "$CONFIG_FILE_SOURCE" = "auto" ]; then
CONFIG_FILE="$(resolve_config_file)"
fi
[ -n "$ACTION" ] || { usage; exit 2; }
case "$ACTION" in

View File

@ -153,7 +153,7 @@ start() {
# Ensure cluster exists before attempting to patch image
if ! kubectl get cluster "$CNPG_CLUSTER_NAME" -n "$NAMESPACE" >/dev/null 2>&1; then
echo "Cluster '$CNPG_CLUSTER_NAME' not found in namespace '$NAMESPACE'. Applying manifest..."
kubectl apply -n "$NAMESPACE" -f "$CNPG_MANIFEST"
prole_render_manifest "$CNPG_MANIFEST" | kubectl apply -n "$NAMESPACE" -f -
fi
# Compare latest image with deployed
@ -178,7 +178,7 @@ start() {
fi
echo "Starting prole-db cluster (ensuring manifest is applied)..."
kubectl apply -n "$NAMESPACE" -f "$CNPG_MANIFEST"
prole_render_manifest "$CNPG_MANIFEST" | kubectl apply -n "$NAMESPACE" -f -
}
stop() {
@ -248,7 +248,7 @@ deploy() {
# Ensure image is updated if manifest has an older version
# First, apply the manifest to ensure the cluster exists/is updated
echo "Applying manifest $CNPG_MANIFEST..."
kubectl apply -n "$NAMESPACE" -f "$CNPG_MANIFEST"
prole_render_manifest "$CNPG_MANIFEST" | kubectl apply -n "$NAMESPACE" -f -
# Then force the specific image version via patch if different
local current_image

View File

@ -118,6 +118,25 @@ prole_set_mode() {
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"

View File

@ -867,7 +867,7 @@ def _render_prole_cfg(inputs: dict, globals_to_save: dict, sections: dict, gener
content.append('')
sections_order = [
'Welcome', 'Dependencies', 'Network', 'System Environment',
'Welcome', 'Dependencies', 'Network', 'Port Forwards', 'System Environment',
'Monitoring', 'Kerberos Authentication', 'Optional Features', 'Database Creation',
'Initialize Cluster', 'Dev Cluster (k3d)', 'Service Cluster (k3s)', 'Prod Cluster (k8s)',
'Docker Build', 'Initialization Scripts', 'Deployment', 'Install'
@ -1197,6 +1197,7 @@ class ProleInstaller:
'Welcome': {},
'Dependencies': {},
'Network': {},
'Port Forwards': {},
'System Environment': {},
'Kerberos Authentication': {},
'Optional Features': {},