571030b8e8
test / stage-firecracker-inputs (pull_request) Successful in 5s
tracker-policy-pr / check-pr (pull_request) Successful in 12s
test / integration-docker (pull_request) Successful in 33s
test / unit (pull_request) Successful in 39s
lint / lint (push) Successful in 49s
test / build-infra (pull_request) Successful in 3m23s
test / integration-firecracker (pull_request) Successful in 1m42s
test / coverage (pull_request) Failing after 1m26s
test / publish-infra (pull_request) Has been skipped
A failed container list or a partial per-container inspect were previously indistinguishable from a legitimately empty/partial live set, so reconciliation could silently unregister healthy bottles. - enumerate_active() now raises EnumerationError instead of returning [] when `container list` fails - Add inspect_container_network_ip() to util, which returns None on inspect failure (vs "" for "no DHCP address yet"), so live_source_ips can tell the two apart - live_source_ips() raises EnumerationError on either failure mode; register_agent() catches it and skips reconciliation, same as OrchestratorClientError - Update tests: rename test_empty_when_the_cli_fails to test_raises_when_the_cli_fails, update patching to the new function, add coverage for list failure, per-container inspect failure, and the launch-not-blocked path
54 lines
1.9 KiB
Python
54 lines
1.9 KiB
Python
"""Active-agent enumeration for the macOS Apple Container backend."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import subprocess
|
|
|
|
from ...bottle_state import read_metadata
|
|
from .. import ActiveAgent
|
|
from .infra import INFRA_NAME
|
|
|
|
# The name every agent container carries: `bot-bottle-<slug>`. Exported
|
|
# because callers that act on a running bottle (gateway-host rewrites,
|
|
# registry reconciliation) have to map an enumerated slug back to a
|
|
# container name.
|
|
CONTAINER_NAME_PREFIX = "bot-bottle-"
|
|
# The shared per-host infra container carries the same prefix as agent
|
|
# containers but is infrastructure, not a bottle — one control plane + gateway
|
|
# serves every agent, so listing it as an agent would invent one per host.
|
|
_INFRA_NAMES = frozenset({INFRA_NAME})
|
|
|
|
|
|
class EnumerationError(RuntimeError):
|
|
"""container list failed; the resulting live set is not authoritative."""
|
|
|
|
|
|
def enumerate_active() -> list[ActiveAgent]:
|
|
result = subprocess.run(
|
|
["container", "list", "--quiet"],
|
|
capture_output=True,
|
|
text=True,
|
|
check=False,
|
|
)
|
|
if result.returncode != 0:
|
|
raise EnumerationError(
|
|
f"container list failed: "
|
|
f"{(result.stderr or '').strip() or '<no stderr>'}"
|
|
)
|
|
out: list[ActiveAgent] = []
|
|
for name in sorted(line.strip() for line in result.stdout.splitlines()):
|
|
if not name.startswith(CONTAINER_NAME_PREFIX) or name in _INFRA_NAMES:
|
|
continue
|
|
slug = name[len(CONTAINER_NAME_PREFIX):]
|
|
metadata = read_metadata(slug)
|
|
out.append(ActiveAgent(
|
|
backend_name="macos-container",
|
|
slug=slug,
|
|
agent_name=metadata.agent_name if metadata else "?",
|
|
started_at=metadata.started_at if metadata else "",
|
|
services=(),
|
|
label=metadata.label if metadata else "",
|
|
color=metadata.color if metadata else "",
|
|
))
|
|
return out
|