From 1daeec356f31e181ba83d6b12dc0c7b355e63cf1 Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Fri, 20 Mar 2020 10:18:27 -0400 Subject: [PATCH 1/4] Initial converstion of tower_label --- awx_collection/plugins/modules/tower_label.py | 92 +++++++++---------- 1 file changed, 44 insertions(+), 48 deletions(-) diff --git a/awx_collection/plugins/modules/tower_label.py b/awx_collection/plugins/modules/tower_label.py index c9c412b8b2..7e15f0135a 100644 --- a/awx_collection/plugins/modules/tower_label.py +++ b/awx_collection/plugins/modules/tower_label.py @@ -1,6 +1,7 @@ #!/usr/bin/python # coding: utf-8 -*- + # (c) 2017, Wayne Witzel III # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -12,97 +13,92 @@ ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community'} - DOCUMENTATION = ''' --- module: tower_label author: "Wayne Witzel III (@wwitzel3)" version_added: "2.3" -short_description: create, update, or destroy Ansible Tower label. +short_description: create, update, or destroy Ansible Tower labels. description: - Create, update, or destroy Ansible Tower labels. See U(https://www.ansible.com/tower) for an overview. options: name: description: - - Name to use for the label. + - Name of this label. + required: True + type: str + new_name: + description: + - Setting this option will change the existing name (looked up via the name field). required: True type: str organization: description: - - Organization the label should be applied to. + - Organization this label belongs to. required: True type: str - state: + tower_oauthtoken: description: - - Desired state of the resource. - default: "present" - choices: ["present", "absent"] + - The Tower OAuth token to use. + required: False type: str - -requirements: -- ansible-tower-cli >= 3.0.2 - + version_added: "3.7" extends_documentation_fragment: awx.awx.auth +note: Labels can only be created via the Tower API, they can not be deleted. Once they are fully disassociated the API will clean them up on its own. ''' - EXAMPLES = ''' - name: Add label to tower organization tower_label: name: Custom Label organization: My Organization - state: present - tower_config_file: "~/tower_cli.cfg" ''' -from ..module_utils.ansible_tower import TowerModule, tower_auth_config, tower_check_mode - -try: - import tower_cli - import tower_cli.exceptions as exc - - from tower_cli.conf import settings -except ImportError: - pass +from ..module_utils.tower_api import TowerModule def main(): + # Any additional arguments that are not fields of the item can be added here argument_spec = dict( - name=dict(required=True), - organization=dict(required=True), - state=dict(choices=['present', 'absent'], default='present'), + name=dict(required=True, type='str'), + new_name=dict(required=False, type='str'), + organization=dict(required=True, type='str'), ) + # Create a module for ourselves module = TowerModule(argument_spec=argument_spec, supports_check_mode=True) + # Extract our parameters name = module.params.get('name') + new_name = module.params.get("new_name") organization = module.params.get('organization') - state = module.params.get('state') - json_output = {'label': name, 'state': state} + # Attempt to look up the related items the user specified (these will fail the module if not found) + organization_id = None + if organization: + organization_id = module.resolve_name_to_id('organizations', organization) - tower_auth = tower_auth_config(module) - with settings.runtime_values(**tower_auth): - tower_check_mode(module) - label = tower_cli.get_resource('label') + # Attempt to look up an existing item based on the provided data + existing_item = module.get_one('labels', **{ + 'data': { + 'name': name, + 'organization': organization_id, + } + }) - try: - org_res = tower_cli.get_resource('organization') - org = org_res.get(name=organization) + # Create the data that gets sent for create and update + new_fields = {} + new_fields['name'] = new_name if new_name else name + if organization != None: + new_fields['organization'] = organization_id - if state == 'present': - result = label.modify(name=name, organization=org['id'], create_on_missing=True) - json_output['id'] = result['id'] - elif state == 'absent': - result = label.delete(name=name, organization=org['id']) - except (exc.NotFound) as excinfo: - module.fail_json(msg='Failed to update label, organization not found: {0}'.format(excinfo), changed=False) - except (exc.ConnectionError, exc.BadRequest, exc.AuthError) as excinfo: - module.fail_json(msg='Failed to update label: {0}'.format(excinfo), changed=False) - - json_output['changed'] = result['changed'] - module.exit_json(**json_output) + module.create_or_update_if_needed( + existing_item, new_fields, + endpoint='labels', item_type='label', + associations={ + } + ) if __name__ == '__main__': From 8cd4e9b48889c573c8b3485ec882387c5075905a Mon Sep 17 00:00:00 2001 From: John Westcott IV Date: Fri, 20 Mar 2020 19:14:00 -0400 Subject: [PATCH 2/4] Adding state back in --- awx_collection/plugins/modules/tower_label.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/awx_collection/plugins/modules/tower_label.py b/awx_collection/plugins/modules/tower_label.py index 7e15f0135a..c22629c930 100644 --- a/awx_collection/plugins/modules/tower_label.py +++ b/awx_collection/plugins/modules/tower_label.py @@ -38,6 +38,12 @@ options: - Organization this label belongs to. required: True type: str + state: + description: + - Desired state of the resource. + default: "present" + choices: ["present"] + type: str tower_oauthtoken: description: - The Tower OAuth token to use. @@ -64,6 +70,7 @@ def main(): name=dict(required=True, type='str'), new_name=dict(required=False, type='str'), organization=dict(required=True, type='str'), + state=dict(choices=['present', 'absent'], default='present'), ) # Create a module for ourselves From abdcdbca7678cc1a86009c9d5512b23d88854828 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Fri, 20 Mar 2020 20:03:59 -0400 Subject: [PATCH 3/4] Add label tests and flake8 fixes --- awx_collection/plugins/modules/tower_label.py | 10 ++-- awx_collection/test/awx/test_label.py | 47 +++++++++++++++++++ 2 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 awx_collection/test/awx/test_label.py diff --git a/awx_collection/plugins/modules/tower_label.py b/awx_collection/plugins/modules/tower_label.py index c22629c930..848e28431e 100644 --- a/awx_collection/plugins/modules/tower_label.py +++ b/awx_collection/plugins/modules/tower_label.py @@ -22,6 +22,8 @@ short_description: create, update, or destroy Ansible Tower labels. description: - Create, update, or destroy Ansible Tower labels. See U(https://www.ansible.com/tower) for an overview. + - Note, labels can only be created via the Tower API, they can not be deleted. + Once they are fully disassociated the API will clean them up on its own. options: name: description: @@ -31,7 +33,6 @@ options: new_name: description: - Setting this option will change the existing name (looked up via the name field). - required: True type: str organization: description: @@ -42,7 +43,7 @@ options: description: - Desired state of the resource. default: "present" - choices: ["present"] + choices: ["present"] type: str tower_oauthtoken: description: @@ -51,7 +52,6 @@ options: type: str version_added: "3.7" extends_documentation_fragment: awx.awx.auth -note: Labels can only be created via the Tower API, they can not be deleted. Once they are fully disassociated the API will clean them up on its own. ''' EXAMPLES = ''' @@ -70,7 +70,7 @@ def main(): name=dict(required=True, type='str'), new_name=dict(required=False, type='str'), organization=dict(required=True, type='str'), - state=dict(choices=['present', 'absent'], default='present'), + state=dict(choices=['present'], default='present'), ) # Create a module for ourselves @@ -97,7 +97,7 @@ def main(): # Create the data that gets sent for create and update new_fields = {} new_fields['name'] = new_name if new_name else name - if organization != None: + if organization: new_fields['organization'] = organization_id module.create_or_update_if_needed( diff --git a/awx_collection/test/awx/test_label.py b/awx_collection/test/awx/test_label.py new file mode 100644 index 0000000000..9ede40f3aa --- /dev/null +++ b/awx_collection/test/awx/test_label.py @@ -0,0 +1,47 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest + +from awx.main.models import Label + + +@pytest.mark.django_db +def test_create_label(run_module, admin_user, organization): + result = run_module('tower_label', dict( + name='test-label', + organization=organization.name + ), admin_user) + assert not result.get('failed'), result.get('msg', result) + assert result.get('changed', False) + + assert Label.objects.get(name='test-label').organization == organization + + +@pytest.mark.django_db +def test_create_label_using_org_id(run_module, admin_user, organization): + result = run_module('tower_label', dict( + name='test-label', + organization=organization.id + ), admin_user) + assert not result.get('failed'), result.get('msg', result) + assert result.get('changed', False) + + assert Label.objects.get(name='test-label').organization == organization + + +@pytest.mark.django_db +def test_modify_label(run_module, admin_user, organization): + label = Label.objects.create(name='test-label', organization=organization) + + result = run_module('tower_label', dict( + name='test-label', + new_name='renamed-label', + organization=organization.name + ), admin_user) + assert not result.get('failed'), result.get('msg', result) + assert result.get('changed', False) + + label.refresh_from_db() + assert label.organization == organization + assert label.name == 'renamed-label' From 4ea110147705c3fa1179fec04dac8c5bf818e0e3 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Fri, 20 Mar 2020 23:49:15 -0400 Subject: [PATCH 4/4] update test assertion --- .../tests/integration/targets/tower_label/tasks/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx_collection/tests/integration/targets/tower_label/tasks/main.yml b/awx_collection/tests/integration/targets/tower_label/tasks/main.yml index a41e602c80..d354376cb7 100644 --- a/awx_collection/tests/integration/targets/tower_label/tasks/main.yml +++ b/awx_collection/tests/integration/targets/tower_label/tasks/main.yml @@ -19,6 +19,6 @@ - assert: that: - - "result.msg == 'Failed to update label, organization not found: The requested object could not be found.'" + - "'Non existing org was not found on the Tower server' in result.msg" # TODO: Deleting labels doesn't seem to work currently