diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 2ae7dc1..b76374a 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -19,14 +19,19 @@ jobs: backend: runs-on: ubuntu-latest - # pytest-databases starts PostgreSQL in a container and then connects to it. Which address - # it connects to is decided by DOCKER_HOST: unset or unix:// means 127.0.0.1, which is wrong - # here because the job is itself a container and the database is a sibling, published on - # another network namespace. Pointing at a dind service instead makes pytest-databases - # resolve the host to `docker`, where the port really is. + # pytest-databases starts PostgreSQL in a container and then connects to it, and those are + # two different addresses that have to be set separately: # - # If the runner is ever configured with its own dind sidecar, delete this block and the - # DOCKER_HOST below: every job gets a daemon then, including the ones in release.yml. + # DOCKER_HOST which daemon to create the container on (_service.py get_docker_host) + # POSTGRES_HOST where the test then connects (docker/postgres.py, default + # 127.0.0.1 -- the job container's own loopback, where nothing listens, + # because the database is a sibling container on another namespace) + # + # Setting only the first leaves the tests dialling 127.0.0.1 and timing out with + # "Service 'pytest_databases_postgres' failed to come online". + # + # If the runner is ever given its own dind sidecar, drop this services block and keep the + # two env vars pointed at whatever host it exposes. services: docker: image: docker:27-dind @@ -36,6 +41,7 @@ jobs: env: DOCKER_HOST: tcp://docker:2375 + POSTGRES_HOST: docker defaults: run: diff --git a/docs/ci-release-pipeline.md b/docs/ci-release-pipeline.md index f14c69a..4f0181d 100644 --- a/docs/ci-release-pipeline.md +++ b/docs/ci-release-pipeline.md @@ -105,14 +105,24 @@ the workflow. volume), or run the runner in host mode. The dind sidecar is the safer of the two — mounting the host socket into job containers gives any workflow root on the runner host. - **Reachability is a separate question from availability, and it bit us.** Mounting the host - socket into a containerised job gives it a working daemon, and `pytest tests/` still fails with - `Service 'pytest_databases_postgres' failed to come online`: the container starts fine, but its - published port lands on the *host's* network namespace while the test process looks for it on - the job container's loopback. `pytest_databases/docker/__init__.py` picks the address from - `DOCKER_HOST` — `127.0.0.1` when it is unset or `unix://`, otherwise the hostname out of - `tcp://host:port`. So a TCP `DOCKER_HOST` is what makes the sibling container addressable, and - the value must include the port or it raises rather than falling back. + **Reachability is a separate question from availability, and it bit us.** A containerised job + with a working daemon still fails `pytest tests/` with + `Service 'pytest_databases_postgres' failed to come online`: the database container starts + fine, but its published port lands on the daemon's network namespace while the test process + looks for it on the job container's loopback. + + Two variables control two different things, and both must be set: + + | Variable | Decides | Read by | + | --- | --- | --- | + | `DOCKER_HOST` | which daemon the container is created on | `_service.py` `get_docker_host()` | + | `POSTGRES_HOST` | the address the test then connects to | `docker/postgres.py` `postgres_host`, default `127.0.0.1` | + + Setting only `DOCKER_HOST` is not enough — `DockerService.run()` takes `container_host` as a + plain argument defaulting to `127.0.0.1`, and the postgres fixture fills it from + `POSTGRES_HOST`. (There *is* a `DOCKER_HOST`-parsing helper in `pytest_databases`, but it is + `_get_docker_ip()` on the docker-compose class in `docker/__init__.py` and no part of this + path uses it. Do not be misled by it, as I was.) Until the runner grows its own sidecar, `ci.yml`'s backend job carries a `docker:dind` service of its own with `DOCKER_HOST: tcp://docker:2375`. That needs the runner to permit