gitops
This commit is contained in:
@@ -0,0 +1,256 @@
|
||||
#!/usr/bin/env bash
|
||||
# =====================================================================
|
||||
# extract-manifests.sh
|
||||
#
|
||||
# Estrae tutte le risorse namespaced dai namespace indicati, le ripulisce
|
||||
# con kubectl-neat (rimuove resourceVersion, uid, status, managedFields,
|
||||
# creationTimestamp, ecc.) e le salva organizzate per namespace/kind,
|
||||
# pronte per essere committate in un repo GitOps.
|
||||
#
|
||||
# Uso:
|
||||
# ./extract-manifests.sh [opzioni] <namespace1> <namespace2> ...
|
||||
# ./extract-manifests.sh [opzioni] --namespaces-file namespaces.txt
|
||||
#
|
||||
# Opzioni:
|
||||
# -o, --output <dir> Directory di output (default: ./export)
|
||||
# -s, --include-secrets Include anche i Secret (in CHIARO, base64 non
|
||||
# cifrato — vedi warning qui sotto). Di default
|
||||
# i Secret vengono SALTATI per sicurezza.
|
||||
# -f, --namespaces-file File con un namespace per riga. Righe vuote e
|
||||
# righe che iniziano con # vengono ignorate.
|
||||
# -k, --kinds <lista> Lista custom di kind separati da virgola,
|
||||
# al posto della discovery automatica
|
||||
# (es. "deployment,service,httproute")
|
||||
# -h, --help Mostra questo help
|
||||
#
|
||||
# Esempi:
|
||||
# ./extract-manifests.sh monitoring minio idcidp-dev
|
||||
# ./extract-manifests.sh -o ./gitops-repo/imported -s monitoring
|
||||
# ./extract-manifests.sh -k "httproute,gateway" nginx-gateway
|
||||
# ./extract-manifests.sh -f ./namespaces.txt -o ./gitops-repo/imported
|
||||
#
|
||||
# Prerequisiti:
|
||||
# - kubectl configurato e puntato al cluster corretto
|
||||
# - kubectl-neat installato (kubectl krew install neat)
|
||||
# https://github.com/itaysk/kubectl-neat
|
||||
# =====================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Default
|
||||
# ---------------------------------------------------------------------
|
||||
OUTPUT_DIR="./export"
|
||||
INCLUDE_SECRETS="false"
|
||||
CUSTOM_KINDS=""
|
||||
NAMESPACES_FILE=""
|
||||
|
||||
# Kind che non ha senso portare in un repo GitOps: generati/gestiti
|
||||
# automaticamente da controller, non sono mai "desired state" da
|
||||
# dichiarare a mano.
|
||||
EXCLUDED_KINDS="events pods replicasets endpoints endpointslices controllerrevisions"
|
||||
|
||||
usage() {
|
||||
grep '^#' "$0" | sed -e 's/^#//' -e 's/^ //'
|
||||
exit 0
|
||||
}
|
||||
|
||||
load_namespaces_from_file() {
|
||||
local file_path="$1"
|
||||
|
||||
if [[ ! -f "$file_path" ]]; then
|
||||
echo "Errore: file namespace non trovato: $file_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
line="${line%$'\r'}"
|
||||
|
||||
[[ -z "$line" ]] && continue
|
||||
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
||||
|
||||
line="${line#"${line%%[![:space:]]*}"}"
|
||||
line="${line%"${line##*[![:space:]]}"}"
|
||||
|
||||
[[ -z "$line" ]] && continue
|
||||
|
||||
NAMESPACES+=("$line")
|
||||
done < "$file_path"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Parsing argomenti
|
||||
# ---------------------------------------------------------------------
|
||||
NAMESPACES=()
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-o|--output)
|
||||
OUTPUT_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
-s|--include-secrets)
|
||||
INCLUDE_SECRETS="true"
|
||||
shift
|
||||
;;
|
||||
-f|--namespaces-file)
|
||||
NAMESPACES_FILE="$2"
|
||||
shift 2
|
||||
;;
|
||||
-k|--kinds)
|
||||
CUSTOM_KINDS="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
;;
|
||||
-*)
|
||||
echo "Opzione sconosciuta: $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
NAMESPACES+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -n "$NAMESPACES_FILE" ]]; then
|
||||
load_namespaces_from_file "$NAMESPACES_FILE"
|
||||
fi
|
||||
|
||||
if [[ ${#NAMESPACES[@]} -eq 0 ]]; then
|
||||
echo "Errore: specifica almeno un namespace o usa --namespaces-file." >&2
|
||||
echo "Uso: $0 [opzioni] <namespace1> <namespace2> ..." >&2
|
||||
echo " o: $0 [opzioni] --namespaces-file namespaces.txt" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Prerequisiti
|
||||
# ---------------------------------------------------------------------
|
||||
if ! command -v kubectl >/dev/null 2>&1; then
|
||||
echo "Errore: kubectl non trovato nel PATH." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! kubectl neat --help >/dev/null 2>&1; then
|
||||
echo "Errore: plugin kubectl-neat non trovato." >&2
|
||||
echo "Installa con: kubectl krew install neat" >&2
|
||||
echo "(krew: https://krew.sigs.k8s.io/docs/user-guide/setup/install/)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
echo "==> Output directory: $OUTPUT_DIR"
|
||||
echo "==> Namespace da processare: ${NAMESPACES[*]}"
|
||||
echo "==> Include Secret: $INCLUDE_SECRETS"
|
||||
echo ""
|
||||
|
||||
if [[ "$INCLUDE_SECRETS" == "true" ]]; then
|
||||
echo "########################################################"
|
||||
echo "# ATTENZIONE: i Secret verranno esportati in CHIARO #"
|
||||
echo "# (base64, NON cifrato). Non committarli in Git così #"
|
||||
echo "# come sono. Usa Sealed Secrets, SOPS o External Secrets #"
|
||||
echo "# Operator prima di aggiungerli al repo. #"
|
||||
echo "########################################################"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Determina i kind namespaced da processare
|
||||
# ---------------------------------------------------------------------
|
||||
get_kinds() {
|
||||
if [[ -n "$CUSTOM_KINDS" ]]; then
|
||||
echo "$CUSTOM_KINDS" | tr ',' '\n'
|
||||
return
|
||||
fi
|
||||
|
||||
# Discovery automatica: tutti i kind namespaced supportati dal cluster
|
||||
# (copre anche le CRD installate, es. httproute, podmonitor, cluster
|
||||
# CNPG, ecc.), escludendo quelli in EXCLUDED_KINDS.
|
||||
kubectl api-resources --namespaced=true --verbs=list -o name 2>/dev/null \
|
||||
| cut -d. -f1 \
|
||||
| sort -u \
|
||||
| while read -r kind; do
|
||||
skip="false"
|
||||
for excl in $EXCLUDED_KINDS; do
|
||||
[[ "$kind" == "$excl" ]] && skip="true" && break
|
||||
done
|
||||
[[ "$skip" == "false" ]] && echo "$kind"
|
||||
done
|
||||
}
|
||||
|
||||
KINDS=$(get_kinds)
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Estrazione
|
||||
# ---------------------------------------------------------------------
|
||||
TOTAL_EXPORTED=0
|
||||
TOTAL_EMPTY=0
|
||||
TOTAL_ERRORS=0
|
||||
|
||||
for ns in "${NAMESPACES[@]}"; do
|
||||
echo "=== Namespace: $ns ==="
|
||||
|
||||
if ! kubectl get namespace "$ns" >/dev/null 2>&1; then
|
||||
echo " ! Namespace '$ns' non trovato, salto." >&2
|
||||
continue
|
||||
fi
|
||||
|
||||
ns_dir="${OUTPUT_DIR}/${ns}"
|
||||
mkdir -p "$ns_dir"
|
||||
|
||||
while IFS= read -r kind; do
|
||||
[[ -z "$kind" ]] && continue
|
||||
|
||||
# Salta i secret a meno che non richiesti esplicitamente
|
||||
if [[ "$kind" == "secrets" || "$kind" == "secret" ]] && [[ "$INCLUDE_SECRETS" != "true" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Conta quante risorse di questo kind esistono nel namespace, per
|
||||
# evitare di scrivere file vuoti/inutili
|
||||
count=$(kubectl get "$kind" -n "$ns" --no-headers 2>/dev/null | wc -l | tr -d ' ')
|
||||
|
||||
if [[ "$count" -eq 0 ]]; then
|
||||
TOTAL_EMPTY=$((TOTAL_EMPTY + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
out_file="${ns_dir}/${kind}.yaml"
|
||||
|
||||
if kubectl get "$kind" -n "$ns" -o yaml 2>/dev/null | kubectl neat > "$out_file" 2>/dev/null; then
|
||||
# kubectl neat su una List vuota/malformata può comunque produrre
|
||||
# un file quasi-vuoto: verifichiamo che contenga davvero "kind:"
|
||||
if grep -q "^kind:" "$out_file" 2>/dev/null || grep -q "^items:" "$out_file" 2>/dev/null; then
|
||||
echo " + ${kind} (${count})"
|
||||
TOTAL_EXPORTED=$((TOTAL_EXPORTED + 1))
|
||||
else
|
||||
rm -f "$out_file"
|
||||
fi
|
||||
else
|
||||
echo " ! Errore esportando ${kind}" >&2
|
||||
rm -f "$out_file"
|
||||
TOTAL_ERRORS=$((TOTAL_ERRORS + 1))
|
||||
fi
|
||||
done <<< "$KINDS"
|
||||
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo "=== Riepilogo ==="
|
||||
echo "Risorse esportate: $TOTAL_EXPORTED"
|
||||
echo "Kind vuoti/saltati: $TOTAL_EMPTY"
|
||||
echo "Errori: $TOTAL_ERRORS"
|
||||
echo ""
|
||||
echo "Output in: $OUTPUT_DIR"
|
||||
echo ""
|
||||
echo "Prossimi passi consigliati:"
|
||||
echo " 1. Rivedi manualmente ogni file: alcuni campi (es. clusterIP,"
|
||||
echo " nodePort, annotazioni iniettate da controller/webhook) vanno"
|
||||
echo " rimossi a mano perché non fanno parte del 'desired state'."
|
||||
echo " 2. Se hai esportato Secret, cifrali (Sealed Secrets / SOPS) prima"
|
||||
echo " di committarli."
|
||||
echo " 3. Riorganizza i file nella struttura del repo GitOps"
|
||||
echo " (infrastructure/ vs apps/, vedi README del repo)."
|
||||
Reference in New Issue
Block a user