From: Pieter Lexis Date: Wed, 1 Apr 2026 13:35:22 +0000 (+0200) Subject: feat(dnsdist): add named maintenance callbacks X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f8f953f5110c1cff0699fd2fa48dbb26a91ea655;p=thirdparty%2Fpdns.git feat(dnsdist): add named maintenance callbacks --- diff --git a/pdns/dnsdistdist/dnsdist-lua-hooks.cc b/pdns/dnsdistdist/dnsdist-lua-hooks.cc index 7080f0cd6b..c23e6d58d8 100644 --- a/pdns/dnsdistdist/dnsdist-lua-hooks.cc +++ b/pdns/dnsdistdist/dnsdist-lua-hooks.cc @@ -1,8 +1,10 @@ #include "dnsdist-lua-hooks.hh" #include "dnsdist-lua.hh" +#include "dnsdist-opentelemetry.hh" #include "lock.hh" #include "tcpiohandler.hh" +#include namespace dnsdist::lua::hooks { @@ -12,21 +14,22 @@ using TicketsKeyAddedHook = std::function; using ServerStateChangeCallback = std::function; static LockGuarded> s_exitCallbacks; -static LockGuarded> s_maintenanceHooks; +static LockGuarded>> s_maintenanceHooks; static LockGuarded> s_serverStateChangeHooks; -void runMaintenanceHooks(const LuaContext& context) +void runMaintenanceHooks(const LuaContext& context, std::shared_ptr& tracer) { (void)context; for (const auto& callback : *(s_maintenanceHooks.lock())) { - callback(); + pdns::trace::dnsdist::getCloserForInternalSpan(tracer, callback.first); + callback.second(); } } -static void addMaintenanceCallback(const LuaContext& context, MaintenanceCallback callback) +static void addMaintenanceCallback(const LuaContext& context, MaintenanceCallback callback, std::string name = "") { (void)context; - s_maintenanceHooks.lock()->push_back(std::move(callback)); + s_maintenanceHooks.lock()->push_back({"maintenanceCallback/" + name, std::move(callback)}); } void clearMaintenanceHooks() @@ -89,9 +92,9 @@ void clearServerStateChangeCallbacks() void setupLuaHooks(LuaContext& luaCtx) { - luaCtx.writeFunction("addMaintenanceCallback", [&luaCtx](const MaintenanceCallback& callback) { + luaCtx.writeFunction("addMaintenanceCallback", [&luaCtx](const MaintenanceCallback& callback, const std::optional name) { setLuaSideEffect(); - addMaintenanceCallback(luaCtx, callback); + addMaintenanceCallback(luaCtx, callback, name.value_or("unnamed")); }); luaCtx.writeFunction("addExitCallback", [&luaCtx](const ExitCallback& callback) { setLuaSideEffect(); diff --git a/pdns/dnsdistdist/dnsdist-lua-hooks.hh b/pdns/dnsdistdist/dnsdist-lua-hooks.hh index 89a4d485e7..83d87718b9 100644 --- a/pdns/dnsdistdist/dnsdist-lua-hooks.hh +++ b/pdns/dnsdistdist/dnsdist-lua-hooks.hh @@ -21,14 +21,15 @@ */ #pragma once -#include +#include "dnsdist-opentelemetry.hh" +#include #include class LuaContext; namespace dnsdist::lua::hooks { -void runMaintenanceHooks(const LuaContext& context); +void runMaintenanceHooks(const LuaContext& context, std::shared_ptr& tracer); void clearMaintenanceHooks(); void runExitCallbacks(const LuaContext& context); void clearExitCallbacks(); diff --git a/pdns/dnsdistdist/docs/reference/config.rst b/pdns/dnsdistdist/docs/reference/config.rst index abb2178358..05addaafe6 100644 --- a/pdns/dnsdistdist/docs/reference/config.rst +++ b/pdns/dnsdistdist/docs/reference/config.rst @@ -2173,14 +2173,17 @@ Other functions :param function callback: The function to be called. It takes no parameter and returns no value. -.. function:: addMaintenanceCallback(callback) +.. function:: addMaintenanceCallback(callback[, name]) .. versionadded:: 1.9.0 + .. versionchanged:: 2.2.0 + ``name`` parameter added. Register a Lua function to be called as part of the ``maintenance`` hook, which is executed roughly every second. The function should not block for a long period of time, as it would otherwise delay the execution of the other functions registered for this hook, as well as the execution of the :func:`maintenance` function. :param function callback: The function to be called. It takes no parameter and returns no value. + :param string name: The name of the callback, currently only exposed in :doc:`OpenTelemetry traces `. .. code-block:: lua diff --git a/regression-tests.dnsdist/test_OpenTelemetryTracing.py b/regression-tests.dnsdist/test_OpenTelemetryTracing.py index 9ace75a4c7..6978de8a91 100644 --- a/regression-tests.dnsdist/test_OpenTelemetryTracing.py +++ b/regression-tests.dnsdist/test_OpenTelemetryTracing.py @@ -2,6 +2,7 @@ import base64 import binascii +import pprint import threading import time @@ -71,6 +72,7 @@ class DNSDistOpenTelemetryProtobufTest(test_Protobuf.DNSDistProtobufTest): return self.getFirstProtobufMessage(timeout=1) def checkOTDataBase(self, otData): + pprint.pprint(otData) self.assertEqual(len(otData["resource_spans"]), 1) self.assertEqual(len(otData["resource_spans"][0]["resource"]["attributes"]), 1) @@ -1281,12 +1283,12 @@ class TestOpenTelemetryTracingInternalBase(DNSDistOpenTelemetryProtobufTest): return result[0] raise KeyError(f"{name} not found in OT Data") - def checkMaintenanceSpanNames(self, all_span_name, extra_names=set()): + def checkMaintenanceSpanNames(self, all_span_name, extra_names=set(), callback_names=set()): all_names = { "maintenanceThread", "maintenanceHooks", "DynamicBlocks::runRegisteredGroups", - }.union(extra_names) + }.union(extra_names).union({f"maintenanceCallback/{name}" for name in callback_names}) self.assertSetEqual(all_span_name, all_names) @@ -1434,6 +1436,61 @@ addMaintenanceCallback(my_maintenance) { "my-span", }, + { + "unnamed", + }, + ) + + maintenanceFunction_span = self.getSpan(otData, "maintenanceHooks") + self.assertListEqual( + maintenanceFunction_span["attributes"], + [{"key": "outside", "value": {"string_value": "hello from the outside"}}], + ) + + my_span = self.getSpan(otData, "my-span") + self.assertListEqual( + my_span["attributes"], + [{"key": "inside", "value": {"string_value": "hello from the inside"}}], + ) + + +class TestOpenTelemetryTracingInternalWithFunctionsNamedCallbackLua(TestOpenTelemetryTracingInternalBase): + _config_template = """ +local function my_maintenance() + setSpanAttribute("outside", "hello from the outside") + withTraceSpan("my-span", function() + setSpanAttribute("inside", "hello from the inside") + os.execute("sleep 0.1") + end) +end + +newServer{address="127.0.0.1:%d"} +getServer(0):setUp() +rl = newRemoteLogger('127.0.0.1:%d') +setOpenTelemetryTracing(true) +setOpenTelemetryInternalTrace('maintenance', {rl}, 60) + +addMaintenanceCallback(my_maintenance, "my_maintenance") +""" + + _config_params = [ + "_testServerPort", + "_protobufServerPort", + ] + + def testMaintenance(self): + otData = self.getFirstMaintenanceProtobufMessage() + + msg_span_name = {v["name"] for v in otData["resource_spans"][0]["scope_spans"][0]["spans"]} + + self.checkMaintenanceSpanNames( + msg_span_name, + { + "my-span", + }, + { + "my_maintenance", + }, ) maintenanceFunction_span = self.getSpan(otData, "maintenanceHooks")