#!/bin/bash # Foxhunt HFT Trading System - Deployment Validation Script # Comprehensive testing of deployment infrastructure for production readiness set -euo pipefail # Configuration SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" LOG_FILE="/tmp/foxhunt-deployment-validation.log" VALIDATION_RESULTS="/tmp/foxhunt-validation-results.json" # 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 tracking TESTS_PASSED=0 TESTS_FAILED=0 TESTS_TOTAL=0 # Logging function log() { echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "${LOG_FILE}" } error() { log "${RED}ERROR: $1${NC}" } warning() { log "${YELLOW}WARNING: $1${NC}" } info() { log "${BLUE}INFO: $1${NC}" } success() { log "${GREEN}SUCCESS: $1${NC}" } # Test execution framework run_test() { local test_name="$1" local test_function="$2" ((TESTS_TOTAL++)) info "Running test: $test_name" if $test_function; then success "✓ $test_name" ((TESTS_PASSED++)) echo " \"$test_name\": {\"status\": \"PASS\", \"timestamp\": \"$(date -Iseconds)\"}," >> "$VALIDATION_RESULTS" else error "✗ $test_name" ((TESTS_FAILED++)) echo " \"$test_name\": {\"status\": \"FAIL\", \"timestamp\": \"$(date -Iseconds)\"}," >> "$VALIDATION_RESULTS" fi } # Individual test functions test_prerequisites() { local success=true # Check if running as root if [[ $EUID -ne 0 ]]; then error "Script must be run as root (use sudo)" success=false fi # Check Docker if ! command -v docker &> /dev/null; then error "Docker is not installed" success=false else info "Docker version: $(docker --version)" fi # Check Docker Compose if ! command -v docker-compose &> /dev/null; then error "Docker Compose is not installed" success=false else info "Docker Compose version: $(docker-compose --version)" fi # Check Rust/Cargo if ! command -v cargo &> /dev/null; then error "Cargo (Rust) is not installed" success=false else info "Cargo version: $(cargo --version)" fi # Check systemctl if ! command -v systemctl &> /dev/null; then error "systemctl is not available (SystemD required)" success=false fi $success } test_project_structure() { local success=true # Check main directories local required_dirs=( "deployment" "deployment/scripts" "deployment/systemd" "docker" "config" "tli" "core" "risk" "ml" ) for dir in "${required_dirs[@]}"; do if [[ ! -d "${PROJECT_ROOT}/$dir" ]]; then error "Required directory missing: $dir" success=false fi done # Check key files local required_files=( "deployment/scripts/deploy.sh" "deployment/scripts/rollback.sh" "deployment/scripts/migrate-db.sh" "deployment/systemd/foxhunt-tli.service" "deployment/systemd/foxhunt-database-stack.service" "deployment/systemd/install-services.sh" "docker/docker-compose.yml" "config/monitoring/prometheus-hft.yml" ) for file in "${required_files[@]}"; do if [[ ! -f "${PROJECT_ROOT}/$file" ]]; then error "Required file missing: $file" success=false fi done $success } test_environment_configuration() { local success=true # Check for .env template if [[ ! -f "${PROJECT_ROOT}/docker/.env.template" ]]; then warning ".env.template not found, creating basic template" cat > "${PROJECT_ROOT}/docker/.env.template" << 'EOF' # Database passwords (required) POSTGRES_PASSWORD=your_secure_password REDIS_PASSWORD=your_secure_password INFLUXDB_PASSWORD=your_secure_password INFLUXDB_TOKEN=your_secure_token GRAFANA_ADMIN_PASSWORD=your_secure_password # Trading configuration FOXHUNT_TRADING_MODE=paper FOXHUNT_RISK_LIMIT=100000 # Broker configuration IB_HOST=localhost IB_PORT=7497 POLYGON_API_KEY=your_polygon_key EOF fi # Create test .env file if [[ ! -f "${PROJECT_ROOT}/docker/.env" ]]; then info "Creating test .env file" cp "${PROJECT_ROOT}/docker/.env.template" "${PROJECT_ROOT}/docker/.env" # Set secure test passwords sed -i 's/your_secure_password/test_password_$(openssl rand -hex 8)/g' "${PROJECT_ROOT}/docker/.env" sed -i 's/your_secure_token/test_token_$(openssl rand -hex 16)/g' "${PROJECT_ROOT}/docker/.env" sed -i 's/your_polygon_key/test_key/g' "${PROJECT_ROOT}/docker/.env" fi # Validate required environment variables source "${PROJECT_ROOT}/docker/.env" local required_vars=( "POSTGRES_PASSWORD" "REDIS_PASSWORD" "INFLUXDB_PASSWORD" "INFLUXDB_TOKEN" "GRAFANA_ADMIN_PASSWORD" ) for var in "${required_vars[@]}"; do if [[ -z "${!var:-}" ]]; then error "Required environment variable $var is not set" success=false fi done $success } test_deployment_scripts() { local success=true # Test deploy script syntax if ! bash -n "${PROJECT_ROOT}/deployment/scripts/deploy.sh"; then error "deploy.sh has syntax errors" success=false fi # Test rollback script syntax if ! bash -n "${PROJECT_ROOT}/deployment/scripts/rollback.sh"; then error "rollback.sh has syntax errors" success=false fi # Test migrate-db script syntax if ! bash -n "${PROJECT_ROOT}/deployment/scripts/migrate-db.sh"; then error "migrate-db.sh has syntax errors" success=false fi # Test script permissions local scripts=( "deployment/scripts/deploy.sh" "deployment/scripts/rollback.sh" "deployment/scripts/migrate-db.sh" "deployment/systemd/install-services.sh" ) for script in "${scripts[@]}"; do if [[ ! -x "${PROJECT_ROOT}/$script" ]]; then warning "Script not executable: $script" chmod +x "${PROJECT_ROOT}/$script" fi done $success } test_systemd_services() { local success=true # Check service file syntax local service_files=( "deployment/systemd/foxhunt-tli.service" "deployment/systemd/foxhunt-database-stack.service" "deployment/systemd/foxhunt-backtesting.service" ) for service_file in "${service_files[@]}"; do if [[ -f "${PROJECT_ROOT}/$service_file" ]]; then # Basic syntax check for systemd files if ! systemd-analyze verify "${PROJECT_ROOT}/$service_file" 2>/dev/null; then warning "SystemD service file may have issues: $service_file" fi else error "Service file missing: $service_file" success=false fi done $success } test_docker_configuration() { local success=true # Validate docker-compose file syntax if ! docker-compose -f "${PROJECT_ROOT}/docker/docker-compose.yml" config > /dev/null 2>&1; then error "docker-compose.yml has syntax errors" success=false fi # Check if required networks and volumes are defined local compose_content=$(cat "${PROJECT_ROOT}/docker/docker-compose.yml") if ! echo "$compose_content" | grep -q "networks:"; then warning "No custom networks defined in docker-compose.yml" fi if ! echo "$compose_content" | grep -q "volumes:"; then warning "No volumes defined in docker-compose.yml" fi $success } test_build_process() { local success=true info "Testing TLI build process..." cd "${PROJECT_ROOT}" # Test cargo check if ! cargo check -p tli --quiet; then error "TLI package fails to compile" success=false fi # Test if binary can be built if ! cargo build -p tli --quiet; then error "TLI binary fails to build" success=false fi $success } test_health_endpoints() { local success=true # Build TLI with health endpoints info "Building TLI with health endpoints..." cd "${PROJECT_ROOT}" if cargo build -p tli --quiet; then info "TLI built successfully with health endpoints" # Check if health module exists if [[ -f "tli/src/health.rs" ]]; then info "Health endpoints module found" else warning "Health endpoints module not found" fi else error "Failed to build TLI with health endpoints" success=false fi $success } test_monitoring_configuration() { local success=true # Check Prometheus configuration if [[ -f "${PROJECT_ROOT}/config/monitoring/prometheus-hft.yml" ]]; then # Basic YAML syntax check if command -v python3 &> /dev/null; then if ! python3 -c "import yaml; yaml.safe_load(open('${PROJECT_ROOT}/config/monitoring/prometheus-hft.yml'))" 2>/dev/null; then error "Prometheus configuration has YAML syntax errors" success=false fi fi else error "Prometheus configuration file missing" success=false fi $success } test_security_configuration() { local success=true # Check for secure defaults local env_file="${PROJECT_ROOT}/docker/.env" if [[ -f "$env_file" ]]; then # Check for default passwords if grep -q "your_secure_password\|password123\|admin" "$env_file"; then warning "Default passwords detected in .env file" fi # Check file permissions local env_perms=$(stat -c "%a" "$env_file") if [[ "$env_perms" != "600" ]]; then warning ".env file permissions should be 600 (currently $env_perms)" chmod 600 "$env_file" fi fi $success } # Generate test report generate_report() { local report_file="/tmp/foxhunt-deployment-validation-report.html" cat > "$report_file" << EOF
Generated: $(date)
Project: $(pwd)
Total Tests: $TESTS_TOTAL
Passed: $TESTS_PASSED
Failed: $TESTS_FAILED
Success Rate: $(( (TESTS_PASSED * 100) / TESTS_TOTAL ))%
" >> "$report_file"
cat "$VALIDATION_RESULTS" >> "$report_file"
echo "" >> "$report_file"
fi
cat >> "$report_file" << EOF
$(cat "$LOG_FILE" 2>/dev/null || echo "No logs available")