#!/usr/bin/env bash set -euo pipefail BACKUP_DIR="$HOME/homey-backup" TIMESTAMP=$(date +%Y%m%d_%H%M%S) NAMESPACE="homey" NODE="root@192.168.1.100" NC_DATA_DEST="$BACKUP_DIR/nextcloud-data/backups/$TIMESTAMP" NC_DB_DEST="$BACKUP_DIR/nextcloud-postgres/backups/$TIMESTAMP" show_progress() { local file="$1" local label="$2" local total="${3:-0}" while [[ ! -f "$file" ]]; do sleep 0.2 done while kill -0 "$BACKUP_PID" 2>/dev/null; do local size=$(stat -c%s "$file" 2>/dev/null || echo "0") if [[ "$total" -gt 0 ]]; then local pct=$((size * 100 / total)) local size_hr=$(numfmt --to=iec-i --suffix=B "$size" 2>/dev/null || echo "${size}B") local total_hr=$(numfmt --to=iec-i --suffix=B "$total" 2>/dev/null || echo "${total}B") printf "\r%s: %s / %s (%d%%) " "$label" "$size_hr" "$total_hr" "$pct" else local size_hr=$(numfmt --to=iec-i --suffix=B "$size" 2>/dev/null || echo "${size}B") printf "\r%s: %s " "$label" "$size_hr" fi sleep 0.5 done local final=$(stat -c%s "$file" 2>/dev/null || echo "0") local final_hr=$(numfmt --to=iec-i --suffix=B "$final" 2>/dev/null || echo "${final}B") printf "\r%s: %s - done! \n" "$label" "$final_hr" } wait_for_cluster() { echo "Waiting for Kubernetes cluster..." local max_wait=300 local start=$(date +%s) while true; do if kubectl get nodes &>/dev/null; then echo "Cluster ready!" return 0 fi local now=$(date +%s) if [[ $((now - start)) -ge $max_wait ]]; then echo "ERROR: Cluster not available after ${max_wait}s" return 1 fi printf "\rWaiting... %ds " $((now - start)) sleep 5 done } echo "=== Nextcloud Backup ===" echo "Started: $(date)" mkdir -p "$NC_DATA_DEST" mkdir -p "$NC_DB_DEST" if ! wait_for_cluster; then echo "Cluster unavailable. Cannot proceed." exit 1 fi echo "" echo "--- Backing up PostgreSQL ---" DB_POD=$(kubectl get pods -n "$NAMESPACE" -l app=nextcloud-postgres -o jsonpath='{.items[0].metadata.name}') if [[ -z "$DB_POD" ]]; then echo "ERROR: PostgreSQL pod not found" exit 1 fi DB_SIZE=$(kubectl exec -n "$NAMESPACE" "$DB_POD" -- psql -U postgres -d nextcloud_db -t -c "SELECT pg_database_size('nextcloud_db');" 2>/dev/null | tr -d ' ' || echo "0") echo "Database size: $(numfmt --to=iec-i --suffix=B "$DB_SIZE" 2>/dev/null || echo "$DB_SIZE bytes")" echo "Dumping database from $DB_POD..." kubectl exec -n "$NAMESPACE" "$DB_POD" -- sh -c "pg_dump -U postgres nextcloud_db" | gzip > "$NC_DB_DEST/dump.sql.gz" & BACKUP_PID=$! show_progress "$NC_DB_DEST/dump.sql.gz" "Database" "$DB_SIZE" wait $BACKUP_PID 2>/dev/null || true echo "Database backup: $(du -sh "$NC_DB_DEST/dump.sql.gz" | cut -f1)" echo "" echo "--- Backing up Nextcloud data ---" NC_POD=$(kubectl get pods -n "$NAMESPACE" -l app=nextcloud -o jsonpath='{.items[0].metadata.name}') if [[ -z "$NC_POD" ]]; then echo "ERROR: Nextcloud pod not found" exit 1 fi NC_SIZE=$(kubectl exec -n "$NAMESPACE" "$NC_POD" -- du -sb /var/www/html 2>/dev/null | awk '{print $1}' || echo "0") echo "Data size: $(numfmt --to=iec-i --suffix=B "$NC_SIZE" 2>/dev/null || echo "$NC_SIZE bytes")" echo "Creating tar archive from $NC_POD..." kubectl exec -n "$NAMESPACE" "$NC_POD" -- tar cf - -C /var/www/html . 2>/dev/null | gzip > "$NC_DATA_DEST/data.tar.gz" & BACKUP_PID=$! show_progress "$NC_DATA_DEST/data.tar.gz" "Data" "$NC_SIZE" wait $BACKUP_PID 2>/dev/null || true echo "Data backup: $(du -sh "$NC_DATA_DEST/data.tar.gz" | cut -f1)" echo "" echo "--- Creating latest symlinks ---" rm -f "$BACKUP_DIR/nextcloud-data/latest" "$BACKUP_DIR/nextcloud-postgres/latest" ln -sf "backups/$TIMESTAMP" "$BACKUP_DIR/nextcloud-data/latest" ln -sf "backups/$TIMESTAMP" "$BACKUP_DIR/nextcloud-postgres/latest" echo "" echo "=== Backup Complete ===" echo "Timestamp: $TIMESTAMP" echo "Finished: $(date)" echo "" du -sh "$NC_DATA_DEST" "$NC_DB_DEST"