]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
datamodel: management socket default based on rundir
authorAleš Mrázek <ales.mrazek@nic.cz>
Wed, 19 Feb 2025 12:16:46 +0000 (13:16 +0100)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 26 Feb 2025 08:16:31 +0000 (09:16 +0100)
doc/_static/config.schema.json
python/knot_resolver/client/command.py
python/knot_resolver/datamodel/config_schema.py
tests/manager/datamodel/test_config_schema.py

index c0e30d3d36e0ded2e5371e0ae188e863e526683b..aee7c3c4a5521e237e33bf8434baee5f0fe33f79 100644 (file)
@@ -54,7 +54,7 @@
             "default": 256
         },
         "management": {
-            "description": "Configuration of management HTTP API.",
+            "description": "Configuration of management HTTP API. By default, unix-socket is located in 'rundir'.",
             "type": "object",
             "properties": {
                 "unix-socket": {
@@ -75,7 +75,7 @@
                 }
             },
             "default": {
-                "unix_socket": "/run/knot-resolver/kres-api.sock",
+                "unix_socket": "kres-api.sock",
                 "interface": null
             }
         },
index 3966f8ca93663b58cbe619cb665f982d3158471a..464eb16e6186220f562b8c2fc5768fa38f68a759 100644 (file)
@@ -4,7 +4,7 @@ from pathlib import Path
 from typing import Dict, List, Optional, Set, Tuple, Type, TypeVar
 from urllib.parse import quote
 
-from knot_resolver.constants import API_SOCK_FILE, CONFIG_FILE
+from knot_resolver.constants import API_SOCK_FILE, API_SOCK_NAME, CONFIG_FILE, RUN_DIR
 from knot_resolver.datamodel.types import IPAddressPort
 from knot_resolver.utils.modeling import parsing
 from knot_resolver.utils.modeling.exceptions import DataValidationError
@@ -154,21 +154,38 @@ def get_socket_from_config(config: Path, optional_file: bool) -> Optional[Socket
     try:
         with open(config, "r", encoding="utf8") as f:
             data = parsing.try_to_parse(f.read())
+
+        rkey = "rundir"
+        rundir = Path(data[rkey]) if rkey in data else RUN_DIR
+
         mkey = "management"
         if mkey in data:
             management = data[mkey]
-            if "unix-socket" in management:
-                return SocketDesc(
-                    f'http+unix://{quote(management["unix-socket"], safe="")}/',
-                    f'Key "/management/unix-socket" in "{config}" file',
-                )
-            if "interface" in management:
-                ip = IPAddressPort(management["interface"], object_path=f"/{mkey}/interface")
+
+            ikey = "interface"
+            if ikey in data[mkey]:
+                ip = IPAddressPort(data[mkey][ikey], object_path=f"/{mkey}/{ikey}")
                 return SocketDesc(
                     f"http://{ip.addr}:{ip.port}",
                     f'Key "/management/interface" in "{config}" file',
                 )
-        return None
+
+            skey = "unix-socket"
+            if skey in management:
+                socket = Path(management[skey])
+                if not socket.is_absolute():
+                    socket = rundir / socket
+                return SocketDesc(
+                    f'http+unix://{quote(str(socket), safe="")}/',
+                    f'Key "/management/unix-socket" in "{config}" file',
+                )
+
+        socket = rundir / API_SOCK_NAME
+        return SocketDesc(
+            f'http+unix://{quote(str(socket), safe="")}/',
+            f'Key "/rundir" in "{config}" file',
+        )
+
     except ValueError as e:
         raise DataValidationError(*e.args) from e  # pylint: disable=no-value-for-parameter
     except OSError as e:
index 410e94d7c9c3520d4f577d77d5ec319ed72f83b6..f8733cb11e61c7649b821ecd6c9dbf5f8624db6b 100644 (file)
@@ -1,9 +1,10 @@
 import logging
 import os
 import socket
+from pathlib import Path
 from typing import Any, Dict, List, Literal, Optional, Tuple, Union
 
-from knot_resolver.constants import API_SOCK_FILE, RUN_DIR, VERSION
+from knot_resolver.constants import API_SOCK_NAME, RUN_DIR, VERSION
 from knot_resolver.datamodel.cache_schema import CacheSchema
 from knot_resolver.datamodel.defer_schema import DeferSchema
 from knot_resolver.datamodel.dns64_schema import Dns64Schema
@@ -95,7 +96,7 @@ class KresConfig(ConfigSchema):
         rundir: Directory where the resolver can create files and which will be it's cwd.
         workers: The number of running kresd (Knot Resolver daemon) workers. If set to 'auto', it is equal to number of CPUs available.
         max_workers: The maximum number of workers allowed. Cannot be changed in runtime.
-        management: Configuration of management HTTP API.
+        management: Configuration of management HTTP API. By default, unix-socket is located in 'rundir'.
         webmgmt: Configuration of legacy web management endpoint.
         options: Fine-tuning global parameters of DNS resolver operation.
         network: Network connections and protocols configuration.
@@ -118,7 +119,7 @@ class KresConfig(ConfigSchema):
         rundir: WritableDir = lazy_default(WritableDir, str(RUN_DIR))
         workers: Union[Literal["auto"], IntPositive] = IntPositive(1)
         max_workers: IntPositive = IntPositive(WORKERS_MAX)
-        management: ManagementSchema = lazy_default(ManagementSchema, {"unix-socket": str(API_SOCK_FILE)})
+        management: ManagementSchema = lazy_default(ManagementSchema, {"unix-socket": str(API_SOCK_NAME)})
         webmgmt: Optional[WebmgmtSchema] = None
         options: OptionsSchema = OptionsSchema()
         network: NetworkSchema = NetworkSchema()
@@ -173,6 +174,14 @@ class KresConfig(ConfigSchema):
             )
         return obj.workers
 
