From c57543df2a530e15a71aa6d889963b661ef524e7 Mon Sep 17 00:00:00 2001 From: Aldo Ortega Date: Thu, 12 Sep 2024 09:53:37 -0400 Subject: [PATCH 1/4] Changed controller started to async --- kytos/core/controller.py | 8 +++++--- kytos/core/kytosd.py | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/kytos/core/controller.py b/kytos/core/controller.py index 7f421dba..31d46497 100644 --- a/kytos/core/controller.py +++ b/kytos/core/controller.py @@ -263,7 +263,7 @@ def toggle_debug(self, name=None): # enable debug logger.setLevel(logging.DEBUG) - def start(self, restart=False): + async def start(self, restart=False): """Create pidfile and call start_controller method.""" self.enable_logs() # pylint: disable=broad-except @@ -275,7 +275,7 @@ def start(self, restart=False): self.apm = init_apm(self.options.apm, app=self.api_server.app) if not restart: self.create_pidfile() - self.start_controller() + await self.start_controller() except (KytosDBInitException, KytosAPMInitException) as exc: message = f"Kytos couldn't start because of {str(exc)}" sys.exit(message) @@ -361,7 +361,7 @@ def buffers(self) -> KytosBuffers: self._buffers = KytosBuffers() return self._buffers - def start_controller(self): + async def start_controller(self): """Start the controller. Starts the KytosServer (TCP Server) coroutine. @@ -382,6 +382,8 @@ def start_controller(self): task = self.loop.create_task(self.api_server.serve()) self._tasks.append(task) + while not self.api_server.server.started: + await asyncio.sleep(0.1) # ASYNC TODO: ensure all threads started correctly # This is critical, if any of them failed starting we should exit. diff --git a/kytos/core/kytosd.py b/kytos/core/kytosd.py index b40ba361..bd5100bc 100644 --- a/kytos/core/kytosd.py +++ b/kytos/core/kytosd.py @@ -156,7 +156,7 @@ def async_main(config): if controller.options.debug: loop.set_debug(True) - loop.call_soon(controller.start) + loop.run_until_complete(controller.start()) shell_task = None if controller.options.foreground: From 1a1df5b735c1f4a6f8126939dd604a42bdc053d2 Mon Sep 17 00:00:00 2001 From: Aldo Ortega Date: Mon, 16 Sep 2024 12:20:10 -0400 Subject: [PATCH 2/4] Added tests --- tests/unit/test_core/test_controller.py | 18 +++++++++--------- tests/unit/test_core/test_kytosd.py | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/unit/test_core/test_controller.py b/tests/unit/test_core/test_controller.py index bdf79df2..3306aa71 100644 --- a/tests/unit/test_core/test_controller.py +++ b/tests/unit/test_core/test_controller.py @@ -170,12 +170,12 @@ def test_debug_wrong_name(self): @patch('kytos.core.controller.Controller.start_controller') @patch('kytos.core.controller.Controller.create_pidfile') @patch('kytos.core.controller.Controller.enable_logs') - def test_start(self, *args): + async def test_start(self, *args): """Test start method.""" (mock_enable_logs, mock_create_pidfile, mock_start_controller, mock_db_conn_wait, mock_init_apm) = args - self.controller.start() + await self.controller.start() mock_enable_logs.assert_called() mock_create_pidfile.assert_called() @@ -190,13 +190,13 @@ def test_start(self, *args): @patch('kytos.core.controller.Controller.start_controller') @patch('kytos.core.controller.Controller.create_pidfile') @patch('kytos.core.controller.Controller.enable_logs') - def test_start_error_broad_exception(self, *args): + async def test_start_error_broad_exception(self, *args): """Test start error handling broad exception.""" (mock_enable_logs, mock_create_pidfile, mock_start_controller, mock_db_conn_wait, mock_init_apm, mock_sys) = args mock_start_controller.side_effect = Exception - self.controller.start() + await self.controller.start() mock_enable_logs.assert_called() mock_create_pidfile.assert_called() @@ -210,14 +210,14 @@ def test_start_error_broad_exception(self, *args): @patch('kytos.core.controller.Controller.start_controller') @patch('kytos.core.controller.Controller.create_pidfile') @patch('kytos.core.controller.Controller.enable_logs') - def test_start_with_mongodb_and_apm(self, *args): + async def test_start_with_mongodb_and_apm(self, *args): """Test start method with database and APM options set.""" (mock_enable_logs, mock_create_pidfile, mock_start_controller, mock_db_conn_wait, mock_init_apm) = args self.controller.options.database = "mongodb" self.controller.options.apm = "es" - self.controller.start() + await self.controller.start() mock_enable_logs.assert_called() mock_create_pidfile.assert_called() @@ -229,11 +229,11 @@ def test_start_with_mongodb_and_apm(self, *args): @patch('kytos.core.controller.sys.exit') @patch('kytos.core.controller.Controller.create_pidfile') @patch('kytos.core.controller.Controller.enable_logs') - def test_start_with_invalid_database_backend(self, *args): + async def test_start_with_invalid_database_backend(self, *args): """Test start method with unsupported database backend.""" (mock_enable_logs, _, mock_sys_exit) = args self.controller.options.database = "invalid" - self.controller.start() + await self.controller.start() mock_enable_logs.assert_called() mock_sys_exit.assert_called() @@ -722,7 +722,7 @@ async def test_start_controller(self, controller, monkeypatch): controller.api_server = MagicMock() controller.load_napps = MagicMock() controller.options.napps_pre_installed = [napp] - controller.start_controller() + await controller.start_controller() assert controller.buffers controller.server.serve_forever.assert_called() diff --git a/tests/unit/test_core/test_kytosd.py b/tests/unit/test_core/test_kytosd.py index abc0a188..b7b770f0 100644 --- a/tests/unit/test_core/test_kytosd.py +++ b/tests/unit/test_core/test_kytosd.py @@ -89,7 +89,7 @@ def test_async_main(*args): async_main(MagicMock()) - event_loop.call_soon.assert_called_with(controller.start) + event_loop.run_until_complete.assert_called_with(controller.start()) @patch("builtins.open", create=True) @patch('kytos.core.kytosd.os') From a2bffecbfa6e70e026be5305b432d9689504d5a0 Mon Sep 17 00:00:00 2001 From: Aldo Ortega Date: Mon, 16 Sep 2024 17:19:29 -0400 Subject: [PATCH 3/4] Updated changelog --- CHANGELOG.rst | 7 +++++++ docs/conf.py | 4 ++-- kytos/core/controller.py | 11 ++++++++--- kytos/core/metadata.py | 2 +- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2f1022ef..8b550755 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,13 @@ All notable changes to the kytos project will be documented in this file. UNRELEASED - Under development ****************************** +[2024.1.2] - 2024-09-16 +*********************** + +Changed +======= +- Kytos will wait until uvicorn server starts before loading NApps. + [2024.1.1] - 2024-08-04 *********************** diff --git a/docs/conf.py b/docs/conf.py index 0d437338..bd37bcf3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -64,10 +64,10 @@ # built documents. # # The short X.Y version. -version = u'2024.1.1' +version = u'2024.1.2' show_version = False # The full version, including alpha/beta/rc tags. -release = u'2024.1.1' +release = u'2024.1.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/kytos/core/controller.py b/kytos/core/controller.py index 31d46497..787092b8 100644 --- a/kytos/core/controller.py +++ b/kytos/core/controller.py @@ -270,7 +270,6 @@ async def start(self, restart=False): try: if self.options.database: db_conn_wait(db_backend=self.options.database) - self.start_auth() if self.options.apm: self.apm = init_apm(self.options.apm, app=self.api_server.app) if not restart: @@ -361,6 +360,11 @@ def buffers(self) -> KytosBuffers: self._buffers = KytosBuffers() return self._buffers + async def _wait_api_server_started(self): + """Use this method to wait for Uvicorn server to start.""" + while not self.api_server.server.started: + await asyncio.sleep(0.1) + async def start_controller(self): """Start the controller. @@ -382,13 +386,14 @@ async def start_controller(self): task = self.loop.create_task(self.api_server.serve()) self._tasks.append(task) - while not self.api_server.server.started: - await asyncio.sleep(0.1) + await self._wait_api_server_started() # ASYNC TODO: ensure all threads started correctly # This is critical, if any of them failed starting we should exit. # sys.exit(error_msg.format(thread, exception)) + self.log.info("Starting authorization.") + self.start_auth() self.log.info("Loading Kytos NApps...") self.napp_dir_listener.start() self.pre_install_napps(self.options.napps_pre_installed) diff --git a/kytos/core/metadata.py b/kytos/core/metadata.py index 4e81b6c5..7e054791 100644 --- a/kytos/core/metadata.py +++ b/kytos/core/metadata.py @@ -2,7 +2,7 @@ The present metadata is intended to be used mainly on the setup. """ -__version__ = '2024.1.1' +__version__ = '2024.1.2' __author__ = 'Kytos Team' __author_email__ = 'devel@lists.kytos.io' __license__ = 'MIT' From ccb91492ed177d948ffb3dca1bfb880fcfa36361 Mon Sep 17 00:00:00 2001 From: Aldo Ortega Date: Tue, 17 Sep 2024 10:11:29 -0400 Subject: [PATCH 4/4] Updated order of servers starts --- kytos/core/controller.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kytos/core/controller.py b/kytos/core/controller.py index 787092b8..d912d5d8 100644 --- a/kytos/core/controller.py +++ b/kytos/core/controller.py @@ -381,13 +381,14 @@ async def start_controller(self): self, self.options.protocol_name) - self.log.info("Starting TCP server: %s", self.server) - self.server.serve_forever() - + self.log.info("Starting API server") task = self.loop.create_task(self.api_server.serve()) self._tasks.append(task) await self._wait_api_server_started() + self.log.info(f"Starting TCP server: {self.server}") + self.server.serve_forever() + # ASYNC TODO: ensure all threads started correctly # This is critical, if any of them failed starting we should exit. # sys.exit(error_msg.format(thread, exception))