docker_engine
tit.jobs.docker_engine ¶
Dependency-free Docker Engine API client — stdlib http.client over AF_UNIX.
The Node-side twin of this module is desktop/src/main/docker/engine.ts; both talk the same
Docker Engine REST API subset directly over the Unix socket, never the docker CLI and never
the third-party docker PyPI package (r5-docker-integration.md §5 — that package is not
installed in idossha/ti-toolbox:v3.0.0 today, and adding it would only grow a 19 GB image
further for something ~15 lines of stdlib already does).
Scope, and why it is narrower than the TypeScript client: this module is used from inside
the tit container, where QSIPrep/QSIRecon are spawned as sibling containers via
Docker-outside-of-Docker (DooD) — the root docker-compose.yml mounts the host's
/var/run/docker.sock straight through, a Unix path, regardless of what OS the host actually
is. So unlike the Electron client (which must speak to a real Windows named pipe on a Windows
host), this module never needs npipe transport at all — Windows support here would mean
supporting it as a pywin32 dependency for a case that cannot occur in this architecture, which
is why it is explicitly out of scope, not merely unimplemented. If a future redesign ever has
Electron spawn QSIPrep containers directly instead of via DooD (r5 §5's "bigger architectural
change, out of scope"), that would need a real npipe-capable Python client; this one is not it.
Discovery: DOCKER_HOST (unix:// only — tcp:///npipe:// are parsed for
completeness but this module never drives the tcp/npipe internals http.client would need),
else the well-known mount path /var/run/docker.sock. No docker context inspect shell-out
here (unlike discover.ts): the Python side always runs inside the tit container, where
the socket location is a known, fixed bind mount, not an environment to be discovered the way a
user's host machine is (r5 §5's "the Python client only ever needs the Unix-socket transport").
API version negotiation: identical strategy to engine.ts — :meth:DockerEngineClient.version
hits the always-unversioned GET /version, reads ApiVersion from the response, and every
later call is prefixed /v<ApiVersion>/....
What it covers: only the read-and-stop subset production calls --
:meth:DockerEngineClient.version, :meth:~DockerEngineClient.system_df and
:meth:~DockerEngineClient.list_images (the System page's Docker panel, tit/server/routes/system.py),
:meth:~DockerEngineClient.list_containers and :meth:~DockerEngineClient.stop_container
(tit/jobs/runner.py's stop_docker_siblings_via_engine, which stops a stranded job's
QSIPrep/QSIRecon siblings by their tit.job_id label at startup) and
:meth:~DockerEngineClient.inspect_container (tit/host_path.py and the System page, reading
the container's own mounts). QSIPrep/QSIRecon themselves are still started through the
docker CLI by tit/pre/qsi/docker_builder.py, which sets the same tit.job_id label.
DockerEngineError ¶
Bases: Exception
Carries a stable kind (mirrors DockerErrorKind in engine.ts) and, for an HTTP-
level failure, the status_code that produced it.
Source code in tit/jobs/docker_engine.py
DockerEngineClient ¶
DockerEngineClient(conn: DockerConnection, timeout_s: float | None = DEFAULT_TIMEOUT_S)
Version, disk usage, images, containers (list/inspect/stop) over the Engine REST API.
Source code in tit/jobs/docker_engine.py
system_df ¶
GET /system/df -- the Engine-API equivalent of docker system df.
Read-only, used by the System page's Docker-health panel. It is a genuinely expensive call on the daemon side (it walks the image graph), which is why the caller polls it on a multi-second TTL and passes an explicit timeout rather than the client default.
Source code in tit/jobs/docker_engine.py
list_images ¶
GET /images/json -- docker images. Read-only; see :meth:system_df.
Source code in tit/jobs/docker_engine.py
list_containers ¶
list_containers(*, filters: dict[str, list[str]] | None = None, all_containers: bool = False, timeout_s: float | None | object = 'default') -> list[dict[str, Any]]
GET /containers/json — the Engine-API equivalent of docker ps [--filter …].
filters is the Engine's own filter map (e.g. {"label": ["tit.job_id=abc"]}); it is
JSON-encoded into the query string by :meth:_request. Every caller in this repo passes
an explicit timeout_s (startup reconciliation must never block on a wedged daemon).
Source code in tit/jobs/docker_engine.py
discover ¶
DOCKER_HOST=unix://... if set, else the well-known DooD bind-mount path. See the module
docstring for why this is intentionally narrower than discover.ts's multi-candidate,
CLI-assisted search — this module always runs inside a container with a known socket mount.