0
# Docker Compose Orchestration
1
2
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.
3
4
## Capabilities
5
6
### Compose Environment Management
7
8
Manage entire Docker Compose environments with automatic service startup, configuration loading, and coordinated shutdown.
9
10
```python { .api }
11
@dataclass
12
class DockerCompose:
13
context: Union[str, PathLike[str]]
14
compose_file_name: Optional[Union[str, list[str]]] = None
15
pull: bool = False
16
build: bool = False
17
wait: bool = True
18
keep_volumes: bool = False
19
env_file: Optional[str] = None
20
services: Optional[list[str]] = None
21
docker_command_path: Optional[str] = None
22
profiles: Optional[list[str]] = None
23
"""
24
Initialize Docker Compose environment.
25
26
Args:
27
context: Path to compose context directory
28
compose_file_name: Compose file name (default: docker-compose.yml)
29
pull: Pull images before starting
30
build: Build images before starting
31
wait: Wait for services to be ready
32
keep_volumes: Preserve volumes on shutdown
33
env_file: Environment file path
34
services: Specific services to run
35
docker_command_path: Custom docker-compose command path
36
profiles: Compose profiles to activate
37
**kwargs: Additional compose options
38
"""
39
40
def start(self) -> "DockerCompose":
41
"""
42
Start the compose environment.
43
44
Returns:
45
Self for method chaining
46
"""
47
48
def stop(self, down: bool = True) -> None:
49
"""
50
Stop the compose environment.
51
52
Args:
53
down: Use 'docker-compose down' instead of 'stop'
54
"""
55
56
def __enter__(self) -> "DockerCompose":
57
"""Context manager entry - starts compose environment."""
58
59
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
60
"""Context manager exit - stops compose environment."""
61
```
62
63
### Service Discovery and Access
64
65
Access individual services within the compose environment, retrieve connection information, and interact with running containers.
66
67
```python { .api }
68
def get_container(self, service_name: Optional[str] = None, include_all: bool = False) -> ComposeContainer:
69
"""
70
Get container for specific service.
71
72
Args:
73
service_name: Service name (first service if None)
74
include_all: Include stopped containers
75
76
Returns:
77
ComposeContainer instance
78
"""
79
80
def get_containers(self, include_all: bool = False) -> list[ComposeContainer]:
81
"""
82
Get all containers in the compose environment.
83
84
Args:
85
include_all: Include stopped containers
86
87
Returns:
88
List of ComposeContainer instances
89
"""
90
91
def get_service_host(self, service_name: Optional[str] = None, port: Optional[int] = None) -> str:
92
"""
93
Get host address for service.
94
95
Args:
96
service_name: Service name
97
port: Service port
98
99
Returns:
100
Host address string
101
"""
102
103
def get_service_port(self, service_name: Optional[str] = None, port: Optional[int] = None) -> int:
104
"""
105
Get mapped port for service.
106
107
Args:
108
service_name: Service name
109
port: Internal service port
110
111
Returns:
112
Mapped host port number
113
"""
114
115
def get_service_host_and_port(self, service_name: Optional[str] = None, port: Optional[int] = None) -> tuple[str, int]:
116
"""
117
Get host and port for service.
118
119
Args:
120
service_name: Service name
121
port: Internal service port
122
123
Returns:
124
Tuple of (host, port)
125
"""
126
```
127
128
### Container Operations
129
130
Execute commands in running services, retrieve logs, and interact with the compose environment.
131
132
```python { .api }
133
def exec_in_container(self, command: str, service_name: Optional[str] = None) -> str:
134
"""
135
Execute command in service container.
136
137
Args:
138
command: Command to execute
139
service_name: Target service name
140
141
Returns:
142
Command output string
143
"""
144
145
def get_logs(self, *services: str) -> str:
146
"""
147
Get logs from services.
148
149
Args:
150
*services: Service names (all services if none specified)
151
152
Returns:
153
Combined log output string
154
"""
155
156
def get_config(
157
self,
158
path_resolution: bool = True,
159
normalize: bool = True,
160
interpolate: bool = True
161
) -> dict:
162
"""
163
Get compose configuration.
164
165
Args:
166
path_resolution: Resolve file paths
167
normalize: Normalize configuration format
168
interpolate: Interpolate environment variables
169
170
Returns:
171
Compose configuration dictionary
172
"""
173
```
174
175
### Service Health Checking
176
177
Wait for services to become available and ready for connections.
178
179
```python { .api }
180
def wait_for(self, url: str) -> None:
181
"""
182
Wait for URL to become available.
183
184
Args:
185
url: URL to check for availability
186
"""
187
```
188
189
### Container Information
190
191
Access detailed information about individual containers within the compose environment.
192
193
```python { .api }
194
class ComposeContainer:
195
ID: str # Container ID
196
Name: str # Container name
197
Command: str # Container command
198
Project: str # Compose project name
199
Service: str # Service name
200
State: str # Container state
201
Health: str # Health status
202
ExitCode: int # Exit code
203
Publishers: list[PublishedPortModel] # Published ports
204
205
def get_publisher(
206
self,
207
by_port: Optional[int] = None,
208
by_host: Optional[str] = None,
209
prefer_ip_version: str = "IPv4"
210
) -> PublishedPortModel:
211
"""
212
Get port publisher information.
213
214
Args:
215
by_port: Filter by port number
216
by_host: Filter by host address
217
prefer_ip_version: Preferred IP version ("IPv4" or "IPv6")
218
219
Returns:
220
PublishedPortModel instance
221
"""
222
223
class PublishedPortModel:
224
URL: str # Published URL
225
TargetPort: int # Target container port
226
PublishedPort: int # Published host port
227
Protocol: str # Protocol (tcp/udp)
228
229
def normalize(self) -> "PublishedPortModel":
230
"""
231
Normalize for Windows compatibility.
232
233
Returns:
234
Normalized PublishedPortModel
235
"""
236
```
237
238
## Usage Examples
239
240
### Basic Compose Usage
241
242
```python
243
from testcontainers.compose import DockerCompose
244
import requests
245
246
# docker-compose.yml in current directory with web and db services
247
with DockerCompose(".") as compose:
248
# Get service endpoints
249
web_host = compose.get_service_host("web", 80)
250
web_port = compose.get_service_port("web", 80)
251
252
# Make request to web service
253
response = requests.get(f"http://{web_host}:{web_port}/health")
254
assert response.status_code == 200
255
256
# Get database connection info
257
db_host = compose.get_service_host("db", 5432)
258
db_port = compose.get_service_port("db", 5432)
259
print(f"Database available at {db_host}:{db_port}")
260
```
261
262
### Custom Compose File
263
264
```python
265
from testcontainers.compose import DockerCompose
266
267
# Use specific compose file and environment
268
compose = DockerCompose(
269
context="./docker",
270
compose_file_name="docker-compose.test.yml",
271
pull=True, # Pull latest images
272
build=True, # Build custom images
273
env_file="test.env"
274
)
275
276
with compose:
277
# Execute command in service
278
result = compose.exec_in_container("ls -la", service_name="app")
279
print(f"Container contents: {result}")
280
281
# Get logs from specific services
282
logs = compose.get_logs("app", "worker")
283
print(f"Service logs: {logs}")
284
```
285
286
### Service-Specific Operations
287
288
```python
289
from testcontainers.compose import DockerCompose
290
291
with DockerCompose(".", compose_file_name="microservices.yml") as compose:
292
# Get all running containers
293
containers = compose.get_containers()
294
295
for container in containers:
296
print(f"Service: {container.Service}")
297
print(f"State: {container.State}")
298
print(f"Health: {container.Health}")
299
300
# Get port information
301
for publisher in container.Publishers:
302
print(f"Port {publisher.TargetPort} -> {publisher.PublishedPort}")
303
304
# Access specific service container
305
api_container = compose.get_container("api")
306
print(f"API container ID: {api_container.ID}")
307
```
308
309
### Profile-Based Deployment
310
311
```python
312
from testcontainers.compose import DockerCompose
313
314
# Use compose profiles for different environments
315
test_compose = DockerCompose(
316
context=".",
317
profiles=["test", "monitoring"],
318
services=["app", "db", "redis"] # Only start specific services
319
)
320
321
with test_compose:
322
# Only services in 'test' and 'monitoring' profiles are started
323
app_url = f"http://{test_compose.get_service_host('app', 8080)}:{test_compose.get_service_port('app', 8080)}"
324
print(f"Test app available at: {app_url}")
325
```
326
327
### Integration Testing Setup
328
329
```python
330
from testcontainers.compose import DockerCompose
331
import pytest
332
import requests
333
334
@pytest.fixture(scope="session")
335
def app_stack():
336
"""Pytest fixture for full application stack."""
337
with DockerCompose(".", compose_file_name="test-stack.yml") as compose:
338
# Wait for services to be ready
339
compose.wait_for(f"http://{compose.get_service_host('app', 8080)}:{compose.get_service_port('app', 8080)}/health")
340
341
yield compose
342
343
def test_api_endpoints(app_stack):
344
"""Test API endpoints with full stack."""
345
compose = app_stack
346
347
# Get API endpoint
348
api_host = compose.get_service_host("api", 3000)
349
api_port = compose.get_service_port("api", 3000)
350
base_url = f"http://{api_host}:{api_port}"
351
352
# Test endpoints
353
response = requests.get(f"{base_url}/users")
354
assert response.status_code == 200
355
356
response = requests.post(f"{base_url}/users", json={"name": "Test User"})
357
assert response.status_code == 201
358
359
def test_database_integration(app_stack):
360
"""Test database operations."""
361
compose = app_stack
362
363
# Execute database command
364
result = compose.exec_in_container("psql -U postgres -c 'SELECT version();'", "db")
365
assert "PostgreSQL" in result
366
```
367
368
### Complex Multi-Service Architecture
369
370
```python
371
from testcontainers.compose import DockerCompose
372
import time
373
374
# docker-compose.yml with web, api, worker, db, redis, elasticsearch
375
with DockerCompose(".", compose_file_name="full-stack.yml") as compose:
376
# Get all service endpoints
377
services = {
378
"web": compose.get_service_host_and_port("web", 80),
379
"api": compose.get_service_host_and_port("api", 3000),
380
"db": compose.get_service_host_and_port("db", 5432),
381
"redis": compose.get_service_host_and_port("redis", 6379),
382
"elasticsearch": compose.get_service_host_and_port("elasticsearch", 9200)
383
}
384
385
print("Service endpoints:")
386
for service, (host, port) in services.items():
387
print(f" {service}: {host}:{port}")
388
389
# Wait for all services to be healthy
390
for container in compose.get_containers():
391
while container.Health not in ["healthy", ""]:
392
print(f"Waiting for {container.Service} to be healthy...")
393
time.sleep(2)
394
# Refresh container info
395
container = compose.get_container(container.Service)
396
397
print("All services are ready!")
398
399
# Run integration tests
400
web_host, web_port = services["web"]
401
response = requests.get(f"http://{web_host}:{web_port}")
402
print(f"Web response status: {response.status_code}")
403
```
404
405
### Environment Configuration
406
407
```python
408
from testcontainers.compose import DockerCompose
409
import os
410
411
# Set environment variables for compose
412
os.environ["DATABASE_URL"] = "postgres://test:test@db:5432/testdb"
413
os.environ["REDIS_URL"] = "redis://redis:6379"
414
os.environ["DEBUG"] = "true"
415
416
# Compose with environment file and variable interpolation
417
compose = DockerCompose(
418
context="./infrastructure",
419
env_file="test.env",
420
keep_volumes=False # Clean up volumes after testing
421
)
422
423
with compose:
424
# Environment variables are available in compose services
425
config = compose.get_config()
426
print("Compose configuration:", config)
427
428
# Services use interpolated environment variables
429
app_logs = compose.get_logs("app")
430
print("Application logs:", app_logs)
431
```