From 42ec9bb7ab16d5521c38cb83469f1f0f43c99156 Mon Sep 17 00:00:00 2001 From: chrisfu Date: Fri, 6 Feb 2026 21:22:26 -0800 Subject: [PATCH] Refactor shell scripts for manifest rendering via `prole_render_manifest` function, introduce dynamic port forwarding configuration, and improve handling for k3d compatibility. --- .gitignore | 1 + conf/port-mappings.properties | 2 +- etc/init_cloudnative_pg.sh | 26 ++++++++++++--- etc/init_garage_store.sh | 35 +++++++++++++++++++- etc/init_openbao.sh | 8 ++--- etc/init_opentofu.sh | 2 +- etc/init_port_forwards.sh | 61 +++++++++++++++++++++++++++++++++-- etc/init_prole-db.sh | 6 ++-- etc/prole_cfg.sh | 19 +++++++++++ install.py | 3 +- 10 files changed, 145 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 67a918b..9140242 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ /prole/backup/ # Secrets and local config +*.kubeconfig etc/secrets/ prole/conf/ *-password.txt diff --git a/conf/port-mappings.properties b/conf/port-mappings.properties index 7357178..76c2198 100644 --- a/conf/port-mappings.properties +++ b/conf/port-mappings.properties @@ -3,6 +3,6 @@ - + diff --git a/etc/init_cloudnative_pg.sh b/etc/init_cloudnative_pg.sh index 6a51963..c6597b9 100755 --- a/etc/init_cloudnative_pg.sh +++ b/etc/init_cloudnative_pg.sh @@ -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 diff --git a/etc/init_garage_store.sh b/etc/init_garage_store.sh index e8f8801..26861b1 100755 --- a/etc/init_garage_store.sh +++ b/etc/init_garage_store.sh @@ -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 diff --git a/etc/init_openbao.sh b/etc/init_openbao.sh index 0dba596..38b78b0 100755 --- a/etc/init_openbao.sh +++ b/etc/init_openbao.sh @@ -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 diff --git a/etc/init_opentofu.sh b/etc/init_opentofu.sh index 12cf281..268eb7e 100755 --- a/etc/init_opentofu.sh +++ b/etc/init_opentofu.sh @@ -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 } diff --git a/etc/init_port_forwards.sh b/etc/init_port_forwards.sh index a55b7e8..d1f54a9 100755 --- a/etc/init_port_forwards.sh +++ b/etc/init_port_forwards.sh @@ -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 < [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 diff --git a/etc/init_prole-db.sh b/etc/init_prole-db.sh index 9149ca6..5ee445f 100755 --- a/etc/init_prole-db.sh +++ b/etc/init_prole-db.sh @@ -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 diff --git a/etc/prole_cfg.sh b/etc/prole_cfg.sh index b23edab..4b17de6 100644 --- a/etc/prole_cfg.sh +++ b/etc/prole_cfg.sh @@ -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" diff --git a/install.py b/install.py index ab5a14f..98103b3 100755 --- a/install.py +++ b/install.py @@ -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': {},