#!/bin/bash # WHOOSH Production Deployment Script set -euo pipefail # Configuration DEPLOY_ENV="${DEPLOY_ENV:-production}" REGISTRY="${REGISTRY:-registry.home.deepblack.cloud}" PROJECT_NAME="whoosh" DOMAIN="${DOMAIN:-whoosh.deepblack.cloud}" BACKUP_DIR="/rust/containers/whoosh/backups" COMPOSE_FILE="docker-compose.prod.yml" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color log() { echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" } success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } error() { echo -e "${RED}[ERROR]${NC} $1" exit 1 } # Function to check prerequisites check_prerequisites() { log "Checking deployment prerequisites..." # Check if running as non-root if [[ $EUID -eq 0 ]]; then error "This script should not be run as root" fi # Check required commands for cmd in docker docker-compose git curl; do if ! command -v $cmd &> /dev/null; then error "$cmd is not installed" fi done # Check Docker daemon if ! docker info >/dev/null 2>&1; then error "Docker daemon is not running" fi # Check if in swarm mode (optional) if docker info | grep -q "Swarm: active"; then log "Docker Swarm is active" SWARM_MODE=true else log "Docker Swarm is not active, using compose mode" SWARM_MODE=false fi success "Prerequisites check completed" } # Function to setup secrets setup_secrets() { log "Setting up production secrets..." if [[ "$SWARM_MODE" == "true" ]]; then # Docker Swarm secrets echo "Setting up Docker Swarm secrets..." # Check if secrets exist, create if they don't if ! docker secret ls | grep -q "whoosh_postgres_password"; then openssl rand -base64 32 | docker secret create whoosh_postgres_password - fi if ! docker secret ls | grep -q "whoosh_secret_key"; then openssl rand -base64 64 | docker secret create whoosh_secret_key - fi if ! docker secret ls | grep -q "whoosh_age_master_key"; then age-keygen | grep "AGE-SECRET-KEY" | docker secret create whoosh_age_master_key - fi # GITEA token should be provided externally if ! docker secret ls | grep -q "whoosh_gitea_token"; then warning "GITEA token secret not found. Please create it manually:" echo " echo 'your_gitea_token' | docker secret create whoosh_gitea_token -" fi else # Docker Compose secrets (using .env file) if [[ ! -f ".env.prod" ]]; then log "Creating .env.prod file..." cat > .env.prod << EOF POSTGRES_PASSWORD=$(openssl rand -base64 32) SECRET_KEY=$(openssl rand -base64 64) AGE_MASTER_KEY=$(age-keygen | grep "AGE-SECRET-KEY") GITEA_TOKEN=${GITEA_TOKEN:-""} SENTRY_DSN=${SENTRY_DSN:-""} GRAFANA_PASSWORD=$(openssl rand -base64 16) EOF warning "Created .env.prod file. Please update GITEA_TOKEN and SENTRY_DSN" fi fi success "Secrets setup completed" } # Function to build and push images build_and_push() { log "Building and pushing Docker images..." # Build backend log "Building backend image..." docker build -f backend/Dockerfile.prod -t ${REGISTRY}/${PROJECT_NAME}/backend:latest backend/ docker build -f backend/Dockerfile.prod -t ${REGISTRY}/${PROJECT_NAME}/backend:$(git rev-parse --short HEAD) backend/ # Build frontend log "Building frontend image..." docker build -f frontend/Dockerfile.prod -t ${REGISTRY}/${PROJECT_NAME}/frontend:latest frontend/ docker build -f frontend/Dockerfile.prod -t ${REGISTRY}/${PROJECT_NAME}/frontend:$(git rev-parse --short HEAD) frontend/ # Push to registry if [[ "${PUSH_IMAGES:-true}" == "true" ]]; then log "Pushing images to registry..." docker push ${REGISTRY}/${PROJECT_NAME}/backend:latest docker push ${REGISTRY}/${PROJECT_NAME}/backend:$(git rev-parse --short HEAD) docker push ${REGISTRY}/${PROJECT_NAME}/frontend:latest docker push ${REGISTRY}/${PROJECT_NAME}/frontend:$(git rev-parse --short HEAD) fi success "Images built and pushed successfully" } # Function to backup database backup_database() { if docker ps | grep -q "whoosh_postgres"; then log "Creating database backup..." mkdir -p "$BACKUP_DIR" BACKUP_FILE="$BACKUP_DIR/whoosh_backup_$(date +%Y%m%d_%H%M%S).sql" docker exec whoosh_postgres_prod pg_dump -U whoosh whoosh > "$BACKUP_FILE" gzip "$BACKUP_FILE" success "Database backup created: ${BACKUP_FILE}.gz" # Keep only last 7 backups find "$BACKUP_DIR" -name "whoosh_backup_*.sql.gz" -mtime +7 -delete else log "No existing database found to backup" fi } # Function to deploy application deploy_application() { log "Deploying WHOOSH application..." # Create necessary directories mkdir -p logs nginx/ssl monitoring/grafana/{dashboards,datasources} if [[ "$SWARM_MODE" == "true" ]]; then # Deploy using Docker Swarm log "Deploying to Docker Swarm..." docker stack deploy -c docker-compose.prod.yml ${PROJECT_NAME} # Wait for services to be ready log "Waiting for services to be ready..." for i in {1..30}; do if docker service ls | grep -q "${PROJECT_NAME}_whoosh_backend" && docker service ls | grep -q "${PROJECT_NAME}_whoosh_frontend"; then break fi echo -n "." sleep 10 done echo else # Deploy using Docker Compose log "Deploying with Docker Compose..." docker-compose -f $COMPOSE_FILE --env-file .env.prod up -d # Wait for services to be ready log "Waiting for services to be ready..." for i in {1..30}; do if docker-compose -f $COMPOSE_FILE ps | grep -q "Up" && curl -f http://localhost:8087/health >/dev/null 2>&1; then break fi echo -n "." sleep 10 done echo fi success "Application deployed successfully" } # Function to run health checks run_health_checks() { log "Running health checks..." # Check backend health if curl -f http://localhost:8087/health >/dev/null 2>&1; then success "Backend health check passed" else error "Backend health check failed" fi # Check frontend if curl -f http://localhost:3000 >/dev/null 2>&1; then success "Frontend health check passed" else warning "Frontend health check failed" fi # Check database if docker exec whoosh_postgres_prod pg_isready -U whoosh >/dev/null 2>&1; then success "Database health check passed" else error "Database health check failed" fi success "Health checks completed" } # Function to setup monitoring setup_monitoring() { log "Setting up monitoring and alerting..." # Create Prometheus configuration cat > monitoring/prometheus.yml << EOF global: scrape_interval: 15s evaluation_interval: 15s rule_files: - "alert_rules.yml" alerting: alertmanagers: - static_configs: - targets: - alertmanager:9093 scrape_configs: - job_name: 'whoosh-backend' static_configs: - targets: ['whoosh_backend:8087'] metrics_path: /metrics scrape_interval: 30s - job_name: 'whoosh-postgres' static_configs: - targets: ['postgres_exporter:9187'] - job_name: 'whoosh-redis' static_configs: - targets: ['redis_exporter:9121'] - job_name: 'node-exporter' static_configs: - targets: ['node_exporter:9100'] EOF # Create Grafana datasource mkdir -p monitoring/grafana/datasources cat > monitoring/grafana/datasources/prometheus.yml << EOF apiVersion: 1 datasources: - name: Prometheus type: prometheus access: proxy url: http://whoosh_prometheus:9090 isDefault: true EOF success "Monitoring setup completed" } # Function to cleanup old deployments cleanup() { log "Cleaning up old deployments..." # Remove old containers docker container prune -f # Remove old images docker image prune -f # Remove old volumes (careful!) if [[ "${CLEANUP_VOLUMES:-false}" == "true" ]]; then warning "Cleaning up old volumes..." docker volume prune -f fi success "Cleanup completed" } # Function to show deployment status show_status() { log "Deployment Status:" echo "====================" if [[ "$SWARM_MODE" == "true" ]]; then docker stack services ${PROJECT_NAME} else docker-compose -f $COMPOSE_FILE ps fi echo log "Application URLs:" echo "Frontend: http://localhost:3000" echo "Backend API: http://localhost:8087" echo "Prometheus: http://localhost:9090" echo "Grafana: http://localhost:3001" echo log "Logs:" echo "Backend: docker logs whoosh_backend_prod" echo "Frontend: docker logs whoosh_frontend_prod" echo "Database: docker logs whoosh_postgres_prod" } # Main deployment flow main() { log "Starting WHOOSH Production Deployment" echo "======================================" case "${1:-deploy}" in "check") check_prerequisites ;; "secrets") setup_secrets ;; "build") build_and_push ;; "backup") backup_database ;; "deploy") check_prerequisites setup_secrets backup_database build_and_push setup_monitoring deploy_application run_health_checks show_status success "WHOOSH deployment completed successfully!" ;; "status") show_status ;; "cleanup") cleanup ;; "rollback") log "Rolling back to previous deployment..." if [[ "$SWARM_MODE" == "true" ]]; then docker service update --rollback ${PROJECT_NAME}_whoosh_backend docker service update --rollback ${PROJECT_NAME}_whoosh_frontend else docker-compose -f $COMPOSE_FILE down # Would need previous image tags for proper rollback warning "Manual rollback required for compose mode" fi ;; *) echo "Usage: $0 {check|secrets|build|backup|deploy|status|cleanup|rollback}" echo " check - Check prerequisites" echo " secrets - Setup production secrets" echo " build - Build and push images" echo " backup - Backup database" echo " deploy - Full deployment (default)" echo " status - Show deployment status" echo " cleanup - Clean up old resources" echo " rollback- Rollback to previous version" exit 1 ;; esac } # Run main function with all arguments main "$@"