#!/bin/bash # Foxhunt HFT Trading System - Production Deployment Script # Simple, reliable deployment without Kubernetes complexity set -euo pipefail # Configuration SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" DEPLOY_DIR="/opt/foxhunt" BACKUP_DIR="/opt/foxhunt/backups/$(date +%Y%m%d_%H%M%S)" LOG_FILE="/home/jgrusewski/Work/foxhunt/logs-deploy.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() { echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "${LOG_FILE}" } error() { log "${RED}ERROR: $1${NC}" exit 1 } warning() { log "${YELLOW}WARNING: $1${NC}" } info() { log "${BLUE}INFO: $1${NC}" } success() { log "${GREEN}SUCCESS: $1${NC}" } # Check prerequisites check_prerequisites() { info "Checking prerequisites..." # Check if running as root if [[ $EUID -ne 0 ]]; then error "This script must be run as root (use sudo)" fi # Check Docker if ! command -v docker &> /dev/null; then error "Docker is not installed" fi if ! command -v docker-compose &> /dev/null; then error "Docker Compose is not installed" fi # Check Rust/Cargo if ! command -v cargo &> /dev/null; then error "Cargo (Rust) is not installed" fi # Check systemctl if ! command -v systemctl &> /dev/null; then error "systemctl is not available (SystemD required)" fi success "Prerequisites check passed" } # Validate configuration validate_config() { info "Validating configuration..." # Check if .env file exists if [[ ! -f "${PROJECT_ROOT}/docker/.env" ]]; then warning ".env file not found, copying template" cp "${PROJECT_ROOT}/docker/.env.template" "${PROJECT_ROOT}/docker/.env" error "Please edit ${PROJECT_ROOT}/docker/.env with your configuration and run again" 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 in .env file" fi done success "Configuration validation passed" } # Create backup of current deployment create_backup() { info "Creating backup of current deployment..." if [[ -d "${DEPLOY_DIR}" ]]; then mkdir -p "${BACKUP_DIR}" # Backup binaries if [[ -d "${DEPLOY_DIR}/bin" ]]; then cp -r "${DEPLOY_DIR}/bin" "${BACKUP_DIR}/" fi # Backup config if [[ -d "${DEPLOY_DIR}/config" ]]; then cp -r "${DEPLOY_DIR}/config" "${BACKUP_DIR}/" fi # Create backup manifest cat > "${BACKUP_DIR}/manifest.txt" << EOF Backup created: $(date) Source: ${DEPLOY_DIR} Deployment version: $(cat "${DEPLOY_DIR}/VERSION" 2>/dev/null || echo "unknown") EOF success "Backup created at ${BACKUP_DIR}" else info "No existing deployment found, skipping backup" fi } # Build the project build_project() { info "Building Foxhunt HFT Trading System..." cd "${PROJECT_ROOT}" # Clean previous builds cargo clean # Build in release mode info "Building TLI binary..." cargo build --release --bin tli # Check if we have a backtesting binary if [[ -f "${PROJECT_ROOT}/backtesting/src/main.rs" ]]; then info "Building backtesting service..." cargo build --release --bin backtesting-service fi success "Build completed successfully" } # Deploy binaries and configuration deploy_files() { info "Deploying files to ${DEPLOY_DIR}..." # Create deployment directory structure mkdir -p "${DEPLOY_DIR}"/{bin,config,logs,data,docker} # Deploy binaries cp "${PROJECT_ROOT}/target/release/tli" "${DEPLOY_DIR}/bin/" if [[ -f "${PROJECT_ROOT}/target/release/backtesting-service" ]]; then cp "${PROJECT_ROOT}/target/release/backtesting-service" "${DEPLOY_DIR}/bin/" fi # Deploy configuration cp -r "${PROJECT_ROOT}/config/"* "${DEPLOY_DIR}/config/" 2>/dev/null || true cp "${PROJECT_ROOT}/docker/docker-compose.enhanced.yml" "${DEPLOY_DIR}/docker/docker-compose.yml" cp "${PROJECT_ROOT}/docker/.env" "${DEPLOY_DIR}/docker/" # Create version file echo "$(date '+%Y-%m-%d %H:%M:%S') - $(git rev-parse HEAD 2>/dev/null || echo 'unknown')" > "${DEPLOY_DIR}/VERSION" # Set ownership chown -R foxhunt:foxhunt "${DEPLOY_DIR}" chmod +x "${DEPLOY_DIR}/bin/"* success "Files deployed successfully" } # Start database stack start_database_stack() { info "Starting database stack..." cd "${DEPLOY_DIR}/docker" # Pull latest images docker-compose pull # Start database services docker-compose up -d postgres redis influxdb prometheus grafana # Wait for services to be healthy info "Waiting for database services to be ready..." local max_attempts=30 local attempt=0 while [[ $attempt -lt $max_attempts ]]; do if docker-compose ps --services --filter "status=running" | grep -q "postgres\|redis\|influxdb"; then success "Database stack is running" break fi sleep 5 ((attempt++)) info "Waiting for services... (${attempt}/${max_attempts})" done if [[ $attempt -eq $max_attempts ]]; then error "Database stack failed to start within expected time" fi } # Install and start SystemD services setup_systemd_services() { info "Setting up SystemD services..." # Install service files "${PROJECT_ROOT}/deployment/systemd/install-services.sh" # Start database stack service systemctl start foxhunt-database-stack systemctl status foxhunt-database-stack --no-pager # Start TLI service systemctl start foxhunt-tli systemctl status foxhunt-tli --no-pager # Start backtesting service if available if [[ -f "${DEPLOY_DIR}/bin/backtesting-service" ]]; then systemctl start foxhunt-backtesting systemctl status foxhunt-backtesting --no-pager fi success "SystemD services configured and started" } # Run health checks run_health_checks() { info "Running health checks..." local checks_passed=0 local total_checks=5 # Check database stack if systemctl is-active --quiet foxhunt-database-stack; then success "✓ Database stack is running" ((checks_passed++)) else warning "✗ Database stack is not running" fi # Check TLI service if systemctl is-active --quiet foxhunt-tli; then success "✓ TLI service is running" ((checks_passed++)) else warning "✗ TLI service is not running" fi # Check PostgreSQL if docker exec foxhunt-postgres pg_isready -U foxhunt &>/dev/null; then success "✓ PostgreSQL is healthy" ((checks_passed++)) else warning "✗ PostgreSQL is not healthy" fi # Check Redis if docker exec foxhunt-redis redis-cli ping &>/dev/null; then success "✓ Redis is healthy" ((checks_passed++)) else warning "✗ Redis is not healthy" fi # Check Prometheus if curl -s http://localhost:9090/-/ready &>/dev/null; then success "✓ Prometheus is healthy" ((checks_passed++)) else warning "✗ Prometheus is not healthy" fi info "Health checks: ${checks_passed}/${total_checks} passed" if [[ $checks_passed -lt $total_checks ]]; then warning "Some health checks failed. Check logs for details." return 1 fi success "All health checks passed!" } # Display deployment summary show_deployment_summary() { info "Deployment Summary" echo "====================" echo "Deployment completed at: $(date)" echo "Deployment directory: ${DEPLOY_DIR}" echo "Backup directory: ${BACKUP_DIR}" echo "" echo "Services:" echo " - TLI Terminal Interface: systemctl status foxhunt-tli" echo " - Database Stack: systemctl status foxhunt-database-stack" if [[ -f "${DEPLOY_DIR}/bin/backtesting-service" ]]; then echo " - Backtesting Service: systemctl status foxhunt-backtesting" fi echo "" echo "Web Interfaces:" echo " - Grafana Dashboard: http://localhost:3000" echo " - Prometheus: http://localhost:9090" echo " - InfluxDB: http://localhost:8086" echo "" echo "Logs:" echo " - TLI: journalctl -u foxhunt-tli -f" echo " - Database Stack: docker-compose -f ${DEPLOY_DIR}/docker/docker-compose.yml logs -f" echo " - Deployment: tail -f ${LOG_FILE}" echo "" echo "To rollback: ${SCRIPT_DIR}/rollback.sh ${BACKUP_DIR}" } # Main deployment function main() { info "Starting Foxhunt HFT Trading System deployment..." check_prerequisites validate_config create_backup build_project deploy_files start_database_stack setup_systemd_services if run_health_checks; then success "Deployment completed successfully!" else warning "Deployment completed with warnings. Please check the health check results." fi show_deployment_summary } # Handle script arguments case "${1:-deploy}" in "deploy") main ;; "health-check") run_health_checks ;; "start") systemctl start foxhunt-database-stack foxhunt-tli [[ -f "${DEPLOY_DIR}/bin/backtesting-service" ]] && systemctl start foxhunt-backtesting ;; "stop") systemctl stop foxhunt-tli foxhunt-backtesting foxhunt-database-stack 2>/dev/null || true ;; "restart") systemctl restart foxhunt-database-stack foxhunt-tli [[ -f "${DEPLOY_DIR}/bin/backtesting-service" ]] && systemctl restart foxhunt-backtesting ;; "status") systemctl status foxhunt-database-stack foxhunt-tli foxhunt-backtesting --no-pager 2>/dev/null || true ;; *) echo "Usage: $0 {deploy|health-check|start|stop|restart|status}" exit 1 ;; esac