186 lines
4.8 KiB
Bash
Executable File
186 lines
4.8 KiB
Bash
Executable File
#!/bin/bash
|
|
# Health check script for blue-green deployment
|
|
# Verifies container health and HTTP endpoints
|
|
#
|
|
# Usage: ./health-check.sh <stack> [timeout_seconds]
|
|
# stack: blue or green
|
|
# timeout_seconds: max wait time (default: 60)
|
|
#
|
|
# Exit codes:
|
|
# 0 - All health checks passed
|
|
# 1 - Health check failed
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
|
|
STACK="${1:-}"
|
|
TIMEOUT="${2:-60}"
|
|
|
|
if [[ -z "$STACK" ]] || [[ ! "$STACK" =~ ^(blue|green)$ ]]; then
|
|
echo "Usage: $0 <blue|green> [timeout_seconds]"
|
|
exit 1
|
|
fi
|
|
|
|
FRONTEND_CONTAINER="mvp-frontend-$STACK"
|
|
BACKEND_CONTAINER="mvp-backend-$STACK"
|
|
|
|
echo "========================================"
|
|
echo "Health Check - $STACK Stack"
|
|
echo "Timeout: ${TIMEOUT}s"
|
|
echo "========================================"
|
|
|
|
# Function to check Docker container health
|
|
check_container_health() {
|
|
local container="$1"
|
|
local status
|
|
|
|
status=$(docker inspect --format='{{.State.Health.Status}}' "$container" 2>/dev/null || echo "not found")
|
|
|
|
case "$status" in
|
|
"healthy")
|
|
return 0
|
|
;;
|
|
"starting")
|
|
return 2 # Still starting
|
|
;;
|
|
"unhealthy"|"not found"|"")
|
|
return 1
|
|
;;
|
|
*)
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Function to check HTTP endpoint
|
|
check_http_endpoint() {
|
|
local container="$1"
|
|
local port="$2"
|
|
local path="$3"
|
|
|
|
if docker exec "$container" curl -sf "http://localhost:${port}${path}" > /dev/null 2>&1; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Function to check database connectivity via backend
|
|
check_database_connectivity() {
|
|
local container="$1"
|
|
|
|
# The /health endpoint should verify database connectivity
|
|
if docker exec "$container" curl -sf "http://localhost:3001/health" 2>/dev/null | grep -q '"database"'; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Wait for containers to be healthy
|
|
wait_for_health() {
|
|
local container="$1"
|
|
local elapsed=0
|
|
|
|
while [[ $elapsed -lt $TIMEOUT ]]; do
|
|
check_container_health "$container"
|
|
local status=$?
|
|
|
|
if [[ $status -eq 0 ]]; then
|
|
return 0
|
|
elif [[ $status -eq 1 ]]; then
|
|
echo " ERROR: Container $container is unhealthy"
|
|
docker logs "$container" --tail 20 2>/dev/null || true
|
|
return 1
|
|
fi
|
|
|
|
# Still starting, wait
|
|
sleep 2
|
|
elapsed=$((elapsed + 2))
|
|
echo " Waiting for $container... (${elapsed}s/${TIMEOUT}s)"
|
|
done
|
|
|
|
echo " ERROR: Timeout waiting for $container"
|
|
return 1
|
|
}
|
|
|
|
# Main health check sequence
|
|
echo ""
|
|
echo "Step 1/4: Checking container status..."
|
|
echo "----------------------------------------"
|
|
|
|
for container in "$FRONTEND_CONTAINER" "$BACKEND_CONTAINER"; do
|
|
running=$(docker inspect --format='{{.State.Running}}' "$container" 2>/dev/null || echo "false")
|
|
if [[ "$running" != "true" ]]; then
|
|
echo " ERROR: Container $container is not running"
|
|
docker ps -a --filter "name=$container" --format "table {{.Names}}\t{{.Status}}"
|
|
exit 1
|
|
fi
|
|
echo " OK: $container is running"
|
|
done
|
|
|
|
echo ""
|
|
echo "Step 2/4: Waiting for Docker health checks..."
|
|
echo "----------------------------------------"
|
|
|
|
for container in "$FRONTEND_CONTAINER" "$BACKEND_CONTAINER"; do
|
|
echo " Checking $container..."
|
|
if ! wait_for_health "$container"; then
|
|
echo " FAILED: $container health check"
|
|
exit 1
|
|
fi
|
|
echo " OK: $container is healthy"
|
|
done
|
|
|
|
echo ""
|
|
echo "Step 3/4: Verifying HTTP endpoints..."
|
|
echo "----------------------------------------"
|
|
|
|
# Check frontend
|
|
echo " Checking frontend HTTP..."
|
|
if ! check_http_endpoint "$FRONTEND_CONTAINER" 3000 "/"; then
|
|
echo " FAILED: Frontend HTTP check"
|
|
exit 1
|
|
fi
|
|
echo " OK: Frontend responds on port 3000"
|
|
|
|
# Check backend health endpoint
|
|
echo " Checking backend HTTP..."
|
|
if ! check_http_endpoint "$BACKEND_CONTAINER" 3001 "/health"; then
|
|
echo " FAILED: Backend HTTP check"
|
|
exit 1
|
|
fi
|
|
echo " OK: Backend responds on port 3001"
|
|
|
|
echo ""
|
|
echo "Step 4/4: Verifying database connectivity..."
|
|
echo "----------------------------------------"
|
|
|
|
if ! check_database_connectivity "$BACKEND_CONTAINER"; then
|
|
echo " WARNING: Could not verify database connectivity"
|
|
echo " (Backend may not expose database status in /health)"
|
|
else
|
|
echo " OK: Database connectivity verified"
|
|
fi
|
|
|
|
echo ""
|
|
echo "========================================"
|
|
echo "Health Check PASSED - $STACK Stack"
|
|
echo "========================================"
|
|
|
|
# Update state file
|
|
STATE_FILE="$PROJECT_ROOT/config/deployment/state.json"
|
|
if [[ -f "$STATE_FILE" ]]; then
|
|
# Update stack health status using jq if available
|
|
if command -v jq &> /dev/null; then
|
|
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
jq --arg stack "$STACK" --arg ts "$TIMESTAMP" \
|
|
'.[$stack].healthy = true | .[$stack].last_health_check = $ts' \
|
|
"$STATE_FILE" > "${STATE_FILE}.tmp" && mv "${STATE_FILE}.tmp" "$STATE_FILE"
|
|
fi
|
|
fi
|
|
|
|
exit 0
|