#!/usr/bin/env python3 """ Docker setup script for AMO CRM service with FastStream. This script helps set up and manage Docker containers for the AMO CRM service with FastStream workers. """ import subprocess import sys import os import logging import argparse from pathlib import Path logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def run_command(command: str, capture_output: bool = False) -> subprocess.CompletedProcess: """ Run a shell command. Args: command: Command to execute capture_output: Whether to capture output Returns: CompletedProcess result """ logger.info(f"Running: {command}") return subprocess.run( command.split(), capture_output=capture_output, text=True ) def check_docker() -> bool: """ Check if Docker is installed and running. Returns: True if Docker is available, False otherwise """ try: result = run_command("docker --version", capture_output=True) if result.returncode == 0: logger.info(f"Docker found: {result.stdout.strip()}") # Check if Docker daemon is running result = run_command("docker info", capture_output=True) if result.returncode == 0: logger.info("Docker daemon is running") return True else: logger.error("Docker daemon is not running") return False else: logger.error("Docker not found") return False except FileNotFoundError: logger.error("Docker not installed") return False def check_docker_compose() -> bool: """ Check if Docker Compose is installed. Returns: True if Docker Compose is available, False otherwise """ try: result = run_command("docker-compose --version", capture_output=True) if result.returncode == 0: logger.info(f"Docker Compose found: {result.stdout.strip()}") return True else: # Try docker compose (newer syntax) result = run_command("docker compose version", capture_output=True) if result.returncode == 0: logger.info(f"Docker Compose found: {result.stdout.strip()}") return True else: logger.error("Docker Compose not found") return False except FileNotFoundError: logger.error("Docker Compose not installed") return False def create_env_file() -> None: """Create .env file from template if it doesn't exist.""" env_file = Path(".env") env_example = Path("env.example") if not env_file.exists() and env_example.exists(): logger.info("Creating .env file from template") env_file.write_text(env_example.read_text()) logger.warning("Please edit .env file with your configuration") elif not env_file.exists(): logger.warning(".env file not found and no template available") def create_directories() -> None: """Create necessary directories.""" directories = ["data", "credentials", "ssl"] for directory in directories: path = Path(directory) if not path.exists(): logger.info(f"Creating directory: {directory}") path.mkdir(exist_ok=True) def build_images() -> bool: """ Build Docker images. Returns: True if successful, False otherwise """ logger.info("Building Docker images...") result = run_command("docker-compose build") return result.returncode == 0 def start_services(mode: str = "dev") -> bool: """ Start Docker services. Args: mode: Deployment mode (dev or prod) Returns: True if successful, False otherwise """ if mode == "prod": logger.info("Starting services in production mode...") result = run_command("docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d") else: logger.info("Starting services in development mode...") result = run_command("docker-compose up -d") return result.returncode == 0 def stop_services() -> bool: """ Stop Docker services. Returns: True if successful, False otherwise """ logger.info("Stopping services...") result = run_command("docker-compose down") return result.returncode == 0 def show_logs(service: str = None, follow: bool = False) -> None: """ Show service logs. Args: service: Specific service to show logs for follow: Whether to follow logs """ if service: command = f"docker-compose logs {'--follow' if follow else ''} {service}" else: command = f"docker-compose logs {'--follow' if follow else ''}" run_command(command) def show_status() -> None: """Show status of all services.""" logger.info("Service status:") run_command("docker-compose ps") logger.info("\nService health:") run_command("docker-compose exec app python -c \"import requests; print('API:', requests.get('http://localhost:8000/health').json())\"") def main() -> None: """Main function.""" parser = argparse.ArgumentParser(description="Docker setup for AMO CRM service") parser.add_argument("action", choices=["setup", "start", "stop", "restart", "logs", "status", "build"], help="Action to perform") parser.add_argument("--mode", choices=["dev", "prod"], default="dev", help="Deployment mode") parser.add_argument("--service", help="Specific service for logs") parser.add_argument("--follow", action="store_true", help="Follow logs") args = parser.parse_args() # Check prerequisites if not check_docker(): logger.error("Docker is required but not available") sys.exit(1) if not check_docker_compose(): logger.error("Docker Compose is required but not available") sys.exit(1) # Execute action if args.action == "setup": logger.info("Setting up AMO CRM service with FastStream...") create_env_file() create_directories() if build_images(): logger.info("Setup completed successfully!") logger.info("Next steps:") logger.info("1. Edit .env file with your configuration") logger.info("2. Run: python scripts/docker_setup.py start") else: logger.error("Setup failed during image build") sys.exit(1) elif args.action == "build": if not build_images(): logger.error("Build failed") sys.exit(1) elif args.action == "start": if not start_services(args.mode): logger.error("Failed to start services") sys.exit(1) logger.info("Services started successfully!") logger.info("API available at: http://localhost:8000") logger.info("API documentation: http://localhost:8000/docs") elif args.action == "stop": if not stop_services(): logger.error("Failed to stop services") sys.exit(1) elif args.action == "restart": logger.info("Restarting services...") stop_services() if not start_services(args.mode): logger.error("Failed to restart services") sys.exit(1) elif args.action == "logs": show_logs(args.service, args.follow) elif args.action == "status": show_status() if __name__ == "__main__": main()