DEVX-21: fix: always start dockerd in CI runner for molecule tests
Post-merge / detect-type (push) Successful in 6s
Post-merge / validate-commit-msg (push) Successful in 14s
Post-merge / configure-repo (push) Successful in 14s
Post-merge / release (push) Successful in 40s
Post-merge / vikunja (push) Successful in 15s
Post-merge / sync-wiki (push) Successful in 51s
Post-merge / badges (push) Successful in 59s

This commit was merged in pull request #35.
This commit is contained in:
2026-06-23 23:49:13 +00:00
parent f206a9cd8d
commit e76741bfad
4 changed files with 31 additions and 30 deletions
+1 -1
View File
@@ -1 +1 @@
DEVX-20 DEVX-21
+10 -11
View File
@@ -1,10 +1,10 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Start a Docker daemon inside a CI runner container (Docker-in-Docker). """Start a Docker daemon inside a CI runner container (Docker-in-Docker).
CI runners (e.g. ``gitea/runner-images:ubuntu-latest``) may not have a CI runners (e.g. ``gitea/runner-images:ubuntu-latest``) may have the host's
Docker daemon running. This module starts ``dockerd`` in the background Docker socket mounted, but molecule needs a local Docker daemon to create
and waits for it to become ready, or exits immediately if Docker is nested containers. This module always starts ``dockerd`` in the background
already available. and waits for it to become ready.
Usage:: Usage::
@@ -38,14 +38,13 @@ def is_docker_ready() -> bool:
def start_docker_daemon(timeout: int = DEFAULT_TIMEOUT) -> bool: def start_docker_daemon(timeout: int = DEFAULT_TIMEOUT) -> bool:
"""Start dockerd in the background and wait for it to be ready. """Start dockerd in the background and wait for it to be ready.
Returns ``True`` if Docker is ready (either already running or Always starts a local dockerd even if ``docker info`` succeeds,
successfully started), ``False`` if it failed to start within because the host socket may be mounted but not suitable for
the timeout. molecule's nested container creation.
"""
if is_docker_ready():
click.echo(_("Docker daemon already running"))
return True
Returns ``True`` if Docker is ready, ``False`` if it failed to
start within the timeout.
"""
click.echo(_("Starting Docker daemon...")) click.echo(_("Starting Docker daemon..."))
log_file = open(DOCKERD_LOG, "w") # noqa: SIM115 log_file = open(DOCKERD_LOG, "w") # noqa: SIM115
subprocess.Popen( # nosec B603 B607 subprocess.Popen( # nosec B603 B607
-7
View File
@@ -419,13 +419,6 @@
"ru": "Created release commit.", "ru": "Created release commit.",
"zh": "Created release commit." "zh": "Created release commit."
}, },
"Docker daemon already running": {
"bg": "Docker daemon already running",
"de": "Docker-Daemon läuft bereits",
"en": "Docker daemon already running",
"ru": "Docker-демон уже запущен",
"zh": "Docker 守护进程已在运行"
},
"Docker daemon failed to start": { "Docker daemon failed to start": {
"bg": "Docker daemon failed to start", "bg": "Docker daemon failed to start",
"de": "Docker-Daemon konnte nicht gestartet werden", "de": "Docker-Daemon konnte nicht gestartet werden",
+20 -11
View File
@@ -21,11 +21,6 @@ class TestIsDockerReady:
class TestStartDockerDaemon: class TestStartDockerDaemon:
@patch("devx.molecule.start_docker.is_docker_ready", return_value=True)
def test_already_running(self, mock_ready: MagicMock) -> None:
assert start_docker_daemon() is True
mock_ready.assert_called_once()
@patch("devx.molecule.start_docker.time.sleep") @patch("devx.molecule.start_docker.time.sleep")
@patch("devx.molecule.start_docker.is_docker_ready") @patch("devx.molecule.start_docker.is_docker_ready")
@patch("devx.molecule.start_docker.subprocess.Popen") @patch("devx.molecule.start_docker.subprocess.Popen")
@@ -37,11 +32,11 @@ class TestStartDockerDaemon:
mock_ready: MagicMock, mock_ready: MagicMock,
mock_sleep: MagicMock, mock_sleep: MagicMock,
) -> None: ) -> None:
# First call: initial check (not ready). Second: first loop iteration (ready). # First loop iteration: dockerd not ready yet. Second: ready.
mock_ready.side_effect = [False, True] mock_ready.side_effect = [False, True]
assert start_docker_daemon(timeout=5) is True assert start_docker_daemon(timeout=5) is True
mock_popen.assert_called_once() mock_popen.assert_called_once()
mock_sleep.assert_not_called() mock_sleep.assert_called_once_with(1)
@patch("devx.molecule.start_docker.time.sleep") @patch("devx.molecule.start_docker.time.sleep")
@patch("devx.molecule.start_docker.is_docker_ready", return_value=False) @patch("devx.molecule.start_docker.is_docker_ready", return_value=False)
@@ -54,11 +49,26 @@ class TestStartDockerDaemon:
mock_ready: MagicMock, mock_ready: MagicMock,
mock_sleep: MagicMock, mock_sleep: MagicMock,
) -> None: ) -> None:
# is_docker_ready always returns False: 1 initial + 3 loop iterations = 4 calls
assert start_docker_daemon(timeout=3) is False assert start_docker_daemon(timeout=3) is False
mock_popen.assert_called_once() mock_popen.assert_called_once()
assert mock_sleep.call_count == 3 assert mock_sleep.call_count == 3
@patch("devx.molecule.start_docker.time.sleep")
@patch("devx.molecule.start_docker.is_docker_ready")
@patch("devx.molecule.start_docker.subprocess.Popen")
@patch("builtins.open", new_callable=mock_open)
def test_ready_on_first_check(
self,
mock_file: MagicMock,
mock_popen: MagicMock,
mock_ready: MagicMock,
mock_sleep: MagicMock,
) -> None:
mock_ready.return_value = True
assert start_docker_daemon(timeout=5) is True
mock_popen.assert_called_once()
mock_sleep.assert_not_called()
@patch("devx.molecule.start_docker.time.sleep") @patch("devx.molecule.start_docker.time.sleep")
@patch("devx.molecule.start_docker.is_docker_ready") @patch("devx.molecule.start_docker.is_docker_ready")
@patch("devx.molecule.start_docker.subprocess.Popen") @patch("devx.molecule.start_docker.subprocess.Popen")
@@ -70,9 +80,8 @@ class TestStartDockerDaemon:
mock_ready: MagicMock, mock_ready: MagicMock,
mock_sleep: MagicMock, mock_sleep: MagicMock,
) -> None: ) -> None:
# First call: initial check (not ready). Then 9 loop iterations (not ready), # 9 iterations not ready, 10th ready.
# 10th iteration (ready). mock_ready.side_effect = [False] * 9 + [True]
mock_ready.side_effect = [False] * 10 + [True]
assert start_docker_daemon(timeout=10) is True assert start_docker_daemon(timeout=10) is True
assert mock_sleep.call_count == 9 assert mock_sleep.call_count == 9