prole/scripts/reset-ns.sh
chrisfu fdc1582bd0 Fix DB image build and k3s registry/CNPG robustness
- update Percona Dockerfiles for compatible extension/tooling install flow\n- add HTTP/HTTPS-aware k3s registry configuration path across scripts/Ansible\n- harden CNPG TLS bootstrap CN handling for long namespaces and add regression test\n- improve namespace reset pod-deletion wait behavior

Co-authored-by: Junie <junie@jetbrains.com>
2026-03-29 19:35:37 -07:00

261 lines
8.5 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'EOF'
Usage:
reset-ns.sh -n <namespace> [options]
Required:
-n <namespace> Namespace to reset (REQUIRED; no implicit default)
Options:
--delete-pvcs Delete all PVCs in the namespace (DANGEROUS: wipes state)
--delete-config Delete ConfigMaps and Secrets (VERY DANGEROUS)
--keep-registry Preserve registry workloads/services in the target namespace
--clear-node-reservations
Best-effort clear node reservations by uncordoning nodes
--no-clear-node-reservations
Disable node reservation cleanup (default)
-h, --help Show this help
Notes:
• This script NEVER deletes the namespace itself — only resources inside it.
• It will NOT delete the CNPG operator (cluster-wide), but if CNPG CRDs exist,
it WILL delete CNPG Cluster resources IN THE TARGET NAMESPACE so pods stop respawning.
• It will NEVER delete Service "kubernetes" in the default namespace.
Examples:
./reset-ns.sh -n default
./reset-ns.sh -n default --delete-pvcs
EOF
}
NS=""
DELETE_PVCS=false
DELETE_CONFIG=false
KEEP_REGISTRY=false
CLEAR_NODE_RESERVATIONS=false
[[ $# -eq 0 ]] && usage && exit 1
while [[ $# -gt 0 ]]; do
case "$1" in
-n) NS="${2:-}"; shift 2 ;;
--delete-pvcs) DELETE_PVCS=true; shift ;;
--delete-config) DELETE_CONFIG=true; shift ;;
--keep-registry) KEEP_REGISTRY=true; shift ;;
--clear-node-reservations) CLEAR_NODE_RESERVATIONS=true; shift ;;
--no-clear-node-reservations) CLEAR_NODE_RESERVATIONS=false; shift ;;
-h|--help) usage; exit 0 ;;
*) echo "Unknown argument: $1"; echo; usage; exit 1 ;;
esac
done
if [[ -z "${NS}" ]]; then
echo "ERROR: -n <namespace> is required"
echo
usage
exit 1
fi
# Refuse truly critical system namespaces; allow "default" if explicitly passed.
case "$NS" in
kube-system|kube-public|kube-node-lease)
echo "REFUSING to operate on protected namespace: $NS"
exit 2
;;
esac
echo "Resetting namespace: $NS"
echo " DELETE_PVCS=$DELETE_PVCS"
echo " DELETE_CONFIG=$DELETE_CONFIG"
echo " KEEP_REGISTRY=$KEEP_REGISTRY"
echo " CLEAR_NODE_RESERVATIONS=$CLEAR_NODE_RESERVATIONS"
echo
if ! kubectl get namespace "$NS" >/dev/null 2>&1; then
echo "Namespace '$NS' does not exist; skipping."
exit 0
fi
is_registry_resource() {
local kind="$1"
local name="$2"
[[ "$KEEP_REGISTRY" == "true" ]] || return 1
if [[ "$name" == "registry" ]]; then
return 0
fi
local app_label=""
app_label=$(kubectl -n "$NS" get "$kind" "$name" -o jsonpath='{.metadata.labels.app}' 2>/dev/null || true)
[[ "$app_label" == "registry" ]]
}
scale_down_workloads() {
local kind="$1"
local item
while IFS= read -r item; do
[[ -z "$item" ]] && continue
if is_registry_resource "$kind" "$item"; then
echo " preserving ${kind}/${item}"
continue
fi
kubectl -n "$NS" scale "$kind" "$item" --replicas=0 2>/dev/null || true
done < <(kubectl -n "$NS" get "$kind" -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' 2>/dev/null || true)
}
delete_kind_resources() {
local kind="$1"
local item
while IFS= read -r item; do
[[ -z "$item" ]] && continue
if is_registry_resource "$kind" "$item"; then
echo " preserving ${kind}/${item}"
continue
fi
kubectl -n "$NS" delete "$kind" "$item" --ignore-not-found --wait=false 2>/dev/null || true
done < <(kubectl -n "$NS" get "$kind" -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' 2>/dev/null || true)
}
# -------------------------
# CNPG: stop respawning in THIS namespace by deleting CNPG Cluster CRs
# Detect via CRD existence (more reliable than kubectl api-resources on some setups)
# -------------------------
if kubectl get crd clusters.postgresql.cnpg.io >/dev/null 2>&1; then
echo "CNPG detected (CRD clusters.postgresql.cnpg.io exists)."
echo "Deleting CNPG resources in namespace '$NS' (operator NOT touched)..."
# List first (helpful visibility; won't fail the script)
kubectl -n "$NS" get clusters.postgresql.cnpg.io 2>/dev/null || true
# Delete Cluster CRs (this is what causes StatefulSets/Pods to be recreated)
kubectl -n "$NS" delete clusters.postgresql.cnpg.io --all --ignore-not-found --wait=false
# Related CNPG CRs (safe best-effort)
if kubectl get crd poolers.postgresql.cnpg.io >/dev/null 2>&1; then
kubectl -n "$NS" delete poolers.postgresql.cnpg.io --all --ignore-not-found --wait=false || true
fi
if kubectl get crd backups.postgresql.cnpg.io >/dev/null 2>&1; then
kubectl -n "$NS" delete backups.postgresql.cnpg.io --all --ignore-not-found --wait=false || true
fi
if kubectl get crd scheduledbackups.postgresql.cnpg.io >/dev/null 2>&1; then
kubectl -n "$NS" delete scheduledbackups.postgresql.cnpg.io --all --ignore-not-found --wait=false || true
fi
# Small pause so the operator observes deletion and stops reconciling
sleep 2
else
echo "CNPG not detected via CRD check; skipping CNPG cleanup."
fi
# -------------------------
# Scale down controllers to reduce churn
# -------------------------
echo "Scaling down Deployments/StatefulSets..."
scale_down_workloads deployment
scale_down_workloads statefulset
# -------------------------
# Delete common workload controllers
# -------------------------
echo "Deleting workload controllers..."
delete_kind_resources deployment
delete_kind_resources statefulset
delete_kind_resources daemonset
delete_kind_resources replicaset
delete_kind_resources job
delete_kind_resources cronjob
# -------------------------
# Delete services safely (NEVER delete "kubernetes" service in default)
# -------------------------
echo "Deleting services (excluding service/kubernetes)..."
for s in $(kubectl -n "$NS" get svc -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' 2>/dev/null || true); do
if [[ "$NS" == "default" && "$s" == "kubernetes" ]]; then
echo " skipping protected service: default/kubernetes"
continue
fi
if is_registry_resource service "$s"; then
echo " preserving service/$s"
continue
fi
kubectl -n "$NS" delete svc "$s" --ignore-not-found --wait=false
done
echo "Deleting ingress & networkpolicy..."
kubectl -n "$NS" delete ingress,networkpolicy --all --ignore-not-found --wait=false
# -------------------------
# Delete HPA / PDB
# -------------------------
echo "Deleting HPA / PDB (if any)..."
kubectl -n "$NS" delete hpa,pdb --all --ignore-not-found --wait=false 2>/dev/null || true
# -------------------------
# Optional config wipe
# -------------------------
if [[ "$DELETE_CONFIG" == "true" ]]; then
echo "Deleting ConfigMaps..."
kubectl -n "$NS" delete configmap --all --ignore-not-found --wait=false
echo "Deleting Secrets (excluding service-account tokens)..."
for s in $(kubectl -n "$NS" get secret -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' 2>/dev/null || true); do
type=$(kubectl -n "$NS" get secret "$s" -o jsonpath='{.type}' 2>/dev/null || true)
[[ "$type" == "kubernetes.io/service-account-token" ]] && continue
kubectl -n "$NS" delete secret "$s" --ignore-not-found --wait=false
done
fi
# -------------------------
# Optional PVC wipe
# -------------------------
if [[ "$DELETE_PVCS" == "true" ]]; then
echo "Deleting PVCs..."
kubectl -n "$NS" delete pvc --all --ignore-not-found --wait=false
fi
# -------------------------
# Final pod cleanup
# -------------------------
echo "Deleting remaining pods..."
deleted_pods=()
for p in $(kubectl -n "$NS" get pod -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' 2>/dev/null || true); do
if is_registry_resource pod "$p"; then
echo " preserving pod/$p"
continue
fi
kubectl -n "$NS" delete pod "$p" --ignore-not-found --wait=false 2>/dev/null || true
deleted_pods+=("$p")
done
echo "Waiting for pods to terminate..."
if [[ ${#deleted_pods[@]} -gt 0 ]]; then
deadline=$((SECONDS + 180))
for p in "${deleted_pods[@]}"; do
while kubectl -n "$NS" get pod "$p" >/dev/null 2>&1; do
if (( SECONDS >= deadline )); then
echo " timeout waiting for pod/$p to terminate; continuing"
break
fi
sleep 2
done
if ! kubectl -n "$NS" get pod "$p" >/dev/null 2>&1; then
echo " pod/$p terminated"
fi
done
fi
if [[ "$CLEAR_NODE_RESERVATIONS" == "true" ]]; then
echo "Clearing node reservations (uncordon all nodes)..."
while IFS= read -r node; do
[[ -z "$node" ]] && continue
kubectl uncordon "$node" >/dev/null 2>&1 || true
done < <(kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' 2>/dev/null || true)
fi
echo
echo "Namespace '$NS' reset complete (namespace preserved)."