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
if _watchdog:
from watchdog.events import (
- DirDeletedEvent,
- DirModifiedEvent,
- FileDeletedEvent,
- FileModifiedEvent,
+ FileSystemEvent,
FileSystemEventHandler,
)
from watchdog.observers import Observer
_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()
_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()