255 lines
8.1 KiB
Bash
Executable file
255 lines
8.1 KiB
Bash
Executable file
#!/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
|
||
|