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:
2026-10-16 03:10:01 -07:00
parent 8a9843658c
commit 55c2f269ec
6 changed files with 90 additions and 4 deletions

View 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"}