feat(supervise): start orchestrator on demand via backend-agnostic bring-up
Add BottleBackend.ensure_orchestrator() -> str: the backend-agnostic entry point that brings up the per-host orchestrator + shared gateway (idempotent) and returns its host-reachable control-plane URL. Docker starts the orchestrator + gateway containers (OrchestratorService); firecracker boots the infra VM; macos-container dies with a pointer (no orchestrator). Previously bring-up was reachable only through each backend's consolidated_launch, with no shared handle. Wire it into `bot-bottle supervise`: supervise is often the first thing an operator runs, before any bottle has booted the control plane, so `_resolve_orchestrator_url` now starts the selected backend's orchestrator on demand when discovery finds nothing, instead of failing with "launch a bottle first". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UoEZHDjv84ChoZbozQERhJ
This commit is contained in:
@@ -244,5 +244,40 @@ class TestHasBackend(unittest.TestCase):
|
||||
self.assertFalse(has_backend("nonexistent"))
|
||||
|
||||
|
||||
class TestEnsureOrchestrator(unittest.TestCase):
|
||||
"""The backend-agnostic orchestrator bring-up entry point. Docker starts
|
||||
the orchestrator + gateway containers; firecracker boots the infra VM;
|
||||
backends without one (macos-container) die with a pointer."""
|
||||
|
||||
def test_docker_delegates_to_orchestrator_service(self):
|
||||
b = get_bottle_backend("docker")
|
||||
with patch(
|
||||
"bot_bottle.orchestrator.lifecycle.OrchestratorService"
|
||||
) as service_cls:
|
||||
service_cls.return_value.ensure_running.return_value = (
|
||||
"http://127.0.0.1:8099"
|
||||
)
|
||||
url = b.ensure_orchestrator()
|
||||
self.assertEqual(url, "http://127.0.0.1:8099")
|
||||
service_cls.return_value.ensure_running.assert_called_once_with()
|
||||
|
||||
def test_firecracker_delegates_to_infra_vm(self):
|
||||
b = get_bottle_backend("firecracker")
|
||||
with patch(
|
||||
"bot_bottle.backend.firecracker.infra_vm.ensure_running"
|
||||
) as ensure_running:
|
||||
ensure_running.return_value.control_plane_url = (
|
||||
"http://10.243.255.1:8099"
|
||||
)
|
||||
url = b.ensure_orchestrator()
|
||||
self.assertEqual(url, "http://10.243.255.1:8099")
|
||||
|
||||
def test_macos_default_dies(self):
|
||||
from bot_bottle.log import Die
|
||||
b = get_bottle_backend("macos-container")
|
||||
with self.assertRaises(Die):
|
||||
b.ensure_orchestrator()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -176,5 +176,31 @@ class TestEditInEditor(unittest.TestCase):
|
||||
os.environ["EDITOR"] = original_editor
|
||||
|
||||
|
||||
class TestResolveOrchestratorUrl(unittest.TestCase):
|
||||
"""`_resolve_orchestrator_url` starts the backend orchestrator on demand
|
||||
when discovery finds nothing — supervise is often the first thing run."""
|
||||
|
||||
def test_returns_discovered_url_without_starting(self) -> None:
|
||||
with patch.object(
|
||||
supervise_cli, "discover_orchestrator_url",
|
||||
return_value="http://127.0.0.1:8099",
|
||||
), patch("bot_bottle.backend.get_bottle_backend") as get_backend:
|
||||
url = supervise_cli._resolve_orchestrator_url()
|
||||
self.assertEqual(url, "http://127.0.0.1:8099")
|
||||
get_backend.assert_not_called() # nothing to start; discovery won
|
||||
|
||||
def test_starts_backend_orchestrator_when_none_running(self) -> None:
|
||||
backend = MagicMock()
|
||||
backend.name = "firecracker"
|
||||
backend.ensure_orchestrator.return_value = "http://10.243.255.1:8099"
|
||||
with patch.object(
|
||||
supervise_cli, "discover_orchestrator_url",
|
||||
side_effect=supervise_cli.OrchestratorClientError("none"),
|
||||
), patch("bot_bottle.backend.get_bottle_backend", return_value=backend):
|
||||
url = supervise_cli._resolve_orchestrator_url()
|
||||
self.assertEqual(url, "http://10.243.255.1:8099")
|
||||
backend.ensure_orchestrator.assert_called_once_with()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user