From 6f1f6ada8aa16526f47ad4ba1aae4a12d3102e5f Mon Sep 17 00:00:00 2001 From: Dirk Julich Date: Wed, 20 May 2026 19:11:23 +0200 Subject: [PATCH] [AAP-74343] Use Ansible plugin config system for collect_host_queries Declare collect_host_queries as a formal plugin option in DOCUMENTATION with env var AWX_COLLECT_HOST_QUERIES, replacing the raw os.getenv() call with self.get_option(). This follows the standard Ansible plugin configuration pattern. Co-Authored-By: Claude Opus 4.6 --- .../unit/test_indirect_query_discovery.py | 19 +++++++++++++++---- .../library/indirect_instance_count.py | 9 ++++++++- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/awx/main/tests/unit/test_indirect_query_discovery.py b/awx/main/tests/unit/test_indirect_query_discovery.py index 338906f9d8..b871910e4e 100644 --- a/awx/main/tests/unit/test_indirect_query_discovery.py +++ b/awx/main/tests/unit/test_indirect_query_discovery.py @@ -39,6 +39,13 @@ def create_queries_dir_mock(file_lookup_func): class MockCallbackBase: def __init__(self): self._display = mock.MagicMock() + self._plugin_options = {} + + def get_option(self, key): + return self._plugin_options.get(key) + + def set_option(self, key, value): + self._plugin_options[key] = value def v2_playbook_on_stats(self, stats): pass @@ -274,7 +281,7 @@ class TestExternalQueryDiscovery: @mock.patch('awx.playbooks.library.indirect_instance_count.list_collections') @mock.patch('awx.playbooks.library.indirect_instance_count.files') @mock.patch('awx.playbooks.library.indirect_instance_count.find_external_query_with_fallback') - @mock.patch.dict('os.environ', {'AWX_ISOLATED_DATA_DIR': '/tmp/artifacts', 'AWX_COLLECT_HOST_QUERIES': '1'}) + @mock.patch.dict('os.environ', {'AWX_ISOLATED_DATA_DIR': '/tmp/artifacts'}) def test_precedence_embedded_over_external(self, mock_fallback, mock_files, mock_list_collections): """AC7.1: Embedded query takes precedence when both embedded and external exist.""" from awx.playbooks.library.indirect_instance_count import CallbackModule @@ -289,6 +296,7 @@ class TestExternalQueryDiscovery: callback = CallbackModule() callback._display = mock.Mock() + callback.set_option('collect_host_queries', True) with mock.patch('builtins.open', mock.mock_open()): with mock.patch('json.dumps', return_value='{}'): @@ -300,7 +308,7 @@ class TestExternalQueryDiscovery: @mock.patch('awx.playbooks.library.indirect_instance_count.list_collections') @mock.patch('awx.playbooks.library.indirect_instance_count.files') @mock.patch('awx.playbooks.library.indirect_instance_count.find_external_query_with_fallback') - @mock.patch.dict('os.environ', {'AWX_ISOLATED_DATA_DIR': '/tmp/artifacts', 'AWX_COLLECT_HOST_QUERIES': '1'}) + @mock.patch.dict('os.environ', {'AWX_ISOLATED_DATA_DIR': '/tmp/artifacts'}) def test_external_query_when_embedded_missing(self, mock_fallback, mock_files, mock_list_collections): """AC7.2: External query is discovered when embedded query is missing.""" from awx.playbooks.library.indirect_instance_count import CallbackModule @@ -318,6 +326,7 @@ class TestExternalQueryDiscovery: callback = CallbackModule() callback._display = mock.Mock() + callback.set_option('collect_host_queries', True) with mock.patch('builtins.open', mock.mock_open()): with mock.patch('json.dumps', return_value='{}'): @@ -329,7 +338,7 @@ class TestExternalQueryDiscovery: @mock.patch('awx.playbooks.library.indirect_instance_count.list_collections') @mock.patch('awx.playbooks.library.indirect_instance_count.files') @mock.patch('awx.playbooks.library.indirect_instance_count.find_external_query_with_fallback') - @mock.patch.dict('os.environ', {'AWX_ISOLATED_DATA_DIR': '/tmp/artifacts', 'AWX_COLLECT_HOST_QUERIES': '1'}) + @mock.patch.dict('os.environ', {'AWX_ISOLATED_DATA_DIR': '/tmp/artifacts'}) def test_no_query_when_both_missing(self, mock_fallback, mock_files, mock_list_collections): """AC7.3: No query is used when both embedded and external queries are missing.""" from awx.playbooks.library.indirect_instance_count import CallbackModule @@ -342,6 +351,7 @@ class TestExternalQueryDiscovery: callback = CallbackModule() callback._display = mock.Mock() + callback.set_option('collect_host_queries', True) with mock.patch('builtins.open', mock.mock_open()): with mock.patch('json.dumps', return_value='{}'): @@ -352,7 +362,7 @@ class TestExternalQueryDiscovery: @mock.patch('awx.playbooks.library.indirect_instance_count.list_collections') @mock.patch('awx.playbooks.library.indirect_instance_count.files') @mock.patch('awx.playbooks.library.indirect_instance_count.find_external_query_with_fallback') - @mock.patch.dict('os.environ', {'AWX_ISOLATED_DATA_DIR': '/tmp/artifacts', 'AWX_COLLECT_HOST_QUERIES': '1'}) + @mock.patch.dict('os.environ', {'AWX_ISOLATED_DATA_DIR': '/tmp/artifacts'}) def test_info_log_on_fallback(self, mock_fallback, mock_files, mock_list_collections): """AC7.8: Log message is emitted when fallback version is used. @@ -372,6 +382,7 @@ class TestExternalQueryDiscovery: callback = CallbackModule() callback._display = mock.Mock() + callback.set_option('collect_host_queries', True) with mock.patch('builtins.open', mock.mock_open()): with mock.patch('json.dumps', return_value='{}'): diff --git a/awx/playbooks/library/indirect_instance_count.py b/awx/playbooks/library/indirect_instance_count.py index 695bd63cc8..4cbce89a43 100644 --- a/awx/playbooks/library/indirect_instance_count.py +++ b/awx/playbooks/library/indirect_instance_count.py @@ -17,6 +17,13 @@ DOCUMENTATION = ''' requirements: - Whitelist in configuration - Set AWX_ISOLATED_DATA_DIR, AWX will do this + options: + collect_host_queries: + description: When enabled, scan collections for host query files used in indirect node counting. + type: bool + default: false + env: + - name: AWX_COLLECT_HOST_QUERIES ''' import os @@ -168,7 +175,7 @@ class CallbackModule(CallbackBase): if not artifact_dir: raise RuntimeError('Only suitable in AWX, did not find private_data_dir') - collect_host_queries = os.getenv('AWX_COLLECT_HOST_QUERIES') == '1' + collect_host_queries = self.get_option('collect_host_queries') collections_print = {} for candidate in list_collections():