diff --git a/plugins/inventory/kubevirt.py b/plugins/inventory/kubevirt.py index d3a02d9..9ff5df1 100644 --- a/plugins/inventory/kubevirt.py +++ b/plugins/inventory/kubevirt.py @@ -157,6 +157,14 @@ except ImportError as e: from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable +# Handle import errors of trust_as_template. +# It is only available on ansible-core >=2.19. +try: + from ansible.template import trust_as_template +except ImportError: + trust_as_template = None + + from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import ( get_api_client, K8SClient, @@ -445,13 +453,13 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): results = {} if attempt_to_read_cache: try: - results = self._cache[cache_key] + results = self.cache[cache_key] except KeyError: cache_needs_update = True if not attempt_to_read_cache or cache_needs_update: results = self._fetch_objects(get_api_client(**config_data), opts) if cache_needs_update: - self._cache[cache_key] = results + self.cache[cache_key] = results self._populate_inventory(results, opts) @@ -883,12 +891,32 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): """ hostvars = self.inventory.get_host(hostname).get_vars() strict = self.get_option("strict") + + def trust_compose_groups(data: Dict) -> Dict: + if trust_as_template is not None: + return {k: trust_as_template(v) for k, v in data.items()} + return data + + def trust_keyed_groups(data: List) -> List: + if trust_as_template is not None: + return [{**d, "key": trust_as_template(d["key"])} for d in data] + return data + self._set_composite_vars( - self.get_option("compose"), hostvars, hostname, strict=True + trust_compose_groups(self.get_option("compose")), + hostvars, + hostname, + strict=True, ) self._add_host_to_composed_groups( - self.get_option("groups"), hostvars, hostname, strict=strict + trust_compose_groups(self.get_option("groups")), + hostvars, + hostname, + strict=strict, ) self._add_host_to_keyed_groups( - self.get_option("keyed_groups"), hostvars, hostname, strict=strict + trust_keyed_groups(self.get_option("keyed_groups")), + hostvars, + hostname, + strict=strict, ) diff --git a/tests/unit/plugins/inventory/test_kubevirt_parse.py b/tests/unit/plugins/inventory/test_kubevirt_parse.py index b9a471d..28159db 100644 --- a/tests/unit/plugins/inventory/test_kubevirt_parse.py +++ b/tests/unit/plugins/inventory/test_kubevirt_parse.py @@ -17,6 +17,8 @@ from ansible_collections.kubevirt.core.plugins.inventory.kubevirt import ( KubeVirtInventoryException, ) +from ansible.plugins.inventory import Cacheable + @pytest.mark.parametrize( "config_data,expected", @@ -96,9 +98,8 @@ from ansible_collections.kubevirt.core.plugins.inventory.kubevirt import ( ], ) def test_config_data_to_opts(mocker, inventory, config_data, expected): - read_config_data = mocker.patch.object( - inventory, "_read_config_data", return_value=config_data - ) + mocker.patch.object(Cacheable, "cache", new_callable=mocker.PropertyMock) + mocker.patch.object(inventory, "_read_config_data", return_value=config_data) mocker.patch.object(inventory, "get_cache_key") mocker.patch.object(inventory, "get_option") mocker.patch.object(kubevirt, "get_api_client") @@ -129,7 +130,9 @@ def test_use_of_cache( path = "/testpath" config_data = {"host_format": "test-format"} - mocker.patch.dict(inventory._cache, cache_data) + mocker.patch.object( + Cacheable, "cache", new_callable=mocker.PropertyMock, return_value=cache_data + ) read_config_data = mocker.patch.object( inventory, "_read_config_data", return_value=config_data @@ -168,6 +171,7 @@ def test_use_of_cache( ], ) def test_k8s_client_missing(mocker, inventory, present): + mocker.patch.object(Cacheable, "cache", new_callable=mocker.PropertyMock) mocker.patch.object(kubevirt, "HAS_K8S_MODULE_HELPER", present) mocker.patch.object(kubevirt, "get_api_client") mocker.patch.object(inventory, "_read_config_data", return_value={}) diff --git a/tests/unit/plugins/modules/test_kubevirt_vm.py b/tests/unit/plugins/modules/test_kubevirt_vm.py index 15d36ac..f199295 100644 --- a/tests/unit/plugins/modules/test_kubevirt_vm.py +++ b/tests/unit/plugins/modules/test_kubevirt_vm.py @@ -16,14 +16,21 @@ from ansible_collections.kubevirt.core.tests.unit.utils.ansible_module_mock impo AnsibleExitJson, exit_json, fail_json, - set_module_args, ) +# Handle import errors of patch_module_args. +# It is only available on ansible-core >=2.19. +try: + from ansible.module_utils.testing import patch_module_args +except ImportError as e: + from ansible_collections.kubevirt.core.tests.unit.utils.ansible_module_mock import ( + patch_module_args, + ) + def test_module_fails_when_required_args_missing(mocker): mocker.patch.object(AnsibleModule, "fail_json", fail_json) - with pytest.raises(AnsibleFailJson): - set_module_args({}) + with pytest.raises(AnsibleFailJson), patch_module_args({}): kubevirt_vm.main() @@ -290,8 +297,7 @@ def test_module(mocker, module_params, k8s_module_params, vm_definition, method) }, ) - with pytest.raises(AnsibleExitJson): - set_module_args(module_params) + with pytest.raises(AnsibleExitJson), patch_module_args(module_params): kubevirt_vm.main() perform_action.assert_called_once_with( diff --git a/tests/unit/plugins/modules/test_kubevirt_vm_info.py b/tests/unit/plugins/modules/test_kubevirt_vm_info.py index 5444c14..5804b0a 100644 --- a/tests/unit/plugins/modules/test_kubevirt_vm_info.py +++ b/tests/unit/plugins/modules/test_kubevirt_vm_info.py @@ -23,14 +23,21 @@ from ansible_collections.kubevirt.core.tests.unit.utils.ansible_module_mock impo AnsibleFailJson, exit_json, fail_json, - set_module_args, ) +# Handle import errors of patch_module_args. +# It is only available on ansible-core >=2.19. +try: + from ansible.module_utils.testing import patch_module_args +except ImportError as e: + from ansible_collections.kubevirt.core.tests.unit.utils.ansible_module_mock import ( + patch_module_args, + ) + def test_module_fails_when_required_args_missing(mocker): mocker.patch.object(AnsibleModule, "fail_json", fail_json) - with pytest.raises(AnsibleFailJson): - set_module_args({"running": False}) + with pytest.raises(AnsibleFailJson), patch_module_args({"running": False}): kubevirt_vm_info.main() @@ -96,8 +103,7 @@ def test_module(mocker, module_args, find_args): }, ) - with pytest.raises(AnsibleExitJson): - set_module_args(module_args) + with pytest.raises(AnsibleExitJson), patch_module_args(module_args): kubevirt_vm_info.main() find.assert_called_once_with(**find_args) diff --git a/tests/unit/plugins/modules/test_kubevirt_vmi_info.py b/tests/unit/plugins/modules/test_kubevirt_vmi_info.py index c004805..bc7de6a 100644 --- a/tests/unit/plugins/modules/test_kubevirt_vmi_info.py +++ b/tests/unit/plugins/modules/test_kubevirt_vmi_info.py @@ -21,9 +21,17 @@ from ansible_collections.kubevirt.core.plugins.module_utils import ( from ansible_collections.kubevirt.core.tests.unit.utils.ansible_module_mock import ( AnsibleExitJson, exit_json, - set_module_args, ) +# Handle import errors of patch_module_args. +# It is only available on ansible-core >=2.19. +try: + from ansible.module_utils.testing import patch_module_args +except ImportError as e: + from ansible_collections.kubevirt.core.tests.unit.utils.ansible_module_mock import ( + patch_module_args, + ) + FIND_ARGS_DEFAULT = { "kind": "VirtualMachineInstance", "api_version": "kubevirt.io/v1", @@ -74,8 +82,7 @@ def test_module(mocker, module_args, find_args): }, ) - with pytest.raises(AnsibleExitJson): - set_module_args(module_args) + with pytest.raises(AnsibleExitJson), patch_module_args(module_args): kubevirt_vmi_info.main() find.assert_called_once_with(**find_args) diff --git a/tests/unit/utils/ansible_module_mock.py b/tests/unit/utils/ansible_module_mock.py index 616d1f2..8381ced 100644 --- a/tests/unit/utils/ansible_module_mock.py +++ b/tests/unit/utils/ansible_module_mock.py @@ -10,16 +10,24 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -import json +from contextlib import contextmanager +from json import dumps +from typing import ( + Any, + Dict, +) +from unittest import mock from ansible.module_utils import basic from ansible.module_utils.common.text.converters import to_bytes -def set_module_args(args): +@contextmanager +def patch_module_args(args: Dict[str, Any] | None = None): """prepare arguments so that they will be picked up during module creation""" - args = json.dumps({"ANSIBLE_MODULE_ARGS": args}) - basic._ANSIBLE_ARGS = to_bytes(args) + args = dumps({"ANSIBLE_MODULE_ARGS": args}) + with mock.patch.object(basic, "_ANSIBLE_ARGS", to_bytes(args)): + yield class AnsibleExitJson(Exception):