+    def _management(self, obj: Raw) -> Any:
+        if obj.management.unix_socket:
+            soc = Path(obj.management.unix_socket.serialize())
+            if soc.is_absolute():
+                return obj.management
+            return ManagementSchema({"unix-socket": str(obj.rundir.to_path() / soc)})
+        return obj.management
+
     def _dnssec(self, obj: Raw) -> Any:
         if obj.dnssec is True:
             return DnssecSchema()
@@ -259,7 +268,7 @@ def kres_config_json_schema() -> Dict[str, Any]:
     """
 
     context = get_global_validation_context()
-    set_global_validation_context(Context(None, False))
+    set_global_validation_context(Context(RUN_DIR, False))
 
     schema = KresConfig.json_schema(
         schema_id=f"https://www.knot-resolver.cz/documentation/v{VERSION}/_static/config.schema.json",
index 9ec2b31b281784bdaf8b2b4546a2c2a9522db743..437cade8d68dbd5e3d8e5190c56690d4b775661e 100644 (file)
@@ -1,7 +1,9 @@
 import inspect
 import json
+import os
 from typing import Any, Dict, Type, cast
 
+from knot_resolver.constants import API_SOCK_FILE, API_SOCK_NAME, RUN_DIR
 from knot_resolver.datamodel import KresConfig
 from knot_resolver.datamodel.lua_schema import LuaSchema
 from knot_resolver.utils.modeling import BaseSchema
@@ -49,10 +51,31 @@ def test_config_check_str_type():
 def test_config_defaults():
     config = KresConfig()
 
+    # Management API default
+    assert config.management.unix_socket.to_path() == API_SOCK_FILE
+
     # DNS64 default
     assert config.dns64 == False
 
 
+def test_management_unix_socket():
+    cwd = os.getcwd()
+    config = KresConfig({"rundir": cwd})
+    assert str(config.management.unix_socket) == f"{cwd}/{API_SOCK_NAME}"
+
+    my_soc = "my-new.soc"
+    config = KresConfig({"management": {"unix-socket": my_soc}})
+    assert str(config.management.unix_socket) == f"{RUN_DIR}/{my_soc}"
+
+
+def test_management_interface():
+    cwd = os.getcwd()
+    config = KresConfig({"rundir": cwd, "management": {"interface": "127.0.0.1@5000"}})
+
+    assert config.management.unix_socket == None
+    assert str(config.management.interface) == "127.0.0.1@5000"
+
+
 def test_dnssec_false():
     config = KresConfig({"dnssec": False})