diff --git a/.taskid b/.taskid index 381ab2e..afa38ff 100644 --- a/.taskid +++ b/.taskid @@ -1 +1 @@ -DEVX-20 +DEVX-21 diff --git a/src/devx/molecule/start_docker.py b/src/devx/molecule/start_docker.py index 46bb48c..284cecd 100644 --- a/src/devx/molecule/start_docker.py +++ b/src/devx/molecule/start_docker.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 """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 -Docker daemon running. This module starts ``dockerd`` in the background -and waits for it to become ready, or exits immediately if Docker is -already available. +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. Usage:: @@ -38,14 +38,13 @@ def is_docker_ready() -> bool: def start_docker_daemon(timeout: int = DEFAULT_TIMEOUT) -> bool: """Start dockerd in the background and wait for it to be ready. - Returns ``True`` if Docker is ready (either already running or - successfully started), ``False`` if it failed to start within - the timeout. - """ - if is_docker_ready(): - click.echo(_("Docker daemon already running")) - return True + 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. + Returns ``True`` if Docker is ready, ``False`` if it failed to + start within the timeout. + """ click.echo(_("Starting Docker daemon...")) log_file = open(DOCKERD_LOG, "w") # noqa: SIM115 subprocess.Popen( # nosec B603 B607 diff --git a/src/devx/translations.json b/src/devx/translations.json index 9bd217b..d1e09d9 100644 --- a/src/devx/translations.json +++ b/src/devx/translations.json @@ -419,13 +419,6 @@ "ru": "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": { "bg": "Docker daemon failed to start", "de": "Docker-Daemon konnte nicht gestartet werden", diff --git a/tests/unit/test_start_docker.py b/tests/unit/test_start_docker.py index db9daf2..9e768bb 100644 --- a/tests/unit/test_start_docker.py +++ b/tests/unit/test_start_docker.py @@ -21,11 +21,6 @@ class TestIsDockerReady: 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.is_docker_ready") @patch("devx.molecule.start_docker.subprocess.Popen") @@ -37,11 +32,11 @@ class TestStartDockerDaemon: mock_ready: MagicMock, mock_sleep: MagicMock, ) -> 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] assert start_docker_daemon(timeout=5) is True 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.is_docker_ready", return_value=False) @@ -54,11 +49,26 @@ class TestStartDockerDaemon: mock_ready: MagicMock, mock_sleep: MagicMock, ) -> None: - # is_docker_ready always returns False: 1 initial + 3 loop iterations = 4 calls assert start_docker_daemon(timeout=3) is False mock_popen.assert_called_once() 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.is_docker_ready") @patch("devx.molecule.start_docker.subprocess.Popen") @@ -70,9 +80,8 @@ class TestStartDockerDaemon: mock_ready: MagicMock, mock_sleep: MagicMock, ) -> None: - # First call: initial check (not ready). Then 9 loop iterations (not ready), - # 10th iteration (ready). - mock_ready.side_effect = [False] * 10 + [True] + # 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