From 356218b564d6483f295ae89026c97c94a2048a6b Mon Sep 17 00:00:00 2001 From: Teddy Andrieux Date: Thu, 7 May 2026 08:09:32 +0200 Subject: [PATCH] fix(logging): tolerate unset options dict in worker bootstrap CLI parsers seed salt._logging's global options dict at startup via LogLevelMixIn.__setup_logging_config(). Non-CLI consumers (RunnerClient.asynchronous, SSHClient, salt.utils.process.Process subclasses, parallel states) have no parser, so the dict stays None. Process.__new__ snapshots that None into instance.__logging_config__; wrapped_run_func then calls set_logging_options_dict(None) defensively, which forwards to set_lowest_log_level_by_opts(None).get(...) and AttributeErrors on the worker. The parent exits 0 with a misleading "Target did not return any data" / dead jid / 'result': None. Make set_logging_options_dict(None) and setup_logging() (when nothing has been seeded) no-op gracefully. Workers fall back to whatever logger configuration they inherited from the parent. CLI tools always seed before calling and are unaffected. Fixes #68332 Signed-off-by: Teddy Andrieux --- changelog/68332.fixed.md | 1 + salt/_logging/impl.py | 4 +++- tests/pytests/functional/utils/test_process.py | 15 +++++++++++++++ tests/pytests/unit/_logging/test_impl.py | 18 ++++++++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 changelog/68332.fixed.md diff --git a/changelog/68332.fixed.md b/changelog/68332.fixed.md new file mode 100644 index 000000000000..ea0d32d84c6e --- /dev/null +++ b/changelog/68332.fixed.md @@ -0,0 +1 @@ +Fixed worker process crash when salt is used outside CLI tools. diff --git a/salt/_logging/impl.py b/salt/_logging/impl.py index 124633c0aa76..7b35b99a27dc 100644 --- a/salt/_logging/impl.py +++ b/salt/_logging/impl.py @@ -434,6 +434,8 @@ def set_logging_options_dict(opts): """ Create a logging related options dictionary based off of the loaded salt config """ + if opts is None: + return try: if isinstance(set_logging_options_dict.__options_dict__, ImmutableDict): raise RuntimeError( @@ -969,7 +971,7 @@ def setup_log_granular_levels(log_granular_levels): def setup_logging(): opts = get_logging_options_dict() if not opts: - raise RuntimeError("The logging options have not been set yet.") + return if ( opts.get("configure_console_logger", True) and not is_console_handler_configured() diff --git a/tests/pytests/functional/utils/test_process.py b/tests/pytests/functional/utils/test_process.py index ac5218eb3e59..7ffe5a401a5d 100644 --- a/tests/pytests/functional/utils/test_process.py +++ b/tests/pytests/functional/utils/test_process.py @@ -125,3 +125,18 @@ def test_process_preimports_multiprocessing_connection_68573(tmp_path): cwd=str(tmp_path), ) assert result.returncode == 0, f"stdout={result.stdout!r} stderr={result.stderr!r}" + + +def test_process_unseeded_logging_options(): + """ + Regression test for issue #68332. + """ + + def target(): + pass + + salt._logging.set_logging_options_dict.__options_dict__ = None + proc = salt.utils.process.Process(target=target) + proc.start() + proc.join() + assert proc.exitcode == 0 diff --git a/tests/pytests/unit/_logging/test_impl.py b/tests/pytests/unit/_logging/test_impl.py index b4e5b6a28d17..5d90b47a835e 100644 --- a/tests/pytests/unit/_logging/test_impl.py +++ b/tests/pytests/unit/_logging/test_impl.py @@ -13,7 +13,10 @@ SaltLogRecord, get_log_record_factory, set_log_record_factory, + set_logging_options_dict, + setup_logging, ) +from tests.support.mock import patch @pytest.fixture @@ -119,3 +122,18 @@ def test_deferred_records_flushed_through_color_formatter( output = console_stream.getvalue() assert "buffered message" in output assert "DEBUG" in output + + +def test_set_logging_options_dict_with_none(): + """ + Regression test for issue #68332. + """ + set_logging_options_dict(None) + + +def test_setup_logging_with_unseeded_options(): + """ + Regression test for issue #68332. + """ + with patch.object(set_logging_options_dict, "__options_dict__", None, create=True): + setup_logging()