summaryrefslogtreecommitdiffstats
path: root/test/integration/targets/module_utils
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-14 20:03:01 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-14 20:03:01 +0000
commita453ac31f3428614cceb99027f8efbdb9258a40b (patch)
treef61f87408f32a8511cbd91799f9cececb53e0374 /test/integration/targets/module_utils
parentInitial commit. (diff)
downloadansible-a453ac31f3428614cceb99027f8efbdb9258a40b.tar.xz
ansible-a453ac31f3428614cceb99027f8efbdb9258a40b.zip
Adding upstream version 2.10.7+merged+base+2.10.8+dfsg.upstream/2.10.7+merged+base+2.10.8+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'test/integration/targets/module_utils')
-rw-r--r--test/integration/targets/module_utils/aliases3
-rw-r--r--test/integration/targets/module_utils/callback/pure_json.py31
-rw-r--r--test/integration/targets/module_utils/collections/ansible_collections/testns/testcoll/plugins/module_utils/legit.py6
-rw-r--r--test/integration/targets/module_utils/library/test.py85
-rw-r--r--test/integration/targets/module_utils/library/test_alias_deprecation.py15
-rw-r--r--test/integration/targets/module_utils/library/test_cwd_missing.py33
-rw-r--r--test/integration/targets/module_utils/library/test_cwd_unreadable.py28
-rw-r--r--test/integration/targets/module_utils/library/test_env_override.py11
-rw-r--r--test/integration/targets/module_utils/library/test_failure.py12
-rw-r--r--test/integration/targets/module_utils/library/test_no_log.py35
-rw-r--r--test/integration/targets/module_utils/library/test_optional.py84
-rw-r--r--test/integration/targets/module_utils/library/test_override.py7
-rw-r--r--test/integration/targets/module_utils/library/test_recursive_diff.py29
-rw-r--r--test/integration/targets/module_utils/module_utils/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/a/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/a/b/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/a/b/c/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/a/b/c/d/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/a/b/c/d/e/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/a/b/c/d/e/f/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/a/b/c/d/e/f/g/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/a/b/c/d/e/f/g/h/__init__.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/bar0/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/bar0/foo.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/bar1/__init__.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/bar2/__init__.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/baz1/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/baz1/one.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/baz2/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/baz2/one.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/facts.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/foo.py3
-rw-r--r--test/integration/targets/module_utils/module_utils/foo0.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/foo1.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/foo2.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/qux1/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/qux1/quux.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/qux2/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/qux2/quux.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/qux2/quuz.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/service.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/spam1/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/spam1/ham/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/spam1/ham/eggs/__init__.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/spam2/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/spam2/ham/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/spam2/ham/eggs/__init__.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/spam3/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/spam3/ham/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/spam3/ham/bacon.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/spam4/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/spam4/ham/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/spam4/ham/bacon.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/spam5/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/spam5/ham/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/spam5/ham/bacon.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/spam5/ham/eggs.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/spam6/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/spam6/ham/__init__.py2
-rw-r--r--test/integration/targets/module_utils/module_utils/spam7/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/spam7/ham/__init__.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/spam7/ham/bacon.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/spam8/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/spam8/ham/__init__.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/spam8/ham/bacon.py1
-rw-r--r--test/integration/targets/module_utils/module_utils/sub/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/sub/bam.py3
-rw-r--r--test/integration/targets/module_utils/module_utils/sub/bam/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/sub/bam/bam.py3
-rw-r--r--test/integration/targets/module_utils/module_utils/sub/bar/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/sub/bar/bam.py3
-rw-r--r--test/integration/targets/module_utils/module_utils/sub/bar/bar.py3
-rw-r--r--test/integration/targets/module_utils/module_utils/yak/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/yak/zebra/__init__.py0
-rw-r--r--test/integration/targets/module_utils/module_utils/yak/zebra/foo.py1
-rw-r--r--test/integration/targets/module_utils/module_utils_basic_setcwd.yml22
-rw-r--r--test/integration/targets/module_utils/module_utils_common_dict_transformation.yml34
-rw-r--r--test/integration/targets/module_utils/module_utils_envvar.yml51
-rw-r--r--test/integration/targets/module_utils/module_utils_test.yml71
-rw-r--r--test/integration/targets/module_utils/module_utils_test_no_log.yml9
-rw-r--r--test/integration/targets/module_utils/module_utils_vvvvv.yml27
-rw-r--r--test/integration/targets/module_utils/other_mu_dir/__init__.py0
-rw-r--r--test/integration/targets/module_utils/other_mu_dir/a/__init__.py0
-rw-r--r--test/integration/targets/module_utils/other_mu_dir/a/b/__init__.py0
-rw-r--r--test/integration/targets/module_utils/other_mu_dir/a/b/c/__init__.py0
-rw-r--r--test/integration/targets/module_utils/other_mu_dir/a/b/c/d/__init__.py0
-rw-r--r--test/integration/targets/module_utils/other_mu_dir/a/b/c/d/e/__init__.py0
-rw-r--r--test/integration/targets/module_utils/other_mu_dir/a/b/c/d/e/f/__init__.py0
-rw-r--r--test/integration/targets/module_utils/other_mu_dir/a/b/c/d/e/f/g/__init__.py0
-rw-r--r--test/integration/targets/module_utils/other_mu_dir/a/b/c/d/e/f/g/h/__init__.py1
-rw-r--r--test/integration/targets/module_utils/other_mu_dir/facts.py1
-rw-r--r--test/integration/targets/module_utils/other_mu_dir/json_utils.py1
-rw-r--r--test/integration/targets/module_utils/other_mu_dir/mork.py1
-rwxr-xr-xtest/integration/targets/module_utils/runme.sh14
94 files changed, 653 insertions, 0 deletions
diff --git a/test/integration/targets/module_utils/aliases b/test/integration/targets/module_utils/aliases
new file mode 100644
index 00000000..2f5770ff
--- /dev/null
+++ b/test/integration/targets/module_utils/aliases
@@ -0,0 +1,3 @@
+shippable/posix/group3
+needs/root
+needs/target/setup_nobody
diff --git a/test/integration/targets/module_utils/callback/pure_json.py b/test/integration/targets/module_utils/callback/pure_json.py
new file mode 100644
index 00000000..1723d7bb
--- /dev/null
+++ b/test/integration/targets/module_utils/callback/pure_json.py
@@ -0,0 +1,31 @@
+# (c) 2021 Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+DOCUMENTATION = '''
+ name: pure_json
+ type: stdout
+ short_description: only outputs the module results as json
+'''
+
+import json
+
+from ansible.plugins.callback import CallbackBase
+
+
+class CallbackModule(CallbackBase):
+
+ CALLBACK_VERSION = 2.0
+ CALLBACK_TYPE = 'stdout'
+ CALLBACK_NAME = 'pure_json'
+
+ def v2_runner_on_failed(self, result, ignore_errors=False):
+ self._display.display(json.dumps(result._result))
+
+ def v2_runner_on_ok(self, result):
+ self._display.display(json.dumps(result._result))
+
+ def v2_runner_on_skipped(self, result):
+ self._display.display(json.dumps(result._result))
diff --git a/test/integration/targets/module_utils/collections/ansible_collections/testns/testcoll/plugins/module_utils/legit.py b/test/integration/targets/module_utils/collections/ansible_collections/testns/testcoll/plugins/module_utils/legit.py
new file mode 100644
index 00000000..b9d63482
--- /dev/null
+++ b/test/integration/targets/module_utils/collections/ansible_collections/testns/testcoll/plugins/module_utils/legit.py
@@ -0,0 +1,6 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+def importme():
+ return "successfully imported from testns.testcoll"
diff --git a/test/integration/targets/module_utils/library/test.py b/test/integration/targets/module_utils/library/test.py
new file mode 100644
index 00000000..fbb7e6e2
--- /dev/null
+++ b/test/integration/targets/module_utils/library/test.py
@@ -0,0 +1,85 @@
+#!/usr/bin/python
+# Most of these names are only available via PluginLoader so pylint doesn't
+# know they exist
+# pylint: disable=no-name-in-module
+results = {}
+
+# Test import with no from
+import ansible.module_utils.foo0
+results['foo0'] = ansible.module_utils.foo0.data
+
+# Test depthful import with no from
+import ansible.module_utils.bar0.foo
+results['bar0'] = ansible.module_utils.bar0.foo.data
+
+# Test import of module_utils/foo1.py
+from ansible.module_utils import foo1
+results['foo1'] = foo1.data
+
+# Test import of an identifier inside of module_utils/foo2.py
+from ansible.module_utils.foo2 import data
+results['foo2'] = data
+
+# Test import of module_utils/bar1/__init__.py
+from ansible.module_utils import bar1
+results['bar1'] = bar1.data
+
+# Test import of an identifier inside of module_utils/bar2/__init__.py
+from ansible.module_utils.bar2 import data
+results['bar2'] = data
+
+# Test import of module_utils/baz1/one.py
+from ansible.module_utils.baz1 import one
+results['baz1'] = one.data
+
+# Test import of an identifier inside of module_utils/baz2/one.py
+from ansible.module_utils.baz2.one import data
+results['baz2'] = data
+
+# Test import of module_utils/spam1/ham/eggs/__init__.py
+from ansible.module_utils.spam1.ham import eggs
+results['spam1'] = eggs.data
+
+# Test import of an identifier inside module_utils/spam2/ham/eggs/__init__.py
+from ansible.module_utils.spam2.ham.eggs import data
+results['spam2'] = data
+
+# Test import of module_utils/spam3/ham/bacon.py
+from ansible.module_utils.spam3.ham import bacon
+results['spam3'] = bacon.data
+
+# Test import of an identifier inside of module_utils/spam4/ham/bacon.py
+from ansible.module_utils.spam4.ham.bacon import data
+results['spam4'] = data
+
+# Test import of module_utils.spam5.ham bacon and eggs (modules)
+from ansible.module_utils.spam5.ham import bacon, eggs
+results['spam5'] = (bacon.data, eggs.data)
+
+# Test import of module_utils.spam6.ham bacon and eggs (identifiers)
+from ansible.module_utils.spam6.ham import bacon, eggs
+results['spam6'] = (bacon, eggs)
+
+# Test import of module_utils.spam7.ham bacon and eggs (module and identifier)
+from ansible.module_utils.spam7.ham import bacon, eggs
+results['spam7'] = (bacon.data, eggs)
+
+# Test import of module_utils/spam8/ham/bacon.py and module_utils/spam8/ham/eggs.py separately
+from ansible.module_utils.spam8.ham import bacon
+from ansible.module_utils.spam8.ham import eggs
+results['spam8'] = (bacon.data, eggs)
+
+# Test that import of module_utils/qux1/quux.py using as works
+from ansible.module_utils.qux1 import quux as one
+results['qux1'] = one.data
+
+# Test that importing qux2/quux.py and qux2/quuz.py using as works
+from ansible.module_utils.qux2 import quux as one, quuz as two
+results['qux2'] = (one.data, two.data)
+
+# Test depth
+from ansible.module_utils.a.b.c.d.e.f.g.h import data
+
+results['abcdefgh'] = data
+from ansible.module_utils.basic import AnsibleModule
+AnsibleModule(argument_spec=dict()).exit_json(**results)
diff --git a/test/integration/targets/module_utils/library/test_alias_deprecation.py b/test/integration/targets/module_utils/library/test_alias_deprecation.py
new file mode 100644
index 00000000..96410fc4
--- /dev/null
+++ b/test/integration/targets/module_utils/library/test_alias_deprecation.py
@@ -0,0 +1,15 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.facts import data
+
+results = {"data": data}
+
+arg_spec = dict(
+ foo=dict(type='str', aliases=['baz'], deprecated_aliases=[dict(name='baz', version='9.99')])
+)
+
+AnsibleModule(argument_spec=arg_spec).exit_json(**results)
diff --git a/test/integration/targets/module_utils/library/test_cwd_missing.py b/test/integration/targets/module_utils/library/test_cwd_missing.py
new file mode 100644
index 00000000..cd1f9c77
--- /dev/null
+++ b/test/integration/targets/module_utils/library/test_cwd_missing.py
@@ -0,0 +1,33 @@
+#!/usr/bin/python
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import os
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ # This module verifies that AnsibleModule works when cwd does not exist.
+ # This situation can occur as a race condition when the following conditions are met:
+ #
+ # 1) Execute a module which has high startup overhead prior to instantiating AnsibleModule (0.5s is enough in many cases).
+ # 2) Run the module async as the last task in a playbook using connection=local (a fire-and-forget task).
+ # 3) Remove the directory containing the playbook immediately after playbook execution ends (playbook in a temp dir).
+ #
+ # To ease testing of this race condition the deletion of cwd is handled in this module.
+ # This avoids race conditions in the test, including timing cwd deletion between AnsiballZ wrapper execution and AnsibleModule instantiation.
+ # The timing issue with AnsiballZ is due to cwd checking in the wrapper when code coverage is enabled.
+
+ temp = os.path.abspath('temp')
+
+ os.mkdir(temp)
+ os.chdir(temp)
+ os.rmdir(temp)
+
+ module = AnsibleModule(argument_spec=dict())
+ module.exit_json(before=temp, after=os.getcwd())
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/integration/targets/module_utils/library/test_cwd_unreadable.py b/test/integration/targets/module_utils/library/test_cwd_unreadable.py
new file mode 100644
index 00000000..d65f31ac
--- /dev/null
+++ b/test/integration/targets/module_utils/library/test_cwd_unreadable.py
@@ -0,0 +1,28 @@
+#!/usr/bin/python
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import os
+
+from ansible.module_utils.basic import AnsibleModule
+
+
+def main():
+ # This module verifies that AnsibleModule works when cwd exists but is unreadable.
+ # This situation can occur when running tasks as an unprivileged user.
+
+ try:
+ cwd = os.getcwd()
+ except OSError:
+ # Compensate for macOS being unable to access cwd as an unprivileged user.
+ # This test is a no-op in this case.
+ # Testing for os.getcwd() failures is handled by the test_cwd_missing module.
+ cwd = '/'
+ os.chdir(cwd)
+
+ module = AnsibleModule(argument_spec=dict())
+ module.exit_json(before=cwd, after=os.getcwd())
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/integration/targets/module_utils/library/test_env_override.py b/test/integration/targets/module_utils/library/test_env_override.py
new file mode 100644
index 00000000..94e3051b
--- /dev/null
+++ b/test/integration/targets/module_utils/library/test_env_override.py
@@ -0,0 +1,11 @@
+#!/usr/bin/python
+# Most of these names are only available via PluginLoader so pylint doesn't
+# know they exist
+# pylint: disable=no-name-in-module
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.json_utils import data
+from ansible.module_utils.mork import data as mork_data
+
+results = {"json_utils": data, "mork": mork_data}
+
+AnsibleModule(argument_spec=dict()).exit_json(**results)
diff --git a/test/integration/targets/module_utils/library/test_failure.py b/test/integration/targets/module_utils/library/test_failure.py
new file mode 100644
index 00000000..e5257aef
--- /dev/null
+++ b/test/integration/targets/module_utils/library/test_failure.py
@@ -0,0 +1,12 @@
+#!/usr/bin/python
+
+results = {}
+# Test that we are rooted correctly
+# Following files:
+# module_utils/yak/zebra/foo.py
+from ansible.module_utils.zebra import foo
+
+results['zebra'] = foo.data
+
+from ansible.module_utils.basic import AnsibleModule
+AnsibleModule(argument_spec=dict()).exit_json(**results)
diff --git a/test/integration/targets/module_utils/library/test_no_log.py b/test/integration/targets/module_utils/library/test_no_log.py
new file mode 100644
index 00000000..770e0b3a
--- /dev/null
+++ b/test/integration/targets/module_utils/library/test_no_log.py
@@ -0,0 +1,35 @@
+#!/usr/bin/python
+# (c) 2021 Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+from ansible.module_utils.basic import AnsibleModule, env_fallback
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ explicit_pass=dict(type='str', no_log=True),
+ fallback_pass=dict(type='str', no_log=True, fallback=(env_fallback, ['SECRET_ENV'])),
+ default_pass=dict(type='str', no_log=True, default='zyx'),
+ normal=dict(type='str', default='plaintext'),
+ suboption=dict(
+ type='dict',
+ options=dict(
+ explicit_sub_pass=dict(type='str', no_log=True),
+ fallback_sub_pass=dict(type='str', no_log=True, fallback=(env_fallback, ['SECRET_SUB_ENV'])),
+ default_sub_pass=dict(type='str', no_log=True, default='xvu'),
+ normal=dict(type='str', default='plaintext'),
+ ),
+ ),
+ ),
+ )
+
+ module.exit_json(changed=False)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/integration/targets/module_utils/library/test_optional.py b/test/integration/targets/module_utils/library/test_optional.py
new file mode 100644
index 00000000..4d0225d9
--- /dev/null
+++ b/test/integration/targets/module_utils/library/test_optional.py
@@ -0,0 +1,84 @@
+#!/usr/bin/python
+# Most of these names are only available via PluginLoader so pylint doesn't
+# know they exist
+# pylint: disable=no-name-in-module
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+from ansible.module_utils.basic import AnsibleModule
+
+# internal constants to keep pylint from griping about constant-valued conditionals
+_private_false = False
+_private_true = True
+
+# module_utils import statements nested below any block are considered optional "best-effort" for AnsiballZ to include.
+# test a number of different import shapes and nesting types to exercise this...
+
+# first, some nested imports that should succeed...
+try:
+ from ansible.module_utils.urls import fetch_url as yep1
+except ImportError:
+ yep1 = None
+
+try:
+ import ansible.module_utils.common.text.converters as yep2
+except ImportError:
+ yep2 = None
+
+try:
+ # optional import from a legit collection
+ from ansible_collections.testns.testcoll.plugins.module_utils.legit import importme as yep3
+except ImportError:
+ yep3 = None
+
+# and a bunch that should fail to be found, but not break the module_utils payload build in the process...
+try:
+ from ansible.module_utils.bogus import fromnope1
+except ImportError:
+ fromnope1 = None
+
+if _private_false:
+ from ansible.module_utils.alsobogus import fromnope2
+else:
+ fromnope2 = None
+
+try:
+ import ansible.module_utils.verybogus
+ nope1 = ansible.module_utils.verybogus
+except ImportError:
+ nope1 = None
+
+# deepish nested with multiple block types- make sure the AST walker made it all the way down
+try:
+ if _private_true:
+ if _private_true:
+ if _private_true:
+ if _private_true:
+ try:
+ import ansible.module_utils.stillbogus as nope2
+ except ImportError:
+ raise
+except ImportError:
+ nope2 = None
+
+try:
+ # optional import from a valid collection with an invalid package
+ from ansible_collections.testns.testcoll.plugins.module_utils.bogus import collnope1
+except ImportError:
+ collnope1 = None
+
+try:
+ # optional import from a bogus collection
+ from ansible_collections.bogusns.boguscoll.plugins.module_utils.bogus import collnope2
+except ImportError:
+ collnope2 = None
+
+module = AnsibleModule(argument_spec={})
+
+if not all([yep1, yep2, yep3]):
+ module.fail_json(msg='one or more existing optional imports did not resolve')
+
+if any([fromnope1, fromnope2, nope1, nope2, collnope1, collnope2]):
+ module.fail_json(msg='one or more missing optional imports resolved unexpectedly')
+
+module.exit_json(msg='all missing optional imports behaved as expected')
diff --git a/test/integration/targets/module_utils/library/test_override.py b/test/integration/targets/module_utils/library/test_override.py
new file mode 100644
index 00000000..9ff54bf9
--- /dev/null
+++ b/test/integration/targets/module_utils/library/test_override.py
@@ -0,0 +1,7 @@
+#!/usr/bin/python
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.facts import data
+
+results = {"data": data}
+
+AnsibleModule(argument_spec=dict()).exit_json(**results)
diff --git a/test/integration/targets/module_utils/library/test_recursive_diff.py b/test/integration/targets/module_utils/library/test_recursive_diff.py
new file mode 100644
index 00000000..0cf39d9c
--- /dev/null
+++ b/test/integration/targets/module_utils/library/test_recursive_diff.py
@@ -0,0 +1,29 @@
+#!/usr/bin/python
+# Copyright: (c) 2020, Matt Martz <matt@sivel.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.common.dict_transformations import recursive_diff
+
+
+def main():
+ module = AnsibleModule(
+ {
+ 'a': {'type': 'dict'},
+ 'b': {'type': 'dict'},
+ }
+ )
+
+ module.exit_json(
+ the_diff=recursive_diff(
+ module.params['a'],
+ module.params['b'],
+ ),
+ )
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/integration/targets/module_utils/module_utils/__init__.py b/test/integration/targets/module_utils/module_utils/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/a/__init__.py b/test/integration/targets/module_utils/module_utils/a/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/a/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/a/b/__init__.py b/test/integration/targets/module_utils/module_utils/a/b/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/a/b/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/a/b/c/__init__.py b/test/integration/targets/module_utils/module_utils/a/b/c/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/a/b/c/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/a/b/c/d/__init__.py b/test/integration/targets/module_utils/module_utils/a/b/c/d/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/a/b/c/d/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/a/b/c/d/e/__init__.py b/test/integration/targets/module_utils/module_utils/a/b/c/d/e/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/a/b/c/d/e/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/a/b/c/d/e/f/__init__.py b/test/integration/targets/module_utils/module_utils/a/b/c/d/e/f/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/a/b/c/d/e/f/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/a/b/c/d/e/f/g/__init__.py b/test/integration/targets/module_utils/module_utils/a/b/c/d/e/f/g/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/a/b/c/d/e/f/g/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/a/b/c/d/e/f/g/h/__init__.py b/test/integration/targets/module_utils/module_utils/a/b/c/d/e/f/g/h/__init__.py
new file mode 100644
index 00000000..722f4b77
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/a/b/c/d/e/f/g/h/__init__.py
@@ -0,0 +1 @@
+data = 'abcdefgh'
diff --git a/test/integration/targets/module_utils/module_utils/bar0/__init__.py b/test/integration/targets/module_utils/module_utils/bar0/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/bar0/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/bar0/foo.py b/test/integration/targets/module_utils/module_utils/bar0/foo.py
new file mode 100644
index 00000000..1072dcc2
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/bar0/foo.py
@@ -0,0 +1 @@
+data = 'bar0'
diff --git a/test/integration/targets/module_utils/module_utils/bar1/__init__.py b/test/integration/targets/module_utils/module_utils/bar1/__init__.py
new file mode 100644
index 00000000..68e43509
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/bar1/__init__.py
@@ -0,0 +1 @@
+data = 'bar1'
diff --git a/test/integration/targets/module_utils/module_utils/bar2/__init__.py b/test/integration/targets/module_utils/module_utils/bar2/__init__.py
new file mode 100644
index 00000000..59e86afd
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/bar2/__init__.py
@@ -0,0 +1 @@
+data = 'bar2'
diff --git a/test/integration/targets/module_utils/module_utils/baz1/__init__.py b/test/integration/targets/module_utils/module_utils/baz1/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/baz1/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/baz1/one.py b/test/integration/targets/module_utils/module_utils/baz1/one.py
new file mode 100644
index 00000000..e5d7894a
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/baz1/one.py
@@ -0,0 +1 @@
+data = 'baz1'
diff --git a/test/integration/targets/module_utils/module_utils/baz2/__init__.py b/test/integration/targets/module_utils/module_utils/baz2/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/baz2/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/baz2/one.py b/test/integration/targets/module_utils/module_utils/baz2/one.py
new file mode 100644
index 00000000..1efe196c
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/baz2/one.py
@@ -0,0 +1 @@
+data = 'baz2'
diff --git a/test/integration/targets/module_utils/module_utils/facts.py b/test/integration/targets/module_utils/module_utils/facts.py
new file mode 100644
index 00000000..ba7cbb7b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/facts.py
@@ -0,0 +1 @@
+data = 'overridden facts.py'
diff --git a/test/integration/targets/module_utils/module_utils/foo.py b/test/integration/targets/module_utils/module_utils/foo.py
new file mode 100644
index 00000000..20698f1f
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/foo.py
@@ -0,0 +1,3 @@
+#!/usr/bin/env python
+
+foo = "FOO FROM foo.py"
diff --git a/test/integration/targets/module_utils/module_utils/foo0.py b/test/integration/targets/module_utils/module_utils/foo0.py
new file mode 100644
index 00000000..4b528b6d
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/foo0.py
@@ -0,0 +1 @@
+data = 'foo0'
diff --git a/test/integration/targets/module_utils/module_utils/foo1.py b/test/integration/targets/module_utils/module_utils/foo1.py
new file mode 100644
index 00000000..18e0cef1
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/foo1.py
@@ -0,0 +1 @@
+data = 'foo1'
diff --git a/test/integration/targets/module_utils/module_utils/foo2.py b/test/integration/targets/module_utils/module_utils/foo2.py
new file mode 100644
index 00000000..feb142df
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/foo2.py
@@ -0,0 +1 @@
+data = 'foo2'
diff --git a/test/integration/targets/module_utils/module_utils/qux1/__init__.py b/test/integration/targets/module_utils/module_utils/qux1/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/qux1/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/qux1/quux.py b/test/integration/targets/module_utils/module_utils/qux1/quux.py
new file mode 100644
index 00000000..3d288c96
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/qux1/quux.py
@@ -0,0 +1 @@
+data = 'qux1'
diff --git a/test/integration/targets/module_utils/module_utils/qux2/__init__.py b/test/integration/targets/module_utils/module_utils/qux2/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/qux2/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/qux2/quux.py b/test/integration/targets/module_utils/module_utils/qux2/quux.py
new file mode 100644
index 00000000..496d446a
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/qux2/quux.py
@@ -0,0 +1 @@
+data = 'qux2:quux'
diff --git a/test/integration/targets/module_utils/module_utils/qux2/quuz.py b/test/integration/targets/module_utils/module_utils/qux2/quuz.py
new file mode 100644
index 00000000..cdc0fad7
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/qux2/quuz.py
@@ -0,0 +1 @@
+data = 'qux2:quuz'
diff --git a/test/integration/targets/module_utils/module_utils/service.py b/test/integration/targets/module_utils/module_utils/service.py
new file mode 100644
index 00000000..1492f468
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/service.py
@@ -0,0 +1 @@
+sysv_is_enabled = 'sysv_is_enabled'
diff --git a/test/integration/targets/module_utils/module_utils/spam1/__init__.py b/test/integration/targets/module_utils/module_utils/spam1/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam1/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/spam1/ham/__init__.py b/test/integration/targets/module_utils/module_utils/spam1/ham/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam1/ham/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/spam1/ham/eggs/__init__.py b/test/integration/targets/module_utils/module_utils/spam1/ham/eggs/__init__.py
new file mode 100644
index 00000000..f290e156
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam1/ham/eggs/__init__.py
@@ -0,0 +1 @@
+data = 'spam1'
diff --git a/test/integration/targets/module_utils/module_utils/spam2/__init__.py b/test/integration/targets/module_utils/module_utils/spam2/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam2/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/spam2/ham/__init__.py b/test/integration/targets/module_utils/module_utils/spam2/ham/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam2/ham/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/spam2/ham/eggs/__init__.py b/test/integration/targets/module_utils/module_utils/spam2/ham/eggs/__init__.py
new file mode 100644
index 00000000..5e053d88
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam2/ham/eggs/__init__.py
@@ -0,0 +1 @@
+data = 'spam2'
diff --git a/test/integration/targets/module_utils/module_utils/spam3/__init__.py b/test/integration/targets/module_utils/module_utils/spam3/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam3/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/spam3/ham/__init__.py b/test/integration/targets/module_utils/module_utils/spam3/ham/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam3/ham/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/spam3/ham/bacon.py b/test/integration/targets/module_utils/module_utils/spam3/ham/bacon.py
new file mode 100644
index 00000000..91075089
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam3/ham/bacon.py
@@ -0,0 +1 @@
+data = 'spam3'
diff --git a/test/integration/targets/module_utils/module_utils/spam4/__init__.py b/test/integration/targets/module_utils/module_utils/spam4/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam4/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/spam4/ham/__init__.py b/test/integration/targets/module_utils/module_utils/spam4/ham/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam4/ham/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/spam4/ham/bacon.py b/test/integration/targets/module_utils/module_utils/spam4/ham/bacon.py
new file mode 100644
index 00000000..7d552882
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam4/ham/bacon.py
@@ -0,0 +1 @@
+data = 'spam4'
diff --git a/test/integration/targets/module_utils/module_utils/spam5/__init__.py b/test/integration/targets/module_utils/module_utils/spam5/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam5/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/spam5/ham/__init__.py b/test/integration/targets/module_utils/module_utils/spam5/ham/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam5/ham/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/spam5/ham/bacon.py b/test/integration/targets/module_utils/module_utils/spam5/ham/bacon.py
new file mode 100644
index 00000000..cc947b83
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam5/ham/bacon.py
@@ -0,0 +1 @@
+data = 'spam5:bacon'
diff --git a/test/integration/targets/module_utils/module_utils/spam5/ham/eggs.py b/test/integration/targets/module_utils/module_utils/spam5/ham/eggs.py
new file mode 100644
index 00000000..f0394c87
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam5/ham/eggs.py
@@ -0,0 +1 @@
+data = 'spam5:eggs'
diff --git a/test/integration/targets/module_utils/module_utils/spam6/__init__.py b/test/integration/targets/module_utils/module_utils/spam6/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam6/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/spam6/ham/__init__.py b/test/integration/targets/module_utils/module_utils/spam6/ham/__init__.py
new file mode 100644
index 00000000..8c1a70ea
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam6/ham/__init__.py
@@ -0,0 +1,2 @@
+bacon = 'spam6:bacon'
+eggs = 'spam6:eggs'
diff --git a/test/integration/targets/module_utils/module_utils/spam7/__init__.py b/test/integration/targets/module_utils/module_utils/spam7/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam7/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/spam7/ham/__init__.py b/test/integration/targets/module_utils/module_utils/spam7/ham/__init__.py
new file mode 100644
index 00000000..cd9a05d0
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam7/ham/__init__.py
@@ -0,0 +1 @@
+eggs = 'spam7:eggs'
diff --git a/test/integration/targets/module_utils/module_utils/spam7/ham/bacon.py b/test/integration/targets/module_utils/module_utils/spam7/ham/bacon.py
new file mode 100644
index 00000000..490121f8
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam7/ham/bacon.py
@@ -0,0 +1 @@
+data = 'spam7:bacon'
diff --git a/test/integration/targets/module_utils/module_utils/spam8/__init__.py b/test/integration/targets/module_utils/module_utils/spam8/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam8/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/spam8/ham/__init__.py b/test/integration/targets/module_utils/module_utils/spam8/ham/__init__.py
new file mode 100644
index 00000000..c02bf5fd
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam8/ham/__init__.py
@@ -0,0 +1 @@
+eggs = 'spam8:eggs'
diff --git a/test/integration/targets/module_utils/module_utils/spam8/ham/bacon.py b/test/integration/targets/module_utils/module_utils/spam8/ham/bacon.py
new file mode 100644
index 00000000..28ea2857
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/spam8/ham/bacon.py
@@ -0,0 +1 @@
+data = 'spam8:bacon'
diff --git a/test/integration/targets/module_utils/module_utils/sub/__init__.py b/test/integration/targets/module_utils/module_utils/sub/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/sub/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/sub/bam.py b/test/integration/targets/module_utils/module_utils/sub/bam.py
new file mode 100644
index 00000000..566f8b7c
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/sub/bam.py
@@ -0,0 +1,3 @@
+#!/usr/bin/env python
+
+bam = "BAM FROM sub/bam.py"
diff --git a/test/integration/targets/module_utils/module_utils/sub/bam/__init__.py b/test/integration/targets/module_utils/module_utils/sub/bam/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/sub/bam/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/sub/bam/bam.py b/test/integration/targets/module_utils/module_utils/sub/bam/bam.py
new file mode 100644
index 00000000..b7ed7072
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/sub/bam/bam.py
@@ -0,0 +1,3 @@
+#!/usr/bin/env python
+
+bam = "BAM FROM sub/bam/bam.py"
diff --git a/test/integration/targets/module_utils/module_utils/sub/bar/__init__.py b/test/integration/targets/module_utils/module_utils/sub/bar/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/sub/bar/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/sub/bar/bam.py b/test/integration/targets/module_utils/module_utils/sub/bar/bam.py
new file mode 100644
index 00000000..02fafd40
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/sub/bar/bam.py
@@ -0,0 +1,3 @@
+#!/usr/bin/env python
+
+bam = "BAM FROM sub/bar/bam.py"
diff --git a/test/integration/targets/module_utils/module_utils/sub/bar/bar.py b/test/integration/targets/module_utils/module_utils/sub/bar/bar.py
new file mode 100644
index 00000000..8566901f
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/sub/bar/bar.py
@@ -0,0 +1,3 @@
+#!/usr/bin/env python
+
+bar = "BAR FROM sub/bar/bar.py"
diff --git a/test/integration/targets/module_utils/module_utils/yak/__init__.py b/test/integration/targets/module_utils/module_utils/yak/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/yak/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/yak/zebra/__init__.py b/test/integration/targets/module_utils/module_utils/yak/zebra/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/yak/zebra/__init__.py
diff --git a/test/integration/targets/module_utils/module_utils/yak/zebra/foo.py b/test/integration/targets/module_utils/module_utils/yak/zebra/foo.py
new file mode 100644
index 00000000..89b2bfe8
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils/yak/zebra/foo.py
@@ -0,0 +1 @@
+data = 'yak'
diff --git a/test/integration/targets/module_utils/module_utils_basic_setcwd.yml b/test/integration/targets/module_utils/module_utils_basic_setcwd.yml
new file mode 100644
index 00000000..97dbf873
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils_basic_setcwd.yml
@@ -0,0 +1,22 @@
+- hosts: testhost
+ gather_facts: no
+ tasks:
+ - name: make sure the nobody user is available
+ include_role:
+ name: setup_nobody
+
+ - name: verify AnsibleModule works when cwd is missing
+ test_cwd_missing:
+ register: missing
+
+ - name: verify AnsibleModule works when cwd is unreadable
+ test_cwd_unreadable:
+ register: unreadable
+ become: yes
+ become_user: nobody # root can read cwd regardless of permissions, so a non-root user is required here
+
+ - name: verify AnsibleModule was able to adjust cwd as expected
+ assert:
+ that:
+ - missing.before != missing.after
+ - unreadable.before != unreadable.after or unreadable.before == '/' # allow / fallback on macOS when using an unprivileged user
diff --git a/test/integration/targets/module_utils/module_utils_common_dict_transformation.yml b/test/integration/targets/module_utils/module_utils_common_dict_transformation.yml
new file mode 100644
index 00000000..7d961c4c
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils_common_dict_transformation.yml
@@ -0,0 +1,34 @@
+- hosts: testhost
+ gather_facts: no
+ tasks:
+ - test_recursive_diff:
+ a:
+ foo:
+ bar:
+ - baz:
+ qux: ham_sandwich
+ b:
+ foo:
+ bar:
+ - baz:
+ qux: turkey_sandwich
+ register: recursive_diff_diff
+
+ - test_recursive_diff:
+ a:
+ foo:
+ bar:
+ - baz:
+ qux: ham_sandwich
+ b:
+ foo:
+ bar:
+ - baz:
+ qux: ham_sandwich
+ register: recursive_diff_same
+
+ - assert:
+ that:
+ - recursive_diff_diff.the_diff is not none
+ - recursive_diff_diff.the_diff|length == 2
+ - recursive_diff_same.the_diff is none
diff --git a/test/integration/targets/module_utils/module_utils_envvar.yml b/test/integration/targets/module_utils/module_utils_envvar.yml
new file mode 100644
index 00000000..8d97e0eb
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils_envvar.yml
@@ -0,0 +1,51 @@
+- hosts: testhost
+ gather_facts: no
+ tasks:
+ - name: Use a specially crafted module to see if things were imported correctly
+ test:
+ register: result
+
+ - name: Check that these are all loaded from playbook dir's module_utils
+ assert:
+ that:
+ - 'result["abcdefgh"] == "abcdefgh"'
+ - 'result["bar0"] == "bar0"'
+ - 'result["bar1"] == "bar1"'
+ - 'result["bar2"] == "bar2"'
+ - 'result["baz1"] == "baz1"'
+ - 'result["baz2"] == "baz2"'
+ - 'result["foo0"] == "foo0"'
+ - 'result["foo1"] == "foo1"'
+ - 'result["foo2"] == "foo2"'
+ - 'result["qux1"] == "qux1"'
+ - 'result["qux2"] == ["qux2:quux", "qux2:quuz"]'
+ - 'result["spam1"] == "spam1"'
+ - 'result["spam2"] == "spam2"'
+ - 'result["spam3"] == "spam3"'
+ - 'result["spam4"] == "spam4"'
+ - 'result["spam5"] == ["spam5:bacon", "spam5:eggs"]'
+ - 'result["spam6"] == ["spam6:bacon", "spam6:eggs"]'
+ - 'result["spam7"] == ["spam7:bacon", "spam7:eggs"]'
+ - 'result["spam8"] == ["spam8:bacon", "spam8:eggs"]'
+
+ # Test that overriding something in module_utils with something in the local library works
+ - name: Test that playbook dir's module_utils overrides facts.py
+ test_override:
+ register: result
+
+ - name: Make sure the we used the local facts.py, not the one shipped with ansible
+ assert:
+ that:
+ - 'result["data"] == "overridden facts.py"'
+
+ - name: Test that importing something from the module_utils in the env_vars works
+ test_env_override:
+ register: result
+
+ - name: Make sure we used the module_utils from the env_var for these
+ assert:
+ that:
+ # Override of shipped module_utils
+ - 'result["json_utils"] == "overridden json_utils"'
+ # Only i nthe env vars directory
+ - 'result["mork"] == "mork"'
diff --git a/test/integration/targets/module_utils/module_utils_test.yml b/test/integration/targets/module_utils/module_utils_test.yml
new file mode 100644
index 00000000..0550b9f0
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils_test.yml
@@ -0,0 +1,71 @@
+- hosts: testhost
+ gather_facts: no
+ tasks:
+ - name: Use a specially crafted module to see if things were imported correctly
+ test:
+ register: result
+
+ - name: Check that the module imported the correct version of each module_util
+ assert:
+ that:
+ - 'result["abcdefgh"] == "abcdefgh"'
+ - 'result["bar0"] == "bar0"'
+ - 'result["bar1"] == "bar1"'
+ - 'result["bar2"] == "bar2"'
+ - 'result["baz1"] == "baz1"'
+ - 'result["baz2"] == "baz2"'
+ - 'result["foo0"] == "foo0"'
+ - 'result["foo1"] == "foo1"'
+ - 'result["foo2"] == "foo2"'
+ - 'result["qux1"] == "qux1"'
+ - 'result["qux2"] == ["qux2:quux", "qux2:quuz"]'
+ - 'result["spam1"] == "spam1"'
+ - 'result["spam2"] == "spam2"'
+ - 'result["spam3"] == "spam3"'
+ - 'result["spam4"] == "spam4"'
+ - 'result["spam5"] == ["spam5:bacon", "spam5:eggs"]'
+ - 'result["spam6"] == ["spam6:bacon", "spam6:eggs"]'
+ - 'result["spam7"] == ["spam7:bacon", "spam7:eggs"]'
+ - 'result["spam8"] == ["spam8:bacon", "spam8:eggs"]'
+
+ # Test that overriding something in module_utils with something in the local library works
+ - name: Test that local module_utils overrides facts.py
+ test_override:
+ register: result
+
+ - name: Make sure the we used the local facts.py, not the one shipped with ansible
+ assert:
+ that:
+ - result["data"] == "overridden facts.py"
+
+ - name: Test that importing a module that only exists inside of a submodule does not work
+ test_failure:
+ ignore_errors: True
+ register: result
+
+ - name: Make sure we failed in AnsiBallZ
+ assert:
+ that:
+ - result is failed
+ - result['msg'] == "Could not find imported module support code for ansible.modules.test_failure. Looked for (['ansible.module_utils.zebra.foo', 'ansible.module_utils.zebra'])"
+
+ - name: Test that alias deprecation works
+ test_alias_deprecation:
+ baz: 'bar'
+ register: result
+
+ - name: Assert that the deprecation message is given correctly
+ assert:
+ that:
+ - result.deprecations[0].msg == "Alias 'baz' is deprecated. See the module docs for more information"
+ - result.deprecations[0].version == '9.99'
+
+
+ - name: Test that optional imports behave properly
+ test_optional:
+ register: optionaltest
+
+ - assert:
+ that:
+ - optionaltest is success
+ - optionaltest.msg == 'all missing optional imports behaved as expected' \ No newline at end of file
diff --git a/test/integration/targets/module_utils/module_utils_test_no_log.yml b/test/integration/targets/module_utils/module_utils_test_no_log.yml
new file mode 100644
index 00000000..bad2efd4
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils_test_no_log.yml
@@ -0,0 +1,9 @@
+# This is called by module_utils_vvvvv.yml with a custom callback
+- hosts: testhost
+ gather_facts: no
+ tasks:
+ - name: Check no_log invocation results
+ test_no_log:
+ explicit_pass: abc
+ suboption:
+ explicit_sub_pass: def
diff --git a/test/integration/targets/module_utils/module_utils_vvvvv.yml b/test/integration/targets/module_utils/module_utils_vvvvv.yml
new file mode 100644
index 00000000..1fd91d25
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils_vvvvv.yml
@@ -0,0 +1,27 @@
+- hosts: testhost
+ gather_facts: no
+ tasks:
+ # Invocation usually is output with 3vs or more, our callback plugin displays it anyway
+ - name: Check no_log invocation results
+ command: ansible-playbook -i {{ inventory_file }} module_utils_test_no_log.yml
+ environment:
+ ANSIBLE_CALLBACK_PLUGINS: callback
+ ANSIBLE_STDOUT_CALLBACK: pure_json
+ SECRET_ENV: ghi
+ SECRET_SUB_ENV: jkl
+ register: no_log_invocation
+
+ - set_fact:
+ no_log_invocation: '{{ no_log_invocation.stdout | trim | from_json }}'
+
+ - name: check no log values from fallback or default are masked
+ assert:
+ that:
+ - no_log_invocation.invocation.module_args.default_pass == 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER'
+ - no_log_invocation.invocation.module_args.explicit_pass == 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER'
+ - no_log_invocation.invocation.module_args.fallback_pass == 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER'
+ - no_log_invocation.invocation.module_args.normal == 'plaintext'
+ - no_log_invocation.invocation.module_args.suboption.default_sub_pass == 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER'
+ - no_log_invocation.invocation.module_args.suboption.explicit_sub_pass == 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER'
+ - no_log_invocation.invocation.module_args.suboption.fallback_sub_pass == 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER'
+ - no_log_invocation.invocation.module_args.suboption.normal == 'plaintext'
diff --git a/test/integration/targets/module_utils/other_mu_dir/__init__.py b/test/integration/targets/module_utils/other_mu_dir/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/other_mu_dir/__init__.py
diff --git a/test/integration/targets/module_utils/other_mu_dir/a/__init__.py b/test/integration/targets/module_utils/other_mu_dir/a/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/other_mu_dir/a/__init__.py
diff --git a/test/integration/targets/module_utils/other_mu_dir/a/b/__init__.py b/test/integration/targets/module_utils/other_mu_dir/a/b/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/other_mu_dir/a/b/__init__.py
diff --git a/test/integration/targets/module_utils/other_mu_dir/a/b/c/__init__.py b/test/integration/targets/module_utils/other_mu_dir/a/b/c/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/other_mu_dir/a/b/c/__init__.py
diff --git a/test/integration/targets/module_utils/other_mu_dir/a/b/c/d/__init__.py b/test/integration/targets/module_utils/other_mu_dir/a/b/c/d/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/other_mu_dir/a/b/c/d/__init__.py
diff --git a/test/integration/targets/module_utils/other_mu_dir/a/b/c/d/e/__init__.py b/test/integration/targets/module_utils/other_mu_dir/a/b/c/d/e/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/other_mu_dir/a/b/c/d/e/__init__.py
diff --git a/test/integration/targets/module_utils/other_mu_dir/a/b/c/d/e/f/__init__.py b/test/integration/targets/module_utils/other_mu_dir/a/b/c/d/e/f/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/other_mu_dir/a/b/c/d/e/f/__init__.py
diff --git a/test/integration/targets/module_utils/other_mu_dir/a/b/c/d/e/f/g/__init__.py b/test/integration/targets/module_utils/other_mu_dir/a/b/c/d/e/f/g/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/module_utils/other_mu_dir/a/b/c/d/e/f/g/__init__.py
diff --git a/test/integration/targets/module_utils/other_mu_dir/a/b/c/d/e/f/g/h/__init__.py b/test/integration/targets/module_utils/other_mu_dir/a/b/c/d/e/f/g/h/__init__.py
new file mode 100644
index 00000000..796fed38
--- /dev/null
+++ b/test/integration/targets/module_utils/other_mu_dir/a/b/c/d/e/f/g/h/__init__.py
@@ -0,0 +1 @@
+data = 'should not be visible abcdefgh'
diff --git a/test/integration/targets/module_utils/other_mu_dir/facts.py b/test/integration/targets/module_utils/other_mu_dir/facts.py
new file mode 100644
index 00000000..dbfab271
--- /dev/null
+++ b/test/integration/targets/module_utils/other_mu_dir/facts.py
@@ -0,0 +1 @@
+data = 'should not be visible facts.py'
diff --git a/test/integration/targets/module_utils/other_mu_dir/json_utils.py b/test/integration/targets/module_utils/other_mu_dir/json_utils.py
new file mode 100644
index 00000000..59757e40
--- /dev/null
+++ b/test/integration/targets/module_utils/other_mu_dir/json_utils.py
@@ -0,0 +1 @@
+data = 'overridden json_utils'
diff --git a/test/integration/targets/module_utils/other_mu_dir/mork.py b/test/integration/targets/module_utils/other_mu_dir/mork.py
new file mode 100644
index 00000000..3b700fca
--- /dev/null
+++ b/test/integration/targets/module_utils/other_mu_dir/mork.py
@@ -0,0 +1 @@
+data = 'mork'
diff --git a/test/integration/targets/module_utils/runme.sh b/test/integration/targets/module_utils/runme.sh
new file mode 100755
index 00000000..f25dba63
--- /dev/null
+++ b/test/integration/targets/module_utils/runme.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+
+set -eux
+
+ANSIBLE_ROLES_PATH=../ ansible-playbook module_utils_basic_setcwd.yml -i ../../inventory "$@"
+
+# Keep the -vvvvv here. This acts as a test for testing that higher verbosity
+# doesn't traceback with unicode in the custom module_utils directory path.
+ansible-playbook module_utils_vvvvv.yml -i ../../inventory -vvvvv "$@"
+
+ansible-playbook module_utils_test.yml -i ../../inventory -v "$@"
+ANSIBLE_MODULE_UTILS=other_mu_dir ansible-playbook module_utils_envvar.yml -i ../../inventory -v "$@"
+
+ansible-playbook module_utils_common_dict_transformation.yml -i ../../inventory "$@"