From 2861260caf3f1a7f97edf5de6bc3324c9394666c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Oto=20=C5=A0=C5=A5=C3=A1va?= Date: Fri, 15 Sep 2023 15:42:35 +0200 Subject: [PATCH] kresctl: improve default connection behaviour It now searches `/etc/knot-resolver/config.yml` for `management` configuration first, when no `--config` or `--socket` is specified. --- doc/manager-client.rst | 10 ++- manager/knot_resolver_manager/cli/command.py | 65 ++++++++++++++------ 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/doc/manager-client.rst b/doc/manager-client.rst index 704c1667b..ddb1f695b 100644 --- a/doc/manager-client.rst +++ b/doc/manager-client.rst @@ -30,9 +30,13 @@ the :option:`--config <-c , --config >` or :option:`--socket <-s , --socket >` option to tell ``kresctl`` where to look for the API. -By default, ``kresctl`` connects to the ``/var/run/knot-resolver/manager.sock`` -Unix-domain socket, or, when specified, reads the ``KRES_MANAGER_CONFIG`` -environment variable to retrieve a path to a configuration file. +If the ``management`` key is not present in the configuration file, ``kresctl`` +attempts to connect to the ``/var/run/knot-resolver/manager.sock`` Unix-domain +socket, which is the Manager's default communication channel. + +By default, ``kresctl`` tries to find the correct communication channel in +``/etc/knot-resolver/config.yaml``, or, if present, the file specified by the +``KRES_MANAGER_CONFIG`` environment variable. .. option:: -s , --socket diff --git a/manager/knot_resolver_manager/cli/command.py b/manager/knot_resolver_manager/cli/command.py index 2c532e8db..c8752d673 100644 --- a/manager/knot_resolver_manager/cli/command.py +++ b/manager/knot_resolver_manager/cli/command.py @@ -5,7 +5,7 @@ from pathlib import Path from typing import Dict, List, Optional, Tuple, Type, TypeVar from urllib.parse import quote -from knot_resolver_manager.constants import CONFIG_FILE_ENV_VAR +from knot_resolver_manager.constants import DEFAULT_MANAGER_CONFIG_FILE, CONFIG_FILE_ENV_VAR from knot_resolver_manager.utils.modeling import parsing T = TypeVar("T", bound=Type["Command"]) @@ -37,6 +37,49 @@ def install_commands_parsers(parser: argparse.ArgumentParser) -> None: subparser.set_defaults(command=typ, subparser=subparser) +def get_socket_from_config(config: Path, optional_file: bool) -> Optional[str]: + try: + with open(config, "r") as f: + data = parsing.try_to_parse(f.read()) + if "management" in data: + management = data["management"] + if "unix_socket" in management: + return management["unix_socket"] + elif "interface" in management: + split = management["interface"].split("@") + host = split[0] + port = split[1] if len(split) >= 2 else 80 + return f"http://{host}:{port}" + return None + except OSError as e: + if not optional_file: + raise e + return None + + +def determine_socket(namespace: argparse.Namespace) -> str: + if len(namespace.socket) > 0: + return namespace.socket[0] + + socket: Optional[str] = None + if len(namespace.config) > 0: + socket = get_socket_from_config(namespace.config[0], False) + if socket is not None: + return socket + else: + config_env = os.getenv(CONFIG_FILE_ENV_VAR) + if config_env is not None: + socket = get_socket_from_config(Path(config_env), False) + if socket is not None: + return socket + else: + socket = get_socket_from_config(DEFAULT_MANAGER_CONFIG_FILE, True) + if socket is not None: + return socket + + return DEFAULT_SOCKET + + class CommandArgs: def __init__(self, namespace: argparse.Namespace, parser: argparse.ArgumentParser) -> None: self.namespace = namespace @@ -44,25 +87,7 @@ class CommandArgs: self.subparser: argparse.ArgumentParser = namespace.subparser self.command: Type["Command"] = namespace.command - config_env = os.getenv(CONFIG_FILE_ENV_VAR) - if len(namespace.socket) == 0 and len(namespace.config) == 0 and config_env is not None: - namespace.config = [config_env] - - self.socket: str = DEFAULT_SOCKET - if len(namespace.socket) > 0: - self.socket = namespace.socket[0] - elif len(namespace.config) > 0: - with open(namespace.config[0], "r") as f: - config = parsing.try_to_parse(f.read()) - if "management" in config: - management = config["management"] - if "unix_socket" in management: - self.socket = management["unix_socket"] - elif "interface" in management: - split = management["interface"].split("@") - host = split[0] - port = split[1] if len(split) >= 2 else 80 - self.socket = f"http://{host}:{port}" + self.socket: str = determine_socket(namespace) if Path(self.socket).exists(): self.socket = f'http+unix://{quote(self.socket, safe="")}/' -- 2.47.2