The UmbraAudio side of the cross-machine contract: this node registers with the UmbraTrans controller, polls for jobs, claims them under a lease, and writes artifacts back over NFS. * `umbraaudio/cluster/` - configuration, PostgreSQL access, node registration and heartbeat, the job queue, artifact storage and NFS mount management. * `scripts/cluster_worker.py` - the CLI: `worker`, `check-cluster`, `register-node`. * `scripts/backup_system.sh` / `scripts/verify_backup.sh` - whole-system backup and integrity verification. * `.env` - the `UMBRA_CLUSTER__*` settings. `UMBRA_CLUSTER__PG_PASSWORD` is deliberately empty; the password comes from the environment, not from a committed file. `k3s-agent.yaml` is NOT committed: it contains a K3s server join token. It is now gitignored, and the token should be rotated if that file has ever left this machine. ruff clean on the whole package.
116 lines
2.9 KiB
Bash
Executable File
116 lines
2.9 KiB
Bash
Executable File
#!/bin/zsh
|
|
# verify_backup.sh - Verify UmbraAudio backup integrity
|
|
|
|
set -e
|
|
|
|
SOURCE_DIR=""
|
|
BACKUP_DATE=""
|
|
|
|
# Parse arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--source)
|
|
SOURCE_DIR="$2"
|
|
shift 2
|
|
;;
|
|
--date)
|
|
BACKUP_DATE="$2"
|
|
shift 2
|
|
;;
|
|
--help|-h)
|
|
echo "Usage: $0 --source DIR --date YYYYMMDD"
|
|
echo " --source Backup directory (default: /tmp/backups)"
|
|
echo " --date Date to verify (default: 7 days ago)"
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Set defaults
|
|
if [[ -z "$SOURCE_DIR" ]]; then
|
|
SOURCE_DIR="/tmp/backups"
|
|
fi
|
|
if [[ -z "$BACKUP_DATE" ]]; then
|
|
BACKUP_DATE=$(date -v-7 +%Y%m%d 2>/dev/null || date --date='7 days ago' +%Y%m%d 2>/dev/null || echo "$(date +%Y%m%d)")
|
|
fi
|
|
|
|
echo "========================================="
|
|
echo "UmbraAudio Backup Verification"
|
|
echo "========================================="
|
|
echo "Source: $SOURCE_DIR"
|
|
echo "Date: $BACKUP_DATE"
|
|
echo "========================================="
|
|
|
|
# Validate source
|
|
if [[ ! -d "$SOURCE_DIR" ]]; then
|
|
echo "❌ Error: Source directory missing: $SOURCE_DIR"
|
|
exit 1
|
|
fi
|
|
|
|
# Find backup items matching the date pattern inside the source directory
|
|
# Look for files/dirs with the date pattern in their name
|
|
MATCHED=$(find "$SOURCE_DIR" -maxdepth 1 -name "*${BACKUP_DATE}*" 2>/dev/null | head -5)
|
|
|
|
if [[ -z "$MATCHED" ]]; then
|
|
# Try listing recent subdirectories
|
|
echo "No exact date match. Checking recent backups..."
|
|
MATCHED=$(ls -td "$SOURCE_DIR"/*/ 2>/dev/null | head -1)
|
|
if [[ -z "$MATCHED" ]]; then
|
|
echo "❌ No backups found in $SOURCE_DIR"
|
|
echo "Contents of $SOURCE_DIR:"
|
|
ls -la "$SOURCE_DIR" 2>/dev/null | head -10
|
|
exit 1
|
|
fi
|
|
NAME=$(basename "$MATCHED" | tr -d '/')
|
|
else
|
|
NAME=$(basename "$MATCHED" | tr -d '/')
|
|
fi
|
|
|
|
echo "✓ Found: $NAME"
|
|
echo ""
|
|
|
|
# Check if it's a file or directory inside backups
|
|
if [[ -f "$SOURCE_DIR/$NAME" ]]; then
|
|
TYPE="file"; FULL_PATH="$SOURCE_DIR/$NAME"
|
|
elif [[ -d "$SOURCE_DIR/$NAME" ]]; then
|
|
TYPE="directory"; FULL_PATH="$SOURCE_DIR/$NAME"
|
|
else
|
|
TYPE="unknown"; FULL_PATH=""
|
|
fi
|
|
|
|
echo "Type: $TYPE"
|
|
|
|
# For directory backups, check key items
|
|
if [[ "$TYPE" == "directory" ]]; then
|
|
echo "Checking key backup items..."
|
|
KEYS=("project.db" "voices" "speakers.json" ".env" "config.py")
|
|
ERRORS=0
|
|
for k in "${KEYS[@]}"; do
|
|
if [[ -e "$FULL_PATH/$k" ]] || [[ -e "$FULL_PATH/$k."* ]]; then
|
|
echo " ✓ $k"
|
|
else
|
|
echo " ⚠ $k missing"
|
|
ERRORS=$((ERRORS+1))
|
|
fi
|
|
done
|
|
echo ""
|
|
if [[ $ERRORS -eq 0 ]]; then
|
|
echo "✅ All key items present"
|
|
else
|
|
echo "⚠ $ERRORS item(s) missing"
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
echo "========================================="
|
|
if [[ "$TYPE" == "directory" && $ERRORS -eq 0 ]]; then
|
|
echo "✅ VERIFICATION PASSED"
|
|
else
|
|
echo "⚠ VERIFICATION COMPLETED"
|
|
fi
|
|
echo "========================================="
|