From 61460b63a32b007b2ff6b37f7ae3f8f5959caa92 Mon Sep 17 00:00:00 2001 From: Paul Payne Date: Sun, 31 Aug 2025 15:02:35 -0700 Subject: [PATCH] Adds more to wild-backup. --- bin/wild-backup | 207 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 187 insertions(+), 20 deletions(-) diff --git a/bin/wild-backup b/bin/wild-backup index 0b770fa..1caae22 100755 --- a/bin/wild-backup +++ b/bin/wild-backup @@ -4,6 +4,72 @@ set -e set -o pipefail +# Parse command line flags +BACKUP_HOME=true +BACKUP_APPS=true +BACKUP_CLUSTER=true + +show_help() { + echo "Usage: $0 [OPTIONS]" + echo "Backup components of your wild-cloud infrastructure" + echo "" + echo "Options:" + echo " --home-only Backup only WC_HOME (wild-cloud configuration)" + echo " --apps-only Backup only applications (databases and PVCs)" + echo " --cluster-only Backup only Kubernetes cluster resources" + echo " --no-home Skip WC_HOME backup" + echo " --no-apps Skip application backups" + echo " --no-cluster Skip cluster resource backup" + echo " -h, --help Show this help message" + echo "" + echo "Default: Backup all components (home, apps, cluster)" +} + +# Process command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --home-only) + BACKUP_HOME=true + BACKUP_APPS=false + BACKUP_CLUSTER=false + shift + ;; + --apps-only) + BACKUP_HOME=false + BACKUP_APPS=true + BACKUP_CLUSTER=false + shift + ;; + --cluster-only) + BACKUP_HOME=false + BACKUP_APPS=false + BACKUP_CLUSTER=true + shift + ;; + --no-home) + BACKUP_HOME=false + shift + ;; + --no-apps) + BACKUP_APPS=false + shift + ;; + --no-cluster) + BACKUP_CLUSTER=false + shift + ;; + -h|--help) + show_help + exit 0 + ;; + *) + echo "Unknown option: $1" + show_help + exit 1 + ;; + esac +done + # Initialize Wild Cloud environment if [ -z "${WC_ROOT}" ]; then echo "WC_ROOT is not set." @@ -46,33 +112,134 @@ else echo "Repository initialized successfully." fi -# Backup entire WC_HOME. -restic --verbose --tag wild-cloud --tag wc-home --tag "$(date +%Y-%m-%d)" backup $WC_HOME -# TODO: Ignore wild cloud cache? +# Backup entire WC_HOME +if [ "$BACKUP_HOME" = true ]; then + echo "Backing up WC_HOME..." + restic --verbose --tag wild-cloud --tag wc-home --tag "$(date +%Y-%m-%d)" backup $WC_HOME + echo "WC_HOME backup completed." + # TODO: Ignore wild cloud cache? +else + echo "Skipping WC_HOME backup." +fi mkdir -p "$STAGING_DIR" # Run backup for all apps at once -echo "Running backup for all apps..." -wild-app-backup --all +if [ "$BACKUP_APPS" = true ]; then + echo "Running backup for all apps..." + wild-app-backup --all -# Upload each app's backup to restic individually -for app_dir in "$STAGING_DIR"/apps/*; do - if [ ! -d "$app_dir" ]; then - continue + # Upload each app's backup to restic individually + for app_dir in "$STAGING_DIR"/apps/*; do + if [ ! -d "$app_dir" ]; then + continue + fi + app="$(basename "$app_dir")" + echo "Uploading backup for app: $app" + restic --verbose --tag wild-cloud --tag "$app" --tag "$(date +%Y-%m-%d)" backup "$app_dir" + echo "Backup for app '$app' completed." + done +else + echo "Skipping application backups." +fi + +# --- etcd Backup Function ---------------------------------------------------- +backup_etcd() { + local cluster_backup_dir="$1" + local etcd_backup_file="$cluster_backup_dir/etcd-snapshot.db" + + echo "Creating etcd snapshot..." + + # For Talos, we use talosctl to create etcd snapshots + if command -v talosctl >/dev/null 2>&1; then + # Try to get etcd snapshot via talosctl (works for Talos clusters) + local control_plane_nodes + control_plane_nodes=$(kubectl get nodes -l node-role.kubernetes.io/control-plane -o jsonpath='{.items[*].status.addresses[?(@.type=="InternalIP")].address}' | tr ' ' '\n' | head -1) + + if [[ -n "$control_plane_nodes" ]]; then + echo "Using talosctl to backup etcd from control plane node: $control_plane_nodes" + if talosctl --nodes "$control_plane_nodes" etcd snapshot "$etcd_backup_file"; then + echo " etcd backup created: $etcd_backup_file" + return 0 + else + echo " talosctl etcd snapshot failed, trying alternative method..." + fi + else + echo " No control plane nodes found for talosctl method" + fi fi - app="$(basename "$app_dir")" - echo "Uploading backup for app: $app" - restic --verbose --tag wild-cloud --tag "$app" --tag "$(date +%Y-%m-%d)" backup "$app_dir" - echo "Backup for app '$app' completed." -done + + # Alternative: Try to backup via etcd pod if available + local etcd_pod + etcd_pod=$(kubectl get pods -n kube-system -l component=etcd -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true) + + if [[ -n "$etcd_pod" ]]; then + echo "Using etcd pod: $etcd_pod" + # Create snapshot using etcdctl inside the etcd pod + if kubectl exec -n kube-system "$etcd_pod" -- etcdctl \ + --endpoints=https://127.0.0.1:2379 \ + --cacert=/etc/kubernetes/pki/etcd/ca.crt \ + --cert=/etc/kubernetes/pki/etcd/server.crt \ + --key=/etc/kubernetes/pki/etcd/server.key \ + snapshot save /tmp/etcd-snapshot.db; then + + # Copy snapshot out of pod + kubectl cp -n kube-system "$etcd_pod:/tmp/etcd-snapshot.db" "$etcd_backup_file" + + # Clean up temporary file in pod + kubectl exec -n kube-system "$etcd_pod" -- rm -f /tmp/etcd-snapshot.db + + echo " etcd backup created: $etcd_backup_file" + return 0 + else + echo " etcd pod snapshot failed" + fi + else + echo " No etcd pod found in kube-system namespace" + fi + + # Final fallback: Try direct etcdctl if available on local system + if command -v etcdctl >/dev/null 2>&1; then + echo "Attempting local etcdctl backup..." + # This would need proper certificates and endpoints configured + echo " Local etcdctl backup not implemented (requires certificate configuration)" + fi + + echo " Warning: Could not create etcd backup - no working method found" + echo " Consider installing talosctl or ensuring etcd pods are accessible" + return 1 +} -# Back up Kubernetes resources -# kubectl get all -A -o yaml > "$BACKUP_DIR/all-resources.yaml" -# kubectl get secrets -A -o yaml > "$BACKUP_DIR/secrets.yaml" -# kubectl get configmaps -A -o yaml > "$BACKUP_DIR/configmaps.yaml" +# Back up Kubernetes cluster resources +if [ "$BACKUP_CLUSTER" = true ]; then + echo "Backing up Kubernetes cluster resources..." + CLUSTER_BACKUP_DIR="$STAGING_DIR/cluster" + + # Clean up any existing cluster backup files + if [[ -d "$CLUSTER_BACKUP_DIR" ]]; then + echo "Cleaning up existing cluster backup files..." + rm -rf "$CLUSTER_BACKUP_DIR" + fi + mkdir -p "$CLUSTER_BACKUP_DIR" -# Back up persistent volumes -# TODO: Add logic to back up persistent volume data + kubectl get all -A -o yaml > "$CLUSTER_BACKUP_DIR/all-resources.yaml" + kubectl get secrets -A -o yaml > "$CLUSTER_BACKUP_DIR/secrets.yaml" + kubectl get configmaps -A -o yaml > "$CLUSTER_BACKUP_DIR/configmaps.yaml" + kubectl get persistentvolumes -o yaml > "$CLUSTER_BACKUP_DIR/persistentvolumes.yaml" + kubectl get persistentvolumeclaims -A -o yaml > "$CLUSTER_BACKUP_DIR/persistentvolumeclaims.yaml" + kubectl get storageclasses -o yaml > "$CLUSTER_BACKUP_DIR/storageclasses.yaml" + + echo "Backing up etcd..." + backup_etcd "$CLUSTER_BACKUP_DIR" + + echo "Cluster resources backed up to $CLUSTER_BACKUP_DIR" + + # Upload cluster backup to restic + echo "Uploading cluster backup to restic..." + restic --verbose --tag wild-cloud --tag cluster --tag "$(date +%Y-%m-%d)" backup "$CLUSTER_BACKUP_DIR" + echo "Cluster backup completed." +else + echo "Skipping cluster backup." +fi echo "Backup completed: $BACKUP_DIR"