From c6375d2f57e567b2972ee0ffdfe6978f84f01bb5 Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 26 Jul 2026 22:53:37 +0000 Subject: [PATCH] fix(docker): fail closed on network address scan errors --- bot_bottle/backend/docker/consolidated_launch.py | 5 +++++ tests/unit/test_consolidated_launch.py | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/bot_bottle/backend/docker/consolidated_launch.py b/bot_bottle/backend/docker/consolidated_launch.py index 3fadfaff..10096e06 100644 --- a/bot_bottle/backend/docker/consolidated_launch.py +++ b/bot_bottle/backend/docker/consolidated_launch.py @@ -68,6 +68,11 @@ def _network_container_ips(network: str) -> list[str]: "docker", "network", "inspect", "--format", "{{range .Containers}}{{.IPv4Address}} {{end}}", network, ]) + if proc.returncode != 0: + detail = proc.stderr.strip() or f"exit {proc.returncode}" + raise ConsolidatedLaunchError( + f"could not inspect addresses on gateway network {network}: {detail}" + ) ips: list[str] = [] for entry in proc.stdout.split(): ips.append(entry.split("/", 1)[0]) diff --git a/tests/unit/test_consolidated_launch.py b/tests/unit/test_consolidated_launch.py index ace462c7..8c9d03d0 100644 --- a/tests/unit/test_consolidated_launch.py +++ b/tests/unit/test_consolidated_launch.py @@ -7,6 +7,8 @@ from pathlib import Path from unittest.mock import MagicMock, Mock, patch from bot_bottle.backend.docker.consolidated_launch import ( + ConsolidatedLaunchError, + _network_container_ips, launch_consolidated, deprovision_consolidated, ) @@ -86,6 +88,16 @@ class TestLaunchConsolidated(unittest.TestCase): client.teardown_bottle.assert_called_once_with("b1") # no orphan left +class TestNetworkContainerIps(unittest.TestCase): + def test_fails_closed_when_network_inspection_fails(self) -> None: + result = Mock(returncode=1, stdout="", stderr="daemon unavailable") + with ( + patch(f"{_MOD}.run_docker", return_value=result), + self.assertRaisesRegex(ConsolidatedLaunchError, "daemon unavailable"), + ): + _network_container_ips("bot-bottle-gateway") + + class TestTeardownConsolidated(unittest.TestCase): def test_deregisters_and_deprovisions(self) -> None: client = Mock()