Public Access
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2f66ca70a | ||
|
|
0b88c211f1 |
@@ -2,6 +2,12 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [0.9.4] - 2026-06-24
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Use separate Docker socket for DinD in CI
|
||||
|
||||
## [0.9.3] - 2026-06-24
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
"""devx — reusable development and CI/CD tools for oblachno-oss projects."""
|
||||
|
||||
__version__ = "0.9.3"
|
||||
__version__ = "0.9.4"
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
|
||||
CI runners (e.g. ``gitea/runner-images:ubuntu-latest``) may have the host's
|
||||
Docker socket mounted, but molecule needs a local Docker daemon to create
|
||||
nested containers. This module always starts ``dockerd`` in the background
|
||||
and waits for it to become ready.
|
||||
nested containers. This module starts ``dockerd`` on a separate socket
|
||||
and sets ``DOCKER_HOST`` so both the CLI and Python library connect to
|
||||
the local daemon.
|
||||
|
||||
Usage::
|
||||
|
||||
@@ -13,6 +14,7 @@ Usage::
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess # nosec B404
|
||||
import sys
|
||||
import tempfile
|
||||
@@ -23,34 +25,46 @@ import click
|
||||
from devx.i18n import _
|
||||
|
||||
DEFAULT_TIMEOUT = 30
|
||||
DOCKER_SOCK = "/tmp/dockerd.sock" # nosec B108
|
||||
|
||||
|
||||
def is_docker_ready() -> bool:
|
||||
"""Check if the Docker daemon is responding."""
|
||||
"""Check if the local Docker daemon is responding."""
|
||||
result = subprocess.run( # nosec B603 B607
|
||||
["docker", "info"],
|
||||
capture_output=True,
|
||||
check=False,
|
||||
env={**os.environ, "DOCKER_HOST": f"unix://{DOCKER_SOCK}"},
|
||||
)
|
||||
return result.returncode == 0
|
||||
|
||||
|
||||
def start_docker_daemon(timeout: int = DEFAULT_TIMEOUT) -> bool:
|
||||
"""Start dockerd in the background and wait for it to be ready.
|
||||
"""Start dockerd on a separate socket and wait for it to be ready.
|
||||
|
||||
Always starts a local dockerd even if ``docker info`` succeeds,
|
||||
because the host socket may be mounted but not suitable for
|
||||
molecule's nested container creation.
|
||||
Uses ``/tmp/dockerd.sock`` instead of the default ``/var/run/docker.sock``
|
||||
to avoid conflicts with host-mounted sockets. Sets ``DOCKER_HOST`` in the
|
||||
current environment so molecule's Python docker library connects to the
|
||||
local daemon.
|
||||
|
||||
Returns ``True`` if Docker is ready, ``False`` if it failed to
|
||||
start within the timeout.
|
||||
"""
|
||||
# Point Docker CLI and Python library to our local socket
|
||||
os.environ["DOCKER_HOST"] = f"unix://{DOCKER_SOCK}"
|
||||
|
||||
click.echo(_("Starting Docker daemon..."))
|
||||
log_file = tempfile.NamedTemporaryFile( # noqa: SIM115
|
||||
mode="w", suffix="dockerd.log", delete=False
|
||||
)
|
||||
subprocess.Popen( # nosec B603 B607
|
||||
["dockerd", "--storage-driver", "vfs"],
|
||||
[
|
||||
"dockerd",
|
||||
"--storage-driver",
|
||||
"vfs",
|
||||
"-H",
|
||||
f"unix://{DOCKER_SOCK}",
|
||||
],
|
||||
stdout=log_file,
|
||||
stderr=subprocess.STDOUT,
|
||||
start_new_session=True,
|
||||
|
||||
@@ -4,7 +4,7 @@ from unittest.mock import MagicMock, patch
|
||||
|
||||
from click.testing import CliRunner
|
||||
|
||||
from devx.molecule.start_docker import is_docker_ready, main, start_docker_daemon
|
||||
from devx.molecule.start_docker import DOCKER_SOCK, is_docker_ready, main, start_docker_daemon
|
||||
|
||||
|
||||
class TestIsDockerReady:
|
||||
@@ -12,7 +12,9 @@ class TestIsDockerReady:
|
||||
def test_ready(self, mock_run: MagicMock) -> None:
|
||||
mock_run.return_value = MagicMock(returncode=0)
|
||||
assert is_docker_ready() is True
|
||||
mock_run.assert_called_once_with(["docker", "info"], capture_output=True, check=False)
|
||||
call_kwargs = mock_run.call_args
|
||||
assert call_kwargs.args[0] == ["docker", "info"]
|
||||
assert call_kwargs.kwargs["env"]["DOCKER_HOST"] == f"unix://{DOCKER_SOCK}"
|
||||
|
||||
@patch("devx.molecule.start_docker.subprocess.run")
|
||||
def test_not_ready(self, mock_run: MagicMock) -> None:
|
||||
@@ -33,10 +35,15 @@ class TestStartDockerDaemon:
|
||||
mock_sleep: MagicMock,
|
||||
) -> None:
|
||||
mock_ntf.return_value = MagicMock()
|
||||
# First loop iteration: dockerd not ready yet. Second: ready.
|
||||
mock_ready.side_effect = [False, True]
|
||||
assert start_docker_daemon(timeout=5) is True
|
||||
mock_popen.assert_called_once()
|
||||
popen_args = mock_popen.call_args.args[0]
|
||||
assert "dockerd" in popen_args
|
||||
assert "--storage-driver" in popen_args
|
||||
assert "vfs" in popen_args
|
||||
assert "-H" in popen_args
|
||||
assert f"unix://{DOCKER_SOCK}" in popen_args
|
||||
mock_sleep.assert_called_once_with(1)
|
||||
|
||||
@patch("devx.molecule.start_docker.time.sleep")
|
||||
@@ -84,11 +91,29 @@ class TestStartDockerDaemon:
|
||||
mock_sleep: MagicMock,
|
||||
) -> None:
|
||||
mock_ntf.return_value = MagicMock()
|
||||
# 9 iterations not ready, 10th ready.
|
||||
mock_ready.side_effect = [False] * 9 + [True]
|
||||
assert start_docker_daemon(timeout=10) is True
|
||||
assert mock_sleep.call_count == 9
|
||||
|
||||
@patch("devx.molecule.start_docker.os.environ")
|
||||
@patch("devx.molecule.start_docker.time.sleep")
|
||||
@patch("devx.molecule.start_docker.is_docker_ready")
|
||||
@patch("devx.molecule.start_docker.subprocess.Popen")
|
||||
@patch("devx.molecule.start_docker.tempfile.NamedTemporaryFile")
|
||||
def test_sets_docker_host(
|
||||
self,
|
||||
mock_ntf: MagicMock,
|
||||
mock_popen: MagicMock,
|
||||
mock_ready: MagicMock,
|
||||
mock_sleep: MagicMock,
|
||||
mock_environ: MagicMock,
|
||||
) -> None:
|
||||
"""DOCKER_HOST must be set so molecule connects to local daemon."""
|
||||
mock_ntf.return_value = MagicMock()
|
||||
mock_ready.return_value = True
|
||||
start_docker_daemon(timeout=5)
|
||||
mock_environ.__setitem__.assert_called_with("DOCKER_HOST", f"unix://{DOCKER_SOCK}")
|
||||
|
||||
|
||||
class TestMain:
|
||||
@patch("devx.molecule.start_docker.start_docker_daemon", return_value=True)
|
||||
|
||||
Reference in New Issue
Block a user