From 24eae09ed957020b40df33458df5424a5a9829db Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 31 Oct 2019 09:24:41 -0400 Subject: [PATCH] Make tower_inventory_source org optional, add tests --- .../plugins/modules/tower_inventory_source.py | 34 ++++++----- awx_collection/test/awx/conftest.py | 22 +++++++- .../test/awx/test_inventory_source.py | 56 +++++++++++++++++++ 3 files changed, 96 insertions(+), 16 deletions(-) create mode 100644 awx_collection/test/awx/test_inventory_source.py diff --git a/awx_collection/plugins/modules/tower_inventory_source.py b/awx_collection/plugins/modules/tower_inventory_source.py index bfaba29d30..d71fa96ae5 100644 --- a/awx_collection/plugins/modules/tower_inventory_source.py +++ b/awx_collection/plugins/modules/tower_inventory_source.py @@ -40,7 +40,7 @@ options: organization: description: - Organization the inventory belongs to. - reguired: True + type: str source: description: - Types of inventory source. @@ -229,7 +229,7 @@ def main(): overwrite_vars=dict(type='bool', required=False), update_on_launch=dict(type='bool', required=False), update_cache_timeout=dict(type='int', required=False), - organization=dict(required=True), + organization=dict(type='str'), state=dict(choices=['present', 'absent'], default='present'), ) @@ -255,21 +255,25 @@ def main(): if module.params.get('description'): params['description'] = module.params.get('description') - try: - org_res = tower_cli.get_resource('organization') - org = org_res.get(name=organization) - except (exc.NotFound) as excinfo: - module.fail_json( - msg='Failed to get organization,' - 'organization not found: {0}'.format(excinfo), - changed=False - ) + if organization: + try: + org_res = tower_cli.get_resource('organization') + org = org_res.get(name=organization) + except (exc.NotFound) as excinfo: + module.fail_json( + msg='Failed to get organization,' + 'organization not found: {0}'.format(excinfo), + changed=False + ) + org_id = org['id'] + else: + org_id = None # interpreted as not provided if module.params.get('credential'): credential_res = tower_cli.get_resource('credential') try: credential = credential_res.get( - name=module.params.get('credential'), organization=org['id']) + name=module.params.get('credential'), organization=org_id) params['credential'] = credential['id'] except (exc.NotFound) as excinfo: module.fail_json( @@ -282,7 +286,7 @@ def main(): source_project_res = tower_cli.get_resource('project') try: source_project = source_project_res.get( - name=module.params.get('source_project'), organization=org['id']) + name=module.params.get('source_project'), organization=org_id) params['source_project'] = source_project['id'] except (exc.NotFound) as excinfo: module.fail_json( @@ -295,7 +299,7 @@ def main(): source_script_res = tower_cli.get_resource('inventory_script') try: script = source_script_res.get( - name=module.params.get('source_script'), organization=org['id']) + name=module.params.get('source_script'), organization=org_id) params['source_script'] = script['id'] except (exc.NotFound) as excinfo: module.fail_json( @@ -306,7 +310,7 @@ def main(): try: inventory_res = tower_cli.get_resource('inventory') - params['inventory'] = inventory_res.get(name=inventory, organization=org['id'])['id'] + params['inventory'] = inventory_res.get(name=inventory, organization=org_id)['id'] except (exc.NotFound) as excinfo: module.fail_json( msg='Failed to update inventory source, ' diff --git a/awx_collection/test/awx/conftest.py b/awx_collection/test/awx/conftest.py index 3cf7295246..cd0bc847a6 100644 --- a/awx_collection/test/awx/conftest.py +++ b/awx_collection/test/awx/conftest.py @@ -4,6 +4,7 @@ import datetime import importlib from contextlib import redirect_stdout from unittest import mock +import logging from requests.models import Response @@ -13,6 +14,9 @@ from awx.main.tests.functional.conftest import _request from awx.main.models import Organization, Project, Inventory, Credential, CredentialType +logger = logging.getLogger('awx.main.tests') + + def sanitize_dict(din): '''Sanitize Django response data to purge it of internal types so it may be used to cast a requests response object @@ -34,13 +38,22 @@ def sanitize_dict(din): @pytest.fixture -def run_module(): +def run_module(request): def rf(module_name, module_params, request_user): def new_request(self, method, url, **kwargs): kwargs_copy = kwargs.copy() if 'data' in kwargs: kwargs_copy['data'] = json.loads(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 + kwargs_copy.setdefault('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 # make request rf = _request(method.lower()) @@ -53,6 +66,13 @@ def run_module(): sanitize_dict(py_data) resp._content = bytes(json.dumps(django_response.data), encoding='utf8') resp.status_code = django_response.status_code + + if request.config.getoption('verbose') > 0: + logger.info('{} {} by {}, code:{}'.format( + method, '/api/' + url.split('/api/')[1], + request_user.username, resp.status_code + )) + return resp stdout_buffer = io.StringIO() diff --git a/awx_collection/test/awx/test_inventory_source.py b/awx_collection/test/awx/test_inventory_source.py new file mode 100644 index 0000000000..4953a9a8bc --- /dev/null +++ b/awx_collection/test/awx/test_inventory_source.py @@ -0,0 +1,56 @@ +import pytest + +from awx.main.models import Organization, Inventory, InventorySource + + +@pytest.mark.django_db +def test_create_inventory_source_implied_org(run_module, admin_user): + org = Organization.objects.create(name='test-org') + inv = Inventory.objects.create(name='test-inv', organization=org) + + result = run_module('tower_inventory_source', dict( + name='Test Inventory Source', + inventory='test-inv', + source='ec2', + state='present' + ), admin_user) + assert result.pop('changed', None), result + + inv_src = InventorySource.objects.get(name='Test Inventory Source') + assert inv_src.inventory == inv + + result.pop('invocation') + assert result == { + "inventory_source": "Test Inventory Source", + "state": "present", + "id": inv_src.id, + } + + +@pytest.mark.django_db +def test_create_inventory_source_multiple_orgs(run_module, admin_user): + org = Organization.objects.create(name='test-org') + inv = Inventory.objects.create(name='test-inv', organization=org) + + # make another inventory by same name in another org + org2 = Organization.objects.create(name='test-org-number-two') + Inventory.objects.create(name='test-inv', organization=org2) + + result = run_module('tower_inventory_source', dict( + name='Test Inventory Source', + inventory='test-inv', + source='ec2', + organization='test-org', + state='present' + ), admin_user) + assert result.pop('changed', None), result + + inv_src = InventorySource.objects.get(name='Test Inventory Source') + assert inv_src.inventory == inv + + result.pop('invocation') + assert result == { + "inventory_source": "Test Inventory Source", + "state": "present", + "id": inv_src.id, + }