#!/usr/bin/env bash set -euo pipefail # Stonks Oracle — Database Backup # Creates a compressed pg_dump of the stonks database and optionally # uploads it to MinIO for offsite storage. # # Usage: # ./scripts/backup-db.sh # backup to local file # ./scripts/backup-db.sh --upload-minio # backup + upload to MinIO # # Requires: kubectl access to the cluster NAMESPACE_PG="postgresql-service" PG_POD="postgresql-1" PG_USER="postgres" PG_DB="stonks" BACKUP_DIR="${BACKUP_DIR:-$HOME/backups/stonks-oracle}" TIMESTAMP=$(date +%Y%m%d-%H%M%S) BACKUP_FILE="stonks-${TIMESTAMP}.sql.gz" BACKUP_PATH="${BACKUP_DIR}/${BACKUP_FILE}" MINIO_BUCKET="stonks-backups" UPLOAD_MINIO=false for arg in "$@"; do case "$arg" in --upload-minio) UPLOAD_MINIO=true ;; esac done echo "=== Stonks Oracle Database Backup ===" echo "Timestamp: ${TIMESTAMP}" echo "Target: ${BACKUP_PATH}" # Ensure backup directory exists mkdir -p "${BACKUP_DIR}" # Run pg_dump inside the PostgreSQL pod and stream the compressed output echo "[1/3] Running pg_dump..." kubectl exec -n "${NAMESPACE_PG}" "${PG_POD}" -c postgres -- \ pg_dump -U "${PG_USER}" -d "${PG_DB}" \ --no-owner --no-privileges --clean --if-exists \ | gzip > "${BACKUP_PATH}" BACKUP_SIZE=$(du -h "${BACKUP_PATH}" | cut -f1) echo " Backup size: ${BACKUP_SIZE}" # Verify the backup is not empty if [ ! -s "${BACKUP_PATH}" ]; then echo "ERROR: Backup file is empty!" rm -f "${BACKUP_PATH}" exit 1 fi # Count tables in the backup as a sanity check TABLE_COUNT=$(zcat "${BACKUP_PATH}" | grep -c "^CREATE TABLE" || true) echo " Tables found: ${TABLE_COUNT}" # Upload to MinIO if requested if [ "${UPLOAD_MINIO}" = true ]; then echo "[2/3] Uploading to MinIO bucket ${MINIO_BUCKET}..." # Create bucket if it doesn't exist kubectl exec -n stonks-oracle "$(kubectl get pods -n stonks-oracle -l app=ingestion -o name | head -1 | sed 's|pod/||')" -- \ python -c " from minio import Minio import os client = Minio( os.environ.get('MINIO_ENDPOINT', 'minio.minio-service.svc.cluster.local:80'), access_key=os.environ.get('MINIO_ACCESS_KEY', ''), secret_key=os.environ.get('MINIO_SECRET_KEY', ''), secure=False, ) if not client.bucket_exists('${MINIO_BUCKET}'): client.make_bucket('${MINIO_BUCKET}') print('Created bucket ${MINIO_BUCKET}') else: print('Bucket ${MINIO_BUCKET} exists') " 2>&1 || echo "WARNING: Could not verify MinIO bucket" # Upload via kubectl cp + minio client echo " (MinIO upload requires mc CLI or manual copy)" echo " File ready at: ${BACKUP_PATH}" else echo "[2/3] Skipping MinIO upload (use --upload-minio to enable)" fi # Prune old backups (keep last 7) echo "[3/3] Pruning old backups (keeping last 7)..." ls -t "${BACKUP_DIR}"/stonks-*.sql.gz 2>/dev/null | tail -n +8 | xargs -r rm -v echo "" echo "=== Backup complete ===" echo "File: ${BACKUP_PATH}" echo "Size: ${BACKUP_SIZE}" echo "Tables: ${TABLE_COUNT}"