BEFORE: /opt/aitbc/cli/ ├── aitbc_cli/ # Python package (box in a box) │ ├── commands/ │ ├── main.py │ └── ... ├── setup.py AFTER: /opt/aitbc/cli/ # Flat structure ├── commands/ # Direct access ├── main.py # Direct access ├── auth/ ├── config/ ├── core/ ├── models/ ├── utils/ ├── plugins.py └── setup.py CHANGES MADE: - Moved all files from aitbc_cli/ to cli/ root - Fixed all relative imports (from . to absolute imports) - Updated setup.py entry point: aitbc_cli.main → main - Added CLI directory to Python path in entry script - Simplified deployment.py to remove dependency on deleted core.deployment - Fixed import paths in all command files - Recreated virtual environment with new structure BENEFITS: - Eliminated 'box in a box' nesting - Simpler directory structure - Direct access to all modules - Cleaner imports - Easier maintenance and development - CLI works with both 'python main.py' and 'aitbc' commands
547 lines
21 KiB
Python
Executable File
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()
|