From 63a0fdde89b85a947b830e9fd1848dd62dd53ee4 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ale=C5=A1=20Mr=C3=A1zek?= Date: Wed, 29 Jul 2026 17:03:52 +0200 Subject: [PATCH] controller: change to notify_socket.py module Store original NOTIFY_SOCKET value. Remove obsolete systemd_notify.py module from utils. --- .../supervisord/plugin/manager_integration.py | 11 ++++-- .../supervisord/plugin/sd_notify.py | 21 +++++++---- python/knot_resolver/manager/server.py | 10 ++--- python/knot_resolver/utils/systemd_notify.py | 37 ------------------- 4 files changed, 26 insertions(+), 53 deletions(-) delete mode 100644 python/knot_resolver/utils/systemd_notify.py diff --git a/python/knot_resolver/controller/supervisord/plugin/manager_integration.py b/python/knot_resolver/controller/supervisord/plugin/manager_integration.py index 6c575b956..b8fdab4e2 100644 --- a/python/knot_resolver/controller/supervisord/plugin/manager_integration.py +++ b/python/knot_resolver/controller/supervisord/plugin/manager_integration.py @@ -12,11 +12,14 @@ from supervisor.process import Subprocess from supervisor.states import SupervisorStates from supervisor.supervisord import Supervisor -from knot_resolver.utils.systemd_notify import systemd_notify +from .notify_socket import send_notify_socket_message, NOTIFY_SOCKET superd: Optional[Supervisor] = None +SYSTEMD_NOTIFY_SOCKET: str | None = os.environ.get(NOTIFY_SOCKET) + + def check_for_fatal_manager(event: ProcessStateFatalEvent) -> None: assert superd is not None @@ -38,7 +41,7 @@ def check_for_starting_manager(event: ProcessStateStartingEvent) -> None: processname = as_string(proc.config.name) if processname == "manager": # manager has sucessfully started, report it upstream - systemd_notify(STATUS="Starting services...") + send_notify_socket_message(SYSTEMD_NOTIFY_SOCKET, STATUS="Starting services...") def check_for_runnning_manager(event: ProcessStateRunningEvent) -> None: @@ -48,7 +51,7 @@ def check_for_runnning_manager(event: ProcessStateRunningEvent) -> None: processname = as_string(proc.config.name) if processname == "manager": # manager has sucessfully started, report it upstream - systemd_notify(READY="1", STATUS="Ready") + send_notify_socket_message(SYSTEMD_NOTIFY_SOCKET, READY="1", STATUS="Ready") def get_server_options_signal(self): @@ -69,7 +72,7 @@ def inject(supervisord: Supervisor, **_config: Any) -> Any: # pylint: disable=u # This status notification here unsets the env variable $NOTIFY_SOCKET provided by systemd # and stores it locally. Therefore, it shouldn't clash with $NOTIFY_SOCKET we are providing # downstream - systemd_notify(STATUS="Initializing supervisord...") + send_notify_socket_message(SYSTEMD_NOTIFY_SOCKET, STATUS="Initializing supervisord...") # register events subscribe(ProcessStateFatalEvent, check_for_fatal_manager) diff --git a/python/knot_resolver/controller/supervisord/plugin/sd_notify.py b/python/knot_resolver/controller/supervisord/plugin/sd_notify.py index dea4ed8e7..9cd3ab13f 100644 --- a/python/knot_resolver/controller/supervisord/plugin/sd_notify.py +++ b/python/knot_resolver/controller/supervisord/plugin/sd_notify.py @@ -9,6 +9,7 @@ if NOTIFY_SUPPORT: import signal import time from functools import partial + from pathlib import Path from typing import Any, Callable, Dict, List, Optional, Tuple, TypeVar from supervisor.events import ProcessStateEvent, ProcessStateStartingEvent, subscribe @@ -17,7 +18,12 @@ if NOTIFY_SUPPORT: from supervisor.states import ProcessStates from supervisor.supervisord import Supervisor - from knot_resolver.controller.supervisord.plugin import notify + from knot_resolver.controller.supervisord.plugin.notify_socket import ( + init_notify_socket, + read_notify_socket, + NOTIFY_SOCKET, + NOTIFY_SOCKET_NAME, + ) starting_processes: List[Subprocess] = [] @@ -34,7 +40,7 @@ if NOTIFY_SUPPORT: def __init__(self, supervisor: Supervisor, fd: int): self._supervisor = supervisor self.fd = fd - self.closed = False # True if close() has been called + self.closed: bool = False # True if close() has been called def __repr__(self): return f"<{self.__class__.__name__} with fd={self.fd}>" @@ -48,10 +54,11 @@ if NOTIFY_SUPPORT: def handle_read_event(self): logger: Any = self._supervisor.options.logger - res: Optional[Tuple[int, bytes]] = notify.read_message(self.fd) - if res is None: + result: tuple[int, bytes] | None = read_notify_socket(self.fd) + if result is None: return # there was some junk - pid, data = res + + pid, data = result # pylint: disable=undefined-loop-variable for proc in starting_processes: @@ -162,7 +169,7 @@ if NOTIFY_SUPPORT: def supervisord_get_process_map(supervisord: Any, mp: Dict[Any, Any]) -> Dict[Any, Any]: global notify_dispatcher if notify_dispatcher is None: - notify_dispatcher = NotifySocketDispatcher(supervisord, notify.init_socket()) + notify_dispatcher = NotifySocketDispatcher(supervisord, init_notify_socket()) supervisord.options.logger.info("notify: injected $NOTIFY_SOCKET into event loop") # add our dispatcher to the result @@ -173,7 +180,7 @@ if NOTIFY_SUPPORT: def process_spawn_as_child_add_env(slf: Subprocess, *args: Any) -> Tuple[Any, ...]: if is_type_notify(slf): - slf.config.environment["NOTIFY_SOCKET"] = os.getcwd() + "/supervisor-notify-socket" + slf.config.environment[NOTIFY_SOCKET] = str(Path.cwd() / NOTIFY_SOCKET_NAME) return (slf, *args) T = TypeVar("T") diff --git a/python/knot_resolver/manager/server.py b/python/knot_resolver/manager/server.py index 0c907230f..1c57bbdc5 100644 --- a/python/knot_resolver/manager/server.py +++ b/python/knot_resolver/manager/server.py @@ -23,6 +23,7 @@ from knot_resolver.controller import get_best_controller_implementation from knot_resolver.controller.exceptions import KresSubprocessControllerError, KresSubprocessControllerExec from knot_resolver.controller.interface import SubprocessType from knot_resolver.controller.registered_workers import command_single_registered_worker +from knot_resolver.controller.supervisord.plugin.notify_socket import send_notify_socket_message from knot_resolver.datamodel import kres_config_json_schema from knot_resolver.datamodel.cache_schema import CacheClearRPCSchema from knot_resolver.datamodel.config_schema import KresConfig, get_rundir_without_validation @@ -40,7 +41,6 @@ from knot_resolver.utils.modeling.exceptions import AggregateDataValidationError from knot_resolver.utils.modeling.parsing import DataFormat, data_combine, try_to_parse from knot_resolver.utils.modeling.query import query from knot_resolver.utils.modeling.types import NoneType -from knot_resolver.utils.systemd_notify import systemd_notify from .config_store import ConfigStore from .constants import PID_FILE_NAME, init_user_constants @@ -172,9 +172,9 @@ class Server: async def sighup_handler(self) -> None: logger.info("Received SIGHUP, reloading configuration file") - systemd_notify(RELOADING="1") + send_notify_socket_message(RELOADING="1") await self._reload_config() - systemd_notify(READY="1") + send_notify_socket_message(READY="1") @staticmethod def all_handled_signals() -> Set[signal.Signals]: @@ -681,12 +681,12 @@ async def start_server(config: List[str]) -> int: # noqa: C901, PLR0915 logger.info(f"Manager fully initialized and running in {round(time() - start_time, 3)} seconds") # notify systemd/anything compatible that we are ready - systemd_notify(READY="1") + send_notify_socket_message(READY="1") await server.wait_for_shutdown() # notify systemd that we are shutting down - systemd_notify(STOPPING="1") + send_notify_socket_message(STOPPING="1") # Ok, now we are tearing everything down. diff --git a/python/knot_resolver/utils/systemd_notify.py b/python/knot_resolver/utils/systemd_notify.py deleted file mode 100644 index bf0221c82..000000000 --- a/python/knot_resolver/utils/systemd_notify.py +++ /dev/null @@ -1,37 +0,0 @@ -import logging -import os -import socket - -logger = logging.getLogger(__name__) - - -def systemd_notify(**values: str) -> None: - """ - Send systemd notify message to notify socket. - - Notify socket location (unix socket) should be saved in $NOTIFY_SOCKET environment variable. - It is typically set by the processes supervisor (supervisord). - If $NOTIFY_SOCKET is not configured, it is not possible to send a notification and the operation will fail. - """ - socket_addr = os.getenv("NOTIFY_SOCKET") - if socket_addr is None: - logger.warning("Failed to get $NOTIFY_SOCKET environment variable") - return - - if socket_addr.startswith("@"): - socket_addr = socket_addr.replace("@", "\0", 1) - - try: - notify_socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) - notify_socket.connect(socket_addr) - except OSError: - logger.exception("Failed to connect to $NOTIFY_SOCKET at '%s'", socket_addr) - return - - payload = "\n".join((f"{key}={value}" for key, value in values.items())) - try: - notify_socket.send(payload.encode("utf8")) - except OSError: - logger.exception("Failed to send systemd notification to $NOTIFY_SOCKET at '%s'", socket_addr) - - notify_socket.close() -- 2.47.3