mirror of
https://github.com/ansible/awx.git
synced 2026-03-24 04:15:02 -02:30
Update Collections Syntax to get Collection related CI Checks Passing (#16061)
* Fix collection task breaking collection ci checks * Patch ansible.module_utils.basic._ANSIBLE_PROFILE directly * Conditionalize other santity assertions * Remove added blank lines and identifier from Fail if absent and no identifier set
This commit is contained in:
@@ -116,35 +116,91 @@ def collection_import():
|
||||
return rf
|
||||
|
||||
|
||||
def _process_request_data(kwargs_copy, kwargs):
|
||||
"""Helper to process 'data' in request kwargs."""
|
||||
if 'data' in kwargs:
|
||||
if isinstance(kwargs['data'], dict):
|
||||
kwargs_copy['data'] = kwargs['data']
|
||||
elif kwargs['data'] is None:
|
||||
pass
|
||||
elif isinstance(kwargs['data'], str):
|
||||
kwargs_copy['data'] = json.loads(kwargs['data'])
|
||||
else:
|
||||
raise RuntimeError('Expected data to be dict or str, got {0}, data: {1}'.format(type(kwargs['data']), kwargs['data']))
|
||||
|
||||
|
||||
def _process_request_params(kwargs_copy, kwargs, method):
|
||||
"""Helper to process 'params' in request kwargs."""
|
||||
if 'params' in kwargs and method == 'GET':
|
||||
if not kwargs_copy.get('data'):
|
||||
kwargs_copy['data'] = {}
|
||||
if isinstance(kwargs['params'], dict):
|
||||
kwargs_copy['data'].update(kwargs['params'])
|
||||
elif isinstance(kwargs['params'], list):
|
||||
for k, v in kwargs['params']:
|
||||
kwargs_copy['data'][k] = v
|
||||
|
||||
|
||||
def _get_resource_class(resource_module):
|
||||
"""Helper to determine the Ansible module resource class."""
|
||||
if getattr(resource_module, 'ControllerAWXKitModule', None):
|
||||
return resource_module.ControllerAWXKitModule
|
||||
elif getattr(resource_module, 'ControllerAPIModule', None):
|
||||
return resource_module.ControllerAPIModule
|
||||
else:
|
||||
raise RuntimeError("The module has neither a ControllerAWXKitModule or a ControllerAPIModule")
|
||||
|
||||
|
||||
def _get_tower_cli_mgr(new_request):
|
||||
"""Helper to get the appropriate tower_cli mock context manager."""
|
||||
if HAS_TOWER_CLI:
|
||||
return mock.patch('tower_cli.api.Session.request', new=new_request)
|
||||
elif HAS_AWX_KIT:
|
||||
return mock.patch('awxkit.api.client.requests.Session.request', new=new_request)
|
||||
else:
|
||||
return suppress()
|
||||
|
||||
|
||||
def _run_and_capture_module_output(resource_module, stdout_buffer):
|
||||
"""Helper to run the module and capture its stdout."""
|
||||
try:
|
||||
with redirect_stdout(stdout_buffer):
|
||||
resource_module.main()
|
||||
except SystemExit:
|
||||
pass # A system exit indicates successful execution
|
||||
except Exception:
|
||||
# dump the stdout back to console for debugging
|
||||
print(stdout_buffer.getvalue())
|
||||
raise
|
||||
|
||||
|
||||
def _parse_and_handle_module_result(module_stdout):
|
||||
"""Helper to parse module output and handle exceptions."""
|
||||
try:
|
||||
result = json.loads(module_stdout)
|
||||
except Exception as e:
|
||||
raise_from(Exception('Module did not write valid JSON, error: {0}, stdout:\n{1}'.format(str(e), module_stdout)), e)
|
||||
|
||||
if 'exception' in result:
|
||||
if "ModuleNotFoundError: No module named 'tower_cli'" in result['exception']:
|
||||
pytest.skip('The tower-cli library is needed to run this test, module no longer supported.')
|
||||
raise Exception('Module encountered error:\n{0}'.format(result['exception']))
|
||||
return result
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def run_module(request, collection_import):
|
||||
def run_module(request, collection_import, mocker):
|
||||
def rf(module_name, module_params, request_user):
|
||||
|
||||
def new_request(self, method, url, **kwargs):
|
||||
kwargs_copy = kwargs.copy()
|
||||
if 'data' in kwargs:
|
||||
if isinstance(kwargs['data'], dict):
|
||||
kwargs_copy['data'] = kwargs['data']
|
||||
elif kwargs['data'] is None:
|
||||
pass
|
||||
elif isinstance(kwargs['data'], str):
|
||||
kwargs_copy['data'] = json.loads(kwargs['data'])
|
||||
else:
|
||||
raise RuntimeError('Expected data to be dict or str, got {0}, data: {1}'.format(type(kwargs['data']), kwargs['data']))
|
||||
if 'params' in kwargs and method == 'GET':
|
||||
# query params for GET are handled a bit differently by
|
||||
# tower-cli and python requests as opposed to REST framework APIRequestFactory
|
||||
if not kwargs_copy.get('data'):
|
||||
kwargs_copy['data'] = {}
|
||||
if isinstance(kwargs['params'], dict):
|
||||
kwargs_copy['data'].update(kwargs['params'])
|
||||
elif isinstance(kwargs['params'], list):
|
||||
for k, v in kwargs['params']:
|
||||
kwargs_copy['data'][k] = v
|
||||
_process_request_data(kwargs_copy, kwargs)
|
||||
_process_request_params(kwargs_copy, kwargs, method)
|
||||
|
||||
# make request
|
||||
with transaction.atomic():
|
||||
rf = _request(method.lower())
|
||||
django_response = rf(url, user=request_user, expect=None, **kwargs_copy)
|
||||
rf_django = _request(method.lower()) # Renamed rf to avoid conflict with outer rf
|
||||
django_response = rf_django(url, user=request_user, expect=None, **kwargs_copy)
|
||||
|
||||
# requests library response object is different from the Django response, but they are the same concept
|
||||
# this converts the Django response object into a requests response object for consumption
|
||||
@@ -168,58 +224,25 @@ def run_module(request, collection_import):
|
||||
return m
|
||||
|
||||
stdout_buffer = io.StringIO()
|
||||
# Requies specific PYTHONPATH, see docs
|
||||
# Note that a proper Ansiballz explosion of the modules will have an import path like:
|
||||
# ansible_collections.awx.awx.plugins.modules.{}
|
||||
# We should consider supporting that in the future
|
||||
resource_module = collection_import('plugins.modules.{0}'.format(module_name))
|
||||
|
||||
if not isinstance(module_params, dict):
|
||||
raise RuntimeError('Module params must be dict, got {0}'.format(type(module_params)))
|
||||
|
||||
# Ansible params can be passed as an invocation argument or over stdin
|
||||
# this short circuits within the AnsibleModule interface
|
||||
def mock_load_params(self):
|
||||
self.params = module_params
|
||||
|
||||
if getattr(resource_module, 'ControllerAWXKitModule', None):
|
||||
resource_class = resource_module.ControllerAWXKitModule
|
||||
elif getattr(resource_module, 'ControllerAPIModule', None):
|
||||
resource_class = resource_module.ControllerAPIModule
|
||||
else:
|
||||
raise RuntimeError("The module has neither a ControllerAWXKitModule or a ControllerAPIModule")
|
||||
resource_class = _get_resource_class(resource_module)
|
||||
|
||||
with mock.patch.object(resource_class, '_load_params', new=mock_load_params):
|
||||
# Call the test utility (like a mock server) instead of issuing HTTP requests
|
||||
mocker.patch('ansible.module_utils.basic._ANSIBLE_PROFILE', 'legacy')
|
||||
|
||||
with mock.patch('ansible.module_utils.urls.Request.open', new=new_open):
|
||||
if HAS_TOWER_CLI:
|
||||
tower_cli_mgr = mock.patch('tower_cli.api.Session.request', new=new_request)
|
||||
elif HAS_AWX_KIT:
|
||||
tower_cli_mgr = mock.patch('awxkit.api.client.requests.Session.request', new=new_request)
|
||||
else:
|
||||
tower_cli_mgr = suppress()
|
||||
with tower_cli_mgr:
|
||||
try:
|
||||
# Ansible modules return data to the mothership over stdout
|
||||
with redirect_stdout(stdout_buffer):
|
||||
resource_module.main()
|
||||
except SystemExit:
|
||||
pass # A system exit indicates successful execution
|
||||
except Exception:
|
||||
# dump the stdout back to console for debugging
|
||||
print(stdout_buffer.getvalue())
|
||||
raise
|
||||
with _get_tower_cli_mgr(new_request):
|
||||
_run_and_capture_module_output(resource_module, stdout_buffer)
|
||||
|
||||
module_stdout = stdout_buffer.getvalue().strip()
|
||||
try:
|
||||
result = json.loads(module_stdout)
|
||||
except Exception as e:
|
||||
raise_from(Exception('Module did not write valid JSON, error: {0}, stdout:\n{1}'.format(str(e), module_stdout)), e)
|
||||
# A module exception should never be a test expectation
|
||||
if 'exception' in result:
|
||||
if "ModuleNotFoundError: No module named 'tower_cli'" in result['exception']:
|
||||
pytest.skip('The tower-cli library is needed to run this test, module no longer supported.')
|
||||
raise Exception('Module encountered error:\n{0}'.format(result['exception']))
|
||||
result = _parse_and_handle_module_result(module_stdout)
|
||||
return result
|
||||
|
||||
return rf
|
||||
|
||||
Reference in New Issue
Block a user