mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
feat(runtime): stamp spawned containers with the stack's own compose-project labels
Agent spawns (all five provider paths), intake/secretary chats, and sandbox sidecars now carry com.docker.compose.project/service/oneoff/ config-hash labels copied from the orchestrator's own compose project, so a Docker UI (UGOS) groups them under the stack's project and they die with the stack: bare compose stop/restart affects them, compose down removes them (the config-hash label must be PRESENT for down to even see the container — compose filters its API listing on that key before the orphan predicate runs, verified live), and up -d deliberately does not resurrect them since the orchestrator respawns its own agents. Self-discovery reads the orchestrator's own container id from /proc/self/mountinfo keyed on the root-independent /containers/<id>/ segment — the UGREEN NAS data-root is /volume1/@docker on btrfs, so its mountinfo reads /@docker/containers/..., never the textbook /var/lib/docker path (verified against the live NAS) — with a HOSTNAME short-id fallback, then one docker inspect cached per process. Only definitive outcomes cache; a transient inspect failure logs and retries on the next spawn. Outside compose the helper yields nothing and every spawn command is byte-for-byte unchanged.
This commit is contained in:
@@ -129,6 +129,63 @@ async def test_provision_labels_are_correct() -> None:
|
||||
assert "roboco.sandbox.owner=roboco-agent-dev-2" in labels
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_provision_adds_compose_labels_when_orchestrator_is_compose_managed(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""When the orchestrator resolves its own compose project, every sandbox
|
||||
sidecar carries it too — so `docker compose down --remove-orphans` sweeps
|
||||
the sidecar away with the stack, and UGOS groups it under the same
|
||||
project."""
|
||||
|
||||
async def _fake_label_args(service: str) -> list[str]:
|
||||
return [
|
||||
"--label",
|
||||
"com.docker.compose.project=roboco-nas",
|
||||
"--label",
|
||||
f"com.docker.compose.service={service}",
|
||||
"--label",
|
||||
"com.docker.compose.oneoff=False",
|
||||
"--label",
|
||||
"com.docker.compose.config-hash=roboco-sidecar",
|
||||
]
|
||||
|
||||
monkeypatch.setattr(sandbox_module, "compose_label_args", _fake_label_args)
|
||||
runner = _FakeRunner(run_rc=0, exec_rc=0)
|
||||
provisioner = SandboxProvisioner(network=_NETWORK, runner=runner)
|
||||
|
||||
await provisioner.provision("dev-3", ["postgres"])
|
||||
|
||||
run_call = next(c for c in runner.calls if c[0] == "run")
|
||||
label_indices = [i for i, a in enumerate(run_call) if a == "--label"]
|
||||
labels = [run_call[i + 1] for i in label_indices]
|
||||
assert "com.docker.compose.project=roboco-nas" in labels
|
||||
expected_service = sandbox_module.SANDBOX_ENGINES["postgres"].container_name(
|
||||
"dev-3"
|
||||
)
|
||||
assert f"com.docker.compose.service={expected_service}" in labels
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_provision_omits_compose_labels_outside_compose(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""The real discovery helper returns [] outside a compose stack (dev
|
||||
machines, CI, the eval harness) — no com.docker.compose.* label leaks in."""
|
||||
|
||||
async def _no_labels(_service: str) -> list[str]:
|
||||
return []
|
||||
|
||||
monkeypatch.setattr(sandbox_module, "compose_label_args", _no_labels)
|
||||
runner = _FakeRunner(run_rc=0, exec_rc=0)
|
||||
provisioner = SandboxProvisioner(network=_NETWORK, runner=runner)
|
||||
|
||||
await provisioner.provision("dev-4", ["postgres"])
|
||||
|
||||
run_call = next(c for c in runner.calls if c[0] == "run")
|
||||
assert not any(a.startswith("com.docker.compose.") for a in run_call)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_provision_mongo_engine() -> None:
|
||||
runner = _FakeRunner(run_rc=0, exec_rc=0)
|
||||
|
||||
Reference in New Issue
Block a user