diff --git a/decnet/engine/deployer.py b/decnet/engine/deployer.py index 5ac1417..55de388 100644 --- a/decnet/engine/deployer.py +++ b/decnet/engine/deployer.py @@ -54,7 +54,11 @@ def _sync_logging_helper(config: DecnetConfig) -> None: def _compose(*args: str, compose_file: Path = COMPOSE_FILE, env: dict | None = None) -> None: import os - cmd = ["docker", "compose", "-f", str(compose_file), *args] + # -p decnet pins the compose project name. Without it, docker compose + # derives the project from basename($PWD); when a daemon (systemd) runs + # with WorkingDirectory=/ that basename is empty and compose aborts with + # "project name must not be empty". + cmd = ["docker", "compose", "-p", "decnet", "-f", str(compose_file), *args] merged = {**os.environ, **(env or {})} subprocess.run(cmd, check=True, env=merged) # nosec B603 @@ -79,7 +83,11 @@ def _compose_with_retry( """Run a docker compose command, retrying on transient failures.""" import os last_exc: subprocess.CalledProcessError | None = None - cmd = ["docker", "compose", "-f", str(compose_file), *args] + # -p decnet pins the compose project name. Without it, docker compose + # derives the project from basename($PWD); when a daemon (systemd) runs + # with WorkingDirectory=/ that basename is empty and compose aborts with + # "project name must not be empty". + cmd = ["docker", "compose", "-p", "decnet", "-f", str(compose_file), *args] merged = {**os.environ, **(env or {})} for attempt in range(1, retries + 1): result = subprocess.run(cmd, capture_output=True, text=True, env=merged) # nosec B603 diff --git a/decnet/web/templates/decnet-agent.service.j2 b/decnet/web/templates/decnet-agent.service.j2 index 26d9612..9847345 100644 --- a/decnet/web/templates/decnet-agent.service.j2 +++ b/decnet/web/templates/decnet-agent.service.j2 @@ -6,6 +6,7 @@ Wants=network-online.target [Service] Type=simple +WorkingDirectory=/opt/decnet Environment=DECNET_SYSTEM_LOGS=/var/log/decnet/decnet.agent.log ExecStart=/usr/local/bin/decnet agent --no-forwarder Restart=on-failure diff --git a/decnet/web/templates/decnet-engine.service.j2 b/decnet/web/templates/decnet-engine.service.j2 index 8d5cf14..dadf1b0 100644 --- a/decnet/web/templates/decnet-engine.service.j2 +++ b/decnet/web/templates/decnet-engine.service.j2 @@ -7,6 +7,7 @@ Wants=network-online.target [Service] Type=oneshot RemainAfterExit=yes +WorkingDirectory=/opt/decnet Environment=DECNET_SYSTEM_LOGS=/var/log/decnet/decnet.log ExecStart=/usr/local/bin/decnet deploy StandardOutput=append:/var/log/decnet/decnet.log diff --git a/decnet/web/templates/decnet-forwarder.service.j2 b/decnet/web/templates/decnet-forwarder.service.j2 index 0f6b974..e0a2391 100644 --- a/decnet/web/templates/decnet-forwarder.service.j2 +++ b/decnet/web/templates/decnet-forwarder.service.j2 @@ -7,6 +7,7 @@ PartOf=decnet-agent.service [Service] Type=simple +WorkingDirectory=/opt/decnet Environment=DECNET_SYSTEM_LOGS=/var/log/decnet/decnet.forwarder.log ExecStart=/usr/local/bin/decnet forwarder --master-host {{ master_host }} --master-port 6514 --agent-dir /etc/decnet/agent --log-file /var/log/decnet/decnet.log Restart=on-failure diff --git a/tests/test_deployer.py b/tests/test_deployer.py index 3a81fa2..f922583 100644 --- a/tests/test_deployer.py +++ b/tests/test_deployer.py @@ -43,7 +43,7 @@ class TestCompose: _compose("up", "-d", compose_file=Path("test.yml")) mock_run.assert_called_once() cmd = mock_run.call_args[0][0] - assert cmd[:4] == ["docker", "compose", "-f", "test.yml"] + assert cmd[:6] == ["docker", "compose", "-p", "decnet", "-f", "test.yml"] assert "up" in cmd assert "-d" in cmd