diff --git a/goth/runner/__init__.py b/goth/runner/__init__.py index b10c4f43..d1160cf6 100644 --- a/goth/runner/__init__.py +++ b/goth/runner/__init__.py @@ -173,16 +173,29 @@ async def _start_nodes(self): node_names: Dict[str, str] = {} ports: Dict[str, dict] = {} - # Start the probes' containers and obtain their IP addresses + # Start all probes as asyncio tasks in parallel, cancel them on error + probe_tasks = [ + asyncio.create_task(self._exit_stack.enter_async_context(run_probe(probe))) + for probe in self.probes + ] + try: + future_gather = asyncio.gather(*probe_tasks) + await future_gather + except Exception as e: + for task in probe_tasks: + task.cancel() + logger.error(f"Starting probes failed: {e!r}") + raise e + + # Obtain the probes' IP addresses and port mappings for probe in self.probes: - ip_address = await self._exit_stack.enter_async_context(run_probe(probe)) - node_names[ip_address] = probe.name + node_names[probe.ip_address] = probe.name container_ports = probe.container.ports - ports[ip_address] = container_ports + ports[probe.ip_address] = container_ports logger.debug( - "Probe for %s started on IP: %s with port mapping: %s", + "Probe for %s started. IP address: %s, port mapping: %s", probe.name, - ip_address, + probe.ip_address, container_ports, ) diff --git a/goth/runner/probe/__init__.py b/goth/runner/probe/__init__.py index d768f2ae..7ca421bd 100644 --- a/goth/runner/probe/__init__.py +++ b/goth/runner/probe/__init__.py @@ -361,7 +361,10 @@ def create_probe( @contextlib.asynccontextmanager async def run_probe(probe: Probe) -> AsyncIterator[str]: - """Implement AsyncContextManager for starting and stopping a probe.""" + """Implement AsyncContextManager for starting and stopping a probe. + + Yields the probe's assigned IP address. + """ try: logger.debug("Starting probe. name=%s", probe.name) diff --git a/test/goth/runner/test_shutdown.py b/test/goth/runner/test_shutdown.py index 5945ca29..6f878bcb 100644 --- a/test/goth/runner/test_shutdown.py +++ b/test/goth/runner/test_shutdown.py @@ -127,6 +127,7 @@ async def _coro(*args): ) @pytest.mark.asyncio async def test_runner_startup_shutdown( + caplog, mock_function, manager_start_fails, webserver_start_fails, @@ -195,6 +196,8 @@ async def test_runner_startup_shutdown( or probe_init.failed or probe_start.failed ) + if proxy_start.failed: + assert "Starting probes failed: MockError" in caplog.text @pytest.mark.parametrize("have_test_failure", [False, True])