]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
python/knot_resolver/manager: added files watchdog
authorAleš Mrázek <ales.mrazek@nic.cz>
Fri, 18 Oct 2024 09:13:54 +0000 (11:13 +0200)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Tue, 3 Dec 2024 07:53:33 +0000 (08:53 +0100)
python/knot_resolver/manager/files/__init__.py [new file with mode: 0644]
python/knot_resolver/manager/files/watchdog.py [new file with mode: 0644]
python/knot_resolver/manager/server.py
setup.py

diff --git a/python/knot_resolver/manager/files/__init__.py b/python/knot_resolver/manager/files/__init__.py
new file mode 100644 (file)
index 0000000..4970065
--- /dev/null
@@ -0,0 +1,3 @@
+from .watchdog import init_files_watchdog
+
+__all__ = ["init_files_watchdog"]
diff --git a/python/knot_resolver/manager/files/watchdog.py b/python/knot_resolver/manager/files/watchdog.py
new file mode 100644 (file)
index 0000000..9cb644a
--- /dev/null
@@ -0,0 +1,88 @@
+import importlib
+import logging
+from pathlib import Path
+from typing import List, Optional, Union
+
+from knot_resolver.controller.registered_workers import command_registered_workers
+from knot_resolver.datamodel import KresConfig
+from knot_resolver.datamodel.types import File
+from knot_resolver.manager.config_store import ConfigStore, only_on_real_changes_update
+from knot_resolver.utils import compat
+
+_watchdog = False
+if importlib.util.find_spec("watchdog"):
+    _watchdog = True
+
+logger = logging.getLogger(__name__)
+
+
+def files_to_watch(config: KresConfig) -> List[Path]:
+    files: List[Optional[File]] = [
+        config.network.tls.cert_file,
+        config.network.tls.key_file,
+    ]
+    return [file.to_path() for file in files if file is not None]
+
+
+if _watchdog:
+    from watchdog.events import (
+        DirModifiedEvent,
+        FileModifiedEvent,
+        FileSystemEventHandler,
+    )
+    from watchdog.observers import Observer
+
+    _files_watchdog: Optional["FilesWatchDog"] = None
+
+    class CertificatesEventHandler(FileSystemEventHandler):
+
+        def __init__(self, config: KresConfig) -> None:
+            self._config = config
+            self._command = f"net.tls('{config.network.tls.cert_file}', '{config.network.tls.key_file}')"
+
+        # def on_any_event(self, event: FileSystemEvent) -> None:
+        #     pass
+
+        # def on_created(self, event: Union[DirCreatedEvent, FileCreatedEvent]) -> None:
+        #     pass
+
+        # def on_deleted(self, event: Union[DirDeletedEvent, FileDeletedEvent]) -> None:
+        #     pass
+
+        def on_modified(self, event: Union[DirModifiedEvent, FileModifiedEvent]) -> None:
+            if compat.asyncio.is_event_loop_running():
+                compat.asyncio.create_task(command_registered_workers(self._command))
+            else:
+                compat.asyncio.run(command_registered_workers(self._command))
+
+        # def on_closed(self, event: FileClosedEvent) -> None:
+        #     pass
+
+    class FilesWatchDog:
+        def __init__(self, config: KresConfig, files: List[Path]) -> None:
+            self._observer = Observer()
+            for file in files:
+                self._observer.schedule(CertificatesEventHandler(config), str(file), recursive=False)
+                logger.info(f"Watching '{file}. file")
+
+        def start(self) -> None:
+            if self._observer:
+                self._observer.start()
+
+        def stop(self) -> None:
+            if self._observer:
+                self._observer.stop()
+                self._observer.join()
+
+    @only_on_real_changes_update(files_to_watch)
+    async def _init_files_watchdog(config: KresConfig) -> None:
+        global _files_watchdog
+        if _files_watchdog is None:
+            logger.info("Starting files WatchDog")
+            _files_watchdog = FilesWatchDog(config, files_to_watch(config))
+            _files_watchdog.start()
+
+
+async def init_files_watchdog(config_store: ConfigStore) -> None:
+    if _watchdog:
+        await config_store.register_on_change_callback(_init_files_watchdog)
index 90fd4d3b955f1677092990290909b834c13bfb73..fbfef244234ee0204011247f28822acc85e92ba7 100644 (file)
@@ -27,7 +27,7 @@ from knot_resolver.datamodel.cache_schema import CacheClearRPCSchema
 from knot_resolver.datamodel.config_schema import KresConfig, get_rundir_without_validation
 from knot_resolver.datamodel.globals import Context, set_global_validation_context
 from knot_resolver.datamodel.management_schema import ManagementSchema
-from knot_resolver.manager import metrics
+from knot_resolver.manager import files, metrics
 from knot_resolver.utils import custom_atexit as atexit
 from knot_resolver.utils import ignore_exceptions_optional
 from knot_resolver.utils.async_utils import readfile
@@ -566,6 +566,8 @@ async def start_server(config: Path = CONFIG_FILE) -> int:  # noqa: PLR0915
         # started, therefore before initializing manager
         await metrics.init_prometheus(config_store)
 
+        await files.init_files_watchdog(config_store)
+
         # prepare instance of the server (no side effects)
         server = Server(config_store, config)
 
index 5ab4ae08ef46a1c01be5c29ee5e2e59e5378421c..1e11e0c635f2ecf421cf3bfb9af5fd204490e6a7 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -15,6 +15,7 @@ packages = \
  'knot_resolver.datamodel.templates',
  'knot_resolver.datamodel.types',
  'knot_resolver.manager',
+ 'knot_resolver.manager.files',
  'knot_resolver.manager.metrics',
  'knot_resolver.utils',
  'knot_resolver.utils.compat',