Import sanity test for main() in Ansible modules.

pull/4420/head
Matt Clay 2018-02-15 18:36:48 -08:00
parent 365630df65
commit ff922ac2ad
2 changed files with 117 additions and 49 deletions

View File

@ -15,6 +15,28 @@ try:
except ImportError:
from io import StringIO
import ansible.module_utils.basic
import ansible.module_utils.common.removed
class ImporterAnsibleModuleException(Exception):
"""Exception thrown during initialization of ImporterAnsibleModule."""
pass
class ImporterAnsibleModule(object):
"""Replacement for AnsibleModule to support import testing."""
def __init__(self, *args, **kwargs):
raise ImporterAnsibleModuleException()
# stop Ansible module execution during AnsibleModule instantiation
ansible.module_utils.basic.AnsibleModule = ImporterAnsibleModule
# no-op for _load_params since it may be called before instantiating AnsibleModule
ansible.module_utils.basic._load_params = lambda *args, **kwargs: {}
# no-op for removed_module since it is called in place of AnsibleModule instantiation
ansible.module_utils.common.removed.removed_module = lambda *args, **kwargs: None
def main():
"""Main program function."""
@ -22,13 +44,47 @@ def main():
messages = set()
for path in sys.argv[1:]:
test_python_module(path, base_dir, messages, False)
test_python_module(path, base_dir, messages, True)
if messages:
exit(10)
def test_python_module(path, base_dir, messages, ansible_module):
if ansible_module:
# importing modules with __main__ under Python 2.6 exits with status code 1
if sys.version_info < (2, 7):
return
# only run __main__ protected code for Ansible modules
if not path.startswith('lib/ansible/modules/'):
return
# async_wrapper is not an Ansible module
if path == 'lib/ansible/modules/utilities/logic/async_wrapper.py':
return
# run code protected by __name__ conditional
name = '__main__'
# show the Ansible module responsible for the exception, even if it was thrown in module_utils
filter_dir = os.path.join(base_dir, 'lib/ansible/modules')
else:
# do not run code protected by __name__ conditional
name = 'module_import_test'
# show the Ansible file responsible for the exception, even if it was thrown in 3rd party code
filter_dir = base_dir
capture = Capture()
try:
with open(path, 'r') as module_fd:
with capture_output(capture):
imp.load_module('module_import_test', module_fd, os.path.abspath(path), ('.py', 'r', imp.PY_SOURCE))
imp.load_module(name, module_fd, os.path.abspath(path), ('.py', 'r', imp.PY_SOURCE))
capture_report(path, capture, messages)
except ImporterAnsibleModuleException:
# module instantiated AnsibleModule without raising an exception
pass
except BaseException as ex: # pylint: disable=locally-disabled, broad-except
capture_report(path, capture, messages)
@ -40,7 +96,7 @@ def main():
offset = 0
for result in results:
if result[0].startswith(base_dir):
if result[0].startswith(filter_dir):
source = result[0][len(base_dir) + 1:].replace('test/sanity/import/', '')
line = result[1] or 0
break
@ -66,12 +122,7 @@ def main():
message = re.sub(r'\n *', ': ', message)
error = '%s:%d:%d: %s: %s' % (source, line, offset, exc_type.__name__, message)
if error not in messages:
messages.add(error)
print(error)
if messages:
exit(10)
report_message(error, messages)
class Capture(object):
@ -89,11 +140,19 @@ def capture_report(path, capture, messages):
"""
if capture.stdout.getvalue():
message = '%s:%d:%d: %s: %s' % (path, 0, 0, 'Output', 'Import resulted in output to stdout.')
messages.add(message)
print(message)
report_message(message, messages)
if capture.stderr.getvalue():
message = '%s:%d:%d: %s: %s' % (path, 0, 0, 'Output', 'Import resulted in output to stderr.')
report_message(message, messages)
def report_message(message, messages):
"""Report message if not already reported.
:type message: str
:type messages: set[str]
"""
if message not in messages:
messages.add(message)
print(message)

View File

@ -1,5 +1,14 @@
lib/ansible/modules/cloud/azure/azure_rm_storageaccount.py
lib/ansible/modules/cloud/webfaction/webfaction_app.py
lib/ansible/modules/cloud/webfaction/webfaction_db.py
lib/ansible/modules/cloud/webfaction/webfaction_domain.py
lib/ansible/modules/cloud/webfaction/webfaction_mailbox.py
lib/ansible/modules/cloud/webfaction/webfaction_site.py
lib/ansible/modules/clustering/k8s/k8s_raw.py
lib/ansible/modules/clustering/k8s/k8s_scale.py
lib/ansible/modules/clustering/openshift/openshift_raw.py
lib/ansible/modules/clustering/openshift/openshift_scale.py
lib/ansible/modules/network/avi/avi_gslbservice_patch_member.py
lib/ansible/modules/network/radware/vdirect_commit.py
lib/ansible/modules/network/radware/vdirect_file.py
lib/ansible/modules/network/radware/vdirect_runnable.py