dfce3d9505
lint / lint (push) Successful in 54s
The `DockerGateway` container-lifecycle impl now lives in `backend/docker/gateway.py` (the shape backend gateway classes will share); `orchestrator/gateway.py` keeps only the backend-neutral pieces (the `Gateway` ABC, constants, `rotate_gateway_ca`). Delete the standalone-gateway path it was the only consumer of. `--gateway` on `python -m bot_bottle.orchestrator` was invoked nowhere — the production docker flow runs the gateway data plane inside the combined `bot-bottle-infra` container via `OrchestratorService`, never this class. Removing it takes with it `Orchestrator.ensure_gateway()` and the `gateway` ctor arg; `gateway_status()` becomes a stub reporting `configured: false` so the documented `GET /gateway` control-plane route keeps its contract. Fix a latent bug the move surfaced: `launch.py` read the shared gateway CA via `docker exec bot-bottle-orch-gateway`, a container the consolidated flow never creates — so the agent CA install was reaching a nonexistent name. Read it from `bot-bottle-infra` (INFRA_NAME) instead. Repoint the affected tests to the new module; drop the unit test + fake that covered the deleted standalone wiring. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
50 lines
1.6 KiB
Python
50 lines
1.6 KiB
Python
"""Integration: the consolidated Docker gateway is an idempotent singleton.
|
|
|
|
Gated on a reachable Docker daemon. Uses a unique name so it never
|
|
collides with a real per-host gateway or a leftover from another run.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import secrets
|
|
import subprocess
|
|
import unittest
|
|
|
|
from bot_bottle.backend.docker.gateway import DockerGateway
|
|
from tests._docker import skip_unless_docker
|
|
|
|
IMAGE = "busybox"
|
|
|
|
|
|
@skip_unless_docker()
|
|
class TestDockerGatewayIntegration(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self.name = "bot-bottle-orch-gateway-itest-" + secrets.token_hex(4)
|
|
# Resolver-only data plane (PRD 0070) requires an orchestrator URL to
|
|
# run; busybox never dials it, so a placeholder is enough here.
|
|
self.sc = DockerGateway(
|
|
IMAGE, name=self.name, orchestrator_url="http://orchestrator:9000",
|
|
)
|
|
self.addCleanup(self.sc.stop)
|
|
|
|
def _count(self) -> int:
|
|
proc = subprocess.run(
|
|
["docker", "ps", "-a", "--filter", f"name=^{self.name}$",
|
|
"--format", "{{.Names}}"],
|
|
stdout=subprocess.PIPE, text=True, check=False,
|
|
)
|
|
return sum(1 for n in proc.stdout.split() if n == self.name)
|
|
|
|
def test_ensure_is_idempotent_singleton(self) -> None:
|
|
self.sc.ensure_running()
|
|
self.assertEqual(1, self._count())
|
|
# A second ensure must not spawn a second container — one per host.
|
|
self.sc.ensure_running()
|
|
self.assertEqual(1, self._count())
|
|
self.sc.stop()
|
|
self.assertEqual(0, self._count())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|