#!/usr/bin/env bash set -o errexit set -o nounset set -o pipefail LOG_FILE="/var/log/ailab-config-backup.log" BACKUP_DIR="/srv/backups/ailab-config" RETENTION_DAYS=30 execute=false non_interactive=false usage() { printf 'Usage: %s [--execute [--non-interactive]]\n' "$(basename "$0")" } while (($# > 0)); do case "$1" in --execute) execute=true ;; --non-interactive) non_interactive=true ;; -h|--help) usage; exit 0 ;; *) printf 'CRITICAL: unknown argument: %s\n' "$1" >&2; usage >&2; exit 2 ;; esac shift done if [[ "$non_interactive" == true && "$execute" != true ]]; then printf 'CRITICAL: --non-interactive requires --execute\n' >&2 exit 2 fi if ((EUID != 0)); then printf 'CRITICAL: this script must run as root\n' >&2 exit 2 fi for command_name in tar gzip find; do if ! command -v "$command_name" >/dev/null 2>&1; then printf 'CRITICAL: required command is missing: %s\n' "$command_name" >&2 exit 2 fi done exec > >(tee -a "$LOG_FILE") 2>&1 timestamp="$(date '+%Y%m%d-%H%M%S')" archive="$BACKUP_DIR/ailab-config-$timestamp.tar.gz" candidate_paths=( /etc /root/.bashrc /root/.bashrc.d /opt/ailab-maintenance /var/lib/libvirt/qemu ) source_paths=() printf '\n[%s] Configuration backup\n' "$(date --iso-8601=seconds)" for path in "${candidate_paths[@]}"; do if [[ -e "$path" ]]; then source_paths+=("${path#/}") printf 'OK: include %s\n' "$path" else printf 'INFO: optional path is absent: %s\n' "$path" fi done if ((${#source_paths[@]} == 0)); then printf 'CRITICAL: no backup source paths are present\n' exit 1 fi printf 'Backup destination: %s\n' "$archive" printf 'Retention: matching archives older than %d days\n' "$RETENTION_DAYS" printf 'Configuration beneath /etc includes libvirt, Docker, and systemd when present\n' printf 'Excluded by policy: Docker data, application data, model data, and VM disk images\n' if [[ "$execute" != true ]]; then printf 'INFO: dry-run mode; no archive or directory was created and no retention deletion ran\n' exit 0 fi if [[ "$non_interactive" != true ]]; then printf 'Type EXECUTE to create the archive and apply retention: ' read -r confirmation if [[ "$confirmation" != "EXECUTE" ]]; then printf 'CRITICAL: confirmation failed; no changes made\n' exit 2 fi fi install -d -m 0750 "$BACKUP_DIR" tar --create --gzip --file "$archive" --ignore-failed-read --directory / -- "${source_paths[@]}" find "$BACKUP_DIR" -maxdepth 1 -type f -name 'ailab-config-*.tar.gz' -mtime "+$RETENTION_DAYS" -print -delete printf 'OK: configuration backup created: %s\n' "$archive"