diff --git a/src/devx/molecule/start_docker.py b/src/devx/molecule/start_docker.py index 2ceb1e9..9913177 100644 --- a/src/devx/molecule/start_docker.py +++ b/src/devx/molecule/start_docker.py @@ -223,6 +223,16 @@ def start_docker_daemon(timeout: int = DEFAULT_TIMEOUT) -> bool: click.echo(_("Host Docker not available, starting local dockerd...")) + # Kill any existing dockerd that's using the default data root on the + # CI container's overlay (38G, often 100% full). The new dockerd will + # use /dev/shm/docker (16G tmpfs) which has plenty of space. + subprocess.run( # nosec B603 B607 + ["pkill", "-f", "dockerd"], + capture_output=True, + check=False, + ) + time.sleep(2) + # Reset DOCKER_HOST to host socket for local dockerd os.environ["DOCKER_HOST"] = f"unix://{DOCKER_SOCK}" diff --git a/tests/unit/test_start_docker.py b/tests/unit/test_start_docker.py index 35a7eb2..72be872 100644 --- a/tests/unit/test_start_docker.py +++ b/tests/unit/test_start_docker.py @@ -146,8 +146,12 @@ class TestStartDockerDaemon: with ( patch("devx.molecule.start_docker.glob.glob", return_value=[]), patch("devx.molecule.start_docker.os.path.exists", return_value=False), + patch("devx.molecule.start_docker.subprocess.run"), + patch("devx.molecule.start_docker.subprocess.Popen"), + patch("devx.molecule.start_docker.tempfile.NamedTemporaryFile"), + patch("devx.molecule.start_docker.time.sleep"), ): - # Host ready but low space, no rootless sockets, falls back to host + # Host ready but low space, no rootless sockets, starts local dockerd assert start_docker_daemon(timeout=5) is True @patch("devx.molecule.start_docker._diagnose_socket") @@ -202,12 +206,14 @@ class TestStartDockerDaemon: @patch("devx.molecule.start_docker.os.path.exists", return_value=False) @patch("devx.molecule.start_docker.is_docker_ready", return_value=False) @patch("devx.molecule.start_docker.time.sleep") + @patch("devx.molecule.start_docker.subprocess.run") @patch("devx.molecule.start_docker.subprocess.Popen") @patch("devx.molecule.start_docker.tempfile.NamedTemporaryFile") def test_starts_local_daemon( self, mock_ntf: MagicMock, mock_popen: MagicMock, + mock_run: MagicMock, mock_sleep: MagicMock, mock_ready: MagicMock, mock_exists: MagicMock, @@ -232,12 +238,14 @@ class TestStartDockerDaemon: @patch("devx.molecule.start_docker.os.path.exists", return_value=False) @patch("devx.molecule.start_docker.is_docker_ready", return_value=False) @patch("devx.molecule.start_docker.time.sleep") + @patch("devx.molecule.start_docker.subprocess.run") @patch("devx.molecule.start_docker.subprocess.Popen") @patch("devx.molecule.start_docker.tempfile.NamedTemporaryFile") def test_fails_after_timeout( self, mock_ntf: MagicMock, mock_popen: MagicMock, + mock_run: MagicMock, mock_sleep: MagicMock, mock_ready: MagicMock, mock_exists: MagicMock, @@ -249,7 +257,7 @@ class TestStartDockerDaemon: with patch("builtins.open", mock_open(read_data="dockerd error log")): assert start_docker_daemon(timeout=3) is False mock_popen.assert_called_once() - assert mock_sleep.call_count == 3 + assert mock_sleep.call_count == 4 # 1 pkill + 3 timeout @patch("devx.molecule.start_docker.glob.glob", return_value=[]) @patch("devx.molecule.start_docker._diagnose_socket") @@ -257,12 +265,14 @@ class TestStartDockerDaemon: @patch("devx.molecule.start_docker.os.path.exists", return_value=False) @patch("devx.molecule.start_docker.is_docker_ready", return_value=False) @patch("devx.molecule.start_docker.time.sleep") + @patch("devx.molecule.start_docker.subprocess.run") @patch("devx.molecule.start_docker.subprocess.Popen") @patch("devx.molecule.start_docker.tempfile.NamedTemporaryFile") def test_fails_log_read_error( self, mock_ntf: MagicMock, mock_popen: MagicMock, + mock_run: MagicMock, mock_sleep: MagicMock, mock_ready: MagicMock, mock_exists: MagicMock, @@ -281,12 +291,14 @@ class TestStartDockerDaemon: @patch("devx.molecule.start_docker.os.path.exists", return_value=False) @patch("devx.molecule.start_docker.is_docker_ready") @patch("devx.molecule.start_docker.time.sleep") + @patch("devx.molecule.start_docker.subprocess.run") @patch("devx.molecule.start_docker.subprocess.Popen") @patch("devx.molecule.start_docker.tempfile.NamedTemporaryFile") def test_local_daemon_ready_on_first_check( self, mock_ntf: MagicMock, mock_popen: MagicMock, + mock_run: MagicMock, mock_sleep: MagicMock, mock_ready: MagicMock, mock_exists: MagicMock, @@ -299,7 +311,7 @@ class TestStartDockerDaemon: mock_ready.side_effect = [False, False, True] assert start_docker_daemon(timeout=5) is True assert mock_popen.call_count == 1 - assert mock_sleep.call_count == 1 + assert mock_sleep.call_count == 2 # 1 pkill + 1 timeout @patch("devx.molecule.start_docker._diagnose_socket") @patch("devx.molecule.start_docker._get_docker_free_bytes", return_value=100 * 1024**3)