# Docker Compose Orchestration

Complete Docker Compose integration for managing multi-container environments, service discovery, and complex application stacks during testing. Enables full orchestration of interconnected services with configuration management and lifecycle control.

## Capabilities

### Compose Environment Management

Manage entire Docker Compose environments with automatic service startup, configuration loading, and coordinated shutdown.

```python { .api }
@dataclass
class DockerCompose:
    context: Union[str, PathLike[str]]
    compose_file_name: Optional[Union[str, list[str]]] = None
    pull: bool = False
    build: bool = False
    wait: bool = True
    keep_volumes: bool = False
    env_file: Optional[str] = None
    services: Optional[list[str]] = None
    docker_command_path: Optional[str] = None
    profiles: Optional[list[str]] = None
        """
        Initialize Docker Compose environment.
        
        Args:
            context: Path to compose context directory
            compose_file_name: Compose file name (default: docker-compose.yml)
            pull: Pull images before starting
            build: Build images before starting
            wait: Wait for services to be ready
            keep_volumes: Preserve volumes on shutdown
            env_file: Environment file path
            services: Specific services to run
            docker_command_path: Custom docker-compose command path
            profiles: Compose profiles to activate
            **kwargs: Additional compose options
        """

    def start(self) -> "DockerCompose":
        """
        Start the compose environment.
        
        Returns:
            Self for method chaining
        """

    def stop(self, down: bool = True) -> None:
        """
        Stop the compose environment.
        
        Args:
            down: Use 'docker-compose down' instead of 'stop'
        """

    def __enter__(self) -> "DockerCompose":
        """Context manager entry - starts compose environment."""

    def __exit__(self, exc_type, exc_val, exc_tb) -> None:
        """Context manager exit - stops compose environment."""
```

### Service Discovery and Access

Access individual services within the compose environment, retrieve connection information, and interact with running containers.

```python { .api }
def get_container(self, service_name: Optional[str] = None, include_all: bool = False) -> ComposeContainer:
    """
    Get container for specific service.
    
    Args:
        service_name: Service name (first service if None)
        include_all: Include stopped containers
        
    Returns:
        ComposeContainer instance
    """

def get_containers(self, include_all: bool = False) -> list[ComposeContainer]:
    """
    Get all containers in the compose environment.
    
    Args:
        include_all: Include stopped containers
        
    Returns:
        List of ComposeContainer instances
    """

def get_service_host(self, service_name: Optional[str] = None, port: Optional[int] = None) -> str:
    """
    Get host address for service.
    
    Args:
        service_name: Service name
        port: Service port
        
    Returns:
        Host address string
    """

def get_service_port(self, service_name: Optional[str] = None, port: Optional[int] = None) -> int:
    """
    Get mapped port for service.
    
    Args:
        service_name: Service name
        port: Internal service port
        
    Returns:
        Mapped host port number
    """

def get_service_host_and_port(self, service_name: Optional[str] = None, port: Optional[int] = None) -> tuple[str, int]:
    """
    Get host and port for service.
    
    Args:
        service_name: Service name
        port: Internal service port
        
    Returns:
        Tuple of (host, port)
    """
```

### Container Operations

Execute commands in running services, retrieve logs, and interact with the compose environment.

```python { .api }
def exec_in_container(self, command: str, service_name: Optional[str] = None) -> str:
    """
    Execute command in service container.
    
    Args:
        command: Command to execute
        service_name: Target service name
        
    Returns:
        Command output string
    """

def get_logs(self, *services: str) -> str:
    """
    Get logs from services.
    
    Args:
        *services: Service names (all services if none specified)
        
    Returns:
        Combined log output string
    """

def get_config(
    self,
    path_resolution: bool = True,
    normalize: bool = True,
    interpolate: bool = True
) -> dict:
    """
    Get compose configuration.
    
    Args:
        path_resolution: Resolve file paths
        normalize: Normalize configuration format
        interpolate: Interpolate environment variables
        
    Returns:
        Compose configuration dictionary
    """
```

### Service Health Checking

Wait for services to become available and ready for connections.

```python { .api }
def wait_for(self, url: str) -> None:
    """
    Wait for URL to become available.
    
    Args:
        url: URL to check for availability
    """
```

### Container Information

Access detailed information about individual containers within the compose environment.

```python { .api }
class ComposeContainer:
    ID: str          # Container ID
    Name: str        # Container name
    Command: str     # Container command
    Project: str     # Compose project name
    Service: str     # Service name
    State: str       # Container state
    Health: str      # Health status
    ExitCode: int    # Exit code
    Publishers: list[PublishedPortModel]  # Published ports

    def get_publisher(
        self,
        by_port: Optional[int] = None,
        by_host: Optional[str] = None,
        prefer_ip_version: str = "IPv4"
    ) -> PublishedPortModel:
        """
        Get port publisher information.
        
        Args:
            by_port: Filter by port number
            by_host: Filter by host address
            prefer_ip_version: Preferred IP version ("IPv4" or "IPv6")
            
        Returns:
            PublishedPortModel instance
        """

class PublishedPortModel:
    URL: str           # Published URL
    TargetPort: int    # Target container port
    PublishedPort: int # Published host port
    Protocol: str      # Protocol (tcp/udp)

    def normalize(self) -> "PublishedPortModel":
        """
        Normalize for Windows compatibility.
        
        Returns:
            Normalized PublishedPortModel
        """
```

## Usage Examples

### Basic Compose Usage

