From 8ff93f4709dee0bdb45a214145c87a967d277cf0 Mon Sep 17 00:00:00 2001 From: Tobias Kaupat Date: Mon, 19 Oct 2026 16:13:44 +0200 Subject: [PATCH] test: remove anonymous volumes with short-lived test containers Co-Authored-By: Claude Sonnet 5.5 --- tests/conftest.py | 11 +++++++---- tests/helpers.py | 18 ++++++++++++++++++ tests/test_compat.py | 8 ++++---- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 8fa7caa..fbc686f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -12,7 +12,7 @@ from pathlib import Path import docker import pytest -from helpers import Container, remove_quietly, start_container +from helpers import Container, remove_quietly, run_once, start_container ROOT = Path(__file__).resolve().parent.parent IMAGE = os.environ.get("IMAGE", "restic-backup-docker:test") @@ -108,10 +108,13 @@ def _cleanup_leftovers(docker_client: docker.DockerClient) -> Iterator[None]: @pytest.fixture(scope="session") def detected_features(docker_client: docker.DockerClient, image: str) -> set[str]: - out = docker_client.containers.run( - image, entrypoint=["sh", "-c", FEATURE_PROBE], remove=True, labels={"rbd-test-run": RUN_ID} + out = run_once( + docker_client, + image, + entrypoint=["sh", "-c", FEATURE_PROBE], + labels={"rbd-test-run": RUN_ID}, ) - return set(out.decode().split()) + return set(out.split()) def pytest_collection_modifyitems(items: list[pytest.Item]) -> None: diff --git a/tests/helpers.py b/tests/helpers.py index 1832d1a..78f6299 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -135,3 +135,21 @@ def start_container( def remove_quietly(container: Container) -> None: with contextlib.suppress(docker.errors.NotFound): container.raw.remove(force=True, v=True) + + +def run_once(client: docker.DockerClient, image: str, **kwargs: Any) -> str: + """Run a short-lived container and return its output. + + `containers.run(remove=True)` keeps the image's anonymous volumes (VOLUME /data), + so run detached and remove the container together with its volumes. + """ + container = client.containers.run(image, detach=True, **kwargs) + try: + result = container.wait() + output = container.logs(stdout=True, stderr=False).decode() + if result["StatusCode"] != 0: + raise CommandError(f"container exited {result['StatusCode']}:\n{output}") + return output + finally: + with contextlib.suppress(docker.errors.NotFound): + container.remove(force=True, v=True) diff --git a/tests/test_compat.py b/tests/test_compat.py index ed49c0d..ae1e403 100644 --- a/tests/test_compat.py +++ b/tests/test_compat.py @@ -19,7 +19,7 @@ from types import SimpleNamespace import docker import pytest from conftest import LABELS -from helpers import Container +from helpers import Container, run_once HELLO = "backup me" PRE_BACKUP_HOOK = '#!/bin/sh\necho "PRE-HOOK-RAN"\n' @@ -409,10 +409,10 @@ class TimezoneRunner: image: str def zone(self, env: dict[str, str] | None = None) -> str: - out = self.client.containers.run( - self.image, entrypoint=["date", "+%Z"], environment=env, remove=True, labels=LABELS + out = run_once( + self.client, self.image, entrypoint=["date", "+%Z"], environment=env, labels=LABELS ) - return out.decode().strip() + return out.strip() @pytest.fixture(scope="module")