From: Oto Šťáva Date: Thu, 14 Sep 2023 12:00:28 +0000 (+0200) Subject: manager, kresctl: allow environment variables to specify paths X-Git-Tag: v6.0.3~1^2~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=53b4f2a75b7e59e60c926477db43bcc8fb27a346;p=thirdparty%2Fknot-resolver.git manager, kresctl: allow environment variables to specify paths This is a bigger feature commit for development convenience with Knot Resolver Manager. Firstly, it allows the use of `KRES_MANAGER_CONFIG` and `KRES_MANAGER_RUNTIME` environment variables to specify the paths to the configuration file and Knot Resolver runtime directory. This lets us create local workspace scripts with separate configurations e.g. like this: ```sh script_dir="$(dirname "$(readlink -f "$0")")" export KRES_MANAGER_RUNTIME="$script_dir" export KRES_MANAGER_CONFIG="$script_dir/config.yaml" exec ~/Projects/knot/knot-resolver/manager/poe $@ ``` This ties nicely into the second part, which is that `kresctl` is now able to retrieve the management socket/address directly from the declarative configuration file of Knot Resolver. This again increases convenience for developers who may wish to change these often, and not have to specify/change them every time they want to execute `kresctl` from the command line. `kresctl` adds a new `--config` option to specify the path to the configuration file via command line arguments, and also respects the same `KRES_MANAGER_CONFIG` environment variable, when the argument is not specified. Co-authored-by: Aleš Mrázek --- diff --git a/manager/knot_resolver_manager/cli/command.py b/manager/knot_resolver_manager/cli/command.py index 6533de46c..2c532e8db 100644 --- a/manager/knot_resolver_manager/cli/command.py +++ b/manager/knot_resolver_manager/cli/command.py @@ -1,15 +1,22 @@ import argparse +import os from abc import ABC, abstractmethod # pylint: disable=[no-name-in-module] 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.utils.modeling import parsing + T = TypeVar("T", bound=Type["Command"]) CompWords = Dict[str, Optional[str]] _registered_commands: List[Type["Command"]] = [] +# FIXME ostava: Someone put a FIXME on this value without an explanation, so who knows what is wrong with it? +DEFAULT_SOCKET = "http+unix://%2Fvar%2Frun%2Fknot-resolver%2Fmanager.sock" + def register_command(cls: T) -> T: _registered_commands.append(cls) @@ -37,7 +44,26 @@ class CommandArgs: self.subparser: argparse.ArgumentParser = namespace.subparser self.command: Type["Command"] = namespace.command - self.socket: str = namespace.socket[0] + 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}" + if Path(self.socket).exists(): self.socket = f'http+unix://{quote(self.socket, safe="")}/' if self.socket.endswith("/"): diff --git a/manager/knot_resolver_manager/cli/main.py b/manager/knot_resolver_manager/cli/main.py index dff41df22..6c144a14a 100644 --- a/manager/knot_resolver_manager/cli/main.py +++ b/manager/knot_resolver_manager/cli/main.py @@ -28,13 +28,26 @@ def create_main_argument_parser() -> argparse.ArgumentParser: # default=False, # required=False, # ) - parser.add_argument( + config_or_socket = parser.add_mutually_exclusive_group() + config_or_socket.add_argument( "-s", "--socket", action="store", type=str, - help="Optional, path to Unix-domain socket or network interface of the management API.", - default=["http+unix://%2Fvar%2Frun%2Fknot-resolver%2Fmanager.sock"], # FIXME + help="Optional, path to Unix-domain socket or network interface of the management API. " + "Cannot be used together with '--config'.", + default=[], + nargs=1, + required=False, + ) + config_or_socket.add_argument( + "-c", + "--config", + action="store", + type=str, + help="Optional, path to Knot Resolver declarative configuration to retrieve socket or network " + "interface of the management API from. Cannot be used together with '--socket'.", + default=[], nargs=1, required=False, ) diff --git a/manager/knot_resolver_manager/constants.py b/manager/knot_resolver_manager/constants.py index a502267b9..4ee3a22f7 100644 --- a/manager/knot_resolver_manager/constants.py +++ b/manager/knot_resolver_manager/constants.py @@ -11,6 +11,7 @@ if TYPE_CHECKING: STARTUP_LOG_LEVEL = logging.DEBUG DEFAULT_MANAGER_CONFIG_FILE = Path("/etc/knot-resolver/config.yaml") +CONFIG_FILE_ENV_VAR = "KRES_MANAGER_CONFIG" MANAGER_FIX_ATTEMPT_MAX_COUNTER = 2 FIX_COUNTER_DECREASE_INTERVAL_SEC = 30 * 60 PID_FILE_NAME = "manager.pid" diff --git a/manager/knot_resolver_manager/main.py b/manager/knot_resolver_manager/main.py index f10e1799b..5cbc7f28a 100644 --- a/manager/knot_resolver_manager/main.py +++ b/manager/knot_resolver_manager/main.py @@ -4,12 +4,13 @@ file to allow us to exclude the __main__.py file from black's autoformatting """ import argparse +import os import sys from pathlib import Path from typing import NoReturn from knot_resolver_manager import compat -from knot_resolver_manager.constants import DEFAULT_MANAGER_CONFIG_FILE +from knot_resolver_manager.constants import DEFAULT_MANAGER_CONFIG_FILE, CONFIG_FILE_ENV_VAR from knot_resolver_manager.log import logger_startup from knot_resolver_manager.server import start_server @@ -36,7 +37,13 @@ def main() -> NoReturn: args = parse_args() # where to look for config - config_path = DEFAULT_MANAGER_CONFIG_FILE if args.config is None else Path(args.config[0]) + config_env = os.getenv(CONFIG_FILE_ENV_VAR) + if args.config is not None: + config_path = Path(args.config[0]) + elif config_env is not None: + config_path = Path(config_env) + else: + config_path = DEFAULT_MANAGER_CONFIG_FILE exit_code = compat.asyncio.run(start_server(config=config_path)) sys.exit(exit_code) diff --git a/manager/scripts/run b/manager/scripts/run index db925a0d3..23ed2458a 100755 --- a/manager/scripts/run +++ b/manager/scripts/run @@ -26,6 +26,13 @@ echo Knot Manager API is accessible on http://localhost:5000 echo ------------------------------------------------------- # create runtime directories -mkdir -p etc/knot-resolver/runtime etc/knot-resolver/cache +if [ -z "${KRES_MANAGER_RUNTIME:-}" ]; then + KRES_MANAGER_RUNTIME="etc/knot-resolver" +fi +mkdir -p "$KRES_MANAGER_RUNTIME/runtime" "$KRES_MANAGER_RUNTIME/cache" -python3 -m knot_resolver_manager -c etc/knot-resolver/config.dev.yaml $@ +if [ -z "${KRES_MANAGER_CONFIG:-}" ]; then + KRES_MANAGER_CONFIG="$KRES_MANAGER_RUNTIME/config.dev.yaml" +fi +export KRES_MANAGER_CONFIG +python3 -m knot_resolver_manager $@