diff --git a/k8s/automated-nfs-backup.sh b/k8s/automated-nfs-backup.sh index 10d3158..83ae174 100644 --- a/k8s/automated-nfs-backup.sh +++ b/k8s/automated-nfs-backup.sh @@ -4,7 +4,11 @@ SCRIPT_NAME="$(basename "$0")" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # shellcheck source=lib/common.sh source "${SCRIPT_DIR}/lib/common.sh" -setup_exit_handling + +# Core error/exit traps — ERR trap logs the failure, EXIT trap guarantees +# every scaled-down namespace is restored regardless of how we exit. +trap 'on_error "${LINENO}" "${BASH_COMMAND:-unknown}"' ERR +trap '_exit_code=$?; restore_all_remaining; if [[ "$_exit_code" -eq 0 ]]; then log_info "Finished successfully"; else log_error "Finished with errors (exit code $_exit_code)"; fi' EXIT # Required configuration require_env "NFS_SOURCE_PATH" @@ -14,7 +18,7 @@ require_env "BACKUP_OUTPUT_PATH" BACKUP_PASSWORD="${BACKUP_PASSWORD:-}" KUBECTL_BIN="${KUBECTL_BIN:-kubectl}" KUBE_CONTEXT="${KUBE_CONTEXT:-}" -WORKLOAD_KINDS="${WORKLOAD_KINDS:-deployment,statefulset,replicaset,replicationcontroller}" +WORKLOAD_KINDS="${WORKLOAD_KINDS:-deployment,statefulset,replicaset}" ARCHIVE_PREFIX="${ARCHIVE_PREFIX:-nfs-backup}" ARCHIVE_TS_FORMAT="${ARCHIVE_TS_FORMAT:-%Y%m%d_%H%M%S}" SEVENZ_METHOD="${SEVENZ_METHOD:-lzma2}" @@ -112,11 +116,9 @@ namespace_for_folder() { return 1 } -state_file_for_folder() { - local folder_name="${1:?folder name required}" - local safe_name - safe_name="$(printf '%s' "$folder_name" | tr -c 'A-Za-z0-9._-' '_')" - printf '%s/%s.state' "$TMP_STATE_DIR" "$safe_name" +state_file_for_namespace() { + local namespace="${1:?namespace required}" + printf '%s/%s.state' "$TMP_STATE_DIR" "$namespace" } capture_replicas_state() { @@ -192,6 +194,39 @@ restore_namespace_replicas() { done < "$state_file" } +restore_all_remaining() { + if [[ -z "${TMP_STATE_DIR:-}" ]] || [[ ! -d "${TMP_STATE_DIR:-}" ]]; then + return 0 + fi + + local state_file + local namespace + local kind name replicas + local found=0 + + for state_file in "${TMP_STATE_DIR}"/*.state; do + [[ -f "$state_file" ]] || continue + found=1 + namespace="$(basename "$state_file" .state)" + [[ -z "$namespace" ]] && continue + log_info "EXIT cleanup: restoring workloads in namespace '${namespace}'" + while IFS=$'\t' read -r kind name replicas; do + [[ -z "$kind" || -z "$name" ]] && continue + if scale_resource "$namespace" "$kind" "$name" "$replicas"; then + log_info "EXIT cleanup: restored ${namespace}:${kind}/${name} to ${replicas}" + else + log_warn "EXIT cleanup: failed to restore ${namespace}:${kind}/${name} to ${replicas}" + restore_warnings=$((restore_warnings + 1)) + fi + done < "$state_file" + rm -f "$state_file" + done + + if [[ "$found" -eq 1 ]]; then + log_info "EXIT cleanup complete - all remaining workloads restored" + fi +} + resource_replicas() { local namespace="${1:?namespace required}" local kind="${2:?kind required}" @@ -388,7 +423,7 @@ process_folder() { has_mapping=1 mapped_folders=$((mapped_folders + 1)) log_info "Exact namespace match found for folder '${folder_name}' -> namespace '${namespace}'" - state_file="$(state_file_for_folder "$folder_name")" + state_file="$(state_file_for_namespace "$namespace")" capture_replicas_state "$namespace" "$state_file" scale_namespace_to_zero "$namespace" "$state_file"