veza/scripts/archive/migrate-db.sh

256 lines
8.1 KiB
Bash
Raw Permalink Normal View History

#!/bin/bash
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
ENVIRONMENT=${1:-local}
BACKUP_DIR="./backups/migrations/$(date +%Y%m%d_%H%M%S)"
ROLLBACK_DIR="./rollback-backups/migrations"
# Determine compose file
if [ "$ENVIRONMENT" == "production" ]; then
COMPOSE_FILE="docker-compose.production.yml"
POSTGRES_USER="${POSTGRES_USER:-veza_user}"
POSTGRES_DB="${POSTGRES_DB:-veza_db}"
else
COMPOSE_FILE="docker-compose.yml"
POSTGRES_USER="${POSTGRES_USER:-veza_user}"
POSTGRES_DB="${POSTGRES_DB:-veza_local}"
fi
# Function to print colored messages
print_info() {
echo -e "${BLUE} $1${NC}"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
# Function to check prerequisites
check_prerequisites() {
print_info "Checking prerequisites..."
command -v docker >/dev/null 2>&1 || {
print_error "Docker is required but not installed. Aborting.";
exit 1;
}
command -v docker-compose >/dev/null 2>&1 || {
print_error "Docker Compose is required but not installed. Aborting.";
exit 1;
}
if ! docker info >/dev/null 2>&1; then
print_error "Docker daemon is not running. Please start Docker and try again.";
exit 1;
fi
if [ ! -f "$COMPOSE_FILE" ]; then
print_error "$COMPOSE_FILE not found. Aborting.";
exit 1;
fi
# Check if postgres container is running
if ! docker-compose -f "$COMPOSE_FILE" ps postgres | grep -q "Up"; then
print_error "PostgreSQL container is not running. Please start it first.";
exit 1;
fi
print_success "Prerequisites check passed"
}
# Function to create backup
create_backup() {
print_info "Creating database backup..."
mkdir -p "${BACKUP_DIR}"
mkdir -p "${ROLLBACK_DIR}"
# Backup database
print_info "Backing up database: ${POSTGRES_DB}..."
docker-compose -f "$COMPOSE_FILE" exec -T postgres pg_dump -U "${POSTGRES_USER}" "${POSTGRES_DB}" > "${BACKUP_DIR}/database.sql" 2>/dev/null || {
print_error "Failed to create database backup";
exit 1;
}
# Save backup to rollback location
cp "${BACKUP_DIR}/database.sql" "${ROLLBACK_DIR}/latest.sql"
print_success "Backup created at ${BACKUP_DIR}/database.sql"
}
# Function to check migration status
check_migration_status() {
print_info "Checking migration status..."
# Check if schema_migrations table exists
docker-compose -f "$COMPOSE_FILE" exec -T postgres psql -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -tAc \
"SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'schema_migrations');" \
> /dev/null 2>&1 || {
print_warning "schema_migrations table does not exist (will be created)"
return 0
}
# Get list of applied migrations
APPLIED_MIGRATIONS=$(docker-compose -f "$COMPOSE_FILE" exec -T postgres psql -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -tAc \
"SELECT COUNT(*) FROM schema_migrations;" 2>/dev/null || echo "0")
print_info "Currently applied migrations: ${APPLIED_MIGRATIONS}"
}
# Function to run backend API migrations
run_backend_migrations() {
print_info "Running Backend API migrations (GORM AutoMigrate)..."
# Check if backend-api container is running
if ! docker-compose -f "$COMPOSE_FILE" ps backend-api | grep -q "Up"; then
print_warning "Backend API container is not running. Starting it..."
docker-compose -f "$COMPOSE_FILE" up -d backend-api
sleep 10
fi
# Trigger migrations by restarting the backend-api service
# The backend API runs migrations automatically on startup via Database.Initialize()
print_info "Restarting backend-api to trigger migrations..."
docker-compose -f "$COMPOSE_FILE" restart backend-api || {
print_error "Failed to restart backend-api";
return 1;
}
# Wait for backend to be ready
print_info "Waiting for backend-api to be ready..."
sleep 15
# Check if backend is healthy
if curl -sf "http://localhost:8080/health" >/dev/null 2>&1; then
print_success "Backend API migrations completed successfully"
return 0
else
print_warning "Backend API health check failed (migrations may still be running)"
return 0
fi
}
# Function to run chat server migrations
run_chat_server_migrations() {
print_info "Checking for Chat Server migrations..."
# Check if chat-server has migrations directory
if [ ! -d "veza-chat-server/migrations" ]; then
print_warning "Chat Server migrations directory not found, skipping"
return 0
fi
# Check if chat-server uses sqlx migrate
if docker-compose -f "$COMPOSE_FILE" exec -T chat-server cargo --version >/dev/null 2>&1; then
print_info "Running Chat Server migrations (sqlx migrate)..."
# Run sqlx migrate (if available in the container)
docker-compose -f "$COMPOSE_FILE" exec -T chat-server \
sqlx migrate run --database-url "${DATABASE_URL}" 2>/dev/null || {
print_warning "sqlx migrate not available or migrations already applied"
return 0
}
print_success "Chat Server migrations completed"
else
print_warning "Chat Server migrations will be handled by the service on startup"
fi
}
# Function to verify migrations
verify_migrations() {
print_info "Verifying migrations..."
# Check database connection
if ! docker-compose -f "$COMPOSE_FILE" exec -T postgres psql -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -c "SELECT 1;" >/dev/null 2>&1; then
print_error "Failed to connect to database";
return 1;
fi
# Check if schema_migrations table exists
TABLE_EXISTS=$(docker-compose -f "$COMPOSE_FILE" exec -T postgres psql -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -tAc \
"SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'schema_migrations');" 2>/dev/null || echo "false")
if [ "$TABLE_EXISTS" = "t" ]; then
MIGRATION_COUNT=$(docker-compose -f "$COMPOSE_FILE" exec -T postgres psql -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -tAc \
"SELECT COUNT(*) FROM schema_migrations;" 2>/dev/null || echo "0")
print_info "Migrations table exists with ${MIGRATION_COUNT} applied migrations"
fi
print_success "Migration verification passed"
return 0
}
# Function to rollback
rollback() {
print_error "Migration failed! Initiating rollback..."
if [ ! -f "${ROLLBACK_DIR}/latest.sql" ]; then
print_error "No rollback backup found. Manual intervention required."
exit 1
fi
print_info "Rolling back database to previous state..."
# Drop and recreate database (or restore from backup)
print_warning "Restoring database from backup..."
# Restore database
docker-compose -f "$COMPOSE_FILE" exec -T postgres psql -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" < "${ROLLBACK_DIR}/latest.sql" || {
print_error "Failed to restore database from backup. Manual intervention required.";
exit 1;
}
print_warning "Rollback completed. Please verify database manually."
exit 1
}
# Main migration flow
main() {
print_info "Starting database migrations for ${ENVIRONMENT} environment..."
# Trap errors and rollback
trap rollback ERR
check_prerequisites
check_migration_status
create_backup
run_backend_migrations
run_chat_server_migrations
# Wait a bit for all migrations to complete
sleep 5
# Verify migrations
if ! verify_migrations; then
rollback
fi
# Disable trap on success
trap - ERR
print_success "Database migrations completed successfully for ${ENVIRONMENT}!"
print_info "Backup location: ${BACKUP_DIR}/database.sql"
}
# Run main function
main