Skip to content

Commit

Permalink
manager: files watchdog: watchdog created specifically for TLS certif…
Browse files Browse the repository at this point in the history
…icate
  • Loading branch information
alesmrazek committed Nov 25, 2024
1 parent 564531b commit d90a677
Showing 1 changed file with 66 additions and 36 deletions.
102 changes: 66 additions & 36 deletions python/knot_resolver/manager/files/watchdog.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import importlib
import logging
from pathlib import Path
from typing import List, Optional, Union
from threading import Timer
from typing import Dict, List, Optional, Tuple, Type, Union

from knot_resolver.controller.registered_workers import command_registered_workers
from knot_resolver.datamodel import KresConfig
Expand All @@ -16,54 +16,82 @@
logger = logging.getLogger(__name__)


def files_to_watch(config: KresConfig) -> List[Path]:
def tls_cert_paths(config: KresConfig) -> List[str]:
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]
return [str(file) for file in files if file is not None]


if _watchdog:
from watchdog.events import (
DirDeletedEvent,
DirModifiedEvent,
FileDeletedEvent,
FileModifiedEvent,
FileSystemEventHandler,
)
from watchdog.observers import Observer

_files_watchdog: Optional["FilesWatchDog"] = None
_tls_cert_watchdog: Optional["TLSCertWatchDog"] = None

class CertificatesEventHandler(FileSystemEventHandler):

def __init__(self, config: KresConfig) -> None:
class TLSCertEventHandler(FileSystemEventHandler):
def __init__(self, config: KresConfig, delay: int) -> 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
self._delay = delay
self._cmd = f"net.tls('{config.network.tls.cert_file}', '{config.network.tls.key_file}')"
self._cmd_triggered: bool = False

# 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:
def trigger_cmd(self) -> None:
logger.info("Commanding workers to reload TLS certificate")
if compat.asyncio.is_event_loop_running():
compat.asyncio.create_task(command_registered_workers(self._command))
compat.asyncio.create_task(command_registered_workers(self._cmd))
else:
compat.asyncio.run(command_registered_workers(self._command))
compat.asyncio.run(command_registered_workers(self._cmd))
self._cmd_triggered = False

# def on_closed(self, event: FileClosedEvent) -> None:
# pass
def on_deleted(self, event: Union[DirDeletedEvent, FileDeletedEvent]) -> None:
pass
# path = str(event.src_path)
# logger.info(f"Stopped watching '{path}', because it was replaced or deleted")
# if _tls_cert_watchdog:
# logger.info(f"Trying to reschedule watching '{path}'")
# _tls_cert_watchdog.reschedule(path)

class FilesWatchDog:
def __init__(self, config: KresConfig, files: List[Path]) -> None:
def on_modified(self, event: Union[DirModifiedEvent, FileModifiedEvent]) -> None:
# skipping if command was already triggered
if not self._cmd_triggered:
logger.info(f"Reload TLS certificate will start in {self._delay} seconds")
timer = Timer(self._delay, self.trigger_cmd)
self._cmd_triggered = True
timer.start()

class TLSCertWatchDog:
def __init__(self, config: KresConfig) -> None:
self._observer = Observer()
for file in files:
self._observer.schedule(CertificatesEventHandler(config), str(file), recursive=False)
logger.info(f"Watching '{file}. file")
self._config = config

def schedule(self) -> None:
event_handler = TLSCertEventHandler(self._config, delay=5)

if self._observer and self._config.network.tls.cert_file:
self._observer.schedule(
event_handler,
str(self._config.network.tls.cert_file),
event_filter=[FileModifiedEvent, DirDeletedEvent],
)
if self._observer and self._config.network.tls.key_file:
self._observer.schedule(
event_handler,
str(self._config.network.tls.key_file),
event_filter=[FileModifiedEvent, DirDeletedEvent],
)

def reschedule(self, path: str) -> None:
if self._observer:
self._observer.unschedule_all()
self.schedule()

def start(self) -> None:
if self._observer:
Expand All @@ -74,15 +102,17 @@ def stop(self) -> None:
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()
@only_on_real_changes_update(tls_cert_paths)
async def _init_tls_cert_watchdog(config: KresConfig) -> None:
global _tls_cert_watchdog
if _tls_cert_watchdog is None:
logger.info("Starting TLS certificate WatchDog")
_tls_cert_watchdog = TLSCertWatchDog(config)
_tls_cert_watchdog.schedule()
_tls_cert_watchdog.start()


async def init_files_watchdog(config_store: ConfigStore) -> None:
if _watchdog:
await config_store.register_on_change_callback(_init_files_watchdog)
# watchdog for TLS certificate files
await config_store.register_on_change_callback(_init_tls_cert_watchdog)

0 comments on commit d90a677

Please sign in to comment.