diff --git a/awx_collection/plugins/modules/tower_project_update.py b/awx_collection/plugins/modules/tower_project_update.py new file mode 100644 index 0000000000..cc32446d65 --- /dev/null +++ b/awx_collection/plugins/modules/tower_project_update.py @@ -0,0 +1,150 @@ +#!/usr/bin/python +# coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +ANSIBLE_METADATA = {'metadata_version': '1.0', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = ''' +--- +module: tower_project_update +author: "Sean Sullivan (@sean-m-sullivan)" +short_description: Update a Project in Ansible Tower +description: + - Update a Ansible Tower Project. See + U(https://www.ansible.com/tower) for an overview. +options: + name: + description: + - The name or id of the project to update. + required: True + type: str + aliases: + - project + organization: + description: + - Organization the project exists in. + - Used to help lookup the object, cannot be modified using this module. + - If not provided, will lookup by name only, which does not work with duplicates. + type: str + wait: + description: + - Wait for the project to update. + default: True + type: bool + interval: + description: + - The interval to request an update from Tower. + required: False + default: 1 + type: float + timeout: + description: + - If waiting for the project to update this will abort after this + amount of seconds + type: int +extends_documentation_fragment: awx.awx.auth +''' + +RETURN = ''' +id: + description: project id of the updated project + returned: success + type: int + sample: 86 +status: + description: status of the updated project + returned: success + type: str + sample: pending +''' + + +EXAMPLES = ''' +- name: Launch a project with a timeout of 10 seconds + tower_project_update: + project: "Networking Project" + timeout: 10 + +- name: Launch a Project with extra_vars without waiting + tower_project_update: + project: "Networking Project" + wait: False +''' + +from ..module_utils.tower_api import TowerAPIModule +import json +import time + + +def main(): + # Any additional arguments that are not fields of the item can be added here + argument_spec = dict( + name=dict(required=True, aliases=['project']), + organization=dict(), + wait=dict(default=True, type='bool'), + interval=dict(default=1.0, type='float'), + timeout=dict(default=None, type='int'), + ) + + # Create a module for ourselves + module = TowerAPIModule(argument_spec=argument_spec) + + # Extract our parameters + name = module.params.get('name') + organization = module.params.get('organization') + wait = module.params.get('wait') + interval = module.params.get('interval') + timeout = module.params.get('timeout') + + # Attempt to look up project based on the provided name or id + if name.isdigit(): + results = module.get_endpoint('projects', **{'data': {'id': name}}) + if results['json']['count'] == 0: + module.fail_json(msg='Could not find Project with ID: {0}'.format(name)) + project = results['json']['results'][0] + else: + lookup_data = {'name': name} + if organization: + lookup_data['organization'] = module.resolve_name_to_id('organizations', organization) + project = module.get_one('projects', data=lookup_data) + if project is None: + module.fail_json(msg="Unable to find project") + + # Update the project + result = module.post_endpoint(project['related']['update']) + + if result['status_code'] != 202: + module.fail_json(msg="Failed to update project, see response for details", response=result) + + module.json_output['changed'] = True + module.json_output['id'] = result['json']['id'] + module.json_output['status'] = result['json']['status'] + + if not wait: + module.exit_json(**module.json_output) + + # Grab our start time to compare against for the timeout + start = time.time() + + if not wait: + module.exit_json(**module.json_output) + + # Invoke wait function + module.wait_on_url( + url=result['json']['url'], + object_name=name, + object_type='Project Update', + timeout=timeout, interval=interval + ) + + module.exit_json(**module.json_output) + + +if __name__ == '__main__': + main() diff --git a/awx_collection/test/awx/test_completeness.py b/awx_collection/test/awx/test_completeness.py index 6154a8c71b..2f1fc80d4d 100644 --- a/awx_collection/test/awx/test_completeness.py +++ b/awx_collection/test/awx/test_completeness.py @@ -15,7 +15,7 @@ import re # Normally a read-only endpoint should not have a module (i.e. /api/v2/me) but sometimes we reuse a name # For example, we have a tower_role module but /api/v2/roles is a read only endpoint. # This list indicates which read-only endpoints have associated modules with them. -read_only_endpoints_with_modules = ['tower_settings', 'tower_role'] +read_only_endpoints_with_modules = ['tower_settings', 'tower_role', 'tower_project_update'] # If a module should not be created for an endpoint and the endpoint is not read-only add it here # THINK HARD ABOUT DOING THIS diff --git a/awx_collection/tests/integration/targets/tower_project_update/tasks/main.yml b/awx_collection/tests/integration/targets/tower_project_update/tasks/main.yml new file mode 100644 index 0000000000..08b9852018 --- /dev/null +++ b/awx_collection/tests/integration/targets/tower_project_update/tasks/main.yml @@ -0,0 +1,66 @@ +--- +- name: Generate a random string for test + set_fact: + test_id: "{{ lookup('password', '/dev/null chars=ascii_letters length=16') }}" + when: test_id is not defined + +- name: Generate names + set_fact: + project_name1: "AWX-Collection-tests-tower_project_update-project-{{ test_id }}" + +- name: Create a git project without credentials without waiting + tower_project: + name: "{{ project_name1 }}" + organization: Default + scm_type: git + scm_url: https://github.com/ansible/test-playbooks + wait: false + register: project_create_result + +- assert: + that: + - project_create_result is changed + +- name: Update a project without waiting + tower_project_update: + name: "{{ project_name1 }}" + organization: Default + wait: false + register: result + +- assert: + that: + - result is changed + +- name: Update a project and wait + tower_project_update: + name: "{{ project_name1 }}" + organization: Default + wait: true + register: result + +- assert: + that: + - result is successful + +- name: Update a project by ID + tower_project_update: + name: "{{ project_create_result.id }}" + organization: Default + wait: true + register: result + +- assert: + that: + - result is successful + +- name: Delete the test project 1 + tower_project: + name: "{{ project_name1 }}" + organization: Default + state: absent + register: result + +- assert: + that: + - result is changed