DEVX-27: fix: add Docker socket diagnostics to start_docker
This commit is contained in:
@@ -1,10 +1,16 @@
|
||||
"""Unit tests for devx.molecule.start_docker."""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
from unittest.mock import MagicMock, mock_open, patch
|
||||
|
||||
from click.testing import CliRunner
|
||||
|
||||
from devx.molecule.start_docker import DOCKER_SOCK, is_docker_ready, main, start_docker_daemon
|
||||
from devx.molecule.start_docker import (
|
||||
DOCKER_SOCK,
|
||||
_diagnose_socket,
|
||||
is_docker_ready,
|
||||
main,
|
||||
start_docker_daemon,
|
||||
)
|
||||
|
||||
|
||||
class TestIsDockerReady:
|
||||
@@ -22,13 +28,46 @@ class TestIsDockerReady:
|
||||
assert is_docker_ready() is False
|
||||
|
||||
|
||||
class TestDiagnoseSocket:
|
||||
@patch("devx.molecule.start_docker.os.stat")
|
||||
@patch("devx.molecule.start_docker.os.path.exists", return_value=True)
|
||||
@patch("devx.molecule.start_docker.subprocess.run")
|
||||
def test_socket_exists(self, mock_run: MagicMock, mock_exists: MagicMock, mock_stat: MagicMock) -> None:
|
||||
mock_stat.return_value = MagicMock(st_mode=0o660, st_uid=0, st_gid=0)
|
||||
mock_run.side_effect = [
|
||||
MagicMock(stdout="/dev/sda1 /var/lib/docker ext4\n", returncode=0, text=""),
|
||||
MagicMock(stdout="default\n", returncode=0, text=""),
|
||||
MagicMock(
|
||||
stdout="Server Version: 29.5.2\nStorage Driver: overlay2\nDocker Root Dir: /var/lib/docker\n",
|
||||
returncode=0,
|
||||
text="",
|
||||
),
|
||||
]
|
||||
_diagnose_socket()
|
||||
mock_exists.assert_called_with(DOCKER_SOCK)
|
||||
|
||||
@patch("devx.molecule.start_docker.os.path.exists", return_value=False)
|
||||
@patch("devx.molecule.start_docker.subprocess.run")
|
||||
def test_socket_missing(self, mock_run: MagicMock, mock_exists: MagicMock) -> None:
|
||||
mock_run.side_effect = [
|
||||
MagicMock(stdout="proc on /proc type proc\n", returncode=0, text=""),
|
||||
MagicMock(stdout="default\n", returncode=0, text=""),
|
||||
MagicMock(stdout="", stderr="Cannot connect", returncode=1, text=""),
|
||||
]
|
||||
_diagnose_socket()
|
||||
mock_exists.assert_called_with(DOCKER_SOCK)
|
||||
|
||||
|
||||
class TestStartDockerDaemon:
|
||||
@patch("devx.molecule.start_docker._diagnose_socket")
|
||||
@patch("devx.molecule.start_docker.is_docker_ready", return_value=True)
|
||||
def test_host_socket_available(self, mock_ready: MagicMock) -> None:
|
||||
def test_host_socket_available(self, mock_ready: MagicMock, mock_diag: MagicMock) -> None:
|
||||
"""Should return immediately if host Docker is available."""
|
||||
assert start_docker_daemon(timeout=5) is True
|
||||
mock_ready.assert_called_once()
|
||||
mock_diag.assert_called_once()
|
||||
|
||||
@patch("devx.molecule.start_docker._diagnose_socket")
|
||||
@patch("devx.molecule.start_docker.time.sleep")
|
||||
@patch("devx.molecule.start_docker.is_docker_ready")
|
||||
@patch("devx.molecule.start_docker.subprocess.Popen")
|
||||
@@ -39,9 +78,9 @@ class TestStartDockerDaemon:
|
||||
mock_popen: MagicMock,
|
||||
mock_ready: MagicMock,
|
||||
mock_sleep: MagicMock,
|
||||
mock_diag: MagicMock,
|
||||
) -> None:
|
||||
mock_ntf.return_value = MagicMock()
|
||||
# Host socket not available, then local daemon starts on third check
|
||||
mock_ntf.return_value = MagicMock(name="/tmp/dockerd.log")
|
||||
mock_ready.side_effect = [False, False, False, True]
|
||||
assert start_docker_daemon(timeout=5) is True
|
||||
mock_popen.assert_called_once()
|
||||
@@ -53,6 +92,7 @@ class TestStartDockerDaemon:
|
||||
assert f"unix://{DOCKER_SOCK}" in popen_args
|
||||
assert mock_sleep.call_count == 2
|
||||
|
||||
@patch("devx.molecule.start_docker._diagnose_socket")
|
||||
@patch("devx.molecule.start_docker.time.sleep")
|
||||
@patch("devx.molecule.start_docker.is_docker_ready", return_value=False)
|
||||
@patch("devx.molecule.start_docker.subprocess.Popen")
|
||||
@@ -63,12 +103,33 @@ class TestStartDockerDaemon:
|
||||
mock_popen: MagicMock,
|
||||
mock_ready: MagicMock,
|
||||
mock_sleep: MagicMock,
|
||||
mock_diag: MagicMock,
|
||||
) -> None:
|
||||
mock_ntf.return_value = MagicMock()
|
||||
assert start_docker_daemon(timeout=3) is False
|
||||
mock_ntf.return_value = MagicMock(name="/tmp/dockerd.log")
|
||||
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
|
||||
|
||||
@patch("devx.molecule.start_docker._diagnose_socket")
|
||||
@patch("devx.molecule.start_docker.time.sleep")
|
||||
@patch("devx.molecule.start_docker.is_docker_ready", return_value=False)
|
||||
@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_ready: MagicMock,
|
||||
mock_sleep: MagicMock,
|
||||
mock_diag: MagicMock,
|
||||
) -> None:
|
||||
"""Should handle log read errors gracefully."""
|
||||
mock_ntf.return_value = MagicMock(name="/tmp/dockerd.log")
|
||||
with patch("builtins.open", side_effect=OSError("permission denied")):
|
||||
assert start_docker_daemon(timeout=2) is False
|
||||
|
||||
@patch("devx.molecule.start_docker._diagnose_socket")
|
||||
@patch("devx.molecule.start_docker.time.sleep")
|
||||
@patch("devx.molecule.start_docker.is_docker_ready")
|
||||
@patch("devx.molecule.start_docker.subprocess.Popen")
|
||||
@@ -79,20 +140,22 @@ class TestStartDockerDaemon:
|
||||
mock_popen: MagicMock,
|
||||
mock_ready: MagicMock,
|
||||
mock_sleep: MagicMock,
|
||||
mock_diag: MagicMock,
|
||||
) -> None:
|
||||
mock_ntf.return_value = MagicMock()
|
||||
# Host not available, local daemon ready on first loop check
|
||||
mock_ntf.return_value = MagicMock(name="/tmp/dockerd.log")
|
||||
mock_ready.side_effect = [False, False, True]
|
||||
assert start_docker_daemon(timeout=5) is True
|
||||
mock_popen.assert_called_once()
|
||||
mock_sleep.assert_called_once_with(1)
|
||||
|
||||
@patch("devx.molecule.start_docker._diagnose_socket")
|
||||
@patch("devx.molecule.start_docker.os.environ")
|
||||
@patch("devx.molecule.start_docker.is_docker_ready", return_value=True)
|
||||
def test_sets_docker_host(
|
||||
self,
|
||||
mock_ready: MagicMock,
|
||||
mock_environ: MagicMock,
|
||||
mock_diag: MagicMock,
|
||||
) -> None:
|
||||
"""DOCKER_HOST must be set so molecule connects to correct socket."""
|
||||
start_docker_daemon(timeout=5)
|
||||
|
||||
Reference in New Issue
Block a user