summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.coveragerc1
-rw-r--r--changelogs/fragments/ansiballz_one_interpreter.yml5
-rw-r--r--changelogs/fragments/deprecated-__file__.yaml5
-rw-r--r--docs/docsite/rst/porting_guides/porting_guide_2.7.rst20
-rwxr-xr-xhacking/test-module4
-rw-r--r--lib/ansible/executor/module_common.py438
-rw-r--r--lib/ansible/module_utils/basic.py1
-rw-r--r--lib/ansible/plugins/action/__init__.py2
-rwxr-xr-xtest/runner/injector/injector.py48
-rw-r--r--test/runner/lib/cover.py19
-rw-r--r--test/runner/lib/util.py4
-rw-r--r--test/sanity/validate-modules/utils.py2
-rw-r--r--test/units/mock/procenv.py2
-rw-r--r--test/units/module_utils/aws/test_aws_module.py6
-rw-r--r--test/units/module_utils/basic/test_selinux.py4
-rw-r--r--test/units/module_utils/basic/test_tmpdir.py20
-rw-r--r--test/units/module_utils/conftest.py4
-rw-r--r--test/units/modules/cloud/amazon/test_aws_api_gateway.py1
-rw-r--r--test/units/modules/conftest.py4
-rw-r--r--test/units/modules/network/cnos/cnos_module.py4
-rw-r--r--test/units/modules/network/cnos/test_cnos_config.py4
-rw-r--r--test/units/modules/network/enos/enos_module.py4
-rw-r--r--test/units/modules/network/enos/test_enos_config.py4
-rw-r--r--test/units/modules/network/netact/test_netact_cm_command.py9
-rw-r--r--test/units/modules/network/nso/nso_module.py5
-rw-r--r--test/units/modules/network/nso/test_nso_action.py12
-rw-r--r--test/units/modules/network/nso/test_nso_query.py4
-rw-r--r--test/units/modules/network/nso/test_nso_show.py8
-rw-r--r--test/units/modules/network/nso/test_nso_verify.py8
-rw-r--r--test/units/modules/source_control/test_gitlab_deploy_key.py9
-rw-r--r--test/units/modules/source_control/test_gitlab_hooks.py9
-rw-r--r--test/units/modules/utils.py6
32 files changed, 352 insertions, 324 deletions
diff --git a/.coveragerc b/.coveragerc
index c8283bab61..eb690592ac 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -23,3 +23,4 @@ omit =
*/python*/distutils/*
*/pyshared/*
*/pytest
+ */AnsiballZ_*.py
diff --git a/changelogs/fragments/ansiballz_one_interpreter.yml b/changelogs/fragments/ansiballz_one_interpreter.yml
new file mode 100644
index 0000000000..7dd6abed2a
--- /dev/null
+++ b/changelogs/fragments/ansiballz_one_interpreter.yml
@@ -0,0 +1,5 @@
+---
+minor_changes:
+- Ansible-2.7 changes the Ansiballz strategy for running modules remotely so
+ that invoking a module only needs to invoke python once per module on the
+ remote machine instead of twice.
diff --git a/changelogs/fragments/deprecated-__file__.yaml b/changelogs/fragments/deprecated-__file__.yaml
new file mode 100644
index 0000000000..6ca23a66cb
--- /dev/null
+++ b/changelogs/fragments/deprecated-__file__.yaml
@@ -0,0 +1,5 @@
+---
+deprecated_features:
+- Modules will no longer be able to rely on the __file__ attribute pointing to
+ a real file. If your third party module is using __file__ for something it
+ should be changed before 2.8. See the 2.7 porting guide for more information.
diff --git a/docs/docsite/rst/porting_guides/porting_guide_2.7.rst b/docs/docsite/rst/porting_guides/porting_guide_2.7.rst
index a6837d7fac..286e333164 100644
--- a/docs/docsite/rst/porting_guides/porting_guide_2.7.rst
+++ b/docs/docsite/rst/porting_guides/porting_guide_2.7.rst
@@ -38,6 +38,26 @@ There is an important difference in the way that ``include_role`` (dynamic) will
Deprecated
==========
+Expedited Deprecation: Use of ``__file__`` in ``AnsibleModule``
+---------------------------------------------------------------
+
+.. note:: The use of the ``__file__`` variable is deprecated in Ansible 2.7 and **will be eliminated in Ansible 2.8**. This is much quicker than our usual 4-release deprecation cycle.
+
+We are deprecating the use of the ``__file__`` variable to refer to the file containing the currently-running code. This common Python technique for finding a filesystem path does not always work (even in vanilla Python). Sometimes a Python module can be imported from a virtual location (like inside of a zip file). When this happens, the ``__file__`` variable will reference a virtual location pointing to inside of the zip file. This can cause problems if, for instance, the code was trying to use ``__file__`` to find the directory containing the python module to write some temporary information.
+
+Before the introduction of AnsiBallZ in Ansible 2.1, using ``__file__`` worked in ``AnsibleModule`` sometimes, but any module that used it would fail when pipelining was turned on (because the module would be piped into the python interpreter's standard input, so ``__file__`` wouldn't contain a file path). AnsiBallZ unintentionally made using ``__file__`` always work, by always creating a temporary file for ``AnsibleModule`` to reside in.
+
+Ansible 2.8 will no longer create a temporary file for ``AnsibleModule``; instead it will read the file out of a zip file. This change should speed up module execution, but it does mean that starting with Ansible 2.8, referencing ``__file__`` will always fail in ``AnsibleModule``.
+
+If you are the author of a third-party module which uses ``__file__`` with ``AnsibleModule``, please update your module(s) now, while the use of ``__file__`` is deprecated but still available. The most common use of ``__file__`` is to find a directory to write a temporary file. In Ansible 2.5 and above, you can use the ``tmpdir`` attribute on an ``AnsibleModule`` instance instead, as shown in this code from the :ref:`apt module <apt_module>`:
+
+.. code-block:: diff
+
+ - tempdir = os.path.dirname(__file__)
+ - package = os.path.join(tempdir, to_native(deb.rsplit('/', 1)[1]))
+ + package = os.path.join(module.tmpdir, to_native(deb.rsplit('/', 1)[1]))
+
+
Using a loop on a package module via squash_actions
---------------------------------------------------
diff --git a/hacking/test-module b/hacking/test-module
index c56b8b7216..b0764982a4 100755
--- a/hacking/test-module
+++ b/hacking/test-module
@@ -156,7 +156,7 @@ def boilerplate_module(modfile, args, interpreters, check, destfile):
task_vars=task_vars
)
- if module_style == 'new' and 'ANSIBALLZ_WRAPPER = True' in to_native(module_data):
+ if module_style == 'new' and '_ANSIBALLZ_WRAPPER = True' in to_native(module_data):
module_style = 'ansiballz'
modfile2_path = os.path.expanduser(destfile)
@@ -192,7 +192,7 @@ def ansiballz_setup(modfile, modname, interpreters):
debug_dir = lines[1].strip()
argsfile = os.path.join(debug_dir, 'args')
- modfile = os.path.join(debug_dir, 'ansible_module_%s.py' % modname)
+ modfile = os.path.join(debug_dir, '__main__.py')
print("* ansiballz module detected; extracted module source to: %s" % debug_dir)
return modfile, argsfile
diff --git a/lib/ansible/executor/module_common.py b/lib/ansible/executor/module_common.py
index 1c59af14ba..8842b1e765 100644
--- a/lib/ansible/executor/module_common.py
+++ b/lib/ansible/executor/module_common.py
@@ -70,7 +70,7 @@ _MODULE_UTILS_PATH = os.path.join(os.path.dirname(__file__), '..', 'module_utils
ANSIBALLZ_TEMPLATE = u'''%(shebang)s
%(coding)s
-ANSIBALLZ_WRAPPER = True # For test-module script to tell this is a ANSIBALLZ_WRAPPER
+_ANSIBALLZ_WRAPPER = True # For test-module script to tell this is a ANSIBALLZ_WRAPPER
# This code is part of Ansible, but is an independent component.
# The code in this particular templatable string, and this templatable string
# only, is BSD licensed. Modules which end up using this snippet, which is
@@ -98,201 +98,193 @@ ANSIBALLZ_WRAPPER = True # For test-module script to tell this is a ANSIBALLZ_WR
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-import os
-import os.path
-import sys
-import __main__
-
-# For some distros and python versions we pick up this script in the temporary
-# directory. This leads to problems when the ansible module masks a python
-# library that another import needs. We have not figured out what about the
-# specific distros and python versions causes this to behave differently.
-#
-# Tested distros:
-# Fedora23 with python3.4 Works
-# Ubuntu15.10 with python2.7 Works
-# Ubuntu15.10 with python3.4 Fails without this
-# Ubuntu16.04.1 with python3.5 Fails without this
-# To test on another platform:
-# * use the copy module (since this shadows the stdlib copy module)
-# * Turn off pipelining
-# * Make sure that the destination file does not exist
-# * ansible ubuntu16-test -m copy -a 'src=/etc/motd dest=/var/tmp/m'
-# This will traceback in shutil. Looking at the complete traceback will show
-# that shutil is importing copy which finds the ansible module instead of the
-# stdlib module
-scriptdir = None
-try:
- scriptdir = os.path.dirname(os.path.realpath(__main__.__file__))
-except (AttributeError, OSError):
- # Some platforms don't set __file__ when reading from stdin
- # OSX raises OSError if using abspath() in a directory we don't have
- # permission to read (realpath calls abspath)
- pass
-if scriptdir is not None:
- sys.path = [p for p in sys.path if p != scriptdir]
+def _ansiballz_main():
+ import os
+ import os.path
+ import sys
+ import __main__
+
+ # For some distros and python versions we pick up this script in the temporary
+ # directory. This leads to problems when the ansible module masks a python
+ # library that another import needs. We have not figured out what about the
+ # specific distros and python versions causes this to behave differently.
+ #
+ # Tested distros:
+ # Fedora23 with python3.4 Works
+ # Ubuntu15.10 with python2.7 Works
+ # Ubuntu15.10 with python3.4 Fails without this
+ # Ubuntu16.04.1 with python3.5 Fails without this
+ # To test on another platform:
+ # * use the copy module (since this shadows the stdlib copy module)
+ # * Turn off pipelining
+ # * Make sure that the destination file does not exist
+ # * ansible ubuntu16-test -m copy -a 'src=/etc/motd dest=/var/tmp/m'
+ # This will traceback in shutil. Looking at the complete traceback will show
+ # that shutil is importing copy which finds the ansible module instead of the
+ # stdlib module
+ scriptdir = None
+ try:
+ scriptdir = os.path.dirname(os.path.realpath(__main__.__file__))
+ except (AttributeError, OSError):
+ # Some platforms don't set __file__ when reading from stdin
+ # OSX raises OSError if using abspath() in a directory we don't have
+ # permission to read (realpath calls abspath)
+ pass
+ if scriptdir is not None:
+ sys.path = [p for p in sys.path if p != scriptdir]
+
+ import base64
+ import shutil
+ import tempfile
+ import zipimport
+ import zipfile
+
+ if sys.version_info < (3,):
+ bytes = str
+ PY3 = False
+ else:
+ unicode = str
+ PY3 = True
+
+ ZIPDATA = """%(zipdata)s"""
+
+ def invoke_module(modlib_path, json_params):
+ # When installed via setuptools (including python setup.py install),
+ # ansible may be installed with an easy-install.pth file. That file
+ # may load the system-wide install of ansible rather than the one in
+ # the module. sitecustomize is the only way to override that setting.
+ z = zipfile.ZipFile(modlib_path, mode='a')
+
+ # py3: modlib_path will be text, py2: it's bytes. Need bytes at the end
+ sitecustomize = u'import sys\\nsys.path.insert(0,"%%s")\\n' %% modlib_path
+ sitecustomize = sitecustomize.encode('utf-8')
+ # Use a ZipInfo to work around zipfile limitation on hosts with
+ # clocks set to a pre-1980 year (for instance, Raspberry Pi)
+ zinfo = zipfile.ZipInfo()
+ zinfo.filename = 'sitecustomize.py'
+ zinfo.date_time = ( %(year)i, %(month)i, %(day)i, %(hour)i, %(minute)i, %(second)i)
+ z.writestr(zinfo, sitecustomize)
+ z.close()
+
+ # Put the zipped up module_utils we got from the controller first in the python path so that we
+ # can monkeypatch the right basic
+ sys.path.insert(0, modlib_path)
+
+ # Monkeypatch the parameters into basic
+ from ansible.module_utils import basic
+ basic._ANSIBLE_ARGS = json_params
+%(coverage)s
+ # Run the module! By importing it as '__main__', it thinks it is executing as a script
+ importer = zipimport.zipimporter(modlib_path)
+ importer.load_module('__main__')
+
+ # Ansible modules must exit themselves
+ print('{"msg": "New-style module did not handle its own exit", "failed": true}')
+ sys.exit(1)
-import base64
-import shutil
-import zipfile
-import tempfile
-import subprocess
+ def debug(command, zipped_mod, json_params):
+ # The code here normally doesn't run. It's only used for debugging on the
+ # remote machine.
+ #
+ # The subcommands in this function make it easier to debug ansiballz
+ # modules. Here's the basic steps:
+ #
+ # Run ansible with the environment variable: ANSIBLE_KEEP_REMOTE_FILES=1 and -vvv
+ # to save the module file remotely::
+ # $ ANSIBLE_KEEP_REMOTE_FILES=1 ansible host1 -m ping -a 'data=october' -vvv
+ #
+ # Part of the verbose output will tell you where on the remote machine the
+ # module was written to::
+ # [...]
+ # <host1> SSH: EXEC ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o
+ # PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o
+ # ControlPath=/home/badger/.ansible/cp/ansible-ssh-%%h-%%p-%%r -tt rhel7 '/bin/sh -c '"'"'LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8
+ # LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/badger/.ansible/tmp/ansible-tmp-1461173013.93-9076457629738/ping'"'"''
+ # [...]
+ #
+ # Login to the remote machine and run the module file via from the previous
+ # step with the explode subcommand to extract the module payload into
+ # source files::
+ # $ ssh host1
+ # $ /usr/bin/python /home/badger/.ansible/tmp/ansible-tmp-1461173013.93-9076457629738/ping explode
+ # Module expanded into:
+ # /home/badger/.ansible/tmp/ansible-tmp-1461173408.08-279692652635227/ansible
+ #
+ # You can now edit the source files to instrument the code or experiment with
+ # different parameter values. When you're ready to run the code you've modified
+ # (instead of the code from the actual zipped module), use the execute subcommand like this::
+ # $ /usr/bin/python /home/badger/.ansible/tmp/ansible-tmp-1461173013.93-9076457629738/ping execute
+
+ # Okay to use __file__ here because we're running from a kept file
+ basedir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'debug_dir')
+ args_path = os.path.join(basedir, 'args')
+ script_path = os.path.join(basedir, '__main__.py')
+
+ if command == 'excommunicate':
+ print('The excommunicate debug command is deprecated and will be removed in 2.11. Use execute instead.')
+ command = 'execute'
+
+ if command == 'explode':
+ # transform the ZIPDATA into an exploded directory of code and then
+ # print the path to the code. This is an easy way for people to look
+ # at the code on the remote machine for debugging it in that
+ # environment
+ z = zipfile.ZipFile(zipped_mod)
+ for filename in z.namelist():
+ if filename.startswith('/'):
+ raise Exception('Something wrong with this module zip file: should not contain absolute paths')
+
+ dest_filename = os.path.join(basedir, filename)
+ if dest_filename.endswith(os.path.sep) and not os.path.exists(dest_filename):
+ os.makedirs(dest_filename)
+ else:
+ directory = os.path.dirname(dest_filename)
+ if not os.path.exists(directory):
+ os.makedirs(directory)
+ f = open(dest_filename, 'wb')
+ f.write(z.read(filename))
+ f.close()
+
+ # write the args file
+ f = open(args_path, 'wb')
+ f.write(json_params)
+ f.close()
-if sys.version_info < (3,):
- bytes = str
- PY3 = False
-else:
- unicode = str
- PY3 = True
+ print('Module expanded into:')
+ print('%%s' %% basedir)
+ exitcode = 0
-ZIPDATA = """%(zipdata)s"""
+ elif command == 'execute':
+ # Execute the exploded code instead of executing the module from the
+ # embedded ZIPDATA. This allows people to easily run their modified
+ # code on the remote machine to see how changes will affect it.
-def invoke_module(module, modlib_path, json_params):
- pythonpath = os.environ.get('PYTHONPATH')
- if pythonpath:
- os.environ['PYTHONPATH'] = ':'.join((modlib_path, pythonpath))
- else:
- os.environ['PYTHONPATH'] = modlib_path
+ # Set pythonpath to the debug dir
+ sys.path.insert(0, basedir)
- p = subprocess.Popen([%(interpreter)s, module], env=os.environ, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
- (stdout, stderr) = p.communicate(json_params)
+ # read in the args file which the user may have modified
+ with open(args_path, 'rb') as f:
+ json_params = f.read()
- if not isinstance(stderr, (bytes, unicode)):
- stderr = stderr.read()
- if not isinstance(stdout, (bytes, unicode)):
- stdout = stdout.read()
- if PY3:
- sys.stderr.buffer.write(stderr)
- sys.stdout.buffer.write(stdout)
- else:
- sys.stderr.write(stderr)
- sys.stdout.write(stdout)
- return p.returncode
+ # Monkeypatch the parameters into basic
+ from ansible.module_utils import basic
+ basic._ANSIBLE_ARGS = json_params
+
+ # Run the module! By importing it as '__main__', it thinks it is executing as a script
+ import imp
+ with open(script_path, 'r') as f:
+ importer = imp.load_module('__main__', f, script_path, ('.py', 'r', imp.PY_SOURCE))
+
+ # Ansible modules must exit themselves
+ print('{"msg": "New-style module did not handle its own exit", "failed": true}')
+ sys.exit(1)
-def debug(command, zipped_mod, json_params):
- # The code here normally doesn't run. It's only used for debugging on the
- # remote machine.
- #
- # The subcommands in this function make it easier to debug ansiballz
- # modules. Here's the basic steps:
- #
- # Run ansible with the environment variable: ANSIBLE_KEEP_REMOTE_FILES=1 and -vvv
- # to save the module file remotely::
- # $ ANSIBLE_KEEP_REMOTE_FILES=1 ansible host1 -m ping -a 'data=october' -vvv
- #
- # Part of the verbose output will tell you where on the remote machine the
- # module was written to::
- # [...]
- # <host1> SSH: EXEC ssh -C -q -o ControlMaster=auto -o ControlPersist=60s -o KbdInteractiveAuthentication=no -o
- # PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o ConnectTimeout=10 -o
- # ControlPath=/home/badger/.ansible/cp/ansible-ssh-%%h-%%p-%%r -tt rhel7 '/bin/sh -c '"'"'LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8
- # LC_MESSAGES=en_US.UTF-8 /usr/bin/python /home/badger/.ansible/tmp/ansible-tmp-1461173013.93-9076457629738/ping'"'"''
- # [...]
- #
- # Login to the remote machine and run the module file via from the previous
- # step with the explode subcommand to extract the module payload into
- # source files::
- # $ ssh host1
- # $ /usr/bin/python /home/badger/.ansible/tmp/ansible-tmp-1461173013.93-9076457629738/ping explode
- # Module expanded into:
- # /home/badger/.ansible/tmp/ansible-tmp-1461173408.08-279692652635227/ansible
- #
- # You can now edit the source files to instrument the code or experiment with
- # different parameter values. When you're ready to run the code you've modified
- # (instead of the code from the actual zipped module), use the execute subcommand like this::
- # $ /usr/bin/python /home/badger/.ansible/tmp/ansible-tmp-1461173013.93-9076457629738/ping execute
-
- # Okay to use __file__ here because we're running from a kept file
- basedir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'debug_dir')
- args_path = os.path.join(basedir, 'args')
- script_path = os.path.join(basedir, 'ansible_module_%(ansible_module)s.py')
-
- if command == 'explode':
- # transform the ZIPDATA into an exploded directory of code and then
- # print the path to the code. This is an easy way for people to look
- # at the code on the remote machine for debugging it in that
- # environment
- z = zipfile.ZipFile(zipped_mod)
- for filename in z.namelist():
- if filename.startswith('/'):
- raise Exception('Something wrong with this module zip file: should not contain absolute paths')
-
- dest_filename = os.path.join(basedir, filename)
- if dest_filename.endswith(os.path.sep) and not os.path.exists(dest_filename):
- os.makedirs(dest_filename)
- else:
- directory = os.path.dirname(dest_filename)
- if not os.path.exists(directory):
- os.makedirs(directory)
- f = open(dest_filename, 'wb')
- f.write(z.read(filename))
- f.close()
-
- # write the args file
- f = open(args_path, 'wb')
- f.write(json_params)
- f.close()
-
- print('Module expanded into:')
- print('%%s' %% basedir)
- exitcode = 0
-
- elif command == 'execute':
- # Execute the exploded code instead of executing the module from the
- # embedded ZIPDATA. This allows people to easily run their modified
- # code on the remote machine to see how changes will affect it.
- # This differs slightly from default Ansible execution of Python modules
- # as it passes the arguments to the module via a file instead of stdin.
-
- # Set pythonpath to the debug dir
- pythonpath = os.environ.get('PYTHONPATH')
- if pythonpath:
- os.environ['PYTHONPATH'] = ':'.join((basedir, pythonpath))
- else:
- os.environ['PYTHONPATH'] = basedir
-
- p = subprocess.Popen([%(interpreter)s, script_path, args_path],
- env=os.environ, shell=False, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE, stdin=subprocess.PIPE)
- (stdout, stderr) = p.communicate()
-
- if not isinstance(stderr, (bytes, unicode)):
- stderr = stderr.read()
- if not isinstance(stdout, (bytes, unicode)):
- stdout = stdout.read()
- if PY3:
- sys.stderr.buffer.write(stderr)
- sys.stdout.buffer.write(stdout)
else:
- sys.stderr.write(stderr)
- sys.stdout.write(stdout)
- return p.returncode
-
- elif command == 'excommunicate':
- # This attempts to run the module in-process (by importing a main
- # function and then calling it). It is not the way ansible generally
- # invokes the module so it won't work in every case. It is here to
- # aid certain debuggers which work better when the code doesn't change
- # from one process to another but there may be problems that occur
- # when using this that are only artifacts of how we're invoking here,
- # not actual bugs (as they don't affect the real way that we invoke
- # ansible modules)
-
- # stub the args and python path
- sys.argv = ['%(ansible_module)s', args_path]
- sys.path.insert(0, basedir)
-
- from ansible_module_%(ansible_module)s import main
- main()
- print('WARNING: Module returned to wrapper instead of exiting')
- sys.exit(1)
- else:
- print('WARNING: Unknown debug command. Doing nothing.')
- exitcode = 0
+ print('WARNING: Unknown debug command. Doing nothing.')
+ exitcode = 0
- return exitcode
+ return exitcode
-if __name__ == '__main__':
#
# See comments in the debug() method for information on debugging
#
@@ -306,38 +298,14 @@ if __name__ == '__main__':
# store this in remote_tmpdir (use system tempdir instead)
temp_path = tempfile.mkdtemp(prefix='ansible_')
- zipped_mod = os.path.join(temp_path, 'ansible_modlib.zip')
- modlib = open(zipped_mod, 'wb')
- modlib.write(base64.b64decode(ZIPDATA))
- modlib.close()
+ zipped_mod = os.path.join(temp_path, 'ansible_%(ansible_module)s_payload.zip')
+ with open(zipped_mod, 'wb') as modlib:
+ modlib.write(base64.b64decode(ZIPDATA))
if len(sys.argv) == 2:
exitcode = debug(sys.argv[1], zipped_mod, ANSIBALLZ_PARAMS)
else:
- z = zipfile.ZipFile(zipped_mod, mode='r')
- module = os.path.join(temp_path, 'ansible_module_%(ansible_module)s.py')
- f = open(module, 'wb')
- f.write(z.read('ansible_module_%(ansible_module)s.py'))
- f.close()
-
- # When installed via setuptools (including python setup.py install),
- # ansible may be installed with an easy-install.pth file. That file
- # may load the system-wide install of ansible rather than the one in
- # the module. sitecustomize is the only way to override that setting.
- z = zipfile.ZipFile(zipped_mod, mode='a')
-
- # py3: zipped_mod will be text, py2: it's bytes. Need bytes at the end
- sitecustomize = u'import sys\\nsys.path.insert(0,"%%s")\\n' %% zipped_mod
- sitecustomize = sitecustomize.encode('utf-8')
- # Use a ZipInfo to work around zipfile limitation on hosts with
- # clocks set to a pre-1980 year (for instance, Raspberry Pi)
- zinfo = zipfile.ZipInfo()
- zinfo.filename = 'sitecustomize.py'
- zinfo.date_time = ( %(year)i, %(month)i, %(day)i, %(hour)i, %(minute)i, %(second)i)
- z.writestr(zinfo, sitecustomize)
- z.close()
-
- exitcode = invoke_module(module, zipped_mod, ANSIBALLZ_PARAMS)
+ invoke_module(zipped_mod, ANSIBALLZ_PARAMS)
finally:
try:
shutil.rmtree(temp_path)
@@ -345,6 +313,33 @@ if __name__ == '__main__':
# tempdir creation probably failed
pass
sys.exit(exitcode)
+
+if __name__ == '__main__':
+ _ansiballz_main()
+'''
+
+ANSIBALLZ_COVERAGE_TEMPLATE = '''
+ # Access to the working directory is required by coverage.
+ # Some platforms, such as macOS, may not allow querying the working directory when using become to drop privileges.
+ try:
+ os.getcwd()
+ except OSError:
+ os.chdir('/')
+
+ os.environ['COVERAGE_FILE'] = '%(coverage_output)s'
+
+ import atexit
+ import coverage
+
+ cov = coverage.Coverage(config_file='%(coverage_config)s')
+
+ def atexit_coverage():
+ cov.stop()
+ cov.save()
+
+ atexit.register(atexit_coverage)
+
+ cov.start()
'''
@@ -759,7 +754,7 @@ def _find_module_utils(module_name, b_module_data, module_path, module_args, tas
to_bytes(__author__) + b'"\n')
zf.writestr('ansible/module_utils/__init__.py', b'from pkgutil import extend_path\n__path__=extend_path(__path__,__name__)\n')
- zf.writestr('ansible_module_%s.py' % module_name, b_module_data)
+ zf.writestr('__main__.py', b_module_data)
py_module_cache = {('__init__',): (b'', '[builtin]')}
recursive_finder(module_name, b_module_data, py_module_names, py_module_cache, zf)
@@ -805,6 +800,18 @@ def _find_module_utils(module_name, b_module_data, module_path, module_args, tas
interpreter_parts = interpreter.split(u' ')
interpreter = u"'{0}'".format(u"', '".join(interpreter_parts))
+ coverage_config = os.environ.get('_ANSIBLE_COVERAGE_CONFIG')
+
+ if coverage_config:
+ # Enable code coverage analysis of the module.
+ # This feature is for internal testing and may change without notice.
+ coverage = ANSIBALLZ_COVERAGE_TEMPLATE % dict(
+ coverage_config=coverage_config,
+ coverage_output=os.environ['_ANSIBLE_COVERAGE_OUTPUT']
+ )
+ else:
+ coverage = ''
+
now = datetime.datetime.utcnow()
output.write(to_bytes(ACTIVE_ANSIBALLZ_TEMPLATE % dict(
zipdata=zipdata,
@@ -819,6 +826,7 @@ def _find_module_utils(module_name, b_module_data, module_path, module_args, tas
hour=now.hour,
minute=now.minute,
second=now.second,
+ coverage=coverage,
)))
b_module_data = output.getvalue()
diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py
index 9dd3ee5309..913cdae6b6 100644
--- a/lib/ansible/module_utils/basic.py
+++ b/lib/ansible/module_utils/basic.py
@@ -62,6 +62,7 @@ PASS_BOOLS = ('no_log', 'debug', 'diff')
# The functions available here can be used to do many common tasks,
# to simplify development of Python modules.
+import __main__
import atexit
import locale
import os
diff --git a/lib/ansible/plugins/action/__init__.py b/lib/ansible/plugins/action/__init__.py
index 3460d5be39..72dd769686 100644
--- a/lib/ansible/plugins/action/__init__.py
+++ b/lib/ansible/plugins/action/__init__.py
@@ -762,7 +762,7 @@ class ActionBase(with_metaclass(ABCMeta, object)):
tmpdir = self._connection._shell.tmpdir
remote_module_filename = self._connection._shell.get_remote_filename(module_path)
- remote_module_path = self._connection._shell.join_path(tmpdir, remote_module_filename)
+ remote_module_path = self._connection._shell.join_path(tmpdir, 'AnsiballZ_%s' % remote_module_filename)
args_file_path = None
if module_style in ('old', 'non_native_want_json', 'binary'):
diff --git a/test/runner/injector/injector.py b/test/runner/injector/injector.py
index 761a7c51ff..3bb9b9c22f 100755
--- a/test/runner/injector/injector.py
+++ b/test/runner/injector/injector.py
@@ -103,36 +103,13 @@ def main():
logger.debug('Remote interpreter: %s', config.remote_interpreter)
logger.debug('Coverage file: %s', config.coverage_file)
- require_cwd = False
-
if os.path.basename(__file__) == 'injector.py':
- if config.coverage_file:
- args, env, require_cwd = cover()
- else:
- args, env = runner()
+ args, env = runner() # code coverage collection is baked into the AnsiballZ wrapper when needed
else:
args, env = injector()
logger.debug('Run command: %s', ' '.join(pipes.quote(c) for c in args))
- altered_cwd = False
-
- try:
- cwd = os.getcwd()
- except OSError as ex:
- # some platforms, such as macOS, may not allow querying the working directory when using become to drop privileges
- if ex.errno != errno.EACCES:
- raise
- if require_cwd:
- # make sure the program we execute can determine the working directory if it's required
- cwd = '/'
- os.chdir(cwd)
- altered_cwd = True
- else:
- cwd = None
-
- logger.debug('Working directory: %s%s', cwd or '?', ' (altered)' if altered_cwd else '')
-
for key in sorted(env.keys()):
logger.debug('%s=%s', key, env[key])
@@ -183,29 +160,6 @@ def runner():
return args, env
-def cover():
- """
- :rtype: list[str], dict[str, str], bool
- """
- if len(config.arguments) > 1:
- executable = config.arguments[1]
- else:
- executable = ''
-
- require_cwd = False
-
- if os.path.basename(executable).startswith('ansible_module_'):
- args, env = coverage_command()
- # coverage requires knowing the working directory
- require_cwd = True
- else:
- args, env = [config.python_interpreter], os.environ.copy()
-
- args += config.arguments[1:]
-
- return args, env, require_cwd
-
-
def coverage_command():
"""
:rtype: list[str], dict[str, str]
diff --git a/test/runner/lib/cover.py b/test/runner/lib/cover.py
index a1b131ee0b..0b7dcaa70e 100644
--- a/test/runner/lib/cover.py
+++ b/test/runner/lib/cover.py
@@ -88,10 +88,17 @@ def command_coverage_combine(args):
continue
if '/ansible_modlib.zip/ansible/' in filename:
+ # Rewrite the module_utils path from the remote host to match the controller. Ansible 2.6 and earlier.
new_name = re.sub('^.*/ansible_modlib.zip/ansible/', ansible_path, filename)
display.info('%s -> %s' % (filename, new_name), verbosity=3)
filename = new_name
+ elif re.search(r'/ansible_[^/]+_payload\.zip/ansible/', filename):
+ # Rewrite the module_utils path from the remote host to match the controller. Ansible 2.7 and later.
+ new_name = re.sub(r'^.*/ansible_[^/]+_payload\.zip/ansible/', ansible_path, filename)
+ display.info('%s -> %s' % (filename, new_name), verbosity=3)
+ filename = new_name
elif '/ansible_module_' in filename:
+ # Rewrite the module path from the remote host to match the controller. Ansible 2.6 and earlier.
module_name = re.sub('^.*/ansible_module_(?P<module>.*).py$', '\\g<module>', filename)
if module_name not in modules:
display.warning('Skipping coverage of unknown module: %s' % module_name)
@@ -99,7 +106,19 @@ def command_coverage_combine(args):
new_name = os.path.abspath(modules[module_name])
display.info('%s -> %s' % (filename, new_name), verbosity=3)
filename = new_name
+ elif re.search(r'/ansible_[^/]+_payload(_[^/]+|\.zip)/__main__\.py$', filename):
+ # Rewrite the module path from the remote host to match the controller. Ansible 2.7 and later.
+ # AnsiballZ versions using zipimporter will match the `.zip` portion of the regex.
+ # AnsiballZ versions not using zipimporter will match the `_[^/]+` portion of the regex.
+ module_name = re.sub(r'^.*/ansible_(?P<module>[^/]+)_payload(_[^/]+|\.zip)/__main__\.py$', '\\g<module>', filename).rstrip('_')
+ if module_name not in modules:
+ display.warning('Skipping coverage of unknown module: %s' % module_name)
+ continue
+ new_name = os.path.abspath(modules[module_name])
+ display.info('%s -> %s' % (filename, new_name), verbosity=3)
+ filename = new_name
elif re.search('^(/.*?)?/root/ansible/', filename):
+ # Rewrite the path of code running on a remote host or in a docker container as root.
new_name = re.sub('^(/.*?)?/root/ansible/', root_path, filename)
display.info('%s -> %s' % (filename, new_name), verbosity=3)
filename = new_name
diff --git a/test/runner/lib/util.py b/test/runner/lib/util.py
index 2c60da6fe3..7770623cf9 100644
--- a/test/runner/lib/util.py
+++ b/test/runner/lib/util.py
@@ -169,6 +169,10 @@ def intercept_command(args, cmd, target_name, capture=False, env=None, data=None
env['ANSIBLE_TEST_PYTHON_VERSION'] = version
env['ANSIBLE_TEST_PYTHON_INTERPRETER'] = interpreter
+ if args.coverage:
+ env['_ANSIBLE_COVERAGE_CONFIG'] = os.path.join(inject_path, '.coveragerc')
+ env['_ANSIBLE_COVERAGE_OUTPUT'] = coverage_file
+
config = dict(
python_interpreter=interpreter,
coverage_file=coverage_file if args.coverage else None,
diff --git a/test/sanity/validate-modules/utils.py b/test/sanity/validate-modules/utils.py
index 07ae5f8353..de60a4efc2 100644
--- a/test/sanity/validate-modules/utils.py
+++ b/test/sanity/validate-modules/utils.py
@@ -140,4 +140,4 @@ class NoArgsAnsibleModule(AnsibleModule):
methods within AnsibleModule without having to fake a bunch of data
"""
def _load_params(self):
- self.params = {}
+ self.params = {'_ansible_selinux_special_fs': [], '_ansible_remote_tmp': '/tmp', '_ansible_keep_remote_files': False, '_ansible_check_mode': False}
diff --git a/test/units/mock/procenv.py b/test/units/mock/procenv.py
index a74b4f5c58..e7326c0935 100644
--- a/test/units/mock/procenv.py
+++ b/test/units/mock/procenv.py
@@ -77,7 +77,7 @@ def swap_stdout():
class ModuleTestCase(unittest.TestCase):
def setUp(self, module_args=None):
if module_args is None:
- module_args = {}
+ module_args = {'_ansible_remote_tmp': '/tmp', '_ansible_keep_remote_files': False}
args = json.dumps(dict(ANSIBLE_MODULE_ARGS=module_args))
diff --git a/test/units/module_utils/aws/test_aws_module.py b/test/units/module_utils/aws/test_aws_module.py
index e17deba70f..9539a2dc93 100644
--- a/test/units/module_utils/aws/test_aws_module.py
+++ b/test/units/module_utils/aws/test_aws_module.py
@@ -32,7 +32,7 @@ botocore = importorskip("botocore")
class AWSModuleTestCase(unittest.TestCase):
- basic._ANSIBLE_ARGS = to_bytes(json.dumps({'ANSIBLE_MODULE_ARGS': {}}))
+ basic._ANSIBLE_ARGS = to_bytes(json.dumps({'ANSIBLE_MODULE_ARGS': {'_ansible_tmpdir': '/tmp/ansible-abc'}}))
def test_create_aws_module_should_set_up_params(self):
m = AnsibleAWSModule(argument_spec=dict(
@@ -59,7 +59,7 @@ class ErrorReportingTestcase(unittest.TestCase):
def test_botocore_exception_reports_nicely_via_fail_json_aws(self):
- basic._ANSIBLE_ARGS = to_bytes(json.dumps({'ANSIBLE_MODULE_ARGS': {}}))
+ basic._ANSIBLE_ARGS = to_bytes(json.dumps({'ANSIBLE_MODULE_ARGS': {'_ansible_tmpdir': '/tmp/ansible-abc'}}))
module = AnsibleAWSModule(argument_spec=dict(
fail_mode=dict(type='list', default=['success'])
))
@@ -97,7 +97,7 @@ class ErrorReportingTestcase(unittest.TestCase):
"Failed to find error/code; was: " + str(fail_json_double.mock_calls[0])
def test_botocore_exception_without_response_reports_nicely_via_fail_json_aws(self):
- basic._ANSIBLE_ARGS = to_bytes(json.dumps({'ANSIBLE_MODULE_ARGS': {}}))
+ basic._ANSIBLE_ARGS = to_bytes(json.dumps({'ANSIBLE_MODULE_ARGS': {'_ansible_tmpdir': '/tmp/ansible-abc'}}))
module = AnsibleAWSModule(argument_spec=dict(
fail_mode=dict(type='list', default=['success'])
))
diff --git a/test/units/module_utils/basic/test_selinux.py b/test/units/module_utils/basic/test_selinux.py
index c95d9acc97..df6ec9d0e8 100644
--- a/test/units/module_utils/basic/test_selinux.py
+++ b/test/units/module_utils/basic/test_selinux.py
@@ -166,7 +166,9 @@ class TestSELinux(ModuleTestCase):
def test_module_utils_basic_ansible_module_is_special_selinux_path(self):
from ansible.module_utils import basic
- args = json.dumps(dict(ANSIBLE_MODULE_ARGS={'_ansible_selinux_special_fs': "nfs,nfsd,foos"}))
+ args = json.dumps(dict(ANSIBLE_MODULE_ARGS={'_ansible_selinux_special_fs': "nfs,nfsd,foos",
+ '_ansible_remote_tmp': "/tmp",
+ '_ansible_keep_remote_files': False}))
with swap_stdin_and_argv(stdin_data=args):
basic._ANSIBLE_ARGS = None
diff --git a/test/units/module_utils/basic/test_tmpdir.py b/test/units/module_utils/basic/test_tmpdir.py
index 2315313253..20b529893a 100644
--- a/test/units/module_utils/basic/test_tmpdir.py
+++ b/test/units/module_utils/basic/test_tmpdir.py
@@ -6,6 +6,7 @@
from __future__ import (absolute_import, division)
__metaclass__ = type
+import json
import os
import shutil
import tempfile
@@ -13,6 +14,9 @@ import tempfile
import pytest
from ansible.compat.tests.mock import patch, MagicMock
+from ansible.module_utils._text import to_bytes
+
+from ansible.module_utils import basic
class TestAnsibleModuleTmpDir:
@@ -58,9 +62,8 @@ class TestAnsibleModuleTmpDir:
# pylint bug: https://github.com/PyCQA/pylint/issues/511
# pylint: disable=undefined-variable
- @pytest.mark.parametrize('stdin, expected, stat_exists', ((s, e, t) for s, t, e in DATA),
- indirect=['stdin'])
- def test_tmpdir_property(self, am, monkeypatch, expected, stat_exists):
+ @pytest.mark.parametrize('args, expected, stat_exists', ((s, e, t) for s, t, e in DATA))
+ def test_tmpdir_property(self, monkeypatch, args, expected, stat_exists):
makedirs = {'called': False}
def mock_mkdtemp(prefix, dir):
@@ -68,18 +71,20 @@ class TestAnsibleModuleTmpDir:
def mock_makedirs(path, mode):
makedirs['called'] = True
- expected = os.path.expanduser(os.path.expandvars(am._remote_tmp))
- assert path == expected
- assert mode == 0o700
+ makedirs['path'] = path
+ makedirs['mode'] = mode
return
monkeypatch.setattr(tempfile, 'mkdtemp', mock_mkdtemp)
monkeypatch.setattr(os.path, 'exists', lambda x: stat_exists)
monkeypatch.setattr(os, 'makedirs', mock_makedirs)
monkeypatch.setattr(shutil, 'rmtree', lambda x: None)
+ monkeypatch.setattr(basic, '_ANSIBLE_ARGS', to_bytes(json.dumps({'ANSIBLE_MODULE_ARGS': args})))
with patch('time.time', return_value=42):
+ am = basic.AnsibleModule(argument_spec={})
actual_tmpdir = am.tmpdir
+
assert actual_tmpdir == expected
# verify subsequent calls always produces the same tmpdir
@@ -87,6 +92,9 @@ class TestAnsibleModuleTmpDir:
if not stat_exists:
assert makedirs['called']
+ expected = os.path.expanduser(os.path.expandvars(am._remote_tmp))
+ assert makedirs['path'] == expected
+ assert makedirs['mode'] == 0o700
@pytest.mark.parametrize('stdin', ({"_ansible_tmpdir": None,
"_ansible_remote_tmp": "$HOME/.test",
diff --git a/test/units/module_utils/conftest.py b/test/units/module_utils/conftest.py
index 8da7df81ec..35912050bd 100644
--- a/test/units/module_utils/conftest.py
+++ b/test/units/module_utils/conftest.py
@@ -25,6 +25,10 @@ def stdin(mocker, request):
elif isinstance(request.param, MutableMapping):
if 'ANSIBLE_MODULE_ARGS' not in request.param:
request.param = {'ANSIBLE_MODULE_ARGS': request.param}
+ if '_ansible_remote_tmp' not in request.param['ANSIBLE_MODULE_ARGS']:
+ request.param['ANSIBLE_MODULE_ARGS']['_ansible_remote_tmp'] = '/tmp'
+ if '_ansible_keep_remote_files' not in request.param['ANSIBLE_MODULE_ARGS']:
+ request.param['ANSIBLE_MODULE_ARGS']['_ansible_keep_remote_files'] = False
args = json.dumps(request.param)
else:
raise Exception('Malformed data to the stdin pytest fixture')
diff --git a/test/units/modules/cloud/amazon/test_aws_api_gateway.py b/test/units/modules/cloud/amazon/test_aws_api_gateway.py
index d6f4c7e719..9efa8460d0 100644
--- a/test/units/modules/cloud/amazon/test_aws_api_gateway.py
+++ b/test/units/modules/cloud/amazon/test_aws_api_gateway.py
@@ -62,6 +62,7 @@ def test_upload_api(monkeypatch):
"state": "present",
"swagger_text": "the-swagger-text-is-fake",
"region": 'mars-north-1',
+ "_ansible_tmpdir": "/tmp/ansibl-abcdef",
})
with pytest.raises(SystemExit):
agw.main()
diff --git a/test/units/modules/conftest.py b/test/units/modules/conftest.py
index 68dc0cbedf..319ddb06dd 100644
--- a/test/units/modules/conftest.py
+++ b/test/units/modules/conftest.py
@@ -17,6 +17,10 @@ def patch_ansible_module(request, mocker):
elif isinstance(request.param, MutableMapping):
if 'ANSIBLE_MODULE_ARGS' not in request.param:
request.param = {'ANSIBLE_MODULE_ARGS': request.param}
+ if '_ansible_remote_tmp' not in request.param['ANSIBLE_MODULE_ARGS']:
+ request.param['ANSIBLE_MODULE_ARGS']['_ansible_remote_tmp'] = '/tmp'
+ if '_ansible_keep_remote_files' not in request.param['ANSIBLE_MODULE_ARGS']:
+ request.param['ANSIBLE_MODULE_ARGS']['_ansible_keep_remote_files'] = '/tmp'
args = json.dumps(request.param)
else:
raise Exception('Malformed data to the patch_ansible_module pytest fixture')
diff --git a/test/units/modules/network/cnos/cnos_module.py b/test/units/modules/network/cnos/cnos_module.py
index 4b0af98bb4..bbcf7b2ac2 100644
--- a/test/units/modules/network/cnos/cnos_module.py
+++ b/test/units/modules/network/cnos/cnos_module.py
@@ -28,10 +28,6 @@ from ansible.module_utils import basic
from ansible.module_utils._text import to_bytes
-def set_module_args(args):
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args)
-
fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures')
fixture_data = {}
diff --git a/test/units/modules/network/cnos/test_cnos_config.py b/test/units/modules/network/cnos/test_cnos_config.py
index 9caa35efeb..35c9199c88 100644
--- a/test/units/modules/network/cnos/test_cnos_config.py
+++ b/test/units/modules/network/cnos/test_cnos_config.py
@@ -24,7 +24,9 @@ import json
from ansible.compat.tests.mock import patch
from ansible.modules.network.cnos import cnos_config
-from .cnos_module import TestCnosModule, load_fixture, set_module_args
+
+from .cnos_module import TestCnosModule, load_fixture
+from units.modules.utils import set_module_args
class TestCnosConfigModule(TestCnosModule):
diff --git a/test/units/modules/network/enos/enos_module.py b/test/units/modules/network/enos/enos_module.py
index 97464e2c25..66c59ca97f 100644
--- a/test/units/modules/network/enos/enos_module.py
+++ b/test/units/modules/network/enos/enos_module.py
@@ -28,10 +28,6 @@ from ansible.module_utils import basic
from ansible.module_utils._text import to_bytes
-def set_module_args(args):
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args)
-
fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures')
fixture_data = {}
diff --git a/test/units/modules/network/enos/test_enos_config.py b/test/units/modules/network/enos/test_enos_config.py
index c0a17fd9b8..1b5e4930d3 100644
--- a/test/units/modules/network/enos/test_enos_config.py
+++ b/test/units/modules/network/enos/test_enos_config.py
@@ -24,7 +24,9 @@ import json
from ansible.compat.tests.mock import patch
from ansible.modules.network.enos import enos_config
-from .enos_module import TestEnosModule, load_fixture, set_module_args
+from .enos_module import TestEnosModule, load_fixture
+
+from units.modules.utils import set_module_args
class TestEnosConfigModule(TestEnosModule):
diff --git a/test/units/modules/network/netact/test_netact_cm_command.py b/test/units/modules/network/netact/test_netact_cm_command.py
index f5bc8724e8..b934452997 100644
--- a/test/units/modules/network/netact/test_netact_cm_command.py
+++ b/test/units/modules/network/netact/test_netact_cm_command.py
@@ -36,14 +36,7 @@ from ansible.module_utils._text import to_bytes
from ansible.modules.network.netact import netact_cm_command
from ansible.compat.tests.mock import patch
-from units.modules.utils import set_module_args as _set_module_args, \
- AnsibleExitJson, AnsibleFailJson, ModuleTestCase
-
-
-def set_module_args(args):
- """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)
+from units.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase
class AnsibleExitJson(Exception):
diff --git a/test/units/modules/network/nso/nso_module.py b/test/units/modules/network/nso/nso_module.py
index 18566cd4eb..8df42fe996 100644
--- a/test/units/modules/network/nso/nso_module.py
+++ b/test/units/modules/network/nso/nso_module.py
@@ -30,11 +30,6 @@ fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures')
fixture_data = {}
-def set_module_args(args):
- args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
- basic._ANSIBLE_ARGS = to_bytes(args)
-
-
def load_fixture(name):
path = os.path.join(fixture_path, name)
if path not in fixture_data:
diff --git a/test/units/modules/network/nso/test_nso_action.py b/test/units/modules/network/nso/test_nso_action.py
index b890b6bc1e..661ad3a869 100644
--- a/test/units/modules/network/nso/test_nso_action.py
+++ b/test/units/modules/network/nso/test_nso_action.py
@@ -25,6 +25,8 @@ from ansible.modules.network.nso import nso_action
from . import nso_module
from .nso_module import MockResponse
+from units.modules.utils import set_module_args
+
class TestNsoAction(nso_module.TestNsoModule):
module = nso_action
@@ -42,7 +44,7 @@ class TestNsoAction(nso_module.TestNsoModule):
]
open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs)
- nso_module.set_module_args({
+ set_module_args({
'username': 'user', 'password': 'password',
'url': 'http://localhost:8080/jsonrpc',
'path': path,
@@ -66,7 +68,7 @@ class TestNsoAction(nso_module.TestNsoModule):
]
open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs)
- nso_module.set_module_args({
+ set_module_args({
'username': 'user', 'password': 'password',
'url': 'http://localhost:8080/jsonrpc',
'path': path,
@@ -92,7 +94,7 @@ class TestNsoAction(nso_module.TestNsoModule):
]
open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs)
- nso_module.set_module_args({
+ set_module_args({
'username': 'user', 'password': 'password',
'url': 'http://localhost:8080/jsonrpc',
'path': path,
@@ -119,7 +121,7 @@ class TestNsoAction(nso_module.TestNsoModule):
]
open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs)
- nso_module.set_module_args({
+ set_module_args({
'username': 'user', 'password': 'password',
'url': 'http://localhost:8080/jsonrpc',
'path': path,
@@ -147,7 +149,7 @@ class TestNsoAction(nso_module.TestNsoModule):
]
open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs)
- nso_module.set_module_args({
+ set_module_args({
'username': 'user', 'password': 'password',
'url': 'http://localhost:8080/jsonrpc',
'path': path,
diff --git a/test/units/modules/network/nso/test_nso_query.py b/test/units/modules/network/nso/test_nso_query.py
index 234a0cb76d..40060fe233 100644
--- a/test/units/modules/network/nso/test_nso_query.py
+++ b/test/units/modules/network/nso/test_nso_query.py
@@ -23,6 +23,8 @@ from ansible.modules.network.nso import nso_query
from . import nso_module
from .nso_module import MockResponse
+from units.modules.utils import set_module_args
+
class TestNsoQuery(nso_module.TestNsoModule):
module = nso_query
@@ -42,7 +44,7 @@ class TestNsoQuery(nso_module.TestNsoModule):
]
open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs)
- nso_module.set_module_args({
+ set_module_args({
'username': 'user', 'password': 'password',
'url': 'http://localhost:8080/jsonrpc',
'xpath': xpath,
diff --git a/test/units/modules/network/nso/test_nso_show.py b/test/units/modules/network/nso/test_nso_show.py
index 75e7014e02..3d63716ccb 100644
--- a/test/units/modules/network/nso/test_nso_show.py
+++ b/test/units/modules/network/nso/test_nso_show.py
@@ -25,6 +25,8 @@ from ansible.modules.network.nso import nso_show
from . import nso_module
from .nso_module import MockResponse
+from units.modules.utils import set_module_args
+
class TestNsoShow(nso_module.TestNsoModule):
module = nso_show
@@ -43,7 +45,7 @@ class TestNsoShow(nso_module.TestNsoModule):
]
open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs)
- nso_module.set_module_args({
+ set_module_args({
'username': 'user', 'password': 'password',
'url': 'http://localhost:8080/jsonrpc',
'path': path
@@ -64,7 +66,7 @@ class TestNsoShow(nso_module.TestNsoModule):
]
open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs)
- nso_module.set_module_args({
+ set_module_args({
'username': 'user', 'password': 'password',
'url': 'http://localhost:8080/jsonrpc',
'path': path,
@@ -85,7 +87,7 @@ class TestNsoShow(nso_module.TestNsoModule):
]
open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs)
- nso_module.set_module_args({
+ set_module_args({
'username': 'user', 'password': 'password',
'url': 'http://localhost:8080/jsonrpc',
'path': path,
diff --git a/test/units/modules/network/nso/test_nso_verify.py b/test/units/modules/network/nso/test_nso_verify.py
index 34aa453031..ac746b4a1f 100644
--- a/test/units/modules/network/nso/test_nso_verify.py
+++ b/test/units/modules/network/nso/test_nso_verify.py
@@ -25,6 +25,8 @@ from ansible.modules.network.nso import nso_verify
from . import nso_module
from .nso_module import MockResponse
+from units.modules.utils import set_module_args
+
class TestNsoVerify(nso_module.TestNsoModule):
module = nso_verify
@@ -39,7 +41,7 @@ class TestNsoVerify(nso_module.TestNsoModule):
open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs)
data = {}
- nso_module.set_module_args({
+ set_module_args({
'username': 'user', 'password': 'password',
'url': 'http://localhost:8080/jsonrpc', 'data': data
})
@@ -68,7 +70,7 @@ class TestNsoVerify(nso_module.TestNsoModule):
open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs)
data = nso_module.load_fixture('verify_violation_data.json')
- nso_module.set_module_args({
+ set_module_args({
'username': 'user', 'password': 'password',
'url': 'http://localhost:8080/jsonrpc', 'data': data
})
@@ -97,7 +99,7 @@ class TestNsoVerify(nso_module.TestNsoModule):
open_url_mock.side_effect = lambda *args, **kwargs: nso_module.mock_call(calls, *args, **kwargs)
data = nso_module.load_fixture('verify_violation_data.json')
- nso_module.set_module_args({
+ set_module_args({
'username': 'user', 'password': 'password',
'url': 'http://localhost:8080/jsonrpc', 'data': data
})
diff --git a/test/units/modules/source_control/test_gitlab_deploy_key.py b/test/units/modules/source_control/test_gitlab_deploy_key.py
index d77f4033a1..162702e520 100644
--- a/test/units/modules/source_control/test_gitlab_deploy_key.py
+++ b/test/units/modules/source_control/test_gitlab_deploy_key.py
@@ -10,6 +10,9 @@ from ansible.module_utils import basic
import pytest
import json
+from units.modules.utils import set_module_args
+
+
fake_server_state = [
{
"id": 1,
@@ -21,12 +24,6 @@ fake_server_state = [
]
-def set_module_args(args):
- """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)
-
-
class FakeReader:
def __init__(self, object):
self.content = json.dumps(object, sort_keys=True)
diff --git a/test/units/modules/source_control/test_gitlab_hooks.py b/test/units/modules/source_control/test_gitlab_hooks.py
index 2487b963dc..7acb8d66f6 100644
--- a/test/units/modules/source_control/test_gitlab_hooks.py
+++ b/test/units/modules/source_control/test_gitlab_hooks.py
@@ -10,6 +10,9 @@ from ansible.module_utils import basic
import pytest
import json
+from units.modules.utils import set_module_args
+
+
fake_server_state = [
{
"id": 1,
@@ -29,12 +32,6 @@ fake_server_state = [
]
-def set_module_args(args):
- """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)
-
-
class FakeReader:
def __init__(self, object):
self.content = json.dumps(object, sort_keys=True)
diff --git a/test/units/modules/utils.py b/test/units/modules/utils.py
index a0e9351b12..91fb4c9522 100644
--- a/test/units/modules/utils.py
+++ b/test/units/modules/utils.py
@@ -7,6 +7,11 @@ from ansible.module_utils._text import to_bytes
def set_module_args(args):
+ if '_ansible_remote_tmp' not in args:
+ args['_ansible_remote_tmp'] = '/tmp'
+ if '_ansible_keep_remote_files' not in args:
+ args['_ansible_keep_remote_files'] = False
+
args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
basic._ANSIBLE_ARGS = to_bytes(args)
@@ -35,4 +40,5 @@ class ModuleTestCase(unittest.TestCase):
def setUp(self):
self.mock_module = patch.multiple(basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json)
self.mock_module.start()
+ set_module_args({})
self.addCleanup(self.mock_module.stop)