Skip to content

Readiness Probes ​

A probe holds a test back until its container is ready, so the test doesn't race the container's startup. If the container isn't ready in time, the test fails with what the probe waited for.

At a Glance ​

  • Probes are imported from kloudkit.testshed.docker.
  • Probes run inside the container: HttpProbe needs curl in the image, or another command.

Built-In Probes ​

python
from kloudkit.testshed.docker import HttpProbe, LogProbe, ShellProbe

# An HTTP endpoint answers.
HttpProbe(port=3000, endpoint="/health", timeout=30.0)

# A regex matches the container's output.
LogProbe(pattern=r"ready to accept connections", timeout=30.0)

# A command exits 0.
ShellProbe(command="pg_isready -U postgres", timeout=30.0)
  • HttpProbe: runs curl http://localhost:<port><endpoint> inside the container. Any HTTP status counts; use command="curl -f" to require a success status. Set command to use another client (command="wget -qO-"), or host for another address (with its scheme, http://localhost by default).
  • LogProbe: searches the container's logs for pattern.
  • ShellProbe: runs command (a string or a list) through the container's shell, like execute.

Each probe retries every 100 ms, for up to timeout seconds (30 by default).

Use a Probe ​

Pass a probe wherever a container starts:

python
from kloudkit.testshed.docker import HttpProbe, ShellProbe
from kloudkit.testshed.docker.decorators import shed_config

@shed_config(probe=HttpProbe(port=8080, endpoint="/healthz"))
def test_app(shed):
  ...


def test_db(docker_sidecar):
  docker_sidecar(
    "postgres:16",
    envs={"POSTGRES_PASSWORD": "x"},
    probe=ShellProbe(command="pg_isready -U postgres"),
  )

For every shed container, set probe in shed_container_defaults (see Containers).

Override the Default Probe ​

A probe passed to a test combines with the default one:

  • Same type: the fields you set replace the default's. Fields you leave out keep the default's value when they default to nothing (port, endpoint); timeout, host and command come from the new probe either way. HttpProbe(endpoint="/ready") keeps the default's port, with a 30-second timeout.
  • Another type: it replaces the default.
  • probe=None: no probe for this container.

To wait longer without restating the probe, pass only timeout:

python
@shed_config(timeout=90.0)
def test_slow_boot(shed):
  ...

Write Your Own ​

Subclass Probe with a check that raises until the container is ready, and a failure_message for the timeout:

python
from dataclasses import dataclass

from kloudkit.testshed.docker import Probe

@dataclass(slots=True)
class FileProbe(Probe):
  path: str = "/tmp/ready"

  def check(self, container):
    if not container.fs.exists(self.path):
      raise FileNotFoundError(self.path)

  @property
  def failure_message(self):
    return f"{self.path} did not appear within {self.timeout}s"

Run commands in check with raises=True: without it, the first failed attempt fails the test instead of retrying.

When a Probe Times Out ​

The test fails with what the probe waited for, such as URL [http://localhost:3000/health] was not reachable within 30.0s. Run with --shed-container-logs to see the container's logs as well.

Next Steps ​

Released under the MIT License