From 53b8e3d68cbfeea5273333726d00c3102418b793 Mon Sep 17 00:00:00 2001 From: David Ankin Date: Sat, 24 Jan 2026 11:49:05 -0500 Subject: [PATCH 1/4] From 3aecc6abfdf16302238822163ad57bc4ab5b56b7 Mon Sep 17 00:00:00 2001 From: David Ankin Date: Sat, 24 Jan 2026 11:55:10 -0500 Subject: [PATCH 2/4] fix logic --- core/tests/test_docker_in_docker.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core/tests/test_docker_in_docker.py b/core/tests/test_docker_in_docker.py index 5bd64e67..964a3174 100644 --- a/core/tests/test_docker_in_docker.py +++ b/core/tests/test_docker_in_docker.py @@ -23,9 +23,9 @@ _DIND_PYTHON_VERSION = (3, 13) -def _should_run_dind() -> bool: +def _should_skip_dind() -> bool: # todo refine macos check -> run in ci but not locally - return not is_mac() and tuple([*sys.version_info][:2]) == _DIND_PYTHON_VERSION + return is_mac() or tuple([*sys.version_info][:2]) != _DIND_PYTHON_VERSION def _wait_for_dind_return_ip(client: DockerClient, dind: Container): @@ -46,7 +46,7 @@ def _wait_for_dind_return_ip(client: DockerClient, dind: Container): return docker_host_ip -@pytest.mark.skipif(_should_run_dind(), reason="Docker socket forwarding (socat) is unsupported on Docker Desktop for macOS") +@pytest.mark.skipif(_should_skip_dind(), reason="Docker socket forwarding (socat) is unsupported on Docker Desktop for macOS") def test_wait_for_logs_docker_in_docker(): # real dind isn't possible (AFAIK) in CI # forwarding the socket to a container port is at least somewhat the same @@ -76,7 +76,7 @@ def test_wait_for_logs_docker_in_docker(): @pytest.mark.skipif( - _should_run_dind(), reason="Bridge networking and Docker socket forwarding are not supported on Docker Desktop for macOS" + _should_skip_dind(), reason="Bridge networking and Docker socket forwarding are not supported on Docker Desktop for macOS" ) def test_dind_inherits_network(): client = DockerClient() @@ -160,7 +160,7 @@ def get_docker_info() -> dict[str, Any]: # see https://forums.docker.com/t/get-a-containers-full-id-from-inside-of-itself @pytest.mark.xfail(reason="Does not work in rootles docker i.e. github actions") @pytest.mark.inside_docker_check -@pytest.mark.skipif(_should_run_dind() or not os.environ.get(EXPECTED_NETWORK_VAR), reason="No expected network given") +@pytest.mark.skipif(_should_skip_dind() or not os.environ.get(EXPECTED_NETWORK_VAR), reason="No expected network given") def test_find_host_network_in_dood() -> None: """ Check that the correct host network is found for DooD @@ -173,7 +173,7 @@ def test_find_host_network_in_dood() -> None: @pytest.mark.skipif( - _should_run_dind(), reason="Docker socket mounting and container networking do not work reliably on Docker Desktop for macOS" + _should_skip_dind(), reason="Docker socket mounting and container networking do not work reliably on Docker Desktop for macOS" ) @pytest.mark.skipif(not Path(tcc.ryuk_docker_socket).exists(), reason="No docker socket available") def test_dood(python_testcontainer_image: str) -> None: @@ -211,7 +211,7 @@ def test_dood(python_testcontainer_image: str) -> None: @pytest.mark.skipif( - _should_run_dind(), reason="Docker socket mounting and container networking do not work reliably on Docker Desktop for macOS" + _should_skip_dind(), reason="Docker socket mounting and container networking do not work reliably on Docker Desktop for macOS" ) def test_dind(python_testcontainer_image: str, tmp_path: Path) -> None: """ From 9a9e50a9d309d714b4fac2eac1bb4edc5a90a551 Mon Sep 17 00:00:00 2001 From: David Ankin Date: Sat, 24 Jan 2026 12:30:16 -0500 Subject: [PATCH 3/4] test ci --- .github/workflows/ci-core.yml | 76 +++++++++++++++-------------- core/tests/test_docker_in_docker.py | 75 +++++++++++++++++----------- 2 files changed, 86 insertions(+), 65 deletions(-) diff --git a/.github/workflows/ci-core.yml b/.github/workflows/ci-core.yml index 162191f2..5a5a842f 100644 --- a/.github/workflows/ci-core.yml +++ b/.github/workflows/ci-core.yml @@ -22,40 +22,42 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Install Python dependencies - run: poetry install --all-extras - - name: Run twine check - run: rm -f LICENSE.txt && poetry build && poetry run twine check dist/*.tar.gz - - name: Run tests - run: make core/tests - - name: Rename coverage file - run: mv .coverage .coverage.${{ matrix.python-version}} - - name: "Save coverage artifact" - uses: actions/upload-artifact@v4 - with: - name: "coverage-artifact-${{ matrix.python-version}}" - include-hidden-files: true - path: ".coverage.*" - retention-days: 1 - - name: Run doctests - run: make core/doctests - - coverage-compile: - needs: "run-tests-and-coverage" - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - - name: Set up Python - uses: ./.github/actions/setup-env - - name: Install Python dependencies - run: poetry install --all-extras - - name: "Download coverage artifacts" - uses: actions/download-artifact@v4 - with: - pattern: "coverage-artifact-*" - merge-multiple: true - - name: Compile coverage - run: make coverage - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4 - with: - token: ${{ secrets.CODECOV_TOKEN }} + run: poetry install --all-groups + - name: debug dind + run: DIND=1 poetry run pytest core/tests/test_docker_in_docker.py +# - name: Run twine check +# run: rm -f LICENSE.txt && poetry build && poetry run twine check dist/*.tar.gz +# - name: Run tests +# run: make core/tests +# - name: Rename coverage file +# run: mv .coverage .coverage.${{ matrix.python-version}} +# - name: "Save coverage artifact" +# uses: actions/upload-artifact@v4 +# with: +# name: "coverage-artifact-${{ matrix.python-version}}" +# include-hidden-files: true +# path: ".coverage.*" +# retention-days: 1 +# - name: Run doctests +# run: make core/doctests +# +# coverage-compile: +# needs: "run-tests-and-coverage" +# runs-on: ubuntu-22.04 +# steps: +# - uses: actions/checkout@v4 +# - name: Set up Python +# uses: ./.github/actions/setup-env +# - name: Install Python dependencies +# run: poetry install --all-extras +# - name: "Download coverage artifacts" +# uses: actions/download-artifact@v4 +# with: +# pattern: "coverage-artifact-*" +# merge-multiple: true +# - name: Compile coverage +# run: make coverage +# - name: Upload coverage to Codecov +# uses: codecov/codecov-action@v4 +# with: +# token: ${{ secrets.CODECOV_TOKEN }} diff --git a/core/tests/test_docker_in_docker.py b/core/tests/test_docker_in_docker.py index 964a3174..00d8dd92 100644 --- a/core/tests/test_docker_in_docker.py +++ b/core/tests/test_docker_in_docker.py @@ -1,9 +1,11 @@ import contextlib import json +import logging import os import time import socket import sys +from textwrap import indent from pathlib import Path from typing import Final, Any, Generator @@ -21,9 +23,12 @@ from testcontainers.core.waiting_utils import wait_for_logs _DIND_PYTHON_VERSION = (3, 13) +logger = logging.getLogger(__name__) def _should_skip_dind() -> bool: + if os.getenv("DIND"): + return False # todo refine macos check -> run in ci but not locally return is_mac() or tuple([*sys.version_info][:2]) != _DIND_PYTHON_VERSION @@ -50,6 +55,7 @@ def _wait_for_dind_return_ip(client: DockerClient, dind: Container): def test_wait_for_logs_docker_in_docker(): # real dind isn't possible (AFAIK) in CI # forwarding the socket to a container port is at least somewhat the same + logger.info("starting test_wait_for_logs_docker_in_docker") client = DockerClient() not_really_dind = client.run( image="alpine/socat", @@ -58,21 +64,26 @@ def test_wait_for_logs_docker_in_docker(): detach=True, ) + logger.info("starting not_really_dind") not_really_dind.start() + logger.info("started not_really_dind") docker_host_ip = _wait_for_dind_return_ip(client, not_really_dind) + logger.info("waited for not_really_dind: '_wait_for_dind_return_ip'") docker_host = f"tcp://{docker_host_ip}:2375" - with DockerContainer( - image="hello-world", - docker_client_kw={"environment": {"DOCKER_HOST": docker_host, "DOCKER_CERT_PATH": "", "DOCKER_TLS_VERIFY": ""}}, - ) as container: - assert container.get_container_host_ip() == docker_host_ip - wait_for_logs(container, "Hello from Docker!") - stdout, stderr = container.get_logs() - assert stdout, "There should be something on stdout" - - not_really_dind.stop() - not_really_dind.remove() + try: + with DockerContainer( + image="hello-world", + docker_client_kw={"environment": {"DOCKER_HOST": docker_host, "DOCKER_CERT_PATH": "", "DOCKER_TLS_VERIFY": ""}}, + ) as container: + logger.info("started hello-world container") + assert container.get_container_host_ip() == docker_host_ip + wait_for_logs(container, "Hello from Docker!") + stdout, stderr = container.get_logs() + assert stdout, "There should be something on stdout" + finally: + not_really_dind.stop() + not_really_dind.remove() @pytest.mark.skipif( @@ -122,19 +133,19 @@ def print_surround_header(what: str, header_len: int = 80) -> Generator[None, No start = f"# Beginning of {what}" end = f"# End of {what}" - print("\n") - print("#" * header_len) - print(start + " " * (header_len - len(start) - 1) + "#") - print("#" * header_len) - print("\n") + logger.info("\n") + logger.info("#" * header_len) + logger.info(start + " " * (header_len - len(start) - 1) + "#") + logger.info("#" * header_len) + logger.info("\n") yield - print("\n") - print("#" * header_len) - print(end + " " * (header_len - len(end) - 1) + "#") - print("#" * header_len) - print("\n") + logger.info("\n") + logger.info("#" * header_len) + logger.info(end + " " * (header_len - len(end) - 1) + "#") + logger.info("#" * header_len) + logger.info("\n") EXPECTED_NETWORK_VAR: Final[str] = "TCC_EXPECTED_NETWORK" @@ -165,10 +176,10 @@ def test_find_host_network_in_dood() -> None: """ Check that the correct host network is found for DooD """ - LOGGER.info(f"Running container id={utils.get_running_in_container_id()}") + logger.info(f"Running container id={utils.get_running_in_container_id()}") # Get some debug information in the hope this helps to find - LOGGER.info(f"hostname: {socket.gethostname()}") - LOGGER.info(f"docker info: {json.dumps(get_docker_info(), indent=2)}") + logger.info(f"hostname: {socket.gethostname()}") + logger.info(f"docker info: {json.dumps(get_docker_info(), indent=2)}") assert DockerClient().find_host_network() == os.environ[EXPECTED_NETWORK_VAR] @@ -183,6 +194,7 @@ def test_dood(python_testcontainer_image: str) -> None: docker_sock = tcc.ryuk_docker_socket with Network() as network: + logger.info("test_dood - created network") with ( DockerContainer( image=python_testcontainer_image, @@ -196,7 +208,9 @@ def test_dood(python_testcontainer_image: str) -> None: .with_env("RYUK_RECONNECTION_TIMEOUT", "1s") .with_network(network) ) as container: + logger.info("test_dood - created container") status = container.get_wrapped_container().wait() + logger.info("test_dood - container returned status %s", status) stdout, stderr = container.get_logs() # ensure ryuk removed the containers created inside container # because they are bound our network the deletion of the network @@ -205,8 +219,8 @@ def test_dood(python_testcontainer_image: str) -> None: # Show what was done inside test with print_surround_header("test_dood results"): - print(stdout.decode("utf-8", errors="replace")) - print(stderr.decode("utf-8", errors="replace")) + logger.info(indent(stdout.decode("utf-8", errors="replace"), prefix=(" " * 4) + "container log: ")) + logger.info(indent(stderr.decode("utf-8", errors="replace"), prefix=(" " * 4) + "container log: ")) assert status["StatusCode"] == 0 @@ -220,6 +234,7 @@ def test_dind(python_testcontainer_image: str, tmp_path: Path) -> None: cert_dir = tmp_path / "certs" dind_name = f"docker_{SESSION_ID}" with Network() as network: + logger.info("test_dind - created network") with ( DockerContainer(image="docker:dind", privileged=True) .with_name(dind_name) @@ -229,7 +244,9 @@ def test_dind(python_testcontainer_image: str, tmp_path: Path) -> None: .with_network(network) .with_network_aliases("docker") ) as dind_container: + logger.info("test_dind - created docker:dind container") wait_for_logs(dind_container, "API listen on") + logger.info("test_dind - waited for ready message in logs") client_dir = cert_dir / "docker" / "client" ca_file = client_dir / "ca.pem" assert ca_file.is_file() @@ -247,7 +264,9 @@ def test_dind(python_testcontainer_image: str, tmp_path: Path) -> None: .with_env("DOCKER_HOST", "tcp://docker:2376") .with_network(network) ) as test_container: + logger.info("test_dind - created test suite container") status = test_container.get_wrapped_container().wait() + logger.info("test_dind - test suite container returned status %s", status) stdout, stderr = test_container.get_logs() finally: # ensure the certs are deleted from inside the container @@ -258,6 +277,6 @@ def test_dind(python_testcontainer_image: str, tmp_path: Path) -> None: # Show what was done inside test with print_surround_header("test_dood results"): - print(stdout.decode("utf-8", errors="replace")) - print(stderr.decode("utf-8", errors="replace")) + logger.info(indent(stdout.decode("utf-8", errors="replace"), prefix=(" " * 4) + "container log: ")) + logger.info(indent(stderr.decode("utf-8", errors="replace"), prefix=(" " * 4) + "container log: ")) assert status["StatusCode"] == 0 From 45d17d4089ac730398e4550d732c0fbca1c3bf26 Mon Sep 17 00:00:00 2001 From: David Ankin Date: Sat, 24 Jan 2026 12:39:16 -0500 Subject: [PATCH 4/4] learn that dind bug is actually python syntax bug --- .github/workflows/ci-core.yml | 76 ++++++++++++++--------------- core/tests/test_docker_in_docker.py | 2 +- 2 files changed, 38 insertions(+), 40 deletions(-) diff --git a/.github/workflows/ci-core.yml b/.github/workflows/ci-core.yml index 5a5a842f..162191f2 100644 --- a/.github/workflows/ci-core.yml +++ b/.github/workflows/ci-core.yml @@ -22,42 +22,40 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Install Python dependencies - run: poetry install --all-groups - - name: debug dind - run: DIND=1 poetry run pytest core/tests/test_docker_in_docker.py -# - name: Run twine check -# run: rm -f LICENSE.txt && poetry build && poetry run twine check dist/*.tar.gz -# - name: Run tests -# run: make core/tests -# - name: Rename coverage file -# run: mv .coverage .coverage.${{ matrix.python-version}} -# - name: "Save coverage artifact" -# uses: actions/upload-artifact@v4 -# with: -# name: "coverage-artifact-${{ matrix.python-version}}" -# include-hidden-files: true -# path: ".coverage.*" -# retention-days: 1 -# - name: Run doctests -# run: make core/doctests -# -# coverage-compile: -# needs: "run-tests-and-coverage" -# runs-on: ubuntu-22.04 -# steps: -# - uses: actions/checkout@v4 -# - name: Set up Python -# uses: ./.github/actions/setup-env -# - name: Install Python dependencies -# run: poetry install --all-extras -# - name: "Download coverage artifacts" -# uses: actions/download-artifact@v4 -# with: -# pattern: "coverage-artifact-*" -# merge-multiple: true -# - name: Compile coverage -# run: make coverage -# - name: Upload coverage to Codecov -# uses: codecov/codecov-action@v4 -# with: -# token: ${{ secrets.CODECOV_TOKEN }} + run: poetry install --all-extras + - name: Run twine check + run: rm -f LICENSE.txt && poetry build && poetry run twine check dist/*.tar.gz + - name: Run tests + run: make core/tests + - name: Rename coverage file + run: mv .coverage .coverage.${{ matrix.python-version}} + - name: "Save coverage artifact" + uses: actions/upload-artifact@v4 + with: + name: "coverage-artifact-${{ matrix.python-version}}" + include-hidden-files: true + path: ".coverage.*" + retention-days: 1 + - name: Run doctests + run: make core/doctests + + coverage-compile: + needs: "run-tests-and-coverage" + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: ./.github/actions/setup-env + - name: Install Python dependencies + run: poetry install --all-extras + - name: "Download coverage artifacts" + uses: actions/download-artifact@v4 + with: + pattern: "coverage-artifact-*" + merge-multiple: true + - name: Compile coverage + run: make coverage + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/core/tests/test_docker_in_docker.py b/core/tests/test_docker_in_docker.py index 00d8dd92..5cf0e770 100644 --- a/core/tests/test_docker_in_docker.py +++ b/core/tests/test_docker_in_docker.py @@ -22,7 +22,7 @@ from testcontainers.core.utils import is_mac from testcontainers.core.waiting_utils import wait_for_logs -_DIND_PYTHON_VERSION = (3, 13) +_DIND_PYTHON_VERSION = (3, 10) logger = logging.getLogger(__name__)