From 5a78def249b4603085f6a88f7b94b299e8bad7eb Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Sun, 29 Mar 2026 17:26:39 +0200 Subject: [PATCH] testbench: queue timeouts via imdiag module params; yaml-only mode fixes and docs - imdiag: add 5 module-scope config params to control key queue timeouts (mainmsgqueuetimeoutshutdown, mainmsgqueuetimeoutenqueue, inputshutdowntimeout, defaultactionqueuetimeoutshutdown, defaultactionqueuetimeoutenqueue); these replace per-test legacy $MainMsg* directives and eliminate races on slow machines (Solaris) - imdiag: add assert(0) for unhandled params in setModCnf/newInpInst - imdiag: modExit sets abortTimeout=-1 after joining timeoutGuard thread to prevent double-cancel if resetConfigVariables runs afterwards - diag.sh: generate_conf --yaml-only flag for YAML-only test mode - diag.sh: yaml preamble uses new imdiag module params - diag.sh: add RSTB_MAIN_Q_TO_ENQUEUE separate from action queue var - diag.sh: restore .started with instance-ID suffix for content_check - diag.sh: wait_startup() uses imdiag port file for startup detection - tests: add yaml-basic-yamlonly.sh smoke test - doc: 6 new reference pages for new imdiag params (with meta blocks) - doc: imdiag.rst updated with new module-scope params table - doc: dev_testbench.rst corrected .started vs port-file description Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- doc/Makefile.am | 6 + doc/source/configuration/modules/imdiag.rst | 63 ++++++ doc/source/development/dev_testbench.rst | 195 ++++++++++++++++-- ...mdiag-defaultactionqueuetimeoutenqueue.rst | 61 ++++++ ...diag-defaultactionqueuetimeoutshutdown.rst | 61 ++++++ .../imdiag-inputshutdowntimeout.rst | 57 +++++ .../imdiag-listenportfilename-module.rst | 59 ++++++ .../imdiag-mainmsgqueuetimeoutenqueue.rst | 59 ++++++ .../imdiag-mainmsgqueuetimeoutshutdown.rst | 61 ++++++ plugins/imdiag/imdiag.c | 82 +++++++- runtime/yamlconf.c | 2 +- tests/AGENTS.md | 23 ++- tests/diag.sh | 129 +++++------- tests/yaml-basic-yamlonly.sh | 18 +- 14 files changed, 768 insertions(+), 108 deletions(-) create mode 100644 doc/source/reference/parameters/imdiag-defaultactionqueuetimeoutenqueue.rst create mode 100644 doc/source/reference/parameters/imdiag-defaultactionqueuetimeoutshutdown.rst create mode 100644 doc/source/reference/parameters/imdiag-inputshutdowntimeout.rst create mode 100644 doc/source/reference/parameters/imdiag-listenportfilename-module.rst create mode 100644 doc/source/reference/parameters/imdiag-mainmsgqueuetimeoutenqueue.rst create mode 100644 doc/source/reference/parameters/imdiag-mainmsgqueuetimeoutshutdown.rst diff --git a/doc/Makefile.am b/doc/Makefile.am index 070d7f43a..7bd7f079f 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -453,8 +453,14 @@ EXTRA_DIST = \ source/rainerscript/variable_property_types.rst \ source/reference/parameters/im3195-input3195listenport.rst \ source/reference/parameters/imdiag-aborttimeout.rst \ + source/reference/parameters/imdiag-defaultactionqueuetimeoutenqueue.rst \ + source/reference/parameters/imdiag-defaultactionqueuetimeoutshutdown.rst \ source/reference/parameters/imdiag-injectdelaymode.rst \ + source/reference/parameters/imdiag-inputshutdowntimeout.rst \ source/reference/parameters/imdiag-listenportfilename.rst \ + source/reference/parameters/imdiag-listenportfilename-module.rst \ + source/reference/parameters/imdiag-mainmsgqueuetimeoutenqueue.rst \ + source/reference/parameters/imdiag-mainmsgqueuetimeoutshutdown.rst \ source/reference/parameters/imdiag-maxsessions.rst \ source/reference/parameters/imdiag-serverinputname.rst \ source/reference/parameters/imdiag-serverrun.rst \ diff --git a/doc/source/configuration/modules/imdiag.rst b/doc/source/configuration/modules/imdiag.rst index d8ae606e3..bcd1d4186 100644 --- a/doc/source/configuration/modules/imdiag.rst +++ b/doc/source/configuration/modules/imdiag.rst @@ -37,6 +37,30 @@ Module Parameters - .. include:: ../../reference/parameters/imdiag-aborttimeout.rst :start-after: .. summary-start :end-before: .. summary-end + * - :ref:`param-imdiag-listenportfilename-module` + - .. include:: ../../reference/parameters/imdiag-listenportfilename-module.rst + :start-after: .. summary-start + :end-before: .. summary-end + * - :ref:`param-imdiag-mainmsgqueuetimeoutshutdown` + - .. include:: ../../reference/parameters/imdiag-mainmsgqueuetimeoutshutdown.rst + :start-after: .. summary-start + :end-before: .. summary-end + * - :ref:`param-imdiag-mainmsgqueuetimeoutenqueue` + - .. include:: ../../reference/parameters/imdiag-mainmsgqueuetimeoutenqueue.rst + :start-after: .. summary-start + :end-before: .. summary-end + * - :ref:`param-imdiag-inputshutdowntimeout` + - .. include:: ../../reference/parameters/imdiag-inputshutdowntimeout.rst + :start-after: .. summary-start + :end-before: .. summary-end + * - :ref:`param-imdiag-defaultactionqueuetimeoutshutdown` + - .. include:: ../../reference/parameters/imdiag-defaultactionqueuetimeoutshutdown.rst + :start-after: .. summary-start + :end-before: .. summary-end + * - :ref:`param-imdiag-defaultactionqueuetimeoutenqueue` + - .. include:: ../../reference/parameters/imdiag-defaultactionqueuetimeoutenqueue.rst + :start-after: .. summary-start + :end-before: .. summary-end * - :ref:`param-imdiag-injectdelaymode` - .. include:: ../../reference/parameters/imdiag-injectdelaymode.rst :start-after: .. summary-start @@ -96,10 +120,49 @@ ephemeral port, and records the chosen port for the testbench to read. listenPortFileName="/var/run/rsyslog/imdiag.port" serverRun="0") +YAML-only testbench configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In YAML-only mode the testbench preamble uses the ``testbench_modules:`` +key (an alias for ``modules:`` reserved for testbench infrastructure) so +that it does not conflict with the test's own ``modules:`` section. + +.. code-block:: yaml + + version: 2 + + global: + debug.abortOnProgramError: "on" + + testbench_modules: + - load: "../plugins/imdiag/.libs/imdiag" + listenportfilename: "test.imdiag.port" + aborttimeout: "580" + mainmsgqueuetimeoutshutdown: "10000" + mainmsgqueuetimeoutenqueue: "30000" + inputshutdowntimeout: "60000" + defaultactionqueuetimeoutshutdown: "20000" + defaultactionqueuetimeoutenqueue: "30000" + + modules: + - load: "../plugins/imtcp/.libs/imtcp" + + inputs: + - type: imdiag + port: "0" + - type: imtcp + port: "0" + .. toctree:: :hidden: ../../reference/parameters/imdiag-aborttimeout + ../../reference/parameters/imdiag-listenportfilename-module + ../../reference/parameters/imdiag-mainmsgqueuetimeoutshutdown + ../../reference/parameters/imdiag-mainmsgqueuetimeoutenqueue + ../../reference/parameters/imdiag-inputshutdowntimeout + ../../reference/parameters/imdiag-defaultactionqueuetimeoutshutdown + ../../reference/parameters/imdiag-defaultactionqueuetimeoutenqueue ../../reference/parameters/imdiag-injectdelaymode ../../reference/parameters/imdiag-maxsessions ../../reference/parameters/imdiag-listenportfilename diff --git a/doc/source/development/dev_testbench.rst b/doc/source/development/dev_testbench.rst index 40721c519..b289aca92 100644 --- a/doc/source/development/dev_testbench.rst +++ b/doc/source/development/dev_testbench.rst @@ -1,20 +1,191 @@ -writing rsyslog tests +Writing rsyslog Tests ===================== -The rsyslog testbench is executed via `make check` or `make distcheck`. For details, on -these modes, see the GNU autotools documentation. The most important thing is that -the `make distcheck` test execution environment is considerably different from its -`make check` counterpart. The rsyslog testbench is crafted to handle both cases and -does so with the (intensive) use of environment variables. +The rsyslog testbench is executed via ``make check`` or ``make distcheck``. For details +on these modes, see the GNU autotools documentation. The most important thing is that +the ``make distcheck`` test execution environment is considerably different from its +``make check`` counterpart. The rsyslog testbench is crafted to handle both cases and +does so with the intensive use of environment variables. The rsyslog testbench aims to support parallel tests. This is not yet fully implemented, but we are working towards that goal. This has a number of implications/requirements: -* all file names, ports, etc need to be unique -* the diag.sh framework supports auto-generation capabilities to support this: - use `${RSYSLOG_DYNNAME}` a prefix for all files you generate. For the frequently - used files, the framework already defines `${RSYSLOG_OUT_LOG}` and `${RSYSLOG_OUT_LOG2}` +* All file names, ports, etc. need to be unique. +* The ``diag.sh`` framework supports auto-generation capabilities to support this: + use ``${RSYSLOG_DYNNAME}`` as a prefix for all files you generate. For the frequently + used files, the framework already defines ``${RSYSLOG_OUT_LOG}`` and + ``${RSYSLOG_OUT_LOG2}``. +When writing new tests, it is in general advisable to copy an existing test and modify +it. This also ensures you inherit the correct boilerplate and environment setup. + +Test Modes +---------- + +The testbench supports two configuration modes for rsyslogd: + +RainerScript mode (default) + The classic mode. ``generate_conf`` writes a ``.conf`` file using + ``module()`` / ``input()`` / ``global()`` v2 RainerScript syntax. Legacy + ``$Directive`` syntax is still accepted but deprecated. + +YAML-only mode + Pure-YAML configuration. ``generate_conf --yaml-only`` writes a ``.yaml`` + file using the rsyslog v2 YAML loader. No RainerScript preamble is written. + See `YAML-only mode`_ below. + +Startup detection uses the imdiag port file in both modes. In RainerScript +mode, rsyslogd's own messages (startup, shutdown, errors) are also captured to +the instance's ``.started`` file via the syslogtag filter rule; tests may +use that file for content checks. In YAML-only mode no ``.started`` file is +written. + +.. _yaml-only-mode: + +YAML-only Mode +-------------- + +Use ``generate_conf --yaml-only`` when a test must validate the YAML +configuration loader or when you want to avoid any RainerScript entirely. + +How It Works +~~~~~~~~~~~~ + +1. ``generate_conf --yaml-only [instance]`` writes + ``${TESTCONF_NM}[instance].yaml`` containing: + + - ``version: 2`` + - ``global:`` section with ``debug.abortOnProgramError: "on"`` (and + ``net.ipprotocol:`` if ``RSTB_FORCE_IPV4=1`` or ``RSTB_NET_IPPROTO`` is + set) + - ``testbench_modules:`` section that loads ``imdiag`` with all testbench + parameters set (port file, abort timeout, queue/input timeouts) + + The ``testbench_modules:`` key is recognised by rsyslogd as an alias for + ``modules:``. It is reserved for testbench infrastructure so it does not + conflict with the test's own ``modules:`` section. + +2. Tests add their own sections with ``add_yaml_conf``. +3. Tests must call ``add_yaml_imdiag_input`` inside their ``inputs:`` section + so that startup detection works. + +Helper Functions +~~~~~~~~~~~~~~~~ + +``generate_conf --yaml-only [instance]`` + Create the YAML preamble. Sets ``RSYSLOG_YAML_ONLY=1``. + +``add_yaml_conf 'fragment' [instance]`` + Append a YAML fragment to ``${TESTCONF_NM}[instance].yaml``. + +``add_yaml_imdiag_input [instance]`` + Append the imdiag ``inputs:`` entry inside an already-opened ``inputs:`` + block. **Required** in every yaml-only test for startup detection. + +Startup detection +~~~~~~~~~~~~~~~~~ + +Both RainerScript and yaml-only modes use the imdiag port file +(``${RSYSLOG_DYNNAME}.imdiag[instance].port``) as the sole startup signal. +``wait_startup`` polls for this file and fast-fails if rsyslogd exits before +it appears. No ``.started`` marker file is used. + +Queue and Input Timeouts +~~~~~~~~~~~~~~~~~~~~~~~~ + +Default testbench timeout values are set via the ``RSTB_*`` environment +variables before ``generate_conf`` is called. The variables and their defaults +are: + +.. list-table:: + :widths: 40 20 40 + :header-rows: 1 + + * - Variable + - Default (ms) + - Controls + * - ``RSTB_GLOBAL_QUEUE_SHUTDOWN_TIMEOUT`` + - 10000 + - Main message queue shutdown timeout + * - ``RSTB_GLOBAL_INPUT_SHUTDOWN_TIMEOUT`` + - 60000 + - Input shutdown timeout + * - ``RSTB_ACTION_DEFAULT_Q_TO_SHUTDOWN`` + - 20000 + - Default action queue shutdown timeout + * - ``RSTB_ACTION_DEFAULT_Q_TO_ENQUEUE`` + - 30000 + - Default action queue enqueue timeout + * - ``RSTB_MAIN_Q_TO_ENQUEUE`` + - 30000 + - Main message queue enqueue timeout + +These are injected as ``imdiag`` module parameters at config-load time, so +they take effect before any test-specific config is parsed. A test can +override a value by setting the variable before calling ``generate_conf``. + +If a test needs a *different* value for the main queue shutdown timeout (for +example, a queue-persistence test that sets ``$MainMsgQueueTimeoutShutdown 1`` +in RainerScript), it should set the value in the configuration fragment itself. +Legacy ``$MainMsgQueueTimeout*`` directives still work and override the +imdiag-supplied defaults in RainerScript mode. + +Limitations in YAML-only Mode +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following RainerScript-specific features are not available in yaml-only +mode: + +* **Legacy ``$Directive`` syntax** -- not parsed by the YAML loader. +* **``setvar_RS_HOSTNAME``** -- uses a RainerScript ``$template`` internally; + it always runs against a RainerScript instance and its result is still + available via ``$RS_HOSTNAME`` for yaml-only tests. + +Example Test +~~~~~~~~~~~~ + +.. code-block:: bash + + #!/bin/bash + . ${srcdir:=.}/diag.sh init + require_plugin imtcp + export NUMMESSAGES=100 + export QUEUE_EMPTY_CHECK_FUNC=wait_file_lines + generate_conf --yaml-only + + # Test-specific modules go in a standard modules: section. + # Testbench infrastructure (imdiag) is already in testbench_modules:. + add_yaml_conf 'modules:' + add_yaml_conf ' - load: "../plugins/imtcp/.libs/imtcp"' + add_yaml_conf '' + add_yaml_conf 'templates:' + add_yaml_conf ' - name: outfmt' + add_yaml_conf ' type: string' + add_yaml_conf ' string: "%msg:F,58:2%\n"' + add_yaml_conf '' + add_yaml_conf 'inputs:' + add_yaml_imdiag_input # required -- provides startup detection + add_yaml_conf " - type: imtcp" + add_yaml_conf ' port: "0"' + add_yaml_conf " listenPortFileName: \"${RSYSLOG_DYNNAME}.tcpflood_port\"" + add_yaml_conf ' ruleset: main' + add_yaml_conf '' + add_yaml_conf 'rulesets:' + add_yaml_conf ' - name: main' + add_yaml_conf ' script: |' + add_yaml_conf " :msg, contains, \"msgnum:\" action(type=\"omfile\"" + add_yaml_conf " template=\"outfmt\" file=\"${RSYSLOG_OUT_LOG}\")" + startup + tcpflood -m $NUMMESSAGES + shutdown_when_empty + wait_shutdown + seq_check + exit_test + +Naming and Registration +~~~~~~~~~~~~~~~~~~~~~~~ + +Name yaml-only tests ``yaml--yamlonly.sh`` (or append ``-yamlonly`` to +an existing test name). Register them in ``tests/Makefile.am`` under +``TESTS_LIBYAML`` so they are skipped on systems without libyaml. -When writing new tests, it is in general advisable to copy an existing test and change -it. This also helps you get requirements files. diff --git a/doc/source/reference/parameters/imdiag-defaultactionqueuetimeoutenqueue.rst b/doc/source/reference/parameters/imdiag-defaultactionqueuetimeoutenqueue.rst new file mode 100644 index 000000000..0a5aa3fde --- /dev/null +++ b/doc/source/reference/parameters/imdiag-defaultactionqueuetimeoutenqueue.rst @@ -0,0 +1,61 @@ +.. _param-imdiag-defaultactionqueuetimeoutenqueue: +.. _imdiag.parameter.module.defaultactionqueuetimeoutenqueue: + +.. meta:: + :description: Reference for the imdiag defaultActionQueueTimeoutEnqueue module parameter. + :keywords: rsyslog, imdiag, defaultactionqueuetimeoutenqueue, testbench, timeout, action queue + +DefaultActionQueueTimeoutEnqueue +================================= + +.. index:: + single: imdiag; DefaultActionQueueTimeoutEnqueue + +.. summary-start + +Sets the default action queue enqueue timeout at config load time. + +.. summary-end + +This parameter applies to :doc:`../../configuration/modules/imdiag`. + +:Name: DefaultActionQueueTimeoutEnqueue +:Scope: module +:Type: integer (milliseconds) +:Default: 30000 +:Required?: no +:Introduced: 8.x + +Description +----------- +Sets ``globals.actq_dflt_toEnq`` — the default enqueue timeout for action +queues created after this point — at config-load time. This only affects +action queues that do not specify their own timeout. When an action queue is +full, a producer waits up to this duration before dropping the message. + +.. note:: + This parameter affects action queues created *after* the module is + loaded. Existing queues are unaffected. + +Override per-test via ``RSTB_ACTION_DEFAULT_Q_TO_ENQUEUE`` before +``generate_conf``. + +Module usage +------------ + +.. code-block:: rsyslog + + module(load="imdiag" defaultActionQueueTimeoutEnqueue="30000") + +YAML usage +---------- + +.. code-block:: yaml + + testbench_modules: + - load: "../plugins/imdiag/.libs/imdiag" + defaultactionqueuetimeoutenqueue: "30000" + +See also +-------- +See also :doc:`../../configuration/modules/imdiag`. diff --git a/doc/source/reference/parameters/imdiag-defaultactionqueuetimeoutshutdown.rst b/doc/source/reference/parameters/imdiag-defaultactionqueuetimeoutshutdown.rst new file mode 100644 index 000000000..61c7bf81d --- /dev/null +++ b/doc/source/reference/parameters/imdiag-defaultactionqueuetimeoutshutdown.rst @@ -0,0 +1,61 @@ +.. _param-imdiag-defaultactionqueuetimeoutshutdown: +.. _imdiag.parameter.module.defaultactionqueuetimeoutshutdown: + +.. meta:: + :description: Reference for the imdiag defaultActionQueueTimeoutShutdown module parameter. + :keywords: rsyslog, imdiag, defaultactionqueuetimeoutshutdown, testbench, timeout, action queue + +DefaultActionQueueTimeoutShutdown +================================== + +.. index:: + single: imdiag; DefaultActionQueueTimeoutShutdown + +.. summary-start + +Sets the default action queue shutdown timeout at config load time. + +.. summary-end + +This parameter applies to :doc:`../../configuration/modules/imdiag`. + +:Name: DefaultActionQueueTimeoutShutdown +:Scope: module +:Type: integer (milliseconds) +:Default: 20000 +:Required?: no +:Introduced: 8.x + +Description +----------- +Sets ``globals.actq_dflt_toQShutdown`` — the default shutdown timeout for +action queues created after this point — at config-load time. This only +affects action queues that do not specify their own timeout. The testbench +uses this to ensure action queues drain cleanly under test conditions. + +.. note:: + This parameter affects action queues created *after* the module is + loaded. Existing queues are unaffected. + +Override per-test via ``RSTB_ACTION_DEFAULT_Q_TO_SHUTDOWN`` before +``generate_conf``. + +Module usage +------------ + +.. code-block:: rsyslog + + module(load="imdiag" defaultActionQueueTimeoutShutdown="20000") + +YAML usage +---------- + +.. code-block:: yaml + + testbench_modules: + - load: "../plugins/imdiag/.libs/imdiag" + defaultactionqueuetimeoutshutdown: "20000" + +See also +-------- +See also :doc:`../../configuration/modules/imdiag`. diff --git a/doc/source/reference/parameters/imdiag-inputshutdowntimeout.rst b/doc/source/reference/parameters/imdiag-inputshutdowntimeout.rst new file mode 100644 index 000000000..fcada8c3e --- /dev/null +++ b/doc/source/reference/parameters/imdiag-inputshutdowntimeout.rst @@ -0,0 +1,57 @@ +.. _param-imdiag-inputshutdowntimeout: +.. _imdiag.parameter.module.inputshutdowntimeout: + +.. meta:: + :description: Reference for the imdiag inputShutdownTimeout module parameter. + :keywords: rsyslog, imdiag, inputshutdowntimeout, testbench, timeout, shutdown + +InputShutdownTimeout +==================== + +.. index:: + single: imdiag; InputShutdownTimeout + +.. summary-start + +Sets the input shutdown timeout at config load time. + +.. summary-end + +This parameter applies to :doc:`../../configuration/modules/imdiag`. + +:Name: InputShutdownTimeout +:Scope: module +:Type: integer (milliseconds) +:Default: 60000 +:Required?: no +:Introduced: 8.x + +Description +----------- +Sets ``globals.inputTimeoutShutdown`` — the time rsyslog allows each input +plugin to stop during an orderly shutdown — at config-load time. The +testbench uses this to ensure inputs have enough time to shut down cleanly +under load, in both RainerScript and YAML-only modes. + +Override per-test via ``RSTB_GLOBAL_INPUT_SHUTDOWN_TIMEOUT`` before +``generate_conf``. + +Module usage +------------ + +.. code-block:: rsyslog + + module(load="imdiag" inputShutdownTimeout="60000") + +YAML usage +---------- + +.. code-block:: yaml + + testbench_modules: + - load: "../plugins/imdiag/.libs/imdiag" + inputshutdowntimeout: "60000" + +See also +-------- +See also :doc:`../../configuration/modules/imdiag`. diff --git a/doc/source/reference/parameters/imdiag-listenportfilename-module.rst b/doc/source/reference/parameters/imdiag-listenportfilename-module.rst new file mode 100644 index 000000000..3b16ca345 --- /dev/null +++ b/doc/source/reference/parameters/imdiag-listenportfilename-module.rst @@ -0,0 +1,59 @@ +.. _param-imdiag-listenportfilename-module: +.. _imdiag.parameter.module.listenportfilename: + +.. meta:: + :description: Reference for the imdiag listenPortFileName module-scope parameter. + :keywords: rsyslog, imdiag, listenportfilename, testbench, port file, startup + +ListenPortFileName (module scope) +================================== + +.. index:: + single: imdiag; ListenPortFileName (module) + +.. summary-start + +Path of the file to which imdiag writes its chosen TCP listen port number, +set at module load time. + +.. summary-end + +This parameter applies to :doc:`../../configuration/modules/imdiag`. + +:Name: ListenPortFileName +:Scope: module +:Type: string (file path) +:Default: none +:Required?: no (but recommended for testbench use) +:Introduced: 8.x + +Description +----------- +When set at module scope, ``ListenPortFileName`` specifies the file that +imdiag will create and populate with its chosen listen port number after the +TCP listener is ready. The testbench reads this file to discover the port +without needing a fixed port number. + +This is the module-scope version of the parameter. The same name can also +be set at input scope via ``input(type="imdiag" listenPortFileName=...)``. + +Module usage +------------ + +.. code-block:: rsyslog + + module(load="imdiag" listenPortFileName="/tmp/imdiag.port") + +YAML usage +---------- + +.. code-block:: yaml + + testbench_modules: + - load: "../plugins/imdiag/.libs/imdiag" + listenportfilename: "/tmp/imdiag.port" + +See also +-------- +See also :doc:`../../configuration/modules/imdiag` and +:ref:`param-imdiag-listenportfilename`. diff --git a/doc/source/reference/parameters/imdiag-mainmsgqueuetimeoutenqueue.rst b/doc/source/reference/parameters/imdiag-mainmsgqueuetimeoutenqueue.rst new file mode 100644 index 000000000..2ef533087 --- /dev/null +++ b/doc/source/reference/parameters/imdiag-mainmsgqueuetimeoutenqueue.rst @@ -0,0 +1,59 @@ +.. _param-imdiag-mainmsgqueuetimeoutenqueue: +.. _imdiag.parameter.module.mainmsgqueuetimeoutenqueue: + +.. meta:: + :description: Reference for the imdiag mainMsgQueueTimeoutEnqueue module parameter. + :keywords: rsyslog, imdiag, mainmsgqueuetimeoutenqueue, testbench, timeout, queue + +MainMsgQueueTimeoutEnqueue +=========================== + +.. index:: + single: imdiag; MainMsgQueueTimeoutEnqueue + +.. summary-start + +Sets the main message queue enqueue timeout at config load time. + +.. summary-end + +This parameter applies to :doc:`../../configuration/modules/imdiag`. + +:Name: MainMsgQueueTimeoutEnqueue +:Scope: module +:Type: integer (milliseconds) +:Default: 30000 +:Required?: no +:Introduced: 8.x + +Description +----------- +Sets ``globals.mainQ.iMainMsgQtoEnq`` — the time a producer will wait when +the main message queue is full before dropping the message — at config-load +time. The testbench uses this to establish a safe default without relying on +legacy ``$MainMsgQueueTimeoutEnqueue`` directives, which are not available in +YAML-only mode. + +Override per-test via ``RSTB_MAIN_Q_TO_ENQUEUE`` before +``generate_conf``, or via a ``$MainMsgQueueTimeoutEnqueue`` directive in the +RainerScript test fragment. + +Module usage +------------ + +.. code-block:: rsyslog + + module(load="imdiag" mainMsgQueueTimeoutEnqueue="30000") + +YAML usage +---------- + +.. code-block:: yaml + + testbench_modules: + - load: "../plugins/imdiag/.libs/imdiag" + mainmsgqueuetimeoutenqueue: "30000" + +See also +-------- +See also :doc:`../../configuration/modules/imdiag`. diff --git a/doc/source/reference/parameters/imdiag-mainmsgqueuetimeoutshutdown.rst b/doc/source/reference/parameters/imdiag-mainmsgqueuetimeoutshutdown.rst new file mode 100644 index 000000000..93d026a49 --- /dev/null +++ b/doc/source/reference/parameters/imdiag-mainmsgqueuetimeoutshutdown.rst @@ -0,0 +1,61 @@ +.. _param-imdiag-mainmsgqueuetimeoutshutdown: +.. _imdiag.parameter.module.mainmsgqueuetimeoutshutdown: + +.. meta:: + :description: Reference for the imdiag mainMsgQueueTimeoutShutdown module parameter. + :keywords: rsyslog, imdiag, mainmsgqueuetimeoutshutdown, testbench, timeout, queue + +MainMsgQueueTimeoutShutdown +============================ + +.. index:: + single: imdiag; MainMsgQueueTimeoutShutdown + +.. summary-start + +Sets the main message queue shutdown timeout at config load time. + +.. summary-end + +This parameter applies to :doc:`../../configuration/modules/imdiag`. + +:Name: MainMsgQueueTimeoutShutdown +:Scope: module +:Type: integer (milliseconds) +:Default: 10000 +:Required?: no +:Introduced: 8.x + +Description +----------- +Sets ``globals.mainQ.iMainMsgQtoQShutdown`` — the time rsyslog waits for +the main message queue to drain during an orderly shutdown — at config-load +time. The testbench uses this to establish a safe default without relying on +legacy ``$MainMsgQueueTimeoutShutdown`` directives, which are not available +in YAML-only mode. + +The value can be overridden per-test by setting the +``RSTB_GLOBAL_QUEUE_SHUTDOWN_TIMEOUT`` environment variable before calling +``generate_conf``, or by writing a ``$MainMsgQueueTimeoutShutdown`` directive +in the test's RainerScript configuration fragment (legacy directives apply +after module params and therefore take precedence). + +Module usage +------------ + +.. code-block:: rsyslog + + module(load="imdiag" mainMsgQueueTimeoutShutdown="10000") + +YAML usage +---------- + +.. code-block:: yaml + + testbench_modules: + - load: "../plugins/imdiag/.libs/imdiag" + mainmsgqueuetimeoutshutdown: "10000" + +See also +-------- +See also :doc:`../../configuration/modules/imdiag`. diff --git a/plugins/imdiag/imdiag.c b/plugins/imdiag/imdiag.c index 489dd8166..da914a77b 100644 --- a/plugins/imdiag/imdiag.c +++ b/plugins/imdiag/imdiag.c @@ -57,6 +57,7 @@ #include "datetime.h" #include "ratelimit.h" #include "queue.h" +#include "rsconf.h" #include "lookup.h" #include "net.h" /* for permittedPeers, may be removed when this is removed */ #include "statsobj.h" @@ -106,7 +107,15 @@ struct modConfData_s { static modConfData_t *loadModConf = NULL; /* modConf ptr for current load process */ /* module-level parameters (module(...)) */ -static struct cnfparamdescr modpdescr[] = {{"listenportfilename", eCmdHdlrString, 0}, {"aborttimeout", eCmdHdlrInt, 0}}; +static struct cnfparamdescr modpdescr[] = { + {"listenportfilename", eCmdHdlrString, 0}, + {"aborttimeout", eCmdHdlrInt, 0}, + {"mainmsgqueuetimeoutshutdown", eCmdHdlrInt, 0}, + {"mainmsgqueuetimeoutenqueue", eCmdHdlrInt, 0}, + {"inputshutdowntimeout", eCmdHdlrInt, 0}, + {"defaultactionqueuetimeoutshutdown", eCmdHdlrInt, 0}, + {"defaultactionqueuetimeoutenqueue", eCmdHdlrInt, 0}, +}; static struct cnfparamblk modpblk = {CNFPARAMBLK_VERSION, sizeof(modpdescr) / sizeof(struct cnfparamdescr), modpdescr}; /* input-level parameters (input(...)) */ @@ -524,6 +533,20 @@ finalize_it: RETiRet; } +/* Parse a non-negative long integer from a command argument string. + * Returns 1 on success (val set), 0 on parse error (non-numeric, negative, or overflow). + */ +static int parsePosLong(const uchar *const s, long *const val) { + char *endptr; + if (s == NULL || *s == '\0') return 0; + errno = 0; + *val = strtol((const char *)s, &endptr, 10); + if (errno != 0) return 0; /* overflow / underflow */ + /* accept optional trailing newline/space but nothing else */ + while (*endptr == ' ' || *endptr == '\r' || *endptr == '\n') ++endptr; + return (*endptr == '\0' && *val >= 0) ? 1 : 0; +} + /* Function to handle received messages. This is our core function! * rgerhards, 2009-05-24 */ @@ -566,6 +589,50 @@ static rsRetVal ATTR_NONNULL() OnMsgReceived(tcps_sess_t *const pSess, uchar *co CHKiRet(awaitHUPComplete(pSess)); } else if (!ustrcmp(cmdBuf, UCHAR_CONSTANT("enabledebug"))) { CHKiRet(enableDebug(pSess)); + } else if (!ustrcmp(cmdBuf, UCHAR_CONSTANT("setmainmsgqueuetimeoutshutdown"))) { + long val; + if (!parsePosLong(pszMsg, &val)) { + CHKiRet(sendResponse(pSess, "ERROR: invalid timeout value\n")); + } else if (runConf->pMsgQueue == NULL) { + CHKiRet(sendResponse(pSess, "ERROR: main queue not yet initialized\n")); + } else { + CHKiRet(qqueueSettoQShutdown(runConf->pMsgQueue, val)); + CHKiRet(sendResponse(pSess, "OK\n")); + } + } else if (!ustrcmp(cmdBuf, UCHAR_CONSTANT("setmainmsgqueuetimeoutenqueue"))) { + long val; + if (!parsePosLong(pszMsg, &val)) { + CHKiRet(sendResponse(pSess, "ERROR: invalid timeout value\n")); + } else if (runConf->pMsgQueue == NULL) { + CHKiRet(sendResponse(pSess, "ERROR: main queue not yet initialized\n")); + } else { + CHKiRet(qqueueSettoEnq(runConf->pMsgQueue, val)); + CHKiRet(sendResponse(pSess, "OK\n")); + } + } else if (!ustrcmp(cmdBuf, UCHAR_CONSTANT("setinputshutdowntimeout"))) { + long val; + if (!parsePosLong(pszMsg, &val)) { + CHKiRet(sendResponse(pSess, "ERROR: invalid timeout value\n")); + } else { + runConf->globals.inputTimeoutShutdown = (int)val; + CHKiRet(sendResponse(pSess, "OK\n")); + } + } else if (!ustrcmp(cmdBuf, UCHAR_CONSTANT("setdefaultactionqueuetimeoutshutdown"))) { + long val; + if (!parsePosLong(pszMsg, &val)) { + CHKiRet(sendResponse(pSess, "ERROR: invalid timeout value\n")); + } else { + runConf->globals.actq_dflt_toQShutdown = (int)val; + CHKiRet(sendResponse(pSess, "OK\n")); + } + } else if (!ustrcmp(cmdBuf, UCHAR_CONSTANT("setdefaultactionqueuetimeoutenqueue"))) { + long val; + if (!parsePosLong(pszMsg, &val)) { + CHKiRet(sendResponse(pSess, "ERROR: invalid timeout value\n")); + } else { + runConf->globals.actq_dflt_toEnq = (int)val; + CHKiRet(sendResponse(pSess, "OK\n")); + } } else { dbgprintf("imdiag unkown command '%s'\n", cmdBuf); CHKiRet(sendResponse(pSess, "unkown command '%s'\n", cmdBuf)); @@ -760,8 +827,19 @@ BEGINsetModCnf CHKmalloc(loadModConf->pszLstnPortFileName = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL)); } else if (!strcmp(modpblk.descr[i].name, "aborttimeout")) { loadModConf->abortTimeout = (int)pvals[i].val.d.n; + } else if (!strcmp(modpblk.descr[i].name, "mainmsgqueuetimeoutshutdown")) { + loadModConf->pConf->globals.mainQ.iMainMsgQtoQShutdown = (int)pvals[i].val.d.n; + } else if (!strcmp(modpblk.descr[i].name, "mainmsgqueuetimeoutenqueue")) { + loadModConf->pConf->globals.mainQ.iMainMsgQtoEnq = (int)pvals[i].val.d.n; + } else if (!strcmp(modpblk.descr[i].name, "inputshutdowntimeout")) { + loadModConf->pConf->globals.inputTimeoutShutdown = (int)pvals[i].val.d.n; + } else if (!strcmp(modpblk.descr[i].name, "defaultactionqueuetimeoutshutdown")) { + loadModConf->pConf->globals.actq_dflt_toQShutdown = (int)pvals[i].val.d.n; + } else if (!strcmp(modpblk.descr[i].name, "defaultactionqueuetimeoutenqueue")) { + loadModConf->pConf->globals.actq_dflt_toEnq = (int)pvals[i].val.d.n; } else { dbgprintf("imdiag: program error, non-handled param '%s' in setModCnf\n", modpblk.descr[i].name); + assert(0); /* should not happen */ } } loadModConf->configSetViaV2Method = 1; @@ -822,6 +900,7 @@ BEGINnewInpInst CHKmalloc(port = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL)); } else { dbgprintf("imdiag: program error, non-handled param '%s' in newInpInst\n", inppblk.descr[i].name); + assert(0); /* should not happen */ } } /* apply module-level listenportfilename to global before addTCPListener uses it */ @@ -912,6 +991,7 @@ BEGINmodExit void *dummy; pthread_join(timeoutGuard_thrd, &dummy); } + abortTimeout = -1; /* mark cleaned up so resetConfigVariables won't double-cancel */ } ENDmodExit diff --git a/runtime/yamlconf.c b/runtime/yamlconf.c index a3b227dd7..559071c19 100644 --- a/runtime/yamlconf.c +++ b/runtime/yamlconf.c @@ -1866,7 +1866,7 @@ static rsRetVal process_top_level(yaml_parser_t *parser, const char *key, const CHKiRet(parse_singleton_obj(parser, CNFOBJ_GLOBAL, fname)); } else if (!strcmp(key, "mainqueue") || !strcmp(key, "main_queue")) { CHKiRet(parse_singleton_obj(parser, CNFOBJ_MAINQ, fname)); - } else if (!strcmp(key, "modules")) { + } else if (!strcmp(key, "modules") || !strcmp(key, "testbench_modules")) { CHKiRet(parse_obj_sequence(parser, CNFOBJ_MODULE, fname)); } else if (!strcmp(key, "inputs")) { CHKiRet(parse_obj_sequence(parser, CNFOBJ_INPUT, fname)); diff --git a/tests/AGENTS.md b/tests/AGENTS.md index 90b592f23..86c4e8fb5 100644 --- a/tests/AGENTS.md +++ b/tests/AGENTS.md @@ -164,13 +164,12 @@ must validate YAML-loader behaviour or when no RainerScript is desired. ### How it works - `generate_conf --yaml-only [instance]` writes `${TESTCONF_NM}[instance].yaml` - containing `version: 2`, `global:`, `mainqueue:`, and `modules:` (imdiag). - The `modules:` sequence is intentionally left open — no `inputs:` section is - written by the preamble. -- Tests append additional module entries as **sequence continuation items** - (2-space indent, ` - load: ...`, no top-level `modules:` key) so YAML parsers - see a single, well-formed `modules:` list. The sequence closes naturally when - the test adds a zero-indent key such as `inputs:`. + containing `version: 2`, `global:`, and `testbench_modules:` (imdiag setup). + `testbench_modules:` is a YAML key understood by rsyslogd as an alias for + `modules:` and is reserved for testbench infrastructure — it avoids any + conflict with the test's own `modules:` section. +- Tests add their own `modules:` section (and `inputs:`, `rulesets:`, etc.) + via `add_yaml_conf`. - `add_yaml_conf 'fragment' [instance]` appends arbitrary YAML to the same file. - `add_yaml_imdiag_input [instance]` appends the imdiag input entry (` - type: imdiag / port: "0"`) inside an already-opened `inputs:` block. @@ -184,8 +183,11 @@ The following testbench features are **not available** in yaml-only mode: | Feature | Reason | Workaround | |---------|--------|-----------| -| `$MainmsgQueueTimeout*` directives | Legacy sysklogd syntax; no YAML equivalent at runtime via `add_yaml_conf` | Set `RSTB_GLOBAL_QUEUE_SHUTDOWN_TIMEOUT` / `RSTB_ACTION_DEFAULT_Q_TO_ENQUEUE` env vars *before* calling `generate_conf --yaml-only`; they are written into the `mainqueue:` section of the preamble | -| `.started` marker file | The syslogtag-based filter rule requires RainerScript/legacy syntax | `wait_startup` requires the imdiag port file (not just the PID file) in yaml-only mode, confirming config loaded and inputs are active; it fast-fails if rsyslog exits before the port file appears | +| Legacy `$` directives | Legacy syntax is not parsed by the YAML loader | Use v2 RainerScript (`module()`, `input()`) or YAML keys instead | + +> **Note**: Startup detection uses the imdiag port file in both RainerScript and +> yaml-only modes. The `.started` marker file mechanism has been removed; the +> imdiag port file is the sole startup signal in all modes. ### Example test structure ```bash @@ -194,7 +196,8 @@ require_plugin imtcp export NUMMESSAGES=100 export QUEUE_EMPTY_CHECK_FUNC=wait_file_lines generate_conf --yaml-only -# Continue the modules: sequence opened by the preamble (2-space indent, no modules: key) +# Test-specific modules in their own modules: section (testbench_modules: is in preamble) +add_yaml_conf 'modules:' add_yaml_conf ' - load: "../plugins/imtcp/.libs/imtcp"' add_yaml_conf '' add_yaml_conf 'inputs:' diff --git a/tests/diag.sh b/tests/diag.sh index 5e0017521..f64196800 100755 --- a/tests/diag.sh +++ b/tests/diag.sh @@ -285,6 +285,10 @@ test_status() { setvar_RS_HOSTNAME() { printf '### Obtaining HOSTNAME (prerequisite, not actual test) ###\n' + # This helper uses legacy RainerScript $template syntax to capture the + # hostname; always run it in RS mode regardless of RSYSLOG_YAML_ONLY. + local _saved_yaml_only="${RSYSLOG_YAML_ONLY}" + export RSYSLOG_YAML_ONLY=0 generate_conf "" add_conf 'module(load="../plugins/imtcp/.libs/imtcp") input(type="imtcp" port="0" listenPortFileName="'$RSYSLOG_DYNNAME'.tcpflood_port") @@ -300,6 +304,7 @@ local0.* ./'${RSYSLOG_DYNNAME}'.HOSTNAME;hostname export RS_HOSTNAME="$(cat ${RSYSLOG_DYNNAME}.HOSTNAME)" rm -f "${RSYSLOG_DYNNAME}.HOSTNAME" echo HOSTNAME is: $RS_HOSTNAME + export RSYSLOG_YAML_ONLY="${_saved_yaml_only}" } @@ -308,34 +313,26 @@ local0.* ./'${RSYSLOG_DYNNAME}'.HOSTNAME;hostname # finished under stress otherwise # $1 is the instance id, if given (or --yaml-only as first arg; see below) # -# YAML-ONLY MODE LIMITATION: In --yaml-only mode, $MainmsgQueueTimeoutEnqueue and -# $MainmsgQueueTimeoutShutdown (legacy global directives) cannot be set because the -# YAML format does not support sysklogd-style directives. Instead, mainqueue timeout -# settings are expressed via the mainqueue: section. Tests requiring custom timeout -# values should set RSTB_GLOBAL_QUEUE_SHUTDOWN_TIMEOUT / RSTB_ACTION_DEFAULT_Q_TO_SHUTDOWN -# / RSTB_ACTION_DEFAULT_Q_TO_ENQUEUE env vars before calling generate_conf --yaml-only. +# Queue timeout settings ($MainmsgQueueTimeoutEnqueue, $MainmsgQueueTimeoutShutdown, +# inputs.timeout.shutdown and default.action.queue.*) are configured via imdiag +# module parameters in the config preamble (module(load="imdiag" ...)). This avoids +# post-startup diagtalker round-trips and works identically for RainerScript and +# YAML-only modes. Tests that need a non-default value must set the relevant +# RSTB_* variable before calling generate_conf. # -# Also, the .started marker file is not written in yaml-only mode because the -# syslogtag-based filter requires legacy/RainerScript syntax. Startup detection relies -# on the imdiag port file and the rsyslogd PID file instead. +# Startup detection relies on the imdiag port file in both RainerScript and +# yaml-only modes; no .started marker file is used. generate_conf() { local yaml_only=0 if [ "$1" = "--yaml-only" ]; then yaml_only=1 shift fi - if [ "$RSTB_GLOBAL_QUEUE_SHUTDOWN_TIMEOUT" == "" ]; then - RSTB_GLOBAL_QUEUE_SHUTDOWN_TIMEOUT="10000" - fi - if [ "$RSTB_GLOBAL_INPUT_SHUTDOWN_TIMEOUT" == "" ]; then - RSTB_GLOBAL_INPUT_SHUTDOWN_TIMEOUT="60000" - fi - if [ "$RSTB_ACTION_DEFAULT_Q_TO_SHUTDOWN" == "" ]; then - RSTB_ACTION_DEFAULT_Q_TO_SHUTDOWN="20000" - fi - if [ "$RSTB_ACTION_DEFAULT_Q_TO_ENQUEUE" == "" ]; then - RSTB_ACTION_DEFAULT_Q_TO_ENQUEUE="30000" - fi + : "${RSTB_GLOBAL_QUEUE_SHUTDOWN_TIMEOUT:=10000}" + : "${RSTB_GLOBAL_INPUT_SHUTDOWN_TIMEOUT:=60000}" + : "${RSTB_ACTION_DEFAULT_Q_TO_SHUTDOWN:=20000}" + : "${RSTB_ACTION_DEFAULT_Q_TO_ENQUEUE:=30000}" + : "${RSTB_MAIN_Q_TO_ENQUEUE:=30000}" export TCPFLOOD_PORT="$(get_free_port)" if [ "$1" == "" ]; then export TESTCONF_NM="${RSYSLOG_DYNNAME}_" # this basename is also used by instance 2! @@ -358,43 +355,38 @@ generate_conf() { fi { printf 'version: 2\n\nglobal:\n' - printf ' inputs.timeout.shutdown: "%s"\n' "$RSTB_GLOBAL_INPUT_SHUTDOWN_TIMEOUT" - printf ' default.action.queue.timeoutshutdown: "%s"\n' "$RSTB_ACTION_DEFAULT_Q_TO_SHUTDOWN" - printf ' default.action.queue.timeoutEnqueue: "%s"\n' "$RSTB_ACTION_DEFAULT_Q_TO_ENQUEUE" printf ' debug.abortOnProgramError: "on"\n' [ -n "$ipproto_line" ] && printf '%s\n' "$ipproto_line" - printf '\nmainqueue:\n' - printf ' timeoutenqueue: "%s"\n' "$RSTB_ACTION_DEFAULT_Q_TO_ENQUEUE" - printf ' timeoutshutdown: "%s"\n' "$RSTB_GLOBAL_QUEUE_SHUTDOWN_TIMEOUT" - printf '\nmodules:\n' + printf '\ntestbench_modules:\n' printf ' - load: "../plugins/imdiag/.libs/imdiag"\n' printf ' listenportfilename: "%s.imdiag%s.port"\n' "$RSYSLOG_DYNNAME" "$1" printf ' aborttimeout: "%s"\n' "$TB_TEST_MAX_RUNTIME" - # The modules: sequence is intentionally left open here. - # Tests append additional ' - load: ...' items (2-space indent, no modules: key) - # which YAML parsers treat as a continuation of this sequence. - # The sequence closes when the test adds a zero-indent key (inputs:, rulesets:, etc.). - # Tests MUST include the imdiag input in their inputs: section; use the - # add_yaml_imdiag_input helper to avoid boilerplate. - printf '\n###### append test modules as continuation items ( - load: ...)\n' - printf '###### then add inputs: (include imdiag via add_yaml_imdiag_input)\n' + printf ' mainmsgqueuetimeoutshutdown: "%s"\n' "$RSTB_GLOBAL_QUEUE_SHUTDOWN_TIMEOUT" + printf ' mainmsgqueuetimeoutenqueue: "%s"\n' "$RSTB_MAIN_Q_TO_ENQUEUE" + printf ' inputshutdowntimeout: "%s"\n' "$RSTB_GLOBAL_INPUT_SHUTDOWN_TIMEOUT" + printf ' defaultactionqueuetimeoutshutdown: "%s"\n' "$RSTB_ACTION_DEFAULT_Q_TO_SHUTDOWN" + printf ' defaultactionqueuetimeoutenqueue: "%s"\n' "$RSTB_ACTION_DEFAULT_Q_TO_ENQUEUE" + printf '\n###### add modules:, inputs:, rulesets: etc. below\n' + printf '###### include imdiag input via add_yaml_imdiag_input\n' printf '###### end of testbench instrumentation part, test conf follows:\n' } > ${TESTCONF_NM}$1.yaml else export RSYSLOG_YAML_ONLY=0 export TESTCONF_EXT="conf" - echo 'module(load="../plugins/imdiag/.libs/imdiag") -global(inputs.timeout.shutdown="'$RSTB_GLOBAL_INPUT_SHUTDOWN_TIMEOUT'" - default.action.queue.timeoutshutdown="'$RSTB_ACTION_DEFAULT_Q_TO_SHUTDOWN'" - default.action.queue.timeoutEnqueue="'$RSTB_ACTION_DEFAULT_Q_TO_ENQUEUE'" - debug.abortOnProgramError="on") -# use legacy-style for the following settings so that we can override if needed -$MainmsgQueueTimeoutEnqueue '$RSTB_ACTION_DEFAULT_Q_TO_ENQUEUE' -$MainmsgQueueTimeoutShutdown '$RSTB_GLOBAL_QUEUE_SHUTDOWN_TIMEOUT' + echo 'module(load="../plugins/imdiag/.libs/imdiag" + mainmsgqueuetimeoutshutdown="'$RSTB_GLOBAL_QUEUE_SHUTDOWN_TIMEOUT'" + mainmsgqueuetimeoutenqueue="'$RSTB_MAIN_Q_TO_ENQUEUE'" + inputshutdowntimeout="'$RSTB_GLOBAL_INPUT_SHUTDOWN_TIMEOUT'" + defaultactionqueuetimeoutshutdown="'$RSTB_ACTION_DEFAULT_Q_TO_SHUTDOWN'" + defaultactionqueuetimeoutenqueue="'$RSTB_ACTION_DEFAULT_Q_TO_ENQUEUE'") +global(debug.abortOnProgramError="on") $IMDiagListenPortFileName '$RSYSLOG_DYNNAME.imdiag$1.port' $IMDiagServerRun 0 $IMDiagAbortTimeout '$TB_TEST_MAX_RUNTIME' - +# Capture rsyslogd own messages (startup, shutdown, errors) to the .started +# file. Tests use this file to check for expected rsyslogd-internal messages. +# This rule also ensures at least one output action exists in the default +# ruleset, which is required by rsyslogd even when only inputs are configured. :syslogtag, contains, "rsyslogd" ./'${RSYSLOG_DYNNAME}$1'.started ###### end of testbench instrumentation part, test conf follows:' > ${TESTCONF_NM}$1.conf # Optionally enforce IPv4 for this test instance. @@ -941,47 +933,38 @@ check_rsyslog_active() { } # wait for rsyslogd startup ($1 is the instance) +# Startup is signalled by the imdiag port file appearing (written only after +# the imdiag input is fully active). This works for both RainerScript and +# yaml-only modes. Fast-fail if the process exits before the port file appears. wait_startup() { local instance=$1 local pid_file="$RSYSLOG_PIDBASE$instance.pid" - local started_file="${RSYSLOG_DYNNAME}$instance.started" local imdiag_port_file="$RSYSLOG_DYNNAME.imdiag$instance.port" - # In yaml-only mode the .started marker is never written (the syslogtag filter - # requires RainerScript/legacy syntax). The PID file alone is insufficient because - # rsyslogd writes it before activating inputs, so a YAML config error would appear - # as a successful startup. Require the imdiag port file (written only after the - # input is fully active) instead, and fast-fail if the process exits before that. - local _imdiag_port_ready while :; do - _imdiag_port_ready=0 - [ -s "$imdiag_port_file" ] && _imdiag_port_ready=1 - if [ "$RSYSLOG_YAML_ONLY" = "1" ]; then - # yaml-only: imdiag port file is the definitive startup signal - [ "$_imdiag_port_ready" = "1" ] && break - # fast-fail: process wrote PID file but is already gone (config error) + if [ -s "$imdiag_port_file" ]; then + # port file seen — quick liveness check before trusting it if [ -s "$pid_file" ] && ! ps -p "$(cat "$pid_file" 2>/dev/null)" > /dev/null 2>&1; then - printf '%s ABORT! rsyslog exited during yaml-only startup (config error?)\n' "$(tb_timestamp)" + printf '%s ABORT! rsyslog exited immediately after writing port file\n' "$(tb_timestamp)" error_exit 1 stacktrace fi - else - { [ -f "$pid_file" ] || [ -f "$started_file" ] || [ "$_imdiag_port_ready" = "1" ]; } \ - && break + break fi - # If we are exactly at/over timeout threshold, re-check once more before aborting + # fast-fail: PID written but process already gone (config error) + if [ -s "$pid_file" ] && ! ps -p "$(cat "$pid_file" 2>/dev/null)" > /dev/null 2>&1; then + printf '%s ABORT! rsyslog exited during startup (config error?)\n' "$(tb_timestamp)" + error_exit 1 stacktrace + fi + # timeout check if [ $(date +%s) -gt $(( TB_STARTTEST + TB_STARTUP_MAX_RUNTIME )) ]; then [ -s "$imdiag_port_file" ] && break - if [ "$RSYSLOG_YAML_ONLY" != "1" ]; then - { [ -f "$pid_file" ] || [ -f "$started_file" ]; } && break - fi - printf '%s ABORT! Timeout waiting startup indicator (%s or %s or %s) after %d seconds\n' "$(tb_timestamp)" "$pid_file" "$started_file" "$imdiag_port_file" $TB_STARTUP_MAX_RUNTIME - # show current file states for diagnostics - ls -l "$pid_file" "$started_file" "$imdiag_port_file" 2>/dev/null || true - # observed late creation on some CI runners; proceed softly and let subsequent waits validate - break + printf '%s ABORT! Timeout waiting for imdiag port file (%s) after %d seconds\n' \ + "$(tb_timestamp)" "$imdiag_port_file" $TB_STARTUP_MAX_RUNTIME + ls -l "$pid_file" "$imdiag_port_file" 2>/dev/null || true + error_exit 1 stacktrace fi $TESTTOOL_DIR/msleep 50 # wait 50 milliseconds done - # If we have a pid file, perform a quick liveness check + # quick liveness check if [ -f "$pid_file" ]; then $TESTTOOL_DIR/msleep 50 check_rsyslog_active $instance @@ -2320,7 +2303,7 @@ exit_test() { rm -f work rsyslog.out.* xlate*.lkp_tbl rm -rf test-logdir stat-file1 rm -f rsyslog.conf.tlscert stat-file1 rsyslog.empty imfile-state:* - rm -f ${TESTCONF_NM}.conf + rm -f ${TESTCONF_NM}.conf ${TESTCONF_NM}.yaml rm -f tmp.qi nocert rm -fr $RSYSLOG_DYNNAME* # delete all of our dynamic files unset TCPFLOOD_EXTRA_OPTS diff --git a/tests/yaml-basic-yamlonly.sh b/tests/yaml-basic-yamlonly.sh index 639c2aedd..05f89556d 100755 --- a/tests/yaml-basic-yamlonly.sh +++ b/tests/yaml-basic-yamlonly.sh @@ -1,18 +1,13 @@ #!/bin/bash # Tests that the YAML-only testbench mode works end-to-end: -# - generate_conf --yaml-only writes a pure YAML preamble (no RainerScript) -# - additional modules are appended as sequence continuations (no duplicate -# modules: key) so YAML parsers see a single well-formed modules: list +# - generate_conf --yaml-only writes a pure YAML preamble using +# testbench_modules: for the imdiag setup (no RainerScript) +# - test-specific modules go in a standard modules: section # - add_yaml_imdiag_input adds the imdiag input for startup detection # - rsyslogd starts and processes messages using only the YAML loader # # This test intentionally avoids any RainerScript or legacy-style directives -# so that it exercises the yaml-only testbench path introduced alongside the -# YAML configuration support. -# -# Note: queue timeout settings are expressed via mainqueue: in the YAML -# preamble (generated by generate_conf --yaml-only) because the legacy -# $MainmsgQueueTimeout* directives are not available in yaml-only mode. +# so that it exercises the yaml-only testbench path. # # Added 2025 by contributors, released under ASL 2.0 . ${srcdir:=.}/diag.sh init @@ -20,8 +15,9 @@ require_plugin imtcp export NUMMESSAGES=100 export QUEUE_EMPTY_CHECK_FUNC=wait_file_lines generate_conf --yaml-only -# Append imtcp as a continuation item of the modules: sequence already opened -# by the preamble (2-space indent, no top-level modules: key). +# Test-specific modules go in a standard modules: section. +# Testbench infrastructure (imdiag) is in testbench_modules: written by the preamble. +add_yaml_conf 'modules:' add_yaml_conf ' - load: "../plugins/imtcp/.libs/imtcp"' add_yaml_conf '' add_yaml_conf 'templates:'