```python
from testcontainers.compose import DockerCompose
import requests

# docker-compose.yml in current directory with web and db services
with DockerCompose(".") as compose:
    # Get service endpoints
    web_host = compose.get_service_host("web", 80)
    web_port = compose.get_service_port("web", 80)
    
    # Make request to web service
    response = requests.get(f"http://{web_host}:{web_port}/health")
    assert response.status_code == 200
    
    # Get database connection info
    db_host = compose.get_service_host("db", 5432)
    db_port = compose.get_service_port("db", 5432)
    print(f"Database available at {db_host}:{db_port}")
```

### Custom Compose File

```python
from testcontainers.compose import DockerCompose

# Use specific compose file and environment
compose = DockerCompose(
    context="./docker",
    compose_file_name="docker-compose.test.yml",
    pull=True,  # Pull latest images
    build=True,  # Build custom images
    env_file="test.env"
)

with compose:
    # Execute command in service
    result = compose.exec_in_container("ls -la", service_name="app")
    print(f"Container contents: {result}")
    
    # Get logs from specific services
    logs = compose.get_logs("app", "worker")
    print(f"Service logs: {logs}")
```

### Service-Specific Operations

```python
from testcontainers.compose import DockerCompose

with DockerCompose(".", compose_file_name="microservices.yml") as compose:
    # Get all running containers
    containers = compose.get_containers()
    
    for container in containers:
        print(f"Service: {container.Service}")
        print(f"State: {container.State}")
        print(f"Health: {container.Health}")
        
        # Get port information
        for publisher in container.Publishers:
            print(f"Port {publisher.TargetPort} -> {publisher.PublishedPort}")
    
    # Access specific service container
    api_container = compose.get_container("api")
    print(f"API container ID: {api_container.ID}")
```

### Profile-Based Deployment

```python
from testcontainers.compose import DockerCompose

# Use compose profiles for different environments
test_compose = DockerCompose(
    context=".",
    profiles=["test", "monitoring"],
    services=["app", "db", "redis"]  # Only start specific services
)

with test_compose:
    # Only services in 'test' and 'monitoring' profiles are started
    app_url = f"http://{test_compose.get_service_host('app', 8080)}:{test_compose.get_service_port('app', 8080)}"
    print(f"Test app available at: {app_url}")
```

### Integration Testing Setup

```python
from testcontainers.compose import DockerCompose
import pytest
import requests

@pytest.fixture(scope="session")
def app_stack():
    """Pytest fixture for full application stack."""
    with DockerCompose(".", compose_file_name="test-stack.yml") as compose:
        # Wait for services to be ready
        compose.wait_for(f"http://{compose.get_service_host('app', 8080)}:{compose.get_service_port('app', 8080)}/health")
        
        yield compose

def test_api_endpoints(app_stack):
    """Test API endpoints with full stack."""
    compose = app_stack
    
    # Get API endpoint
    api_host = compose.get_service_host("api", 3000)
    api_port = compose.get_service_port("api", 3000)
    base_url = f"http://{api_host}:{api_port}"
    
    # Test endpoints
    response = requests.get(f"{base_url}/users")
    assert response.status_code == 200
    
    response = requests.post(f"{base_url}/users", json={"name": "Test User"})
    assert response.status_code == 201

def test_database_integration(app_stack):
    """Test database operations."""
    compose = app_stack
    
    # Execute database command
    result = compose.exec_in_container("psql -U postgres -c 'SELECT version();'", "db")
    assert "PostgreSQL" in result
```

### Complex Multi-Service Architecture

```python
from testcontainers.compose import DockerCompose
import time

# docker-compose.yml with web, api, worker, db, redis, elasticsearch
with DockerCompose(".", compose_file_name="full-stack.yml") as compose:
    # Get all service endpoints
    services = {
        "web": compose.get_service_host_and_port("web", 80),
        "api": compose.get_service_host_and_port("api", 3000),
        "db": compose.get_service_host_and_port("db", 5432),
        "redis": compose.get_service_host_and_port("redis", 6379),
        "elasticsearch": compose.get_service_host_and_port("elasticsearch", 9200)
    }
    
    print("Service endpoints:")
    for service, (host, port) in services.items():
        print(f"  {service}: {host}:{port}")
    
    # Wait for all services to be healthy
    for container in compose.get_containers():
        while container.Health not in ["healthy", ""]:
            print(f"Waiting for {container.Service} to be healthy...")
            time.sleep(2)
            # Refresh container info
            container = compose.get_container(container.Service)
    
    print("All services are ready!")
    
    # Run integration tests
    web_host, web_port = services["web"]
    response = requests.get(f"http://{web_host}:{web_port}")
    print(f"Web response status: {response.status_code}")
```

### Environment Configuration

```python
from testcontainers.compose import DockerCompose
import os

# Set environment variables for compose
os.environ["DATABASE_URL"] = "postgres://test:test@db:5432/testdb"
os.environ["REDIS_URL"] = "redis://redis:6379"
os.environ["DEBUG"] = "true"

# Compose with environment file and variable interpolation
compose = DockerCompose(
    context="./infrastructure",
    env_file="test.env",
    keep_volumes=False  # Clean up volumes after testing
)

with compose:
    # Environment variables are available in compose services
    config = compose.get_config()
    print("Compose configuration:", config)
    
    # Services use interpolated environment variables
    app_logs = compose.get_logs("app")
    print("Application logs:", app_logs)
```