diff --git a/tardis/rest/service.py b/tardis/rest/service.py index 2cb7973f..b8b34530 100644 --- a/tardis/rest/service.py +++ b/tardis/rest/service.py @@ -1,6 +1,7 @@ from .app.security import UserCredentials from cobald.daemon import service from cobald.daemon.plugins import yaml_tag +import threading from uvicorn.config import Config from uvicorn.server import Server @@ -47,4 +48,10 @@ def get_user(self, user_name: str) -> Optional[UserCredentials]: return None async def run(self) -> None: - await Server(config=self._config).serve() + server = Server(config=self._config) + await server.serve() + # See https://github.com/encode/uvicorn/issues/1579 + # The server has shut down after receiving *and suppressing* a signal. + # Explicitly raise the corresponding shutdown exception as a workaround. + if server.should_exit and threading.current_thread() is threading.main_thread(): + raise KeyboardInterrupt diff --git a/tests/rest_t/test_service.py b/tests/rest_t/test_service.py index 26eec459..6f395d47 100644 --- a/tests/rest_t/test_service.py +++ b/tests/rest_t/test_service.py @@ -20,7 +20,10 @@ def test_run(self, mocked_server): mocked_server.reset_mock() - run_async(self.rest_service.run) + # Mocking the server means that all its attributes are set, including + # the "I got killed by SIGINT" flag, which triggers its shutdown heuristic. + with self.assertRaises(KeyboardInterrupt): + run_async(self.rest_service.run) mocked_server.assert_called_with(config=self.rest_service._config) def test_secret_key(self):