core: GPU containers, bind sub-paths, and stop containers on unit stop
- container launcher: `gpus:` passes through as docker `--gpus`. - requires: `path:` is appended to the bound URL, for consumers that want a sub-path as their base (e.g. an OpenAI-compatible server's /v1). - container services now get `ExecStop=-docker stop wildpc-<name>`. SIGTERM only reached the `docker run` client, so a stop/restart left the container running unsupervised and holding its name, and the next start failed with a name conflict until systemd gave up.
This commit is contained in:
@@ -205,3 +205,24 @@ def test_compose_stop_cmd_is_down(tmp_path: Path) -> None:
|
||||
def test_non_compose_runner_has_no_stop_cmd(tmp_path: Path) -> None:
|
||||
run = RunPython(launcher="python", program="svc")
|
||||
assert _build_stop_cmd("svc", run, tmp_path) == []
|
||||
|
||||
|
||||
def test_container_gpus_flag() -> None:
|
||||
"""`gpus` passes through as docker `--gpus`; omitted → no GPU access."""
|
||||
run = RunContainer(launcher="container", image="img:latest", gpus="all")
|
||||
with patch("wildpc_core.deploy.shutil.which", return_value="/usr/bin/docker"):
|
||||
cmd = _build_run_cmd("svc", run, {}, [])
|
||||
assert cmd[cmd.index("--gpus") + 1] == "all"
|
||||
assert cmd.index("--gpus") < cmd.index("img:latest")
|
||||
|
||||
plain = RunContainer(launcher="container", image="img:latest")
|
||||
with patch("wildpc_core.deploy.shutil.which", return_value="/usr/bin/docker"):
|
||||
assert "--gpus" not in _build_run_cmd("svc", plain, {}, [])
|
||||
|
||||
|
||||
def test_container_stop_cmd_stops_named_container() -> None:
|
||||
"""Stopping the unit must stop the container itself, not just the docker client."""
|
||||
run = RunContainer(launcher="container", image="img:latest")
|
||||
with patch("wildpc_core.deploy.shutil.which", return_value="/usr/bin/docker"):
|
||||
stop = _build_stop_cmd("svc", run, None)
|
||||
assert stop == ["-/usr/bin/docker", "stop", "wildpc-svc"]
|
||||
|
||||
48
core/tests/test_requires_env.py
Normal file
48
core/tests/test_requires_env.py
Normal file
@@ -0,0 +1,48 @@
|
||||
"""Tests for `_requires_env` — a `{ref, bind}` requirement projects the target's URL."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import wildpc_core.config as C
|
||||
from wildpc_core.deploy import _requires_env
|
||||
from wildpc_core.manifest import RemoteDeployment, SystemdDeployment
|
||||
|
||||
|
||||
def _svc(requires: list | None = None, port: int | None = None) -> SystemdDeployment:
|
||||
spec: dict = {"manager": "systemd", "run": {"launcher": "command", "argv": ["x"]}}
|
||||
if requires is not None:
|
||||
spec["requires"] = requires
|
||||
if port is not None:
|
||||
spec["expose"] = {"http": {"internal": {"port": port}}}
|
||||
spec["reach"] = "internal"
|
||||
return SystemdDeployment.model_validate(spec)
|
||||
|
||||
|
||||
def _cfg(deployments: dict) -> C.WildpcConfig:
|
||||
return C.WildpcConfig(
|
||||
root=None,
|
||||
gateway=C.GatewayConfig(port=9000, tls="acme", domain="node.example"),
|
||||
repo=None,
|
||||
programs={},
|
||||
deployments=deployments,
|
||||
)
|
||||
|
||||
|
||||
def test_bind_projects_target_url() -> None:
|
||||
consumer = _svc(requires=[{"ref": "api", "bind": "API_URL"}])
|
||||
cfg = _cfg({"api": _svc(port=9100), "app": consumer})
|
||||
assert _requires_env(cfg, consumer) == {"API_URL": "https://api.node.example"}
|
||||
|
||||
|
||||
def test_bind_path_is_appended() -> None:
|
||||
consumer = _svc(requires=[{"ref": "llm", "bind": "LLM_BASE", "path": "/v1"}])
|
||||
cfg = _cfg({"llm": _svc(port=8100), "app": consumer})
|
||||
assert _requires_env(cfg, consumer) == {"LLM_BASE": "https://llm.node.example/v1"}
|
||||
|
||||
|
||||
def test_bind_path_on_reference_base_url() -> None:
|
||||
ref = RemoteDeployment.model_validate(
|
||||
{"manager": "none", "base_url": "https://ext.example/"}
|
||||
)
|
||||
consumer = _svc(requires=[{"ref": "ext", "bind": "EXT", "path": "/v1"}])
|
||||
cfg = _cfg({"ext": ref, "app": consumer})
|
||||
assert _requires_env(cfg, consumer) == {"EXT": "https://ext.example/v1"}
|
||||
Reference in New Issue
Block a user