Files
AzerothCore-RealmMaster/V1/scripts/deploy-and-check.sh
2025-10-17 01:40:50 -04:00

550 lines
20 KiB
Bash
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# ==============================================
# AzerothCore Docker Deployment & Health Check Script
# ==============================================
# This script deploys the complete AzerothCore stack and performs comprehensive health checks
# Usage: ./deploy-and-check.sh [--skip-deploy] [--quick-check] [--setup]
set -e # Exit on any error
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Script options
SKIP_DEPLOY=false
QUICK_CHECK=false
RUN_SETUP=false
MODULES_ENABLED=false
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
--skip-deploy)
SKIP_DEPLOY=true
shift
;;
--quick-check)
QUICK_CHECK=true
shift
;;
--setup)
RUN_SETUP=true
shift
;;
-h|--help)
echo "Usage: $0 [--skip-deploy] [--quick-check] [--setup]"
echo " --skip-deploy Skip deployment, only run health checks"
echo " --quick-check Run basic health checks only"
echo " --setup Run interactive server setup before deployment"
exit 0
;;
*)
echo "Unknown option $1"
exit 1
;;
esac
done
# Function to print colored output
print_status() {
local status=$1
local message=$2
case $status in
"INFO")
echo -e "${BLUE} ${message}${NC}"
;;
"SUCCESS")
echo -e "${GREEN}${message}${NC}"
;;
"WARNING")
echo -e "${YELLOW}⚠️ ${message}${NC}"
;;
"ERROR")
echo -e "${RED}${message}${NC}"
;;
"HEADER")
echo -e "\n${BLUE}=== ${message} ===${NC}"
;;
esac
}
# Function to check if a port is accessible
check_port() {
local port=$1
local service_name=$2
local timeout=${3:-5}
if timeout $timeout bash -c "echo >/dev/tcp/localhost/$port" 2>/dev/null; then
print_status "SUCCESS" "$service_name (port $port): CONNECTED"
return 0
else
print_status "ERROR" "$service_name (port $port): FAILED"
return 1
fi
}
# Function to format seconds as MM:SS
format_time() {
local total_seconds=$1
local minutes=$((total_seconds / 60))
local seconds=$((total_seconds % 60))
printf "%d:%02d" "$minutes" "$seconds"
}
# Function to wait for a service to be ready
wait_for_service() {
local service_name=$1
local max_attempts=$2
local check_command=$3
local container_name=""
# Extract container name from common patterns
if echo "$check_command" | grep -q "ac-client-data"; then
container_name="ac-client-data"
elif echo "$check_command" | grep -q "ac-db-import"; then
container_name="ac-db-import"
elif echo "$check_command" | grep -q "ac-mysql"; then
container_name="ac-mysql"
elif echo "$check_command" | grep -q "ac-worldserver"; then
container_name="ac-worldserver"
elif echo "$check_command" | grep -q "ac-authserver"; then
container_name="ac-authserver"
fi
local timeout_formatted=$(format_time $((max_attempts * 5)))
print_status "INFO" "Waiting for $service_name to be ready... (timeout: $timeout_formatted)"
for i in $(seq 1 $max_attempts); do
if eval "$check_command" &>/dev/null; then
print_status "SUCCESS" "$service_name is ready!"
return 0
fi
if [ $i -eq $max_attempts ]; then
print_status "ERROR" "$service_name failed to start after $max_attempts attempts"
if [ -n "$container_name" ]; then
print_status "INFO" "Last few log lines from $container_name:"
docker logs "$container_name" --tail 5 2>/dev/null | sed 's/^/ /' || echo " (no logs available)"
fi
return 1
fi
# Show progress with more informative output
local elapsed=$((i * 5))
local remaining=$(( (max_attempts - i) * 5))
local elapsed_formatted=$(format_time $elapsed)
local remaining_formatted=$(format_time $remaining)
if [ -n "$container_name" ]; then
# Get container status
local status=$(docker inspect --format='{{.State.Status}}' "$container_name" 2>/dev/null || echo "unknown")
local health=$(docker inspect --format='{{.State.Health.Status}}' "$container_name" 2>/dev/null || echo "no-health-check")
# Show different progress info based on service
case "$service_name" in
"Client Data")
local last_log=$(docker logs "$container_name" --tail 1 2>/dev/null | head -c 80 || echo "...")
printf "${YELLOW}${NC} %s elapsed, %s remaining | Status: %s | Latest: %s\n" "$elapsed_formatted" "$remaining_formatted" "$status" "$last_log"
;;
"Database Import")
printf "${YELLOW}${NC} %s elapsed, %s remaining | Status: %s | Importing databases...\n" "$elapsed_formatted" "$remaining_formatted" "$status"
;;
*)
printf "${YELLOW}${NC} %s elapsed, %s remaining | Status: %s" "$elapsed_formatted" "$remaining_formatted" "$status"
if [ "$health" != "no-health-check" ]; then
printf " | Health: %s" "$health"
fi
printf "\n"
;;
esac
else
printf "${YELLOW}${NC} %s elapsed, %s remaining | Checking...\n" "$elapsed_formatted" "$remaining_formatted"
fi
sleep 5
done
}
# Function to check container health
check_container_health() {
local container_name=$1
local status=$(docker inspect --format='{{.State.Health.Status}}' $container_name 2>/dev/null || echo "no-health-check")
if [ "$status" = "healthy" ]; then
print_status "SUCCESS" "$container_name: healthy"
return 0
elif [ "$status" = "no-health-check" ] || [ "$status" = "<no value>" ]; then
# Check if container is running
if docker ps --format '{{.Names}}' | grep -q "^${container_name}$"; then
print_status "SUCCESS" "$container_name: running (no health check)"
return 0
else
print_status "ERROR" "$container_name: not running"
return 1
fi
else
print_status "WARNING" "$container_name: $status"
return 1
fi
}
# Function to check web service health
check_web_service() {
local url=$1
local service_name=$2
local expected_pattern=$3
response=$(curl -s --max-time 10 "$url" 2>/dev/null || echo "")
if [ -n "$expected_pattern" ]; then
if echo "$response" | grep -q "$expected_pattern"; then
print_status "SUCCESS" "$service_name: HTTP OK (content verified)"
return 0
else
print_status "ERROR" "$service_name: HTTP OK but content verification failed"
return 1
fi
else
if [ -n "$response" ]; then
print_status "SUCCESS" "$service_name: HTTP OK"
return 0
else
print_status "ERROR" "$service_name: HTTP failed"
return 1
fi
fi
}
# Function to deploy the stack
deploy_stack() {
print_status "HEADER" "DEPLOYING AZEROTHCORE STACK"
# Check if custom environment files exist first, then fallback to base files
DB_ENV_FILE="./docker-compose-azerothcore-database-custom.env"
SERVICES_ENV_FILE="./docker-compose-azerothcore-services-custom.env"
MODULES_ENV_FILE="./docker-compose-azerothcore-modules-custom.env"
TOOLS_ENV_FILE="./docker-compose-azerothcore-tools-custom.env"
# Fallback to base files if custom files don't exist
if [ ! -f "$DB_ENV_FILE" ]; then
DB_ENV_FILE="./docker-compose-azerothcore-database.env"
fi
if [ ! -f "$SERVICES_ENV_FILE" ]; then
SERVICES_ENV_FILE="./docker-compose-azerothcore-services.env"
fi
if [ ! -f "$MODULES_ENV_FILE" ]; then
MODULES_ENV_FILE="./docker-compose-azerothcore-modules.env"
fi
if [ ! -f "$TOOLS_ENV_FILE" ]; then
TOOLS_ENV_FILE="./docker-compose-azerothcore-tools.env"
fi
# Check if required environment files exist
for env_file in "$DB_ENV_FILE" "$SERVICES_ENV_FILE" "$TOOLS_ENV_FILE"; do
if [ ! -f "$env_file" ]; then
print_status "ERROR" "Environment file $env_file not found"
print_status "INFO" "Run ./scripts/setup-server.sh first to create environment files"
exit 1
fi
done
# Check if modules are enabled (set global variable)
if [ -f "$MODULES_ENV_FILE" ]; then
MODULES_ENABLED=true
else
MODULES_ENABLED=false
fi
print_status "INFO" "Step 1: Deploying database layer..."
docker compose --env-file "$DB_ENV_FILE" -f ./docker-compose-azerothcore-database.yml up -d --remove-orphans
# Wait for database initialization
wait_for_service "MySQL" 24 "docker exec ac-mysql mysql -uroot -pazerothcore123 -e 'SELECT 1' >/dev/null 2>&1"
# Wait for database import (can succeed with backup restore OR fail without backup)
print_status "INFO" "Waiting for Database Import to complete (backup restore attempt)..."
local import_result=""
local elapsed=0
local max_time=180 # 3 minutes max for import to complete
while [ $elapsed -lt $max_time ]; do
local import_status=$(docker inspect ac-db-import --format='{{.State.Status}}' 2>/dev/null || echo "unknown")
if [ "$import_status" = "exited" ]; then
local exit_code=$(docker inspect ac-db-import --format='{{.State.ExitCode}}' 2>/dev/null || echo "unknown")
if [ "$exit_code" = "0" ]; then
print_status "SUCCESS" "Database Import completed successfully (backup restored)"
import_result="restored"
break
else
print_status "INFO" "Database Import failed (no valid backup found - expected for fresh setup)"
import_result="failed"
break
fi
fi
printf "${YELLOW}${NC} ${elapsed}s elapsed, $((max_time - elapsed))s remaining | Status: $import_status | Checking for backup...\n"
sleep 5
elapsed=$((elapsed + 5))
done
if [ -z "$import_result" ]; then
print_status "ERROR" "Database Import did not complete within timeout"
exit 1
fi
# If import failed (no backup), wait for init to create databases
if [ "$import_result" = "failed" ]; then
print_status "INFO" "Waiting for Database Init to create fresh databases..."
local init_elapsed=0
local init_max_time=120 # 2 minutes for init
while [ $init_elapsed -lt $init_max_time ]; do
local init_status=$(docker inspect ac-db-init --format='{{.State.Status}}' 2>/dev/null || echo "created")
if [ "$init_status" = "exited" ]; then
local init_exit_code=$(docker inspect ac-db-init --format='{{.State.ExitCode}}' 2>/dev/null || echo "unknown")
if [ "$init_exit_code" = "0" ]; then
print_status "SUCCESS" "Database Init completed successfully (fresh databases created)"
break
else
print_status "ERROR" "Database Init failed"
print_status "INFO" "Last few log lines from ac-db-init:"
docker logs ac-db-init --tail 10 2>/dev/null || true
exit 1
fi
elif [ "$init_status" = "running" ]; then
printf "${YELLOW}${NC} ${init_elapsed}s elapsed, $((init_max_time - init_elapsed))s remaining | Status: $init_status | Creating databases...\n"
else
printf "${YELLOW}${NC} ${init_elapsed}s elapsed, $((init_max_time - init_elapsed))s remaining | Status: $init_status | Waiting to start...\n"
fi
sleep 5
init_elapsed=$((init_elapsed + 5))
done
if [ $init_elapsed -ge $init_max_time ]; then
print_status "ERROR" "Database Init did not complete within timeout"
exit 1
fi
fi
print_status "INFO" "Step 2: Deploying services layer..."
docker compose --env-file "$SERVICES_ENV_FILE" -f ./docker-compose-azerothcore-services.yml up -d 2>&1 | grep -v "Found orphan containers"
# Wait for client data extraction
print_status "INFO" "Waiting for client data download and extraction (optimized: 8-15 minutes typical)..."
print_status "INFO" "Press Ctrl+C to exit if needed..."
wait_for_service "Client Data" 360 "docker logs ac-client-data 2>/dev/null | grep -q 'Game data setup complete'"
# Wait for worldserver to be healthy
wait_for_service "World Server" 24 "check_container_health ac-worldserver"
# Deploy modules if enabled
if [ "$MODULES_ENABLED" = true ]; then
print_status "INFO" "Step 3: Deploying modules layer..."
# Ensure ac-modules is recreated with the correct environment
# It may have been created earlier by the services layer using services env
if docker ps -a --format '{{.Names}}' | grep -q '^ac-modules$'; then
print_status "INFO" "Recreating ac-modules with modules env (removing existing container)"
docker rm -f ac-modules >/dev/null 2>&1 || true
fi
docker compose --env-file "$MODULES_ENV_FILE" -f ./docker-compose-azerothcore-modules.yml up -d 2>&1 | grep -v "Found orphan containers"
# Wait for modules to be ready
sleep 5
STEP_NUMBER=4
else
print_status "INFO" "Modules layer skipped (no custom modules configuration found)"
STEP_NUMBER=3
fi
print_status "INFO" "Step $STEP_NUMBER: Deploying tools layer..."
docker compose --env-file "$TOOLS_ENV_FILE" -f ./docker-compose-azerothcore-tools.yml up -d
# Wait for tools to be ready
sleep 10
print_status "SUCCESS" "Deployment completed!"
}
# Function to perform health checks
perform_health_checks() {
print_status "HEADER" "CONTAINER HEALTH STATUS"
# Check all containers
local containers=("ac-mysql" "ac-backup" "ac-authserver" "ac-worldserver" "ac-phpmyadmin" "ac-keira3")
# Add modules container if modules are enabled
if [ "$MODULES_ENABLED" = true ]; then
containers+=("ac-modules")
fi
local container_failures=0
for container in "${containers[@]}"; do
# Only check containers that actually exist
if docker ps -a --format '{{.Names}}' | grep -q "^${container}$"; then
if ! check_container_health "$container"; then
# Only count as failure if container is not running, not just missing health check
if ! docker ps --format '{{.Names}}' | grep -q "^${container}$"; then
((container_failures++))
fi
fi
fi
done
print_status "HEADER" "PORT CONNECTIVITY TESTS"
# Database Layer
print_status "INFO" "Database Layer:"
local port_failures=0
if ! check_port 64306 "MySQL"; then ((port_failures++)); fi
# Services Layer
print_status "INFO" "Services Layer:"
if ! check_port 3784 "Auth Server"; then ((port_failures++)); fi
if ! check_port 8215 "World Server"; then ((port_failures++)); fi
if ! check_port 7778 "SOAP API"; then ((port_failures++)); fi
# Tools Layer
print_status "INFO" "Tools Layer:"
if ! check_port 8081 "PHPMyAdmin"; then ((port_failures++)); fi
if ! check_port 4201 "Keira3"; then ((port_failures++)); fi
if [ "$QUICK_CHECK" = false ]; then
print_status "HEADER" "WEB SERVICE HEALTH CHECKS"
local web_failures=0
if ! check_web_service "http://localhost:8081/" "PHPMyAdmin" "phpMyAdmin"; then ((web_failures++)); fi
if ! check_web_service "http://localhost:4201/health" "Keira3" "healthy"; then ((web_failures++)); fi
print_status "HEADER" "DATABASE CONNECTIVITY TEST"
# Test database connectivity and verify schemas
if docker exec ac-mysql mysql -uroot -pazerothcore123 -e "SHOW DATABASES;" 2>/dev/null | grep -q "acore_auth"; then
print_status "SUCCESS" "Database schemas: verified"
else
print_status "ERROR" "Database schemas: verification failed"
((web_failures++))
fi
# Test realm configuration
realm_count=$(docker exec ac-mysql mysql -uroot -pazerothcore123 -e "USE acore_auth; SELECT COUNT(*) FROM realmlist;" 2>/dev/null | tail -1)
if [ "$realm_count" -gt 0 ] 2>/dev/null; then
print_status "SUCCESS" "Realm configuration: $realm_count realm(s) configured"
else
print_status "ERROR" "Realm configuration: no realms found"
((web_failures++))
fi
fi
print_status "HEADER" "DEPLOYMENT SUMMARY"
# Summary
local total_failures=$((container_failures + port_failures + ${web_failures:-0}))
if [ $total_failures -eq 0 ]; then
print_status "SUCCESS" "All services are healthy and operational!"
print_status "INFO" "Available services:"
echo " 🌐 PHPMyAdmin: http://localhost:8081"
echo " 🛠️ Keira3: http://localhost:4201"
echo " 🎮 Game Server: localhost:8215"
echo " 🔐 Auth Server: localhost:3784"
echo " 🔧 SOAP API: localhost:7778"
echo " 🗄️ MySQL: localhost:64306"
echo ""
print_status "INFO" "Default credentials:"
echo " 🗄️ MySQL: root / azerothcore123"
return 0
else
print_status "ERROR" "Health check failed with $total_failures issue(s)"
print_status "INFO" "Check container logs for details: docker logs <container-name>"
return 1
fi
}
# Function to show container status
show_container_status() {
print_status "HEADER" "CONTAINER STATUS OVERVIEW"
echo -e "${BLUE}Container Name\t\tStatus\t\t\tPorts${NC}"
echo "=================================================================="
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep ac- | while read line; do
echo "$line"
done
}
# Main execution
main() {
print_status "HEADER" "AZEROTHCORE DEPLOYMENT & HEALTH CHECK"
# Check if docker is available
if ! command -v docker &> /dev/null; then
print_status "ERROR" "Docker is not installed or not in PATH"
exit 1
fi
# Check if docker compose is available
if ! docker compose version &> /dev/null; then
print_status "ERROR" "Docker Compose is not available"
exit 1
fi
# Run setup if requested
if [ "$RUN_SETUP" = true ]; then
print_status "HEADER" "RUNNING SERVER SETUP"
print_status "INFO" "Starting interactive server configuration..."
# Change to parent directory to run setup script
cd "$(dirname "$(pwd)")"
if [ -f "scripts/setup-server.sh" ]; then
bash scripts/setup-server.sh
if [ $? -ne 0 ]; then
print_status "ERROR" "Server setup failed or was cancelled"
exit 1
fi
else
print_status "ERROR" "Setup script not found at scripts/setup-server.sh"
exit 1
fi
# Return to scripts directory
cd scripts
print_status "SUCCESS" "Server setup completed!"
echo ""
fi
# Deploy the stack unless skipped
if [ "$SKIP_DEPLOY" = false ]; then
deploy_stack
else
print_status "INFO" "Skipping deployment, running health checks only..."
fi
# Show container status
show_container_status
# Perform health checks
if perform_health_checks; then
print_status "SUCCESS" "🎉 AzerothCore stack is fully operational!"
exit 0
else
print_status "ERROR" "❌ Health check failed - see issues above"
exit 1
fi
}
# Run main function
main "$@"