From 90a6865069ebbac0f13b8b41a0961a7244858172 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Vladim=C3=ADr=20=C4=8Cun=C3=A1t?= Date: Wed, 6 Nov 2024 11:16:11 +0100 Subject: [PATCH] rate-limiting config: use more precise types MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This gives us a bit more consistency. I suppose that IntRangeBase isn't worth doing for the other cases. Aleš helped with this. --- doc/_static/config.schema.json | 12 ++++++-- .../datamodel/rate_limiting_schema.py | 29 +++++++++---------- .../datamodel/templates/rate_limiting.lua.j2 | 2 +- .../knot_resolver/datamodel/types/__init__.py | 2 ++ python/knot_resolver/datamodel/types/types.py | 5 ++++ 5 files changed, 31 insertions(+), 19 deletions(-) diff --git a/doc/_static/config.schema.json b/doc/_static/config.schema.json index c6934109a..a0572813e 100644 --- a/doc/_static/config.schema.json +++ b/doc/_static/config.schema.json @@ -1677,27 +1677,33 @@ "properties": { "capacity": { "type": "integer", + "minimum": 1, "description": "Expected maximal number of blocked networks/hosts at the same time.", "default": 524288 }, "rate-limit": { "type": "integer", + "minimum": 1, "description": "Maximal number of allowed queries per second from a single host." }, "instant-limit": { "type": "integer", + "minimum": 1, "description": "Maximal number of allowed queries at a single point in time from a single host.", "default": 50 }, "slip": { "type": "integer", + "minimum": 0, + "maximum": 32, "description": "Number of restricted responses out of which one is sent as truncated, the others are dropped.", "default": 2 }, "log-period": { - "type": "integer", - "description": "Minimal time in msec between two log messages, or zero to disable.", - "default": 0 + "type": "string", + "pattern": "^(\\d+)(us|ms|s|m|h|d)$", + "description": "Minimal time between two log messages, or '0s' to disable.", + "default": "0s" }, "dry-run": { "type": "boolean", diff --git a/python/knot_resolver/datamodel/rate_limiting_schema.py b/python/knot_resolver/datamodel/rate_limiting_schema.py index d93272da4..60994c206 100644 --- a/python/knot_resolver/datamodel/rate_limiting_schema.py +++ b/python/knot_resolver/datamodel/rate_limiting_schema.py @@ -1,3 +1,8 @@ +from knot_resolver.datamodel.types import ( + Int0_32, + IntPositive, + TimeUnit, +) from knot_resolver.utils.modeling import ConfigSchema @@ -10,26 +15,20 @@ class RateLimitingSchema(ConfigSchema): rate_limit: Maximal number of allowed queries per second from a single host. instant_limit: Maximal number of allowed queries at a single point in time from a single host. slip: Number of restricted responses out of which one is sent as truncated, the others are dropped. - log_period: Minimal time in msec between two log messages, or zero to disable. + log_period: Minimal time between two log messages, or '0s' to disable. dry_run: Perform only classification and logging but no restrictions. """ - capacity: int = 524288 - rate_limit: int - instant_limit: int = 50 - slip: int = 2 - log_period: int = 0 + capacity: IntPositive = IntPositive(524288) + rate_limit: IntPositive + instant_limit: IntPositive = IntPositive(50) + slip: Int0_32 = Int0_32(2) + log_period: TimeUnit = TimeUnit("0s") dry_run: bool = False def _validate(self) -> None: - max_instant_limit = int(2**32 / 768 - 1) - if not 1 <= self.instant_limit <= max_instant_limit: + max_instant_limit = int(2 ** 32 // 768 - 1) + if not int(self.instant_limit) <= max_instant_limit: raise ValueError(f"'instant-limit' has to be in range 1..{max_instant_limit}") - if not 1 <= self.rate_limit <= 1000 * self.instant_limit: + if not int(self.rate_limit) <= 1000 * int(self.instant_limit): raise ValueError("'rate-limit' has to be in range 1..(1000 * instant-limit)") - if not 0 < self.capacity: - raise ValueError("'capacity' has to be positive") - if not 0 <= self.slip <= 100: - raise ValueError("'slip' has to be in range 0..100") - if not 0 <= self.log_period: - raise ValueError("'log-period' has to be non-negative") diff --git a/python/knot_resolver/datamodel/templates/rate_limiting.lua.j2 b/python/knot_resolver/datamodel/templates/rate_limiting.lua.j2 index 4f9547f54..63f921257 100644 --- a/python/knot_resolver/datamodel/templates/rate_limiting.lua.j2 +++ b/python/knot_resolver/datamodel/templates/rate_limiting.lua.j2 @@ -7,6 +7,6 @@ assert(C.ratelimiting_init( {{ cfg.rate_limiting.instant_limit }}, {{ cfg.rate_limiting.rate_limit }}, {{ cfg.rate_limiting.slip }}, - {{ cfg.rate_limiting.log_period }}, + {{ cfg.rate_limiting.log_period.millis() }}, {{ boolean(cfg.rate_limiting.dry_run) }}) == 0) {%- endif %} diff --git a/python/knot_resolver/datamodel/types/__init__.py b/python/knot_resolver/datamodel/types/__init__.py index a3d7db3e6..d1334b5a2 100644 --- a/python/knot_resolver/datamodel/types/__init__.py +++ b/python/knot_resolver/datamodel/types/__init__.py @@ -6,6 +6,7 @@ from .types import ( EscapedStr, EscapedStr32B, IDPattern, + Int0_32, Int0_512, Int0_65535, InterfaceName, @@ -37,6 +38,7 @@ __all__ = [ "EscapedStr", "EscapedStr32B", "IDPattern", + "Int0_32", "Int0_512", "Int0_65535", "InterfaceName", diff --git a/python/knot_resolver/datamodel/types/types.py b/python/knot_resolver/datamodel/types/types.py index 6cd1e4cbd..3c9b9fe1c 100644 --- a/python/knot_resolver/datamodel/types/types.py +++ b/python/knot_resolver/datamodel/types/types.py @@ -14,6 +14,11 @@ class IntPositive(IntRangeBase): _min: int = 1 +class Int0_32(IntRangeBase): # noqa: N801 + _min: int = 0 + _max: int = 32 + + class Int0_512(IntRangeBase): # noqa: N801 _min: int = 0 _max: int = 512 -- 2.47.2