Files
aitbc/cli/aitbc_cli/commands/production_deploy.py
AITBC System b033923756 chore: normalize file permissions across repository
- Remove executable permissions from configuration files (.editorconfig, .env.example, .gitignore)
- Remove executable permissions from documentation files (README.md, LICENSE, SECURITY.md)
- Remove executable permissions from web assets (HTML, CSS, JS files)
- Remove executable permissions from data files (JSON, SQL, YAML, requirements.txt)
- Remove executable permissions from source code files across all apps
- Add executable permissions to Python
2026-03-08 11:26:18 +01:00

547 lines
21 KiB
Python
Executable File

"""
Production Deployment CLI Commands for AITBC
Commands for managing production deployment and operations
"""
import click
import json
import requests
import subprocess
from datetime import datetime
from typing import Dict, Any, List, Optional
@click.group()
def production_deploy():
"""Production deployment management commands"""
pass
@production_deploy.command()
@click.option('--environment', default='production', help='Target environment')
@click.option('--version', default='latest', help='Version to deploy')
@click.option('--region', default='us-east-1', help='Target region')
@click.option('--dry-run', is_flag=True, help='Show what would be deployed without actually deploying')
@click.option('--force', is_flag=True, help='Force deployment even if checks fail')
def deploy(environment, version, region, dry_run, force):
"""Deploy AITBC to production"""
try:
click.echo(f"🚀 Starting production deployment...")
click.echo(f"🌍 Environment: {environment}")
click.echo(f"📦 Version: {version}")
click.echo(f"🗺️ Region: {region}")
if dry_run:
click.echo("🔍 DRY RUN MODE - No actual deployment will be performed")
# Pre-deployment checks
if not force:
click.echo("🔍 Running pre-deployment checks...")
checks = run_pre_deployment_checks(environment, dry_run)
if not all(checks.values()):
failed_checks = [k for k, v in checks.items() if not v]
click.echo(f"❌ Pre-deployment checks failed: {', '.join(failed_checks)}")
click.echo("💡 Use --force to override or fix the issues and try again")
return
else:
click.echo("✅ All pre-deployment checks passed")
# Backup current deployment
if not dry_run:
click.echo("💾 Creating backup of current deployment...")
backup_result = create_backup(environment)
click.echo(f"✅ Backup created: {backup_result['backup_id']}")
else:
click.echo("💾 DRY RUN: Would create backup of current deployment")
# Build images
click.echo("🔨 Building production images...")
build_result = build_production_images(version, dry_run)
if not build_result['success']:
click.echo(f"❌ Build failed: {build_result['error']}")
return
# Deploy services
click.echo("🚀 Deploying services...")
deployment_result = deploy_services(environment, version, region, dry_run)
if not deployment_result['success']:
click.echo(f"❌ Deployment failed: {deployment_result['error']}")
return
# Post-deployment tests
click.echo("🧪 Running post-deployment tests...")
test_result = run_post_deployment_tests(environment, dry_run)
if not test_result['success']:
click.echo(f"❌ Post-deployment tests failed: {test_result['error']}")
click.echo("🔄 Rolling back deployment...")
rollback_result = rollback_deployment(environment, backup_result['backup_id'])
click.echo(f"🔄 Rollback completed: {rollback_result['status']}")
return
# Success
click.echo("🎉 Production deployment completed successfully!")
click.echo(f"🌍 Environment: {environment}")
click.echo(f"📦 Version: {version}")
click.echo(f"🗺️ Region: {region}")
click.echo(f"📅 Deployed at: {datetime.utcnow().isoformat()}")
if not dry_run:
click.echo("🔗 Service URLs:")
click.echo(" 🌐 API: https://api.aitbc.dev")
click.echo(" 🛒 Marketplace: https://marketplace.aitbc.dev")
click.echo(" 🔍 Explorer: https://explorer.aitbc.dev")
click.echo(" 📊 Grafana: https://grafana.aitbc.dev")
except Exception as e:
click.echo(f"❌ Deployment error: {str(e)}", err=True)
@production_deploy.command()
@click.option('--environment', default='production', help='Target environment')
@click.option('--backup-id', help='Specific backup ID to rollback to')
@click.option('--dry-run', is_flag=True, help='Show what would be rolled back without actually rolling back')
def rollback(environment, backup_id, dry_run):
"""Rollback production deployment"""
try:
click.echo(f"🔄 Starting production rollback...")
click.echo(f"🌍 Environment: {environment}")
if dry_run:
click.echo("🔍 DRY RUN MODE - No actual rollback will be performed")
# Get current deployment info
current_info = get_current_deployment_info(environment)
click.echo(f"📦 Current Version: {current_info['version']}")
click.echo(f"📅 Deployed At: {current_info['deployed_at']}")
# Get backup info
if backup_id:
backup_info = get_backup_info(backup_id)
else:
# Get latest backup
backup_info = get_latest_backup(environment)
backup_id = backup_info['backup_id']
click.echo(f"💾 Rolling back to backup: {backup_id}")
click.echo(f"📦 Backup Version: {backup_info['version']}")
click.echo(f"📅 Backup Created: {backup_info['created_at']}")
if not dry_run:
# Perform rollback
rollback_result = rollback_deployment(environment, backup_id)
if rollback_result['success']:
click.echo("✅ Rollback completed successfully!")
click.echo(f"📦 New Version: {backup_info['version']}")
click.echo(f"📅 Rolled back at: {datetime.utcnow().isoformat()}")
else:
click.echo(f"❌ Rollback failed: {rollback_result['error']}")
else:
click.echo("🔄 DRY RUN: Would rollback to specified backup")
except Exception as e:
click.echo(f"❌ Rollback error: {str(e)}", err=True)
@production_deploy.command()
@click.option('--environment', default='production', help='Target environment')
@click.option('--limit', type=int, default=10, help='Number of recent deployments to show')
def history(environment, limit):
"""Show deployment history"""
try:
click.echo(f"📜 Deployment History for {environment}")
click.echo("=" * 60)
# Get deployment history
history_data = get_deployment_history(environment, limit)
for deployment in history_data:
status_icon = "" if deployment['status'] == 'success' else ""
click.echo(f"{status_icon} {deployment['version']} - {deployment['deployed_at']}")
click.echo(f" 🌍 Region: {deployment['region']}")
click.echo(f" 📊 Status: {deployment['status']}")
click.echo(f" ⏱️ Duration: {deployment.get('duration', 'N/A')}")
click.echo(f" 👤 Deployed by: {deployment.get('deployed_by', 'N/A')}")
click.echo("")
except Exception as e:
click.echo(f"❌ Error getting deployment history: {str(e)}", err=True)
@production_deploy.command()
@click.option('--environment', default='production', help='Target environment')
def status(environment):
"""Show current deployment status"""
try:
click.echo(f"📊 Current Deployment Status for {environment}")
click.echo("=" * 60)
# Get current status
status_data = get_deployment_status(environment)
click.echo(f"📦 Version: {status_data['version']}")
click.echo(f"🌍 Region: {status_data['region']}")
click.echo(f"📊 Status: {status_data['status']}")
click.echo(f"📅 Deployed At: {status_data['deployed_at']}")
click.echo(f"⏱️ Uptime: {status_data['uptime']}")
click.echo("")
# Service status
click.echo("🔧 Service Status:")
for service, service_status in status_data['services'].items():
status_icon = "" if service_status['healthy'] else ""
click.echo(f" {status_icon} {service}: {service_status['status']}")
if service_status.get('replicas'):
click.echo(f" 📊 Replicas: {service_status['replicas']['ready']}/{service_status['replicas']['total']}")
click.echo("")
# Performance metrics
if status_data.get('performance'):
click.echo("📈 Performance Metrics:")
perf = status_data['performance']
click.echo(f" 💻 CPU Usage: {perf.get('cpu_usage', 'N/A')}%")
click.echo(f" 🧠 Memory Usage: {perf.get('memory_usage', 'N/A')}%")
click.echo(f" 📥 Requests/sec: {perf.get('requests_per_second', 'N/A')}")
click.echo(f" ⚡ Response Time: {perf.get('avg_response_time', 'N/A')}ms")
except Exception as e:
click.echo(f"❌ Error getting deployment status: {str(e)}", err=True)
@production_deploy.command()
@click.option('--environment', default='production', help='Target environment')
@click.option('--service', help='Specific service to restart')
@click.option('--dry-run', is_flag=True, help='Show what would be restarted without actually restarting')
def restart(environment, service, dry_run):
"""Restart services in production"""
try:
click.echo(f"🔄 Restarting services in {environment}")
if service:
click.echo(f"🔧 Service: {service}")
else:
click.echo("🔧 All services")
if dry_run:
click.echo("🔍 DRY RUN MODE - No actual restart will be performed")
# Get current status
current_status = get_deployment_status(environment)
if service:
if service not in current_status['services']:
click.echo(f"❌ Service '{service}' not found")
return
services_to_restart = [service]
else:
services_to_restart = list(current_status['services'].keys())
click.echo(f"🔧 Services to restart: {', '.join(services_to_restart)}")
if not dry_run:
# Restart services
restart_result = restart_services(environment, services_to_restart)
if restart_result['success']:
click.echo("✅ Services restarted successfully!")
for svc in services_to_restart:
click.echo(f" 🔄 {svc}: Restarted")
else:
click.echo(f"❌ Restart failed: {restart_result['error']}")
else:
click.echo("🔄 DRY RUN: Would restart specified services")
except Exception as e:
click.echo(f"❌ Restart error: {str(e)}", err=True)
@production_deploy.command()
@click.option('--environment', default='production', help='Target environment')
@click.option('--test-type', default='smoke', help='Test type (smoke, load, security)')
@click.option('--timeout', type=int, default=300, help='Test timeout in seconds')
def test(environment, test_type, timeout):
"""Run production tests"""
try:
click.echo(f"🧪 Running {test_type} tests in {environment}")
click.echo(f"⏱️ Timeout: {timeout} seconds")
# Run tests
test_result = run_production_tests(environment, test_type, timeout)
if test_result['success']:
click.echo("✅ All tests passed!")
click.echo(f"📊 Test Results:")
click.echo(f" 🧪 Test Type: {test_type}")
click.echo(f" ⏱️ Duration: {test_result['duration']} seconds")
click.echo(f" ✅ Passed: {test_result['passed']}")
click.echo(f" ❌ Failed: {test_result['failed']}")
else:
click.echo("❌ Tests failed!")
click.echo(f"📊 Test Results:")
click.echo(f" 🧪 Test Type: {test_type}")
click.echo(f" ⏱️ Duration: {test_result['duration']} seconds")
click.echo(f" ✅ Passed: {test_result['passed']}")
click.echo(f" ❌ Failed: {test_result['failed']}")
if test_result.get('failures'):
click.echo("")
click.echo("❌ Failed Tests:")
for failure in test_result['failures']:
click.echo(f"{failure['test']}: {failure['error']}")
except Exception as e:
click.echo(f"❌ Test error: {str(e)}", err=True)
@production_deploy.command()
@click.option('--environment', default='production', help='Target environment')
@click.option('--days', type=int, default=7, help='Number of days to include in report')
def report(environment, days):
"""Generate production deployment report"""
try:
click.echo(f"📊 Production Deployment Report for {environment}")
click.echo(f"📅 Last {days} days")
click.echo("=" * 60)
# Get report data
report_data = generate_deployment_report(environment, days)
# Overview
overview = report_data['overview']
click.echo("📈 Overview:")
click.echo(f" 🚀 Total Deployments: {overview['total_deployments']}")
click.echo(f" ✅ Successful: {overview['successful_deployments']}")
click.echo(f" ❌ Failed: {overview['failed_deployments']}")
click.echo(f" 📊 Success Rate: {overview['success_rate']:.1f}%")
click.echo(f" ⏱️ Avg Deployment Time: {overview['avg_deployment_time']} minutes")
click.echo("")
# Recent deployments
click.echo("📜 Recent Deployments:")
for deployment in report_data['recent_deployments']:
status_icon = "" if deployment['status'] == 'success' else ""
click.echo(f" {status_icon} {deployment['version']} - {deployment['deployed_at']}")
click.echo(f" 📊 Status: {deployment['status']}")
click.echo(f" ⏱️ Duration: {deployment['duration']} minutes")
click.echo("")
# Service health
click.echo("🔧 Service Health:")
for service, health in report_data['service_health'].items():
health_icon = "" if health['healthy'] else ""
uptime = health.get('uptime_percentage', 0)
click.echo(f" {health_icon} {service}: {uptime:.1f}% uptime")
click.echo("")
# Performance metrics
if report_data.get('performance_metrics'):
click.echo("📈 Performance Metrics:")
perf = report_data['performance_metrics']
click.echo(f" 💻 Avg CPU Usage: {perf['avg_cpu_usage']:.1f}%")
click.echo(f" 🧠 Avg Memory Usage: {perf['avg_memory_usage']:.1f}%")
click.echo(f" 📥 Avg Requests/sec: {perf['avg_requests_per_second']}")
click.echo(f" ⚡ Avg Response Time: {perf['avg_response_time']:.1f}ms")
except Exception as e:
click.echo(f"❌ Report generation error: {str(e)}", err=True)
# Helper functions
def run_pre_deployment_checks(environment, dry_run):
"""Run pre-deployment checks"""
if dry_run:
return {
"tests": True,
"infrastructure": True,
"services": True,
"security": True
}
# In production, these would be actual checks
checks = {
"tests": True,
"infrastructure": True,
"services": True,
"security": True
}
return checks
def create_backup(environment):
"""Create backup of current deployment"""
backup_id = f"backup_{environment}_{int(datetime.utcnow().timestamp())}"
return {
"backup_id": backup_id,
"created_at": datetime.utcnow().isoformat(),
"status": "completed"
}
def build_production_images(version, dry_run):
"""Build production images"""
if dry_run:
return {"success": True}
try:
# Simulate build process
return {"success": True}
except Exception as e:
return {"success": False, "error": str(e)}
def deploy_services(environment, version, region, dry_run):
"""Deploy services"""
if dry_run:
return {"success": True}
try:
# Simulate deployment
return {"success": True}
except Exception as e:
return {"success": False, "error": str(e)}
def run_post_deployment_tests(environment, dry_run):
"""Run post-deployment tests"""
if dry_run:
return {"success": True}
try:
# Simulate tests
return {"success": True}
except Exception as e:
return {"success": False, "error": str(e)}
def rollback_deployment(environment, backup_id):
"""Rollback deployment"""
return {
"status": "completed",
"backup_id": backup_id,
"rolled_back_at": datetime.utcnow().isoformat()
}
def get_current_deployment_info(environment):
"""Get current deployment info"""
return {
"version": "1.0.0",
"deployed_at": "2024-03-01T10:30:00Z",
"environment": environment
}
def get_backup_info(backup_id):
"""Get backup info"""
return {
"backup_id": backup_id,
"version": "0.9.0",
"created_at": "2024-02-28T15:45:00Z"
}
def get_latest_backup(environment):
"""Get latest backup"""
return {
"backup_id": f"backup_{environment}_latest",
"version": "0.9.0",
"created_at": "2024-02-28T15:45:00Z"
}
def get_deployment_history(environment, limit):
"""Get deployment history"""
return [
{
"version": "1.0.0",
"deployed_at": "2024-03-01T10:30:00Z",
"status": "success",
"region": "us-east-1",
"duration": 15,
"deployed_by": "ci-cd"
},
{
"version": "0.9.0",
"deployed_at": "2024-02-28T15:45:00Z",
"status": "success",
"region": "us-east-1",
"duration": 12,
"deployed_by": "ci-cd"
}
]
def get_deployment_status(environment):
"""Get deployment status"""
return {
"version": "1.0.0",
"region": "us-east-1",
"status": "healthy",
"deployed_at": "2024-03-01T10:30:00Z",
"uptime": "2 days, 5 hours",
"services": {
"coordinator-api": {
"status": "running",
"healthy": True,
"replicas": {"ready": 3, "total": 3}
},
"exchange-integration": {
"status": "running",
"healthy": True,
"replicas": {"ready": 2, "total": 2}
},
"trading-engine": {
"status": "running",
"healthy": True,
"replicas": {"ready": 3, "total": 3}
}
},
"performance": {
"cpu_usage": 45.2,
"memory_usage": 62.8,
"requests_per_second": 1250,
"avg_response_time": 85.3
}
}
def restart_services(environment, services):
"""Restart services"""
return {
"success": True,
"restarted_services": services,
"restarted_at": datetime.utcnow().isoformat()
}
def run_production_tests(environment, test_type, timeout):
"""Run production tests"""
return {
"success": True,
"duration": 45,
"passed": 10,
"failed": 0,
"failures": []
}
def generate_deployment_report(environment, days):
"""Generate deployment report"""
return {
"overview": {
"total_deployments": 5,
"successful_deployments": 4,
"failed_deployments": 1,
"success_rate": 80.0,
"avg_deployment_time": 13.5
},
"recent_deployments": [
{
"version": "1.0.0",
"deployed_at": "2024-03-01T10:30:00Z",
"status": "success",
"duration": 15
},
{
"version": "0.9.0",
"deployed_at": "2024-02-28T15:45:00Z",
"status": "success",
"duration": 12
}
],
"service_health": {
"coordinator-api": {"healthy": True, "uptime_percentage": 99.9},
"exchange-integration": {"healthy": True, "uptime_percentage": 99.8},
"trading-engine": {"healthy": True, "uptime_percentage": 99.7}
},
"performance_metrics": {
"avg_cpu_usage": 45.2,
"avg_memory_usage": 62.8,
"avg_requests_per_second": 1250,
"avg_response_time": 85.3
}
}
if __name__ == "__main__":
production_deploy()