docker.compose¶
Docker Compose orchestration: bring stacks up/down, register containers as live hosts in the active Lab, and idempotently re-enter when the same stack is already running.
The public surface (re-exported from otto.docker) is:
compose_up()— bring a stack up; returns{service: DockerContainerHost}.compose_down()— stop a stack and remove its container hosts from the lab.composed()— async context manager wrapping the above.compose_ps()— list running stacks on a parent.get_container_host()— lab lookup by id (typed convenience).get_user_compose_project()— name a stack so concurrent runs don’t collide.
-
otto.docker.compose.get_user_compose_project(repo_name, suffix=
None)¶ Return a compose project name unique enough to coexist with other runs.
Format:
otto-<repo>-<suffix>. suffix defaults to the OS username, orOTTO_COMPOSE_SUFFIXif set in the environment. Lowercase only — compose project names must be lowercase.- Return type:¶
str
-
async otto.docker.compose.compose_up(repo, lab, *, on=
None, project_name=None, build=True)¶ Bring up repo’s compose stack on a parent host.
Idempotent at the project-name level: if a stack with the same project_name is already running on parent, this becomes a lookup instead of a fresh
up. Either way, returns a dict mapping each declared service to itsDockerContainerHost, with the hosts also registered inlab.hostsso--list-hostsandotto host <id>see them.- Parameters:¶
build – When True (the default) and the repo declares
[[docker.images]], runbuild_images()first so locally- built images exist on the parent before compose tries to pull them. The build is idempotent via the context-hash skip, so this is cheap when nothing changed. Passbuild=Falseif the compose file references only published images (or if you already built explicitly).- Return type:¶
dict[str,DockerContainerHost]
-
async otto.docker.compose.compose_down(repo, lab, *, on=
None, project_name=None, stop_timeout=1)¶ Tear down repo’s compose stack and unregister its container hosts.
stop_timeout is the per-container graceful-shutdown grace period in seconds passed to
docker compose down --timeout. Defaults to 1s rather than docker’s default of 10s — otto’s typical workload is integration tests with disposable stacks where waiting 10s on every teardown adds up fast (4 tests × 10s = 40s of wall time on the serializeddocker_e2egroup). Pass a larger value for stacks where graceful shutdown matters.
-
otto.docker.compose.composed(repo, lab, *, on=
None, project_name=None, own=False, build=True)¶ Context manager wrapping
compose_up/compose_down.By default the stack is not torn down on exit if it was already running on entry — this lets a suite-level fixture hold the stack while inner instructions also call
composedwithout yanking it from each other. Passown=Trueto force teardown.build is forwarded to
compose_up().- Return type:¶
AsyncIterator[dict[str,DockerContainerHost]]
- async otto.docker.compose.compose_ps(parent)¶
Return a list of dicts describing running containers on parent.
Uses
docker ps --format '{{json .}}'so the output is structured.- Return type:¶
list[dict[str,Any]]
- otto.docker.compose.register_declared_container_hosts(lab, repos)¶
Pre-register placeholder container hosts in lab for every declared
<parent>.<project>.<service>.The placeholders carry an empty
container_idso that any operation against a not-yet-up container fails with a clear “run otto docker up” message rather than a confusing not-found error. Oncecompose_up()runs, it overwrites the placeholder with a real entry containing the resolved container id.Returns the number of placeholders registered.
- Return type:¶
int