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.
135 lines
3.7 KiB
Bash
Executable File
135 lines
3.7 KiB
Bash
Executable File
#!/bin/zsh
|
|
# backup_system.sh - Complete UmbraAudio system backup
|
|
|
|
set -e
|
|
|
|
SOURCE_ROOT="/Volumes/1TB_APL_Storage/UmbraAudio"
|
|
BACKUP_DIR="/tmp/backups"
|
|
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
|
|
|
# Parse arguments
|
|
MODE="full"
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--full) MODE="full"; shift ;;
|
|
--db) MODE="db"; shift ;;
|
|
--voices) MODE="voices"; shift ;;
|
|
--config) MODE="config"; shift ;;
|
|
--help|-h)
|
|
echo "Usage: $0 --full | --db | --voices | --config"
|
|
exit 0 ;;
|
|
*) echo "Unknown option: $1"; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
mkdir -p "$BACKUP_DIR"
|
|
|
|
echo "========================================="
|
|
echo "UmbraAudio System Backup"
|
|
echo "========================================="
|
|
echo "Mode: $MODE"
|
|
echo "Timestamp: $TIMESTAMP"
|
|
echo "========================================="
|
|
|
|
backup_db() {
|
|
local name="$1"
|
|
local src="$SOURCE_ROOT/.umbra/$name"
|
|
if [[ -f "$src" ]]; then
|
|
cp -a "$src" "$BACKUP_DIR/project_${name}.${TIMESTAMP}"
|
|
echo " ✓ $name"
|
|
else
|
|
echo " ⊘ $name (not found)"
|
|
fi
|
|
}
|
|
|
|
# Copy directory to backup with parent dir creation
|
|
backup_dir() {
|
|
local name="$1"
|
|
local rel_path="$name" # relative to SOURCE_ROOT
|
|
local src="$SOURCE_ROOT/$rel_path"
|
|
if [[ -d "$src" ]]; then
|
|
# Destination: flat name under BACKUP_DIR
|
|
local dst="$BACKUP_DIR/${name}_${TIMESTAMP}"
|
|
# Ensure parent directory exists
|
|
local dst_parent=$(dirname "$dst")
|
|
mkdir -p "$dst_parent"
|
|
cp -a "$src" "$dst"
|
|
echo " ✓ $name"
|
|
else
|
|
echo " ⊘ $name (not found)"
|
|
fi
|
|
}
|
|
|
|
case "$MODE" in
|
|
full)
|
|
echo "Backing up complete system..."
|
|
echo ""
|
|
echo "1. Project databases:"
|
|
backup_db "project.db"
|
|
backup_db "voice_registry.db"
|
|
backup_db "umbra.db"
|
|
echo ""
|
|
echo "2. Voice references:"
|
|
backup_dir "umbraaudio/voices"
|
|
echo ""
|
|
echo "3. Speaker configurations:"
|
|
if [[ -f "$SOURCE_ROOT/speakers.json" ]]; then
|
|
cp -a "$SOURCE_ROOT/speakers.json" "$BACKUP_DIR/speakers.${TIMESTAMP}"
|
|
echo " ✓ speakers.json"
|
|
fi
|
|
echo ""
|
|
echo "4. Configuration:"
|
|
if [[ -f "$SOURCE_ROOT/.env" ]]; then
|
|
cp -a "$SOURCE_ROOT/.env" "$BACKUP_DIR/.env.${TIMESTAMP}"
|
|
echo " ✓ .env"
|
|
fi
|
|
if [[ -f "$SOURCE_ROOT/umbraaudio/config.py" ]]; then
|
|
cp -a "$SOURCE_ROOT/umbraaudio/config.py" "$BACKUP_DIR/config.${TIMESTAMP}"
|
|
echo " ✓ config.py"
|
|
fi
|
|
echo ""
|
|
echo "5. Git state:"
|
|
cd "$SOURCE_ROOT" 2>/dev/null && git status --short > "$BACKUP_DIR/git_status.${TIMESTAMP}" 2>/dev/null && echo " ✓ git_status" || echo " ⊘ git (failed)"
|
|
echo ""
|
|
echo "6. Launchd services:"
|
|
launchctl list > "$BACKUP_DIR/launchd.${TIMESTAMP}" 2>/dev/null && echo " ✓ launchd" || echo " ⊘ launchd (failed)"
|
|
echo ""
|
|
echo "7. Audio generation state:"
|
|
if [[ -d "$SOURCE_ROOT/generation" ]]; then
|
|
backup_dir "generation"
|
|
fi
|
|
echo ""
|
|
echo "========================================="
|
|
echo "✅ Full backup complete!"
|
|
echo "Files saved to: $BACKUP_DIR/"
|
|
echo "========================================="
|
|
;;
|
|
|
|
db)
|
|
echo "Backing up databases only..."
|
|
backup_db "project.db"
|
|
backup_db "voice_registry.db"
|
|
backup_db "umbra.db"
|
|
;;
|
|
|
|
voices)
|
|
echo "Backing up voice references..."
|
|
backup_dir "umbraaudio/voices"
|
|
if [[ -f "$SOURCE_ROOT/speakers.json" ]]; then
|
|
cp -a "$SOURCE_ROOT/speakers.json" "$BACKUP_DIR/speakers.${TIMESTAMP}"
|
|
fi
|
|
;;
|
|
|
|
config)
|
|
echo "Backing up configuration only..."
|
|
if [[ -f "$SOURCE_ROOT/.env" ]]; then
|
|
cp -a "$SOURCE_ROOT/.env" "$BACKUP_DIR/.env.${TIMESTAMP}"
|
|
echo " ✓ .env"
|
|
fi
|
|
if [[ -f "$SOURCE_ROOT/umbraaudio/config.py" ]]; then
|
|
cp -a "$SOURCE_ROOT/umbraaudio/config.py" "$BACKUP_DIR/config.${TIMESTAMP}"
|
|
echo " ✓ config.py"
|
|
fi
|
|
;;
|
|
esac
|