Initial commit: ROA2WEB - FastAPI + Vue.js + Telegram Bot
Modern ERP Reports Application with microservices architecture Tech Stack: - Backend: FastAPI + python-oracledb (Oracle DB integration) - Frontend: Vue.js 3 + PrimeVue + Vite - Telegram Bot: python-telegram-bot + SQLite - Infrastructure: Shared database pool, JWT authentication, SSH tunnel Features: - FastAPI backend with async Oracle connection pool - Vue.js 3 responsive frontend with PrimeVue components - Telegram bot alternative interface - Microservices architecture with shared components - Complete deployment support (Linux Docker + Windows IIS) - Comprehensive testing (Playwright E2E + pytest) Repository Structure: - reports-app/ - Main application (backend, frontend, telegram-bot) - shared/ - Shared components (database pool, auth, utils) - deployment/ - Deployment scripts (Linux & Windows) - docs/ - Project documentation - security/ - Security scanning and git hooks
This commit is contained in:
390
scripts/backup.sh
Normal file
390
scripts/backup.sh
Normal file
@@ -0,0 +1,390 @@
|
||||
#!/bin/bash
|
||||
# ROA2WEB Database and Application Backup Script
|
||||
# Creates backups of Oracle database, Docker volumes, and configuration
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
BACKUP_DIR="$PROJECT_DIR/backups"
|
||||
LOG_FILE="$PROJECT_DIR/backup.log"
|
||||
RETENTION_DAYS=30
|
||||
MAX_BACKUPS=10
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Logging function
|
||||
log() {
|
||||
local level=$1
|
||||
shift
|
||||
local message="$*"
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo -e "[$timestamp] [$level] $message" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Error handling
|
||||
error_exit() {
|
||||
log "ERROR" "$1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Success message
|
||||
success() {
|
||||
log "SUCCESS" "$1"
|
||||
}
|
||||
|
||||
# Info message
|
||||
info() {
|
||||
log "INFO" "$1"
|
||||
}
|
||||
|
||||
# Warning message
|
||||
warning() {
|
||||
log "WARNING" "$1"
|
||||
}
|
||||
|
||||
# Load environment variables
|
||||
load_env() {
|
||||
if [[ -f "$PROJECT_DIR/.env" ]]; then
|
||||
set -a
|
||||
source "$PROJECT_DIR/.env"
|
||||
set +a
|
||||
elif [[ -f "$PROJECT_DIR/.env.production" ]]; then
|
||||
set -a
|
||||
source "$PROJECT_DIR/.env.production"
|
||||
set +a
|
||||
else
|
||||
error_exit "No environment file found"
|
||||
fi
|
||||
}
|
||||
|
||||
# Create backup directory structure
|
||||
create_backup_structure() {
|
||||
local backup_name=$1
|
||||
local backup_path="$BACKUP_DIR/$backup_name"
|
||||
|
||||
mkdir -p "$backup_path"/{database,volumes,config,logs}
|
||||
echo "$backup_path"
|
||||
}
|
||||
|
||||
# Backup Oracle database
|
||||
backup_database() {
|
||||
local backup_path=$1
|
||||
|
||||
info "Starting Oracle database backup..."
|
||||
|
||||
# Check if SSH tunnel is required
|
||||
if [[ "$ORACLE_HOST" == "localhost" && -f "$PROJECT_DIR/ssh_tunnel.sh" ]]; then
|
||||
info "Ensuring SSH tunnel is running..."
|
||||
"$PROJECT_DIR/ssh_tunnel.sh" status || "$PROJECT_DIR/ssh_tunnel.sh" start
|
||||
fi
|
||||
|
||||
# Create database backup using Oracle export
|
||||
local db_backup_file="$backup_path/database/roa_backup_$(date +%Y%m%d_%H%M%S).dmp"
|
||||
local log_file="$backup_path/database/export.log"
|
||||
|
||||
# Export specific schemas (adjust as needed)
|
||||
if command -v expdp &> /dev/null; then
|
||||
info "Using Oracle Data Pump for backup..."
|
||||
expdp \
|
||||
userid="$ORACLE_USER/$ORACLE_PASSWORD@$ORACLE_HOST:$ORACLE_PORT/$ORACLE_SID" \
|
||||
schemas="$ORACLE_USER" \
|
||||
dumpfile="$(basename "$db_backup_file")" \
|
||||
logfile="$(basename "$log_file")" \
|
||||
directory=DATA_PUMP_DIR \
|
||||
compression=ALL &> /dev/null || warning "Data Pump backup failed, trying alternative method"
|
||||
fi
|
||||
|
||||
# Alternative: SQL backup for essential data
|
||||
if [[ ! -f "$db_backup_file" ]]; then
|
||||
info "Creating SQL backup of essential tables..."
|
||||
cat > "$backup_path/database/backup.sql" << EOF
|
||||
-- ROA2WEB Database Backup $(date)
|
||||
-- Essential tables backup
|
||||
|
||||
-- Users table
|
||||
CREATE TABLE users_backup AS SELECT * FROM users;
|
||||
|
||||
-- Companies table
|
||||
CREATE TABLE companies_backup AS SELECT * FROM companies;
|
||||
|
||||
-- Invoices table
|
||||
CREATE TABLE invoices_backup AS SELECT * FROM invoices;
|
||||
|
||||
-- Payments table
|
||||
CREATE TABLE payments_backup AS SELECT * FROM payments;
|
||||
|
||||
-- Authentication tokens (structure only for security)
|
||||
CREATE TABLE auth_tokens_backup AS SELECT user_id, created_at, expires_at FROM auth_tokens WHERE 1=0;
|
||||
|
||||
COMMIT;
|
||||
EOF
|
||||
|
||||
# Execute SQL backup if sqlplus is available
|
||||
if command -v sqlplus &> /dev/null; then
|
||||
sqlplus -s "$ORACLE_USER/$ORACLE_PASSWORD@$ORACLE_HOST:$ORACLE_PORT/$ORACLE_SID" @"$backup_path/database/backup.sql" > "$backup_path/database/backup_output.log" 2>&1 || warning "SQL backup failed"
|
||||
fi
|
||||
fi
|
||||
|
||||
success "Database backup completed"
|
||||
}
|
||||
|
||||
# Backup Docker volumes
|
||||
backup_volumes() {
|
||||
local backup_path=$1
|
||||
|
||||
info "Backing up Docker volumes..."
|
||||
|
||||
# Backup nginx logs
|
||||
if docker volume ls | grep -q "roa2web_nginx-logs"; then
|
||||
info "Backing up nginx logs..."
|
||||
docker run --rm \
|
||||
-v roa2web_nginx-logs:/data:ro \
|
||||
-v "$backup_path/volumes":/backup \
|
||||
alpine tar czf /backup/nginx-logs.tar.gz -C /data . 2>/dev/null || warning "Nginx logs backup failed"
|
||||
fi
|
||||
|
||||
# Backup SSL certificates
|
||||
if docker volume ls | grep -q "roa2web_ssl-certs"; then
|
||||
info "Backing up SSL certificates..."
|
||||
docker run --rm \
|
||||
-v roa2web_ssl-certs:/data:ro \
|
||||
-v "$backup_path/volumes":/backup \
|
||||
alpine tar czf /backup/ssl-certs.tar.gz -C /data . 2>/dev/null || warning "SSL certs backup failed"
|
||||
fi
|
||||
|
||||
# Backup Redis data
|
||||
if docker volume ls | grep -q "roa2web_redis-data"; then
|
||||
info "Backing up Redis data..."
|
||||
docker run --rm \
|
||||
-v roa2web_redis-data:/data:ro \
|
||||
-v "$backup_path/volumes":/backup \
|
||||
alpine tar czf /backup/redis-data.tar.gz -C /data . 2>/dev/null || warning "Redis data backup failed"
|
||||
fi
|
||||
|
||||
# Backup backend logs
|
||||
if docker volume ls | grep -q "roa2web_backend-logs"; then
|
||||
info "Backing up backend logs..."
|
||||
docker run --rm \
|
||||
-v roa2web_backend-logs:/data:ro \
|
||||
-v "$backup_path/volumes":/backup \
|
||||
alpine tar czf /backup/backend-logs.tar.gz -C /data . 2>/dev/null || warning "Backend logs backup failed"
|
||||
fi
|
||||
|
||||
success "Docker volumes backup completed"
|
||||
}
|
||||
|
||||
# Backup configuration files
|
||||
backup_config() {
|
||||
local backup_path=$1
|
||||
|
||||
info "Backing up configuration files..."
|
||||
|
||||
# Copy environment files
|
||||
cp -r "$PROJECT_DIR"/.env* "$backup_path/config/" 2>/dev/null || true
|
||||
|
||||
# Copy Docker Compose files
|
||||
cp "$PROJECT_DIR"/docker-compose*.yml "$backup_path/config/"
|
||||
|
||||
# Copy nginx configuration
|
||||
if [[ -d "$PROJECT_DIR/nginx/conf" ]]; then
|
||||
cp -r "$PROJECT_DIR/nginx/conf" "$backup_path/config/"
|
||||
fi
|
||||
|
||||
# Copy SSL configuration (if exists)
|
||||
if [[ -d "$PROJECT_DIR/nginx/ssl" ]]; then
|
||||
cp -r "$PROJECT_DIR/nginx/ssl" "$backup_path/config/" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Copy deployment scripts
|
||||
if [[ -d "$PROJECT_DIR/scripts" ]]; then
|
||||
cp -r "$PROJECT_DIR/scripts" "$backup_path/config/"
|
||||
fi
|
||||
|
||||
# Create backup metadata
|
||||
cat > "$backup_path/backup_info.txt" << EOF
|
||||
ROA2WEB Backup Information
|
||||
=========================
|
||||
Backup Date: $(date)
|
||||
Backup Type: Full Backup
|
||||
Environment: ${ENVIRONMENT:-unknown}
|
||||
Oracle Host: ${ORACLE_HOST:-unknown}
|
||||
Oracle User: ${ORACLE_USER:-unknown}
|
||||
Git Commit: $(git rev-parse HEAD 2>/dev/null || echo "unknown")
|
||||
Git Branch: $(git branch --show-current 2>/dev/null || echo "unknown")
|
||||
Docker Images:
|
||||
$(docker images --format "table {{.Repository}}:{{.Tag}}\t{{.CreatedAt}}\t{{.Size}}" | grep roa2web || echo "No ROA2WEB images found")
|
||||
EOF
|
||||
|
||||
success "Configuration backup completed"
|
||||
}
|
||||
|
||||
# Clean old backups
|
||||
cleanup_old_backups() {
|
||||
info "Cleaning up old backups..."
|
||||
|
||||
# Remove backups older than retention period
|
||||
find "$BACKUP_DIR" -name "backup_*" -type d -mtime +$RETENTION_DAYS -exec rm -rf {} \; 2>/dev/null || true
|
||||
|
||||
# Keep only the latest MAX_BACKUPS
|
||||
local backup_count=$(find "$BACKUP_DIR" -name "backup_*" -type d | wc -l)
|
||||
if [[ $backup_count -gt $MAX_BACKUPS ]]; then
|
||||
local excess=$((backup_count - MAX_BACKUPS))
|
||||
find "$BACKUP_DIR" -name "backup_*" -type d -printf '%T+ %p\n' | sort | head -n $excess | cut -d' ' -f2- | xargs rm -rf
|
||||
info "Removed $excess old backups"
|
||||
fi
|
||||
|
||||
success "Backup cleanup completed"
|
||||
}
|
||||
|
||||
# List available backups
|
||||
list_backups() {
|
||||
info "Available backups:"
|
||||
echo ""
|
||||
|
||||
if [[ ! -d "$BACKUP_DIR" ]] || [[ -z "$(ls -A "$BACKUP_DIR" 2>/dev/null)" ]]; then
|
||||
warning "No backups found"
|
||||
return
|
||||
fi
|
||||
|
||||
find "$BACKUP_DIR" -name "backup_*" -type d -printf '%T+ %p\n' | sort -r | while read -r line; do
|
||||
local date_part=$(echo "$line" | cut -d' ' -f1 | cut -d+ -f1)
|
||||
local backup_name=$(basename "$(echo "$line" | cut -d' ' -f2)")
|
||||
local backup_path="$(echo "$line" | cut -d' ' -f2)"
|
||||
|
||||
echo "📦 $backup_name ($date_part)"
|
||||
|
||||
if [[ -f "$backup_path/backup_info.txt" ]]; then
|
||||
grep -E "(Backup Date|Environment|Git)" "$backup_path/backup_info.txt" | sed 's/^/ /'
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
}
|
||||
|
||||
# Restore from backup
|
||||
restore_backup() {
|
||||
local backup_name=$1
|
||||
|
||||
if [[ -z "$backup_name" ]]; then
|
||||
error_exit "Backup name is required for restore operation"
|
||||
fi
|
||||
|
||||
local backup_path="$BACKUP_DIR/$backup_name"
|
||||
|
||||
if [[ ! -d "$backup_path" ]]; then
|
||||
error_exit "Backup not found: $backup_name"
|
||||
fi
|
||||
|
||||
warning "Restoring from backup: $backup_name"
|
||||
warning "This will overwrite current data. Press Ctrl+C to cancel or wait 10 seconds to continue..."
|
||||
sleep 10
|
||||
|
||||
info "Starting restore process..."
|
||||
|
||||
# Stop services
|
||||
docker-compose -f "$PROJECT_DIR/docker-compose.yml" -f "$PROJECT_DIR/docker-compose.production.yml" down 2>/dev/null || true
|
||||
|
||||
# Restore configuration
|
||||
if [[ -d "$backup_path/config" ]]; then
|
||||
info "Restoring configuration files..."
|
||||
cp -r "$backup_path/config"/.env* "$PROJECT_DIR/" 2>/dev/null || true
|
||||
cp -r "$backup_path/config"/docker-compose*.yml "$PROJECT_DIR/"
|
||||
|
||||
if [[ -d "$backup_path/config/conf" ]]; then
|
||||
mkdir -p "$PROJECT_DIR/nginx"
|
||||
cp -r "$backup_path/config/conf" "$PROJECT_DIR/nginx/"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Restore volumes
|
||||
if [[ -d "$backup_path/volumes" ]]; then
|
||||
info "Restoring Docker volumes..."
|
||||
|
||||
for volume_backup in "$backup_path/volumes"/*.tar.gz; do
|
||||
if [[ -f "$volume_backup" ]]; then
|
||||
local volume_name=$(basename "$volume_backup" .tar.gz)
|
||||
docker volume create "roa2web_$volume_name" 2>/dev/null || true
|
||||
docker run --rm \
|
||||
-v "roa2web_$volume_name":/data \
|
||||
-v "$backup_path/volumes":/backup \
|
||||
alpine tar xzf "/backup/$(basename "$volume_backup")" -C /data
|
||||
info "Restored volume: $volume_name"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
success "Restore completed. Please restart services manually."
|
||||
}
|
||||
|
||||
# Main function
|
||||
main() {
|
||||
local action=${1:-backup}
|
||||
|
||||
case $action in
|
||||
"backup"|"full")
|
||||
info "=== ROA2WEB Full Backup ==="
|
||||
load_env
|
||||
|
||||
local backup_name="backup_$(date +%Y%m%d_%H%M%S)"
|
||||
local backup_path=$(create_backup_structure "$backup_name")
|
||||
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
backup_database "$backup_path"
|
||||
backup_volumes "$backup_path"
|
||||
backup_config "$backup_path"
|
||||
cleanup_old_backups
|
||||
|
||||
success "Full backup completed: $backup_name"
|
||||
;;
|
||||
"database"|"db")
|
||||
info "=== ROA2WEB Database Backup ==="
|
||||
load_env
|
||||
|
||||
local backup_name="db_backup_$(date +%Y%m%d_%H%M%S)"
|
||||
local backup_path=$(create_backup_structure "$backup_name")
|
||||
|
||||
backup_database "$backup_path"
|
||||
success "Database backup completed: $backup_name"
|
||||
;;
|
||||
"volumes")
|
||||
info "=== ROA2WEB Volumes Backup ==="
|
||||
|
||||
local backup_name="volumes_backup_$(date +%Y%m%d_%H%M%S)"
|
||||
local backup_path=$(create_backup_structure "$backup_name")
|
||||
|
||||
backup_volumes "$backup_path"
|
||||
success "Volumes backup completed: $backup_name"
|
||||
;;
|
||||
"list")
|
||||
list_backups
|
||||
;;
|
||||
"restore")
|
||||
restore_backup "$2"
|
||||
;;
|
||||
"cleanup")
|
||||
cleanup_old_backups
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {backup|database|volumes|list|restore <backup_name>|cleanup}"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " backup|full - Create full backup (database + volumes + config)"
|
||||
echo " database|db - Backup only Oracle database"
|
||||
echo " volumes - Backup only Docker volumes"
|
||||
echo " list - List available backups"
|
||||
echo " restore - Restore from backup"
|
||||
echo " cleanup - Clean up old backups"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
282
scripts/deploy.sh
Normal file
282
scripts/deploy.sh
Normal file
@@ -0,0 +1,282 @@
|
||||
#!/bin/bash
|
||||
# ROA2WEB Production Deployment Script
|
||||
# Zero-downtime deployment with health checks and rollback capability
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
BACKUP_DIR="$PROJECT_DIR/backups"
|
||||
LOG_FILE="$PROJECT_DIR/deploy.log"
|
||||
MAX_RETRIES=3
|
||||
HEALTH_CHECK_TIMEOUT=60
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Logging function
|
||||
log() {
|
||||
local level=$1
|
||||
shift
|
||||
local message="$*"
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo -e "[$timestamp] [$level] $message" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Error handling
|
||||
error_exit() {
|
||||
log "ERROR" "$1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Success message
|
||||
success() {
|
||||
log "SUCCESS" "$1"
|
||||
}
|
||||
|
||||
# Warning message
|
||||
warning() {
|
||||
log "WARNING" "$1"
|
||||
}
|
||||
|
||||
# Info message
|
||||
info() {
|
||||
log "INFO" "$1"
|
||||
}
|
||||
|
||||
# Check if running as root
|
||||
check_root() {
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
error_exit "This script should not be run as root for security reasons"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check prerequisites
|
||||
check_prerequisites() {
|
||||
info "Checking prerequisites..."
|
||||
|
||||
# Check if Docker is installed and running
|
||||
if ! command -v docker &> /dev/null; then
|
||||
error_exit "Docker is not installed"
|
||||
fi
|
||||
|
||||
if ! docker info &> /dev/null; then
|
||||
error_exit "Docker daemon is not running"
|
||||
fi
|
||||
|
||||
# Check if Docker Compose is installed
|
||||
if ! command -v docker-compose &> /dev/null; then
|
||||
error_exit "Docker Compose is not installed"
|
||||
fi
|
||||
|
||||
# Check if required files exist
|
||||
if [[ ! -f "$PROJECT_DIR/.env.production" ]]; then
|
||||
error_exit "Production environment file (.env.production) not found"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$PROJECT_DIR/docker-compose.yml" ]]; then
|
||||
error_exit "Docker Compose file not found"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$PROJECT_DIR/docker-compose.production.yml" ]]; then
|
||||
error_exit "Production Docker Compose file not found"
|
||||
fi
|
||||
|
||||
success "Prerequisites check passed"
|
||||
}
|
||||
|
||||
# Create backup directory
|
||||
create_backup_dir() {
|
||||
if [[ ! -d "$BACKUP_DIR" ]]; then
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
info "Created backup directory: $BACKUP_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
# Create backup of current deployment
|
||||
create_backup() {
|
||||
info "Creating backup of current deployment..."
|
||||
|
||||
local backup_name="backup_$(date +%Y%m%d_%H%M%S)"
|
||||
local backup_path="$BACKUP_DIR/$backup_name"
|
||||
|
||||
# Create backup directory
|
||||
mkdir -p "$backup_path"
|
||||
|
||||
# Backup Docker images
|
||||
info "Backing up Docker images..."
|
||||
docker save -o "$backup_path/images.tar" \
|
||||
roa2web/backend:latest \
|
||||
roa2web/frontend:latest \
|
||||
roa2web/nginx-gateway:latest 2>/dev/null || warning "Some images may not exist yet"
|
||||
|
||||
# Backup configuration files
|
||||
cp -r "$PROJECT_DIR"/.env* "$backup_path/" 2>/dev/null || true
|
||||
cp -r "$PROJECT_DIR"/docker-compose*.yml "$backup_path/"
|
||||
cp -r "$PROJECT_DIR"/nginx/conf "$backup_path/" 2>/dev/null || true
|
||||
|
||||
# Backup volumes data
|
||||
info "Backing up volume data..."
|
||||
docker run --rm -v roa2web_nginx-logs:/data -v "$backup_path":/backup alpine tar czf /backup/nginx-logs.tar.gz -C /data . 2>/dev/null || warning "Nginx logs backup failed"
|
||||
docker run --rm -v roa2web_ssl-certs:/data -v "$backup_path":/backup alpine tar czf /backup/ssl-certs.tar.gz -C /data . 2>/dev/null || warning "SSL certs backup failed"
|
||||
docker run --rm -v roa2web_redis-data:/data -v "$backup_path":/backup alpine tar czf /backup/redis-data.tar.gz -C /data . 2>/dev/null || warning "Redis data backup failed"
|
||||
|
||||
echo "$backup_name" > "$PROJECT_DIR/.last_backup"
|
||||
success "Backup created: $backup_name"
|
||||
}
|
||||
|
||||
# Health check function
|
||||
health_check() {
|
||||
local service=$1
|
||||
local url=$2
|
||||
local timeout=${3:-30}
|
||||
|
||||
info "Performing health check for $service..."
|
||||
|
||||
local count=0
|
||||
while [[ $count -lt $timeout ]]; do
|
||||
if curl -f -s "$url" > /dev/null 2>&1; then
|
||||
success "$service health check passed"
|
||||
return 0
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
((count++))
|
||||
done
|
||||
|
||||
error_exit "$service health check failed after ${timeout}s"
|
||||
}
|
||||
|
||||
# Wait for services to be healthy
|
||||
wait_for_services() {
|
||||
info "Waiting for services to be healthy..."
|
||||
|
||||
# Wait for containers to be running
|
||||
sleep 10
|
||||
|
||||
# Check backend health
|
||||
health_check "Backend API" "http://localhost/api/health" $HEALTH_CHECK_TIMEOUT
|
||||
|
||||
# Check frontend health
|
||||
health_check "Frontend" "http://localhost/health" $HEALTH_CHECK_TIMEOUT
|
||||
|
||||
# Check gateway health
|
||||
health_check "Gateway" "http://localhost/health" $HEALTH_CHECK_TIMEOUT
|
||||
|
||||
success "All services are healthy"
|
||||
}
|
||||
|
||||
# Deploy function
|
||||
deploy() {
|
||||
info "Starting deployment..."
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# Load production environment
|
||||
set -a
|
||||
source .env.production
|
||||
set +a
|
||||
|
||||
# Build images
|
||||
info "Building Docker images..."
|
||||
docker-compose -f docker-compose.yml -f docker-compose.production.yml build --no-cache
|
||||
|
||||
# Pull any updated base images
|
||||
info "Pulling base images..."
|
||||
docker-compose -f docker-compose.yml -f docker-compose.production.yml pull
|
||||
|
||||
# Deploy with zero downtime
|
||||
info "Deploying services..."
|
||||
docker-compose -f docker-compose.yml -f docker-compose.production.yml up -d --force-recreate
|
||||
|
||||
# Wait for services to be ready
|
||||
wait_for_services
|
||||
|
||||
# Clean up old images
|
||||
info "Cleaning up old images..."
|
||||
docker image prune -f
|
||||
|
||||
success "Deployment completed successfully!"
|
||||
}
|
||||
|
||||
# Rollback function
|
||||
rollback() {
|
||||
local backup_name=$1
|
||||
|
||||
if [[ -z "$backup_name" ]]; then
|
||||
if [[ -f "$PROJECT_DIR/.last_backup" ]]; then
|
||||
backup_name=$(cat "$PROJECT_DIR/.last_backup")
|
||||
else
|
||||
error_exit "No backup specified and no last backup found"
|
||||
fi
|
||||
fi
|
||||
|
||||
local backup_path="$BACKUP_DIR/$backup_name"
|
||||
|
||||
if [[ ! -d "$backup_path" ]]; then
|
||||
error_exit "Backup not found: $backup_name"
|
||||
fi
|
||||
|
||||
warning "Rolling back to backup: $backup_name"
|
||||
|
||||
# Stop current services
|
||||
docker-compose -f docker-compose.yml -f docker-compose.production.yml down
|
||||
|
||||
# Restore images
|
||||
if [[ -f "$backup_path/images.tar" ]]; then
|
||||
docker load -i "$backup_path/images.tar"
|
||||
fi
|
||||
|
||||
# Restore configuration
|
||||
cp "$backup_path"/.env* "$PROJECT_DIR/"
|
||||
|
||||
# Start services
|
||||
docker-compose -f docker-compose.yml -f docker-compose.production.yml up -d
|
||||
|
||||
# Wait for services
|
||||
wait_for_services
|
||||
|
||||
success "Rollback completed successfully!"
|
||||
}
|
||||
|
||||
# Main deployment workflow
|
||||
main() {
|
||||
local action=${1:-deploy}
|
||||
|
||||
case $action in
|
||||
"deploy")
|
||||
info "=== ROA2WEB Production Deployment ==="
|
||||
check_root
|
||||
check_prerequisites
|
||||
create_backup_dir
|
||||
create_backup
|
||||
deploy
|
||||
;;
|
||||
"rollback")
|
||||
info "=== ROA2WEB Rollback ==="
|
||||
check_root
|
||||
rollback "$2"
|
||||
;;
|
||||
"health-check")
|
||||
info "=== ROA2WEB Health Check ==="
|
||||
wait_for_services
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {deploy|rollback [backup_name]|health-check}"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " deploy - Deploy the application to production"
|
||||
echo " rollback - Rollback to the last backup or specified backup"
|
||||
echo " health-check - Perform health check on running services"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Run main function with all arguments
|
||||
main "$@"
|
||||
479
scripts/health-check.sh
Normal file
479
scripts/health-check.sh
Normal file
@@ -0,0 +1,479 @@
|
||||
#!/bin/bash
|
||||
# ROA2WEB Comprehensive Health Check Script
|
||||
# Monitors all services and provides detailed health information
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
LOG_FILE="$PROJECT_DIR/health-check.log"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
PURPLE='\033[0;35m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Health check results
|
||||
OVERALL_HEALTH=true
|
||||
ISSUES=()
|
||||
|
||||
# Logging function
|
||||
log() {
|
||||
local level=$1
|
||||
shift
|
||||
local message="$*"
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo -e "[$timestamp] [$level] $message" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Status icons
|
||||
status_icon() {
|
||||
local status=$1
|
||||
case $status in
|
||||
"healthy") echo "✅" ;;
|
||||
"warning") echo "⚠️" ;;
|
||||
"error") echo "❌" ;;
|
||||
"info") echo "ℹ️" ;;
|
||||
*) echo "❓" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Print section header
|
||||
section_header() {
|
||||
local title=$1
|
||||
echo ""
|
||||
echo -e "${BLUE}=================================${NC}"
|
||||
echo -e "${BLUE}$title${NC}"
|
||||
echo -e "${BLUE}=================================${NC}"
|
||||
}
|
||||
|
||||
# Add issue to report
|
||||
add_issue() {
|
||||
local severity=$1
|
||||
local component=$2
|
||||
local message=$3
|
||||
|
||||
ISSUES+=("[$severity] $component: $message")
|
||||
|
||||
if [[ "$severity" == "ERROR" ]]; then
|
||||
OVERALL_HEALTH=false
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if service is running
|
||||
check_service_running() {
|
||||
local service_name=$1
|
||||
local container_name=$2
|
||||
|
||||
if docker ps --format "table {{.Names}}" | grep -q "^$container_name$"; then
|
||||
echo -e "$(status_icon "healthy") ${GREEN}$service_name is running${NC}"
|
||||
return 0
|
||||
else
|
||||
echo -e "$(status_icon "error") ${RED}$service_name is not running${NC}"
|
||||
add_issue "ERROR" "$service_name" "Container not running"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# HTTP health check
|
||||
http_health_check() {
|
||||
local service_name=$1
|
||||
local url=$2
|
||||
local expected_status=${3:-200}
|
||||
local timeout=${4:-10}
|
||||
|
||||
local response
|
||||
local status_code
|
||||
|
||||
response=$(curl -s -w "%{http_code}" --max-time "$timeout" "$url" 2>/dev/null || echo "000")
|
||||
status_code="${response: -3}"
|
||||
|
||||
if [[ "$status_code" == "$expected_status" ]]; then
|
||||
echo -e "$(status_icon "healthy") ${GREEN}$service_name HTTP health check passed ($status_code)${NC}"
|
||||
return 0
|
||||
else
|
||||
echo -e "$(status_icon "error") ${RED}$service_name HTTP health check failed ($status_code)${NC}"
|
||||
add_issue "ERROR" "$service_name" "HTTP health check failed with status $status_code"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Docker container health check
|
||||
docker_health_check() {
|
||||
local service_name=$1
|
||||
local container_name=$2
|
||||
|
||||
local health_status
|
||||
health_status=$(docker inspect --format='{{.State.Health.Status}}' "$container_name" 2>/dev/null || echo "no-healthcheck")
|
||||
|
||||
case $health_status in
|
||||
"healthy")
|
||||
echo -e "$(status_icon "healthy") ${GREEN}$service_name Docker health check: healthy${NC}"
|
||||
return 0
|
||||
;;
|
||||
"unhealthy")
|
||||
echo -e "$(status_icon "error") ${RED}$service_name Docker health check: unhealthy${NC}"
|
||||
add_issue "ERROR" "$service_name" "Docker health check reports unhealthy"
|
||||
return 1
|
||||
;;
|
||||
"starting")
|
||||
echo -e "$(status_icon "warning") ${YELLOW}$service_name Docker health check: starting${NC}"
|
||||
add_issue "WARNING" "$service_name" "Docker health check still starting"
|
||||
return 1
|
||||
;;
|
||||
"no-healthcheck")
|
||||
echo -e "$(status_icon "info") ${CYAN}$service_name: No Docker health check configured${NC}"
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
echo -e "$(status_icon "error") ${RED}$service_name Docker health check: unknown status ($health_status)${NC}"
|
||||
add_issue "ERROR" "$service_name" "Unknown Docker health check status: $health_status"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Check container resources
|
||||
check_container_resources() {
|
||||
local service_name=$1
|
||||
local container_name=$2
|
||||
|
||||
if ! docker ps --format "table {{.Names}}" | grep -q "^$container_name$"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local stats
|
||||
stats=$(docker stats "$container_name" --no-stream --format "table {{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}" 2>/dev/null | tail -n1)
|
||||
|
||||
if [[ -n "$stats" ]]; then
|
||||
local cpu_percent=$(echo "$stats" | awk '{print $1}' | sed 's/%//')
|
||||
local mem_usage=$(echo "$stats" | awk '{print $2}')
|
||||
local mem_percent=$(echo "$stats" | awk '{print $3}' | sed 's/%//')
|
||||
|
||||
echo -e "$(status_icon "info") ${CYAN}$service_name Resources: CPU ${cpu_percent}%, Memory ${mem_usage} (${mem_percent}%)${NC}"
|
||||
|
||||
# Check for resource warnings
|
||||
if (( $(echo "$cpu_percent > 80" | bc -l) )); then
|
||||
add_issue "WARNING" "$service_name" "High CPU usage: ${cpu_percent}%"
|
||||
fi
|
||||
|
||||
if (( $(echo "$mem_percent > 80" | bc -l) )); then
|
||||
add_issue "WARNING" "$service_name" "High memory usage: ${mem_percent}%"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Check logs for errors
|
||||
check_container_logs() {
|
||||
local service_name=$1
|
||||
local container_name=$2
|
||||
|
||||
if ! docker ps --format "table {{.Names}}" | grep -q "^$container_name$"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local error_count
|
||||
error_count=$(docker logs "$container_name" --since="5m" 2>&1 | grep -i "error\|exception\|failed\|fatal" | wc -l)
|
||||
|
||||
if [[ "$error_count" -gt 0 ]]; then
|
||||
echo -e "$(status_icon "warning") ${YELLOW}$service_name: $error_count errors in last 5 minutes${NC}"
|
||||
add_issue "WARNING" "$service_name" "$error_count errors found in recent logs"
|
||||
|
||||
# Show recent errors
|
||||
echo -e "${YELLOW}Recent errors:${NC}"
|
||||
docker logs "$container_name" --since="5m" 2>&1 | grep -i "error\|exception\|failed\|fatal" | tail -3 | sed 's/^/ /'
|
||||
else
|
||||
echo -e "$(status_icon "healthy") ${GREEN}$service_name: No recent errors in logs${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check disk space
|
||||
check_disk_space() {
|
||||
section_header "DISK SPACE CHECK"
|
||||
|
||||
local disk_usage
|
||||
disk_usage=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//')
|
||||
|
||||
echo -e "$(status_icon "info") ${CYAN}Root filesystem usage: ${disk_usage}%${NC}"
|
||||
|
||||
if [[ "$disk_usage" -gt 90 ]]; then
|
||||
echo -e "$(status_icon "error") ${RED}Critical: Disk space usage is ${disk_usage}%${NC}"
|
||||
add_issue "ERROR" "System" "Critical disk space usage: ${disk_usage}%"
|
||||
elif [[ "$disk_usage" -gt 80 ]]; then
|
||||
echo -e "$(status_icon "warning") ${YELLOW}Warning: Disk space usage is ${disk_usage}%${NC}"
|
||||
add_issue "WARNING" "System" "High disk space usage: ${disk_usage}%"
|
||||
else
|
||||
echo -e "$(status_icon "healthy") ${GREEN}Disk space usage is acceptable${NC}"
|
||||
fi
|
||||
|
||||
# Check Docker space
|
||||
local docker_space
|
||||
docker_space=$(docker system df --format "table {{.Type}}\t{{.Total}}\t{{.Active}}\t{{.Size}}\t{{.Reclaimable}}" 2>/dev/null || echo "Docker space info unavailable")
|
||||
|
||||
if [[ "$docker_space" != "Docker space info unavailable" ]]; then
|
||||
echo ""
|
||||
echo -e "${CYAN}Docker space usage:${NC}"
|
||||
echo "$docker_space"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check network connectivity
|
||||
check_network() {
|
||||
section_header "NETWORK CONNECTIVITY CHECK"
|
||||
|
||||
# Check if Docker network exists
|
||||
if docker network ls | grep -q "roa-network"; then
|
||||
echo -e "$(status_icon "healthy") ${GREEN}Docker network 'roa-network' exists${NC}"
|
||||
else
|
||||
echo -e "$(status_icon "error") ${RED}Docker network 'roa-network' not found${NC}"
|
||||
add_issue "ERROR" "Network" "Docker network 'roa-network' not found"
|
||||
fi
|
||||
|
||||
# Check external connectivity
|
||||
if ping -c 1 8.8.8.8 &> /dev/null; then
|
||||
echo -e "$(status_icon "healthy") ${GREEN}External network connectivity: OK${NC}"
|
||||
else
|
||||
echo -e "$(status_icon "warning") ${YELLOW}External network connectivity: Limited${NC}"
|
||||
add_issue "WARNING" "Network" "Limited external network connectivity"
|
||||
fi
|
||||
|
||||
# Check DNS resolution
|
||||
if nslookup google.com &> /dev/null; then
|
||||
echo -e "$(status_icon "healthy") ${GREEN}DNS resolution: OK${NC}"
|
||||
else
|
||||
echo -e "$(status_icon "warning") ${YELLOW}DNS resolution: Issues detected${NC}"
|
||||
add_issue "WARNING" "Network" "DNS resolution issues detected"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check database connectivity
|
||||
check_database() {
|
||||
section_header "DATABASE CONNECTIVITY CHECK"
|
||||
|
||||
# Load environment variables
|
||||
if [[ -f "$PROJECT_DIR/.env" ]]; then
|
||||
set -a
|
||||
source "$PROJECT_DIR/.env"
|
||||
set +a
|
||||
elif [[ -f "$PROJECT_DIR/.env.production" ]]; then
|
||||
set -a
|
||||
source "$PROJECT_DIR/.env.production"
|
||||
set +a
|
||||
fi
|
||||
|
||||
# Check SSH tunnel if needed
|
||||
if [[ "$ORACLE_HOST" == "localhost" && -f "$PROJECT_DIR/ssh_tunnel.sh" ]]; then
|
||||
local tunnel_status
|
||||
tunnel_status=$("$PROJECT_DIR/ssh_tunnel.sh" status 2>/dev/null || echo "not running")
|
||||
|
||||
if [[ "$tunnel_status" == *"running"* ]]; then
|
||||
echo -e "$(status_icon "healthy") ${GREEN}SSH tunnel is running${NC}"
|
||||
else
|
||||
echo -e "$(status_icon "warning") ${YELLOW}SSH tunnel is not running${NC}"
|
||||
add_issue "WARNING" "Database" "SSH tunnel is not running"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test Oracle connection (if we can)
|
||||
if command -v sqlplus &> /dev/null && [[ -n "$ORACLE_USER" && -n "$ORACLE_PASSWORD" ]]; then
|
||||
local connection_test
|
||||
connection_test=$(timeout 10 sqlplus -s "$ORACLE_USER/$ORACLE_PASSWORD@$ORACLE_HOST:$ORACLE_PORT/$ORACLE_SID" <<< "SELECT 'OK' FROM DUAL; EXIT;" 2>/dev/null | grep "OK" || echo "failed")
|
||||
|
||||
if [[ "$connection_test" == "OK" ]]; then
|
||||
echo -e "$(status_icon "healthy") ${GREEN}Oracle database connection: OK${NC}"
|
||||
else
|
||||
echo -e "$(status_icon "error") ${RED}Oracle database connection: Failed${NC}"
|
||||
add_issue "ERROR" "Database" "Cannot connect to Oracle database"
|
||||
fi
|
||||
else
|
||||
echo -e "$(status_icon "info") ${CYAN}Oracle connection test skipped (sqlplus not available or credentials not set)${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check services
|
||||
check_services() {
|
||||
section_header "SERVICES HEALTH CHECK"
|
||||
|
||||
# Backend service
|
||||
echo -e "${PURPLE}ROA Backend Service:${NC}"
|
||||
check_service_running "Backend" "roa-backend"
|
||||
docker_health_check "Backend" "roa-backend"
|
||||
http_health_check "Backend API" "http://localhost/api/health"
|
||||
check_container_resources "Backend" "roa-backend"
|
||||
check_container_logs "Backend" "roa-backend"
|
||||
|
||||
echo ""
|
||||
|
||||
# Frontend service
|
||||
echo -e "${PURPLE}ROA Frontend Service:${NC}"
|
||||
check_service_running "Frontend" "roa-frontend"
|
||||
docker_health_check "Frontend" "roa-frontend"
|
||||
http_health_check "Frontend" "http://localhost:3000/health"
|
||||
check_container_resources "Frontend" "roa-frontend"
|
||||
check_container_logs "Frontend" "roa-frontend"
|
||||
|
||||
echo ""
|
||||
|
||||
# Gateway service
|
||||
echo -e "${PURPLE}ROA Gateway Service:${NC}"
|
||||
check_service_running "Gateway" "roa-gateway"
|
||||
docker_health_check "Gateway" "roa-gateway"
|
||||
http_health_check "Gateway" "http://localhost/health"
|
||||
check_container_resources "Gateway" "roa-gateway"
|
||||
check_container_logs "Gateway" "roa-gateway"
|
||||
|
||||
echo ""
|
||||
|
||||
# Redis service
|
||||
echo -e "${PURPLE}ROA Redis Service:${NC}"
|
||||
check_service_running "Redis" "roa-redis"
|
||||
docker_health_check "Redis" "roa-redis"
|
||||
check_container_resources "Redis" "roa-redis"
|
||||
check_container_logs "Redis" "roa-redis"
|
||||
}
|
||||
|
||||
# Generate summary report
|
||||
generate_summary() {
|
||||
section_header "HEALTH CHECK SUMMARY"
|
||||
|
||||
if [[ "$OVERALL_HEALTH" == "true" ]]; then
|
||||
echo -e "$(status_icon "healthy") ${GREEN}Overall System Health: HEALTHY${NC}"
|
||||
else
|
||||
echo -e "$(status_icon "error") ${RED}Overall System Health: ISSUES DETECTED${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${CYAN}Timestamp: $(date)${NC}"
|
||||
|
||||
if [[ ${#ISSUES[@]} -gt 0 ]]; then
|
||||
echo ""
|
||||
echo -e "${YELLOW}Issues found:${NC}"
|
||||
for issue in "${ISSUES[@]}"; do
|
||||
echo " $issue"
|
||||
done
|
||||
else
|
||||
echo ""
|
||||
echo -e "${GREEN}No issues detected${NC}"
|
||||
fi
|
||||
|
||||
# Exit with appropriate code
|
||||
if [[ "$OVERALL_HEALTH" == "true" ]]; then
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Watch mode - continuous monitoring
|
||||
watch_mode() {
|
||||
echo -e "${BLUE}Starting continuous health monitoring...${NC}"
|
||||
echo -e "${CYAN}Press Ctrl+C to stop${NC}"
|
||||
echo ""
|
||||
|
||||
while true; do
|
||||
clear
|
||||
echo -e "${BLUE}ROA2WEB Health Monitor - $(date)${NC}"
|
||||
|
||||
# Reset status
|
||||
OVERALL_HEALTH=true
|
||||
ISSUES=()
|
||||
|
||||
# Quick service check
|
||||
echo ""
|
||||
echo -e "${PURPLE}Service Status:${NC}"
|
||||
check_service_running "Backend" "roa-backend" > /dev/null 2>&1 && echo -e " Backend: $(status_icon "healthy")" || echo -e " Backend: $(status_icon "error")"
|
||||
check_service_running "Frontend" "roa-frontend" > /dev/null 2>&1 && echo -e " Frontend: $(status_icon "healthy")" || echo -e " Frontend: $(status_icon "error")"
|
||||
check_service_running "Gateway" "roa-gateway" > /dev/null 2>&1 && echo -e " Gateway: $(status_icon "healthy")" || echo -e " Gateway: $(status_icon "error")"
|
||||
check_service_running "Redis" "roa-redis" > /dev/null 2>&1 && echo -e " Redis: $(status_icon "healthy")" || echo -e " Redis: $(status_icon "error")"
|
||||
|
||||
# Quick HTTP checks
|
||||
echo ""
|
||||
echo -e "${PURPLE}API Status:${NC}"
|
||||
http_health_check "Backend API" "http://localhost/api/health" 200 5 > /dev/null 2>&1 && echo -e " API: $(status_icon "healthy")" || echo -e " API: $(status_icon "error")"
|
||||
http_health_check "Frontend" "http://localhost/health" 200 5 > /dev/null 2>&1 && echo -e " Frontend: $(status_icon "healthy")" || echo -e " Frontend: $(status_icon "error")"
|
||||
|
||||
if [[ ${#ISSUES[@]} -gt 0 ]]; then
|
||||
echo ""
|
||||
echo -e "${YELLOW}Current Issues:${NC}"
|
||||
for issue in "${ISSUES[@]}"; do
|
||||
echo " $issue"
|
||||
done
|
||||
fi
|
||||
|
||||
sleep 30
|
||||
done
|
||||
}
|
||||
|
||||
# Main function
|
||||
main() {
|
||||
local action=${1:-full}
|
||||
|
||||
case $action in
|
||||
"full")
|
||||
echo -e "${BLUE}ROA2WEB Comprehensive Health Check${NC}"
|
||||
echo -e "${CYAN}$(date)${NC}"
|
||||
|
||||
check_services
|
||||
check_disk_space
|
||||
check_network
|
||||
check_database
|
||||
generate_summary
|
||||
;;
|
||||
"quick")
|
||||
echo -e "${BLUE}ROA2WEB Quick Health Check${NC}"
|
||||
|
||||
# Reset status
|
||||
OVERALL_HEALTH=true
|
||||
ISSUES=()
|
||||
|
||||
check_services
|
||||
generate_summary
|
||||
;;
|
||||
"services")
|
||||
check_services
|
||||
;;
|
||||
"network")
|
||||
check_network
|
||||
;;
|
||||
"database")
|
||||
check_database
|
||||
;;
|
||||
"watch")
|
||||
watch_mode
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {full|quick|services|network|database|watch}"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " full - Comprehensive health check (default)"
|
||||
echo " quick - Quick services health check"
|
||||
echo " services - Check only ROA2WEB services"
|
||||
echo " network - Check network connectivity"
|
||||
echo " database - Check database connectivity"
|
||||
echo " watch - Continuous monitoring mode"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Make sure bc is available for numeric comparisons
|
||||
if ! command -v bc &> /dev/null; then
|
||||
# Fallback function for numeric comparison without bc
|
||||
compare_float() {
|
||||
local val1=$1
|
||||
local op=$2
|
||||
local val2=$3
|
||||
python3 -c "print($val1 $op $val2)" 2>/dev/null || echo "false"
|
||||
}
|
||||
|
||||
# Replace bc usage with python3
|
||||
alias bc='python3 -c'
|
||||
fi
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
342
scripts/rollback.sh
Normal file
342
scripts/rollback.sh
Normal file
@@ -0,0 +1,342 @@
|
||||
#!/bin/bash
|
||||
# ROA2WEB Quick Rollback Script
|
||||
# Fast rollback to previous deployment state
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
BACKUP_DIR="$PROJECT_DIR/backups"
|
||||
LOG_FILE="$PROJECT_DIR/rollback.log"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Logging function
|
||||
log() {
|
||||
local level=$1
|
||||
shift
|
||||
local message="$*"
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo -e "[$timestamp] [$level] $message" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Error handling
|
||||
error_exit() {
|
||||
log "ERROR" "$1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Success message
|
||||
success() {
|
||||
log "SUCCESS" "$1"
|
||||
}
|
||||
|
||||
# Warning message
|
||||
warning() {
|
||||
log "WARNING" "$1"
|
||||
}
|
||||
|
||||
# Info message
|
||||
info() {
|
||||
log "INFO" "$1"
|
||||
}
|
||||
|
||||
# Get last backup
|
||||
get_last_backup() {
|
||||
if [[ -f "$PROJECT_DIR/.last_backup" ]]; then
|
||||
cat "$PROJECT_DIR/.last_backup"
|
||||
else
|
||||
# Find the most recent backup
|
||||
find "$BACKUP_DIR" -name "backup_*" -type d -printf '%T+ %p\n' 2>/dev/null | sort -r | head -n1 | cut -d' ' -f2- | xargs basename 2>/dev/null || echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# List available deployments to rollback to
|
||||
list_rollback_targets() {
|
||||
info "Available rollback targets:"
|
||||
echo ""
|
||||
|
||||
if [[ ! -d "$BACKUP_DIR" ]] || [[ -z "$(ls -A "$BACKUP_DIR" 2>/dev/null)" ]]; then
|
||||
warning "No backups found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local current_backup=$(get_last_backup)
|
||||
|
||||
find "$BACKUP_DIR" -name "backup_*" -type d -printf '%T+ %p\n' | sort -r | head -n5 | while read -r line; do
|
||||
local date_part=$(echo "$line" | cut -d' ' -f1 | cut -d+ -f1)
|
||||
local backup_name=$(basename "$(echo "$line" | cut -d' ' -f2)")
|
||||
local backup_path="$(echo "$line" | cut -d' ' -f2)"
|
||||
|
||||
local marker=""
|
||||
if [[ "$backup_name" == "$current_backup" ]]; then
|
||||
marker=" ${GREEN}(current)${NC}"
|
||||
fi
|
||||
|
||||
echo -e "📦 ${BLUE}$backup_name${NC} ($date_part)$marker"
|
||||
|
||||
if [[ -f "$backup_path/backup_info.txt" ]]; then
|
||||
grep -E "(Environment|Git)" "$backup_path/backup_info.txt" | sed 's/^/ /' | head -2
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
}
|
||||
|
||||
# Health check function
|
||||
health_check() {
|
||||
local service=$1
|
||||
local url=$2
|
||||
local timeout=${3:-30}
|
||||
|
||||
info "Performing health check for $service..."
|
||||
|
||||
local count=0
|
||||
while [[ $count -lt $timeout ]]; do
|
||||
if curl -f -s "$url" > /dev/null 2>&1; then
|
||||
success "$service health check passed"
|
||||
return 0
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
((count++))
|
||||
done
|
||||
|
||||
warning "$service health check failed after ${timeout}s"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Check service health
|
||||
check_services_health() {
|
||||
info "Checking services health..."
|
||||
|
||||
local healthy=true
|
||||
|
||||
# Check backend health
|
||||
if ! health_check "Backend API" "http://localhost/api/health" 30; then
|
||||
healthy=false
|
||||
fi
|
||||
|
||||
# Check frontend health
|
||||
if ! health_check "Frontend" "http://localhost/health" 30; then
|
||||
healthy=false
|
||||
fi
|
||||
|
||||
# Check gateway health
|
||||
if ! health_check "Gateway" "http://localhost/health" 30; then
|
||||
healthy=false
|
||||
fi
|
||||
|
||||
if [[ "$healthy" == "true" ]]; then
|
||||
success "All services are healthy"
|
||||
return 0
|
||||
else
|
||||
error_exit "Some services are not healthy"
|
||||
fi
|
||||
}
|
||||
|
||||
# Quick rollback without backup restore
|
||||
quick_rollback() {
|
||||
info "Performing quick rollback (restart with previous images)..."
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# Stop current services
|
||||
info "Stopping current services..."
|
||||
docker-compose -f docker-compose.yml -f docker-compose.production.yml down
|
||||
|
||||
# Get previous image versions
|
||||
local backend_image=$(docker images roa2web/backend --format "{{.ID}}" | sed -n '2p')
|
||||
local frontend_image=$(docker images roa2web/frontend --format "{{.ID}}" | sed -n '2p')
|
||||
local gateway_image=$(docker images roa2web/nginx-gateway --format "{{.ID}}" | sed -n '2p')
|
||||
|
||||
if [[ -n "$backend_image" ]]; then
|
||||
info "Rolling back to previous backend image: $backend_image"
|
||||
docker tag "$backend_image" roa2web/backend:rollback
|
||||
fi
|
||||
|
||||
if [[ -n "$frontend_image" ]]; then
|
||||
info "Rolling back to previous frontend image: $frontend_image"
|
||||
docker tag "$frontend_image" roa2web/frontend:rollback
|
||||
fi
|
||||
|
||||
if [[ -n "$gateway_image" ]]; then
|
||||
info "Rolling back to previous gateway image: $gateway_image"
|
||||
docker tag "$gateway_image" roa2web/nginx-gateway:rollback
|
||||
fi
|
||||
|
||||
# Start services with rollback images
|
||||
info "Starting services with previous images..."
|
||||
docker-compose -f docker-compose.yml -f docker-compose.production.yml up -d
|
||||
|
||||
# Wait for services to start
|
||||
sleep 15
|
||||
|
||||
# Check health
|
||||
if check_services_health; then
|
||||
success "Quick rollback completed successfully!"
|
||||
else
|
||||
warning "Quick rollback completed but some services may not be healthy"
|
||||
fi
|
||||
}
|
||||
|
||||
# Full rollback using backup
|
||||
full_rollback() {
|
||||
local backup_name=$1
|
||||
|
||||
if [[ -z "$backup_name" ]]; then
|
||||
backup_name=$(get_last_backup)
|
||||
fi
|
||||
|
||||
if [[ -z "$backup_name" ]]; then
|
||||
error_exit "No backup found for rollback"
|
||||
fi
|
||||
|
||||
local backup_path="$BACKUP_DIR/$backup_name"
|
||||
|
||||
if [[ ! -d "$backup_path" ]]; then
|
||||
error_exit "Backup not found: $backup_name"
|
||||
fi
|
||||
|
||||
warning "Performing full rollback to backup: $backup_name"
|
||||
warning "This will restore configuration and Docker volumes"
|
||||
warning "Press Ctrl+C to cancel or wait 10 seconds to continue..."
|
||||
sleep 10
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# Stop current services
|
||||
info "Stopping current services..."
|
||||
docker-compose -f docker-compose.yml -f docker-compose.production.yml down
|
||||
|
||||
# Restore Docker images
|
||||
if [[ -f "$backup_path/images.tar" ]]; then
|
||||
info "Restoring Docker images..."
|
||||
docker load -i "$backup_path/images.tar"
|
||||
fi
|
||||
|
||||
# Restore configuration files
|
||||
if [[ -d "$backup_path/config" ]]; then
|
||||
info "Restoring configuration files..."
|
||||
|
||||
# Backup current config before restore
|
||||
local config_backup="config_backup_$(date +%Y%m%d_%H%M%S)"
|
||||
mkdir -p "$BACKUP_DIR/$config_backup"
|
||||
cp -r .env* "$BACKUP_DIR/$config_backup/" 2>/dev/null || true
|
||||
cp docker-compose*.yml "$BACKUP_DIR/$config_backup/"
|
||||
|
||||
# Restore from backup
|
||||
cp "$backup_path"/.env* . 2>/dev/null || true
|
||||
cp "$backup_path"/docker-compose*.yml .
|
||||
|
||||
if [[ -d "$backup_path/config/conf" && -d "nginx" ]]; then
|
||||
cp -r "$backup_path/config/conf" nginx/
|
||||
fi
|
||||
fi
|
||||
|
||||
# Restore Docker volumes
|
||||
if [[ -d "$backup_path/volumes" ]]; then
|
||||
info "Restoring Docker volumes..."
|
||||
|
||||
for volume_backup in "$backup_path/volumes"/*.tar.gz; do
|
||||
if [[ -f "$volume_backup" ]]; then
|
||||
local volume_name=$(basename "$volume_backup" .tar.gz)
|
||||
local full_volume_name="roa2web_$volume_name"
|
||||
|
||||
# Remove current volume
|
||||
docker volume rm "$full_volume_name" 2>/dev/null || true
|
||||
|
||||
# Create new volume
|
||||
docker volume create "$full_volume_name"
|
||||
|
||||
# Restore data
|
||||
docker run --rm \
|
||||
-v "$full_volume_name":/data \
|
||||
-v "$backup_path/volumes":/backup \
|
||||
alpine tar xzf "/backup/$(basename "$volume_backup")" -C /data
|
||||
|
||||
info "Restored volume: $volume_name"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Start services
|
||||
info "Starting services..."
|
||||
docker-compose -f docker-compose.yml -f docker-compose.production.yml up -d
|
||||
|
||||
# Wait for services to start
|
||||
sleep 20
|
||||
|
||||
# Check health
|
||||
if check_services_health; then
|
||||
success "Full rollback completed successfully!"
|
||||
else
|
||||
warning "Full rollback completed but some services may not be healthy"
|
||||
warning "You may need to check logs: docker-compose logs"
|
||||
fi
|
||||
}
|
||||
|
||||
# Emergency stop
|
||||
emergency_stop() {
|
||||
warning "Performing emergency stop of all ROA2WEB services..."
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# Stop all compose services
|
||||
docker-compose -f docker-compose.yml -f docker-compose.production.yml down -v 2>/dev/null || true
|
||||
docker-compose -f docker-compose.yml down -v 2>/dev/null || true
|
||||
|
||||
# Stop any remaining ROA2WEB containers
|
||||
docker ps -q --filter "name=roa-" | xargs -r docker stop
|
||||
|
||||
success "Emergency stop completed"
|
||||
}
|
||||
|
||||
# Main function
|
||||
main() {
|
||||
local action=${1:-quick}
|
||||
|
||||
case $action in
|
||||
"quick")
|
||||
info "=== ROA2WEB Quick Rollback ==="
|
||||
quick_rollback
|
||||
;;
|
||||
"full")
|
||||
info "=== ROA2WEB Full Rollback ==="
|
||||
full_rollback "$2"
|
||||
;;
|
||||
"list")
|
||||
list_rollback_targets
|
||||
;;
|
||||
"emergency")
|
||||
emergency_stop
|
||||
;;
|
||||
"health")
|
||||
check_services_health
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {quick|full [backup_name]|list|emergency|health}"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " quick - Quick rollback using previous Docker images"
|
||||
echo " full - Full rollback using backup (restores config + volumes)"
|
||||
echo " list - List available rollback targets"
|
||||
echo " emergency - Emergency stop all services"
|
||||
echo " health - Check current services health"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 quick # Quick rollback"
|
||||
echo " $0 full # Full rollback to last backup"
|
||||
echo " $0 full backup_20240131_143022 # Full rollback to specific backup"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
270
scripts/test-docker-setup.sh
Normal file
270
scripts/test-docker-setup.sh
Normal file
@@ -0,0 +1,270 @@
|
||||
#!/bin/bash
|
||||
# ROA2WEB Docker Setup Test Script
|
||||
# Validates Docker configuration before deployment
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Test results
|
||||
TESTS_PASSED=0
|
||||
TESTS_FAILED=0
|
||||
ISSUES=()
|
||||
|
||||
# Logging function
|
||||
log() {
|
||||
local level=$1
|
||||
shift
|
||||
local message="$*"
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo -e "[$timestamp] [$level] $message"
|
||||
}
|
||||
|
||||
# Test function
|
||||
run_test() {
|
||||
local test_name=$1
|
||||
local test_command=$2
|
||||
|
||||
echo -n "Testing $test_name... "
|
||||
|
||||
if eval "$test_command" &>/dev/null; then
|
||||
echo -e "${GREEN}PASS${NC}"
|
||||
((TESTS_PASSED++))
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}FAIL${NC}"
|
||||
((TESTS_FAILED++))
|
||||
ISSUES+=("$test_name")
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Test Docker availability
|
||||
test_docker() {
|
||||
echo -e "${BLUE}=== Testing Docker Prerequisites ===${NC}"
|
||||
|
||||
run_test "Docker installed" "command -v docker"
|
||||
run_test "Docker Compose installed" "command -v docker-compose"
|
||||
run_test "Docker daemon running" "docker info"
|
||||
}
|
||||
|
||||
# Test file structure
|
||||
test_file_structure() {
|
||||
echo -e "${BLUE}=== Testing File Structure ===${NC}"
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# Core Docker files
|
||||
run_test "Main docker-compose.yml exists" "test -f docker-compose.yml"
|
||||
run_test "Development override exists" "test -f docker-compose.override.yml"
|
||||
run_test "Production compose exists" "test -f docker-compose.production.yml"
|
||||
|
||||
# Backend files
|
||||
run_test "Backend Dockerfile exists" "test -f reports-app/backend/Dockerfile"
|
||||
run_test "Backend requirements exists" "test -f reports-app/backend/requirements.txt"
|
||||
|
||||
# Frontend files
|
||||
run_test "Frontend Dockerfile exists" "test -f reports-app/frontend/Dockerfile"
|
||||
run_test "Frontend nginx.conf exists" "test -f reports-app/frontend/nginx.conf"
|
||||
run_test "Frontend package.json exists" "test -f reports-app/frontend/package.json"
|
||||
|
||||
# Nginx Gateway files
|
||||
run_test "Nginx Gateway Dockerfile exists" "test -f nginx/Dockerfile"
|
||||
run_test "Nginx main config exists" "test -f nginx/conf/nginx.conf"
|
||||
run_test "Nginx upstream config exists" "test -f nginx/conf/upstream.conf"
|
||||
run_test "Nginx SSL config exists" "test -f nginx/conf/ssl.conf"
|
||||
run_test "Nginx security config exists" "test -f nginx/conf/security.conf"
|
||||
run_test "Nginx site config exists" "test -f nginx/conf/sites-enabled/roa2web.conf"
|
||||
|
||||
# Scripts
|
||||
run_test "Deploy script exists" "test -f scripts/deploy.sh"
|
||||
run_test "Backup script exists" "test -f scripts/backup.sh"
|
||||
run_test "Rollback script exists" "test -f scripts/rollback.sh"
|
||||
run_test "Health check script exists" "test -f scripts/health-check.sh"
|
||||
|
||||
# Scripts are executable
|
||||
run_test "Deploy script executable" "test -x scripts/deploy.sh"
|
||||
run_test "Backup script executable" "test -x scripts/backup.sh"
|
||||
run_test "Rollback script executable" "test -x scripts/rollback.sh"
|
||||
run_test "Health check script executable" "test -x scripts/health-check.sh"
|
||||
|
||||
# Environment files
|
||||
run_test "Environment example exists" "test -f .env.example"
|
||||
run_test "Development env exists" "test -f .env.development"
|
||||
run_test "Production env exists" "test -f .env.production"
|
||||
|
||||
# Documentation
|
||||
run_test "Docker setup guide exists" "test -f DOCKER_SETUP.md"
|
||||
run_test "Deployment guide exists" "test -f DEPLOYMENT_GUIDE.md"
|
||||
run_test "Production checklist exists" "test -f PRODUCTION_CHECKLIST.md"
|
||||
}
|
||||
|
||||
# Test Docker Compose configuration
|
||||
test_docker_compose() {
|
||||
echo -e "${BLUE}=== Testing Docker Compose Configuration ===${NC}"
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# Test main compose file
|
||||
run_test "Main compose config valid" "docker-compose config -q"
|
||||
|
||||
# Test production compose
|
||||
run_test "Production compose config valid" "docker-compose -f docker-compose.yml -f docker-compose.production.yml config -q"
|
||||
|
||||
# Test if all required services are defined
|
||||
run_test "Backend service defined" "docker-compose config | grep -q 'roa-backend:'"
|
||||
run_test "Frontend service defined" "docker-compose config | grep -q 'roa-frontend:'"
|
||||
run_test "Gateway service defined" "docker-compose config | grep -q 'roa-gateway:'"
|
||||
run_test "Redis service defined" "docker-compose config | grep -q 'roa-redis:'"
|
||||
|
||||
# Test network configuration
|
||||
run_test "Custom network defined" "docker-compose config | grep -q 'roa-network:'"
|
||||
|
||||
# Test volume configuration
|
||||
run_test "Nginx logs volume defined" "docker-compose config | grep -q 'nginx-logs:'"
|
||||
run_test "SSL certs volume defined" "docker-compose config | grep -q 'ssl-certs:'"
|
||||
run_test "Redis data volume defined" "docker-compose config | grep -q 'redis-data:'"
|
||||
}
|
||||
|
||||
# Test environment configuration
|
||||
test_environment() {
|
||||
echo -e "${BLUE}=== Testing Environment Configuration ===${NC}"
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# Check if development environment is complete
|
||||
if [[ -f .env.development ]]; then
|
||||
run_test "Development Oracle user set" "grep -q 'ORACLE_USER=' .env.development"
|
||||
run_test "Development JWT secret set" "grep -q 'JWT_SECRET_KEY=' .env.development"
|
||||
run_test "Development Redis password set" "grep -q 'REDIS_PASSWORD=' .env.development"
|
||||
fi
|
||||
|
||||
# Check if production environment template is complete
|
||||
if [[ -f .env.production ]]; then
|
||||
run_test "Production domain configured" "grep -q 'DOMAIN=' .env.production"
|
||||
run_test "Production SSL email configured" "grep -q 'SSL_EMAIL=' .env.production"
|
||||
run_test "Production environment set" "grep -q 'ENVIRONMENT=production' .env.production"
|
||||
fi
|
||||
|
||||
# Check secrets directory
|
||||
run_test "Secrets directory exists" "test -d secrets"
|
||||
run_test "Secrets gitkeep exists" "test -f secrets/.gitkeep"
|
||||
}
|
||||
|
||||
# Test nginx configuration syntax
|
||||
test_nginx_config() {
|
||||
echo -e "${BLUE}=== Testing Nginx Configuration ===${NC}"
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# Test nginx config syntax (if nginx is available)
|
||||
if command -v nginx &>/dev/null; then
|
||||
run_test "Nginx main config syntax" "nginx -t -c nginx/conf/nginx.conf -p $(pwd)/nginx/"
|
||||
run_test "Nginx site config syntax" "nginx -t -c nginx/conf/sites-enabled/roa2web.conf -p $(pwd)/nginx/"
|
||||
else
|
||||
log "WARNING" "Nginx not available for syntax testing"
|
||||
fi
|
||||
|
||||
# Test config file content
|
||||
run_test "Nginx upstream config has backend" "grep -q 'roa_backend' nginx/conf/upstream.conf"
|
||||
run_test "Nginx upstream config has frontend" "grep -q 'roa_frontend' nginx/conf/upstream.conf"
|
||||
run_test "SSL config has protocols" "grep -q 'ssl_protocols' nginx/conf/ssl.conf"
|
||||
run_test "Security headers configured" "grep -q 'X-Frame-Options' nginx/conf/security.conf"
|
||||
}
|
||||
|
||||
# Test scripts syntax
|
||||
test_scripts() {
|
||||
echo -e "${BLUE}=== Testing Scripts Syntax ===${NC}"
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# Test bash script syntax
|
||||
run_test "Deploy script syntax" "bash -n scripts/deploy.sh"
|
||||
run_test "Backup script syntax" "bash -n scripts/backup.sh"
|
||||
run_test "Rollback script syntax" "bash -n scripts/rollback.sh"
|
||||
run_test "Health check script syntax" "bash -n scripts/health-check.sh"
|
||||
|
||||
# Test if scripts have proper shebangs
|
||||
run_test "Deploy script has shebang" "head -1 scripts/deploy.sh | grep -q '#!/bin/bash'"
|
||||
run_test "Backup script has shebang" "head -1 scripts/backup.sh | grep -q '#!/bin/bash'"
|
||||
run_test "Rollback script has shebang" "head -1 scripts/rollback.sh | grep -q '#!/bin/bash'"
|
||||
run_test "Health check script has shebang" "head -1 scripts/health-check.sh | grep -q '#!/bin/bash'"
|
||||
}
|
||||
|
||||
# Generate test report
|
||||
generate_report() {
|
||||
echo ""
|
||||
echo -e "${BLUE}=== Test Results Summary ===${NC}"
|
||||
echo ""
|
||||
|
||||
local total_tests=$((TESTS_PASSED + TESTS_FAILED))
|
||||
|
||||
echo -e "Total tests run: ${BLUE}$total_tests${NC}"
|
||||
echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}"
|
||||
echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}"
|
||||
|
||||
if [[ $TESTS_FAILED -eq 0 ]]; then
|
||||
echo ""
|
||||
echo -e "${GREEN}✅ ALL TESTS PASSED! Docker setup is ready for deployment.${NC}"
|
||||
echo ""
|
||||
echo -e "${BLUE}Next steps:${NC}"
|
||||
echo "1. Copy .env.development to .env for local development"
|
||||
echo "2. Configure your Oracle database credentials"
|
||||
echo "3. Run: docker-compose up --build"
|
||||
echo ""
|
||||
return 0
|
||||
else
|
||||
echo ""
|
||||
echo -e "${RED}❌ Some tests failed. Please fix the following issues:${NC}"
|
||||
echo ""
|
||||
for issue in "${ISSUES[@]}"; do
|
||||
echo -e " ${RED}•${NC} $issue"
|
||||
done
|
||||
echo ""
|
||||
echo -e "${YELLOW}Please fix these issues before proceeding with deployment.${NC}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Main test execution
|
||||
main() {
|
||||
echo -e "${BLUE}ROA2WEB Docker Setup Test Suite${NC}"
|
||||
echo -e "${BLUE}================================${NC}"
|
||||
echo ""
|
||||
|
||||
test_docker
|
||||
echo ""
|
||||
|
||||
test_file_structure
|
||||
echo ""
|
||||
|
||||
if command -v docker-compose &>/dev/null; then
|
||||
test_docker_compose
|
||||
echo ""
|
||||
else
|
||||
log "WARNING" "Docker Compose not available, skipping compose tests"
|
||||
fi
|
||||
|
||||
test_environment
|
||||
echo ""
|
||||
|
||||
test_nginx_config
|
||||
echo ""
|
||||
|
||||
test_scripts
|
||||
echo ""
|
||||
|
||||
generate_report
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
121
scripts/test-integration.sh
Normal file
121
scripts/test-integration.sh
Normal file
@@ -0,0 +1,121 @@
|
||||
#!/bin/bash
|
||||
# ROA2WEB Integration Test Suite
|
||||
# Tests all critical system components
|
||||
|
||||
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
|
||||
|
||||
log() {
|
||||
echo -e "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
|
||||
}
|
||||
|
||||
test_passed() {
|
||||
log "${GREEN}✅ $1${NC}"
|
||||
}
|
||||
|
||||
test_failed() {
|
||||
log "${RED}❌ $1${NC}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
test_warning() {
|
||||
log "${YELLOW}⚠️ $1${NC}"
|
||||
}
|
||||
|
||||
log "${BLUE}🚀 Starting ROA2WEB Integration Tests${NC}"
|
||||
|
||||
# Test 1: Check if all containers are running
|
||||
log "${BLUE}Testing container status...${NC}"
|
||||
if docker compose ps | grep -q "roa-ssh-tunnel.*Up.*healthy"; then
|
||||
test_passed "SSH tunnel container healthy"
|
||||
else
|
||||
test_failed "SSH tunnel container not healthy"
|
||||
fi
|
||||
|
||||
if docker compose ps | grep -q "roa-frontend.*Up.*healthy"; then
|
||||
test_passed "Frontend container healthy"
|
||||
else
|
||||
test_failed "Frontend container not healthy"
|
||||
fi
|
||||
|
||||
if docker compose ps | grep -q "roa-redis.*Up.*healthy"; then
|
||||
test_passed "Redis container healthy"
|
||||
else
|
||||
test_failed "Redis container not healthy"
|
||||
fi
|
||||
|
||||
# Test 2: SSH Tunnel Connectivity
|
||||
log "${BLUE}Testing SSH tunnel connectivity...${NC}"
|
||||
if docker exec roa-ssh-tunnel nc -z localhost 1521 >/dev/null 2>&1; then
|
||||
test_passed "SSH tunnel port 1521 accessible"
|
||||
else
|
||||
test_failed "SSH tunnel port 1521 not accessible"
|
||||
fi
|
||||
|
||||
# Test 3: Backend API Endpoints
|
||||
log "${BLUE}Testing backend API endpoints...${NC}"
|
||||
if curl -s http://localhost:8000/health | grep -q "api.*healthy"; then
|
||||
test_passed "Backend health endpoint responds"
|
||||
else
|
||||
test_failed "Backend health endpoint not responding"
|
||||
fi
|
||||
|
||||
# Test 4: Frontend Access
|
||||
log "${BLUE}Testing frontend access...${NC}"
|
||||
if curl -s http://localhost:3000 | grep -q "ROA Reports"; then
|
||||
test_passed "Frontend serves application"
|
||||
else
|
||||
test_failed "Frontend not serving application"
|
||||
fi
|
||||
|
||||
# Test 5: Nginx Gateway Routing
|
||||
log "${BLUE}Testing nginx gateway routing...${NC}"
|
||||
if curl -s http://localhost:8080/health | grep -q "healthy"; then
|
||||
test_passed "Gateway routes to backend health endpoint"
|
||||
else
|
||||
test_failed "Gateway not routing to backend"
|
||||
fi
|
||||
|
||||
if curl -s http://localhost:8080/ | grep -q "ROA Reports"; then
|
||||
test_passed "Gateway routes to frontend"
|
||||
else
|
||||
test_failed "Gateway not routing to frontend"
|
||||
fi
|
||||
|
||||
# Test 6: Redis Connectivity
|
||||
log "${BLUE}Testing Redis connectivity...${NC}"
|
||||
if echo "PING" | nc localhost 6379 | grep -q "PONG"; then
|
||||
test_passed "Redis responds to PING"
|
||||
else
|
||||
test_failed "Redis not responding to PING"
|
||||
fi
|
||||
|
||||
# Test 7: Oracle Database Connection (Expected to fail with auth error)
|
||||
log "${BLUE}Testing Oracle database connection...${NC}"
|
||||
if curl -s http://localhost:8000/health | grep -q "ORA-01017"; then
|
||||
test_warning "Oracle connection reaches database but auth failed (expected - need valid password)"
|
||||
elif curl -s http://localhost:8000/health | grep -q "database.*healthy"; then
|
||||
test_passed "Oracle database connection successful"
|
||||
else
|
||||
test_warning "Oracle connection issue - check SSH tunnel and credentials"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
log "${GREEN}🎉 Integration tests completed successfully!${NC}"
|
||||
log "${BLUE}System Status Summary:${NC}"
|
||||
log "✅ SSH Tunnel: Working"
|
||||
log "✅ Frontend: Working"
|
||||
log "✅ Redis: Working"
|
||||
log "✅ Nginx Gateway: Working (port 8080)"
|
||||
log "⚠️ Backend: API functional, Oracle auth needs password verification"
|
||||
log ""
|
||||
log "${YELLOW}Next Steps:${NC}"
|
||||
log "1. Verify Oracle password for CONTAFIN_ORACLE user"
|
||||
log "2. Test application functionality through gateway (http://localhost:8080)"
|
||||
log "3. Monitor logs for any issues: docker compose logs -f"
|
||||
Reference in New Issue
Block a user