From a2c444760d89cf58f3cf54e11809a32d6cf2a30c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0t=C4=9Bp=C3=A1n=20Bal=C3=A1=C5=BEik?= Date: Thu, 30 Oct 2025 13:41:23 +0100 Subject: [PATCH] Refactor ControllableAsyncDnsServer setup When this class was introduced, the constructor of its base class had no parameters. This was changed in the meantime and these parameters were not accessible by users of the subclass. Don't override the constructor. Move command setup to methods. Move subclass-specific storage to cached properties. Take instances of Command instead of the classes themselves for symmetry with install_response_handler. (cherry picked from commit a0970f3d0410afd35799306c24f8ed43955ad027) --- bin/tests/system/chain/ans4/ans.py | 3 +- bin/tests/system/fetchlimit/ans4/ans.py | 3 +- bin/tests/system/forward/ans11/ans.py | 3 +- bin/tests/system/forward/ans6/ans.py | 3 +- bin/tests/system/isctest/asyncserver.py | 40 ++++++++++++++----------- bin/tests/system/xfer/ans9/ans.py | 3 +- 6 files changed, 33 insertions(+), 22 deletions(-) diff --git a/bin/tests/system/chain/ans4/ans.py b/bin/tests/system/chain/ans4/ans.py index 3e042ea58c..249da49b9a 100755 --- a/bin/tests/system/chain/ans4/ans.py +++ b/bin/tests/system/chain/ans4/ans.py @@ -473,7 +473,8 @@ class ChainResponseHandler(DomainHandler): def main() -> None: - server = ControllableAsyncDnsServer(commands=[ChainSetupCommand]) + server = ControllableAsyncDnsServer() + server.install_control_command(ChainSetupCommand()) server.run() diff --git a/bin/tests/system/fetchlimit/ans4/ans.py b/bin/tests/system/fetchlimit/ans4/ans.py index 34891fa310..52be94125c 100644 --- a/bin/tests/system/fetchlimit/ans4/ans.py +++ b/bin/tests/system/fetchlimit/ans4/ans.py @@ -39,7 +39,8 @@ class MaybeDelayedAddressAnswerHandler(ResponseHandler): def main() -> None: - server = ControllableAsyncDnsServer([ToggleResponsesCommand]) + server = ControllableAsyncDnsServer() + server.install_control_command(ToggleResponsesCommand()) server.install_response_handler(MaybeDelayedAddressAnswerHandler()) server.run() diff --git a/bin/tests/system/forward/ans11/ans.py b/bin/tests/system/forward/ans11/ans.py index 8d0b3e9b33..0b4ec5682e 100644 --- a/bin/tests/system/forward/ans11/ans.py +++ b/bin/tests/system/forward/ans11/ans.py @@ -49,7 +49,8 @@ class ExtraAnswersHandler(DomainHandler): def main() -> None: - server = ControllableAsyncDnsServer(commands=[ToggleResponsesCommand]) + server = ControllableAsyncDnsServer() + server.install_control_command(ToggleResponsesCommand()) server.install_response_handler(ExtraAnswersHandler()) server.run() diff --git a/bin/tests/system/forward/ans6/ans.py b/bin/tests/system/forward/ans6/ans.py index f63cdcd4d5..a9fd0b8ac6 100644 --- a/bin/tests/system/forward/ans6/ans.py +++ b/bin/tests/system/forward/ans6/ans.py @@ -72,7 +72,8 @@ class ChaseDsHandler(ResponseHandler): def main() -> None: - server = ControllableAsyncDnsServer([ToggleResponsesCommand]) + server = ControllableAsyncDnsServer() + server.install_control_command(ToggleResponsesCommand()) server.install_response_handler(ChaseDsHandler()) server.run() diff --git a/bin/tests/system/isctest/asyncserver.py b/bin/tests/system/isctest/asyncserver.py index c91f63a123..27a981446b 100644 --- a/bin/tests/system/isctest/asyncserver.py +++ b/bin/tests/system/isctest/asyncserver.py @@ -21,7 +21,6 @@ from typing import ( List, Optional, Tuple, - Type, Union, cast, ) @@ -1281,22 +1280,29 @@ class ControllableAsyncDnsServer(AsyncDnsServer): _CONTROL_DOMAIN = "_control." - def __init__(self, commands: List[Type["ControlCommand"]]): - super().__init__() - self._control_domain = dns.name.from_text(self._CONTROL_DOMAIN) - self._commands: Dict[dns.name.Name, "ControlCommand"] = {} - for command_class in commands: - command = command_class() - command_subdomain = dns.name.Name([command.control_subdomain]) - control_subdomain = command_subdomain.concatenate(self._control_domain) - try: - existing_command = self._commands[control_subdomain] - except KeyError: - self._commands[control_subdomain] = command - else: - raise RuntimeError( - f"{control_subdomain} already handled by {existing_command}" - ) + @functools.cached_property + def _control_domain(self) -> dns.name.Name: + return dns.name.from_text(self._CONTROL_DOMAIN) + + @functools.cached_property + def _commands(self) -> Dict[dns.name.Name, "ControlCommand"]: + return {} + + def install_control_commands(self, commands: List["ControlCommand"]) -> None: + for command in commands: + self.install_control_command(command) + + def install_control_command(self, command: "ControlCommand") -> None: + command_subdomain = dns.name.Name([command.control_subdomain]) + control_subdomain = command_subdomain.concatenate(self._control_domain) + try: + existing_command = self._commands[control_subdomain] + except KeyError: + self._commands[control_subdomain] = command + else: + raise RuntimeError( + f"{control_subdomain} already handled by {existing_command}" + ) async def _prepare_responses( self, qctx: QueryContext diff --git a/bin/tests/system/xfer/ans9/ans.py b/bin/tests/system/xfer/ans9/ans.py index 56c80becb9..f1a4bf5437 100644 --- a/bin/tests/system/xfer/ans9/ans.py +++ b/bin/tests/system/xfer/ans9/ans.py @@ -106,6 +106,7 @@ class AXFRServer(DomainHandler): if __name__ == "__main__": - server = ControllableAsyncDnsServer([ToggleResponsesCommand]) + server = ControllableAsyncDnsServer() + server.install_control_command(ToggleResponsesCommand()) server.install_response_handler(AXFRServer()) server.run()