From: Aleš Mrázek Date: Thu, 28 Nov 2024 16:44:58 +0000 (+0100) Subject: manager: files watchdog: watch parent directory for changes X-Git-Tag: v6.0.10~10^2~3 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=21221339e1dbefdb84dfce79b6455ed4cfdf2614;p=thirdparty%2Fknot-resolver.git manager: files watchdog: watch parent directory for changes Watching the whole parent directory is better because we can see file deletion and creation and there is no need to reinitiate the watchdog when a file is replaced. --- diff --git a/python/knot_resolver/manager/files/watchdog.py b/python/knot_resolver/manager/files/watchdog.py index d365542c7..64547192e 100644 --- a/python/knot_resolver/manager/files/watchdog.py +++ b/python/knot_resolver/manager/files/watchdog.py @@ -1,10 +1,8 @@ import importlib import logging -import os -import time from pathlib import Path from threading import Timer -from typing import List, Optional, Union +from typing import List, Optional from knot_resolver.controller.registered_workers import command_registered_workers from knot_resolver.datamodel import KresConfig @@ -29,10 +27,7 @@ def tls_cert_paths(config: KresConfig) -> List[str]: if _watchdog: from watchdog.events import ( - DirDeletedEvent, - DirModifiedEvent, - FileDeletedEvent, - FileModifiedEvent, + FileSystemEvent, FileSystemEventHandler, ) from watchdog.observers import Observer @@ -40,75 +35,75 @@ if _watchdog: _tls_cert_watchdog: Optional["TLSCertWatchDog"] = None class TLSCertEventHandler(FileSystemEventHandler): - def __init__(self, cmd: str, delay: int = 5) -> None: - self._delay = delay - self._timer: Optional[Timer] = None + def __init__(self, files: List[Path], cmd: str) -> None: + self._files = files self._cmd = cmd + self._timer: Optional[Timer] = None - def _reload_cmd(self) -> None: - logger.info("Reloading TLS certificate files for the all workers") - if compat.asyncio.is_event_loop_running(): - compat.asyncio.create_task(command_registered_workers(self._cmd)) - else: - compat.asyncio.run(command_registered_workers(self._cmd)) - - def on_deleted(self, event: Union[DirDeletedEvent, FileDeletedEvent]) -> None: - path = str(event.src_path) - logger.info(f"Stopped watching '{path}', because it was deleted") - - # do not send command when the file was deleted - if self._timer and self._timer.is_alive(): - self._timer.cancel() - self._timer.join() - - if _tls_cert_watchdog: - _tls_cert_watchdog.reschedule() - - def on_modified(self, event: Union[DirModifiedEvent, FileModifiedEvent]) -> None: - path = str(event.src_path) - logger.info(f"TLS certificate file '{path}' has been modified") + def _reload(self) -> None: + def command() -> None: + if compat.asyncio.is_event_loop_running(): + compat.asyncio.create_task(command_registered_workers(self._cmd)) + else: + compat.asyncio.run(command_registered_workers(self._cmd)) + logger.info("Reloading of TLS certificate files has finished") - # skipping if command was already triggered + # skipping if reload was already triggered if self._timer and self._timer.is_alive(): - logger.info(f"Skipping '{path}', reload file already triggered") + logger.info("Skipping TLS certificate files reloading, reload command was already triggered") return - # start a new timer - self._timer = Timer(self._delay, self._reload_cmd) - self._timer.start() + # start a 5sec timer logger.info("Delayed reload of TLS certificate files has started") + self._timer = Timer(5, command) + self._timer.start() + + def on_created(self, event: FileSystemEvent) -> None: + src_path = Path(str(event.src_path)) + if src_path in self._files: + logger.info(f"Watched file '{src_path}' has been created") + self._reload() + + def on_deleted(self, event: FileSystemEvent) -> None: + src_path = Path(str(event.src_path)) + if src_path in self._files: + logger.warning(f"Watched file '{src_path}' has been deleted") + if self._timer: + self._timer.cancel() + for file in self._files: + if file.parent == src_path: + logger.warning(f"Watched directory '{src_path}' has been deleted") + if self._timer: + self._timer.cancel() + + def on_modified(self, event: FileSystemEvent) -> None: + src_path = Path(str(event.src_path)) + if src_path in self._files: + logger.info(f"Watched file '{src_path}' has been modified") + self._reload() class TLSCertWatchDog: def __init__(self, cert_file: Path, key_file: Path) -> None: self._observer = Observer() - self._cert_file = cert_file - self._key_file = key_file - self._cmd = f"net.tls('{cert_file}', '{key_file}')" - - def schedule(self) -> None: - event_handler = TLSCertEventHandler(self._cmd) - logger.info("Schedule watching of TLS certificate files") - self._observer.schedule( - event_handler, - str(self._cert_file), - recursive=False, - ) - self._observer.schedule( - event_handler, - str(self._key_file), - recursive=False, - ) - def reschedule(self) -> None: - self._observer.unschedule_all() + cmd = f"net.tls('{cert_file}', '{key_file}')" + + cert_files: List[Path] = [] + cert_files.append(cert_file) + cert_files.append(key_file) + + cert_dirs: List[Path] = [] + cert_dirs.append(cert_file.parent) + if cert_file.parent != key_file.parent: + cert_dirs.append(key_file.parent) - # wait for files creation - while not (os.path.exists(self._cert_file) and os.path.exists(self._key_file)): - if os.path.exists(self._cert_file): - logger.error(f"Cannot start watching TLS cert file, '{self._cert_file}' is missing.") - if os.path.exists(self._key_file): - logger.error(f"Cannot start watching TLS cert key file, '{self._key_file}' is missing.") - time.sleep(1) - self.schedule() + event_handler = TLSCertEventHandler(cert_files, cmd) + for d in cert_dirs: + self._observer.schedule( + event_handler, + str(d), + recursive=False, + ) + logger.info(f"Directory '{d}' scheduled for watching") def start(self) -> None: self._observer.start() @@ -124,11 +119,11 @@ if _watchdog: _tls_cert_watchdog.stop() if config.network.tls.cert_file and config.network.tls.key_file: - logger.info("Starting TLS certificate files WatchDog") + logger.info("Initializing TLS certificate files WatchDog") _tls_cert_watchdog = TLSCertWatchDog( - config.network.tls.cert_file.to_path(), config.network.tls.key_file.to_path() + config.network.tls.cert_file.to_path(), + config.network.tls.key_file.to_path(), ) - _tls_cert_watchdog.schedule() _tls_cert_watchdog.start()