diff --git a/awx_collection/plugins/module_utils/controller_api.py b/awx_collection/plugins/module_utils/controller_api.py index 0a677be6eb..ef11a74bec 100644 --- a/awx_collection/plugins/module_utils/controller_api.py +++ b/awx_collection/plugins/module_utils/controller_api.py @@ -47,35 +47,14 @@ class ItemNotDefined(Exception): class ControllerModule(AnsibleModule): url = None AUTH_ARGSPEC = dict( - controller_host=dict( - required=False, - aliases=['tower_host'], - fallback=(env_fallback, ['CONTROLLER_HOST', 'TOWER_HOST'])), - controller_username=dict( - required=False, - aliases=['tower_username'], - fallback=(env_fallback, ['CONTROLLER_USERNAME', 'TOWER_USERNAME'])), - controller_password=dict( - no_log=True, - aliases=['tower_password'], - required=False, - fallback=(env_fallback, ['CONTROLLER_PASSWORD', 'TOWER_PASSWORD'])), - validate_certs=dict( - type='bool', - aliases=['tower_verify_ssl'], - required=False, - fallback=(env_fallback, ['CONTROLLER_VERIFY_SSL', 'TOWER_VERIFY_SSL'])), + controller_host=dict(required=False, aliases=['tower_host'], fallback=(env_fallback, ['CONTROLLER_HOST', 'TOWER_HOST'])), + controller_username=dict(required=False, aliases=['tower_username'], fallback=(env_fallback, ['CONTROLLER_USERNAME', 'TOWER_USERNAME'])), + controller_password=dict(no_log=True, aliases=['tower_password'], required=False, fallback=(env_fallback, ['CONTROLLER_PASSWORD', 'TOWER_PASSWORD'])), + validate_certs=dict(type='bool', aliases=['tower_verify_ssl'], required=False, fallback=(env_fallback, ['CONTROLLER_VERIFY_SSL', 'TOWER_VERIFY_SSL'])), controller_oauthtoken=dict( - type='raw', - no_log=True, - aliases=['tower_oauthtoken'], - required=False, - fallback=(env_fallback, ['CONTROLLER_OAUTH_TOKEN', 'TOWER_OAUTH_TOKEN'])), - controller_config_file=dict( - type='path', - aliases=['tower_config_file'], - required=False, - default=None), + type='raw', no_log=True, aliases=['tower_oauthtoken'], required=False, fallback=(env_fallback, ['CONTROLLER_OAUTH_TOKEN', 'TOWER_OAUTH_TOKEN']) + ), + controller_config_file=dict(type='path', aliases=['tower_config_file'], required=False, default=None), ) short_params = { 'host': 'controller_host', @@ -320,9 +299,7 @@ class ControllerAPIModule(ControllerModule): def __init__(self, argument_spec, direct_params=None, error_callback=None, warn_callback=None, **kwargs): kwargs['supports_check_mode'] = True - super().__init__( - argument_spec=argument_spec, direct_params=direct_params, error_callback=error_callback, warn_callback=warn_callback, **kwargs - ) + super().__init__(argument_spec=argument_spec, direct_params=direct_params, error_callback=error_callback, warn_callback=warn_callback, **kwargs) self.session = Request(cookies=CookieJar(), validate_certs=self.verify_ssl) if 'update_secrets' in self.params: @@ -400,7 +377,7 @@ class ControllerAPIModule(ControllerModule): response['json']['next'] = next_page return response - def get_one(self, endpoint, name_or_id=None, allow_none=True, **kwargs): + def get_one(self, endpoint, name_or_id=None, allow_none=True, check_exists=False, **kwargs): new_kwargs = kwargs.copy() if name_or_id: name_field = self.get_name_field_from_endpoint(endpoint) @@ -441,6 +418,11 @@ class ControllerAPIModule(ControllerModule): # Or we weren't running with a or search and just got back too many to begin with. self.fail_wanted_one(response, endpoint, new_kwargs.get('data')) + if check_exists: + name_field = self.get_name_field_from_endpoint(endpoint) + self.json_output['id'] = response['json']['results'][0]['id'] + self.exit_json(**self.json_output) + return response['json']['results'][0] def fail_wanted_one(self, response, endpoint, query_params): @@ -448,7 +430,8 @@ class ControllerAPIModule(ControllerModule): if len(sample['json']['results']) > 1: sample['json']['results'] = sample['json']['results'][:2] + ['...more results snipped...'] url = self.build_url(endpoint, query_params) - display_endpoint = url.geturl()[len(self.host):] # truncate to not include the base URL + host_length = len(self.host) + display_endpoint = url.geturl()[host_length:] # truncate to not include the base URL self.fail_json( msg="Request to {0} returned {1} items, expected 1".format(display_endpoint, response['json']['count']), query=query_params, @@ -970,11 +953,7 @@ class ControllerAPIModule(ControllerModule): # Attempt to delete our current token from /api/v2/tokens/ # Post to the tokens endpoint with baisc auth to try and get a token endpoint = self.url_prefix.rstrip('/') + '/api/v2/tokens/{0}/'.format(self.oauth_token_id) - api_token_url = ( - self.url._replace( - path=endpoint, query=None # in error cases, fail_json exists before exception handling - ) - ).geturl() + api_token_url = (self.url._replace(path=endpoint, query=None)).geturl() # in error cases, fail_json exists before exception handling try: self.session.open( diff --git a/awx_collection/plugins/modules/application.py b/awx_collection/plugins/modules/application.py index e35e99dfe6..e4101711ee 100644 --- a/awx_collection/plugins/modules/application.py +++ b/awx_collection/plugins/modules/application.py @@ -60,7 +60,7 @@ options: description: - Desired state of the resource. default: "present" - choices: ["present", "absent"] + choices: ["present", "absent", "exists"] type: str skip_authorization: description: @@ -106,7 +106,7 @@ def main(): client_type=dict(choices=['public', 'confidential']), organization=dict(required=True), redirect_uris=dict(type="list", elements='str'), - state=dict(choices=['present', 'absent'], default='present'), + state=dict(choices=['present', 'absent', 'exists'], default='present'), skip_authorization=dict(type='bool'), ) @@ -127,7 +127,7 @@ def main(): org_id = module.resolve_name_to_id('organizations', organization) # Attempt to look up application based on the provided name and org ID - application = module.get_one('applications', name_or_id=name, **{'data': {'organization': org_id}}) + application = module.get_one('applications', name_or_id=name, check_exists=(state == 'exists'), **{'data': {'organization': org_id}}) if state == 'absent': # If the state was absent we can let the module delete it if needed, the module will handle exiting from this diff --git a/awx_collection/plugins/modules/credential.py b/awx_collection/plugins/modules/credential.py index 2f037f68dc..3a278b4a6f 100644 --- a/awx_collection/plugins/modules/credential.py +++ b/awx_collection/plugins/modules/credential.py @@ -247,7 +247,7 @@ def main(): if organization: lookup_data['organization'] = org_id - credential = module.get_one('credentials', name_or_id=name, **{'data': lookup_data}) + credential = module.get_one('credentials', name_or_id=name, check_exists=(state == 'exists'), **{'data': lookup_data}) # Attempt to look up credential to copy based on the provided name if copy_from: @@ -265,10 +265,6 @@ def main(): # If the state was absent we can let the module delete it if needed, the module will handle exiting from this module.delete_if_needed(credential) - if state == 'exists' and credential is not None: - # If credential exists and state is exists, we're done here. - module.exit_json(**module.json_output) - # Attempt to look up the related items the user specified (these will fail the module if not found) if user: user_id = module.resolve_name_to_id('users', user) diff --git a/awx_collection/plugins/modules/credential_input_source.py b/awx_collection/plugins/modules/credential_input_source.py index c7ace4f726..132a0c08ae 100644 --- a/awx_collection/plugins/modules/credential_input_source.py +++ b/awx_collection/plugins/modules/credential_input_source.py @@ -48,7 +48,7 @@ options: state: description: - Desired state of the resource. - choices: ["present", "absent"] + choices: ["present", "absent", "exists"] default: "present" type: str @@ -80,7 +80,7 @@ def main(): target_credential=dict(required=True), source_credential=dict(), metadata=dict(type="dict"), - state=dict(choices=['present', 'absent'], default='present'), + state=dict(choices=['present', 'absent', 'exists'], default='present'), ) # Create a module for ourselves @@ -101,7 +101,7 @@ def main(): 'target_credential': target_credential_id, 'input_field_name': input_field_name, } - credential_input_source = module.get_one('credential_input_sources', **{'data': lookup_data}) + credential_input_source = module.get_one('credential_input_sources', check_exists=(state == 'exists'), **{'data': lookup_data}) if state == 'absent': module.delete_if_needed(credential_input_source) diff --git a/awx_collection/plugins/modules/credential_type.py b/awx_collection/plugins/modules/credential_type.py index f6b56d0ea8..9407eaac38 100644 --- a/awx_collection/plugins/modules/credential_type.py +++ b/awx_collection/plugins/modules/credential_type.py @@ -59,7 +59,7 @@ options: description: - Desired state of the resource. default: "present" - choices: ["present", "absent"] + choices: ["present", "absent", "exists"] type: str extends_documentation_fragment: awx.awx.auth ''' @@ -98,7 +98,7 @@ def main(): kind=dict(choices=list(KIND_CHOICES.keys())), inputs=dict(type='dict'), injectors=dict(type='dict'), - state=dict(choices=['present', 'absent'], default='present'), + state=dict(choices=['present', 'absent', 'exists'], default='present'), ) # Create a module for ourselves @@ -124,7 +124,7 @@ def main(): credential_type_params['injectors'] = module.params.get('injectors') # Attempt to look up credential_type based on the provided name - credential_type = module.get_one('credential_types', name_or_id=name) + credential_type = module.get_one('credential_types', name_or_id=name, check_exists=(state == 'exists')) if state == 'absent': # If the state was absent we can let the module delete it if needed, the module will handle exiting from this diff --git a/awx_collection/plugins/modules/execution_environment.py b/awx_collection/plugins/modules/execution_environment.py index 552c805720..97fb69c78c 100644 --- a/awx_collection/plugins/modules/execution_environment.py +++ b/awx_collection/plugins/modules/execution_environment.py @@ -50,7 +50,7 @@ options: state: description: - Desired state of the resource. - choices: ["present", "absent"] + choices: ["present", "absent", "exists"] default: "present" type: str pull: @@ -83,7 +83,7 @@ def main(): description=dict(), organization=dict(), credential=dict(), - state=dict(choices=['present', 'absent'], default='present'), + state=dict(choices=['present', 'absent', 'exists'], default='present'), # NOTE: Default for pull differs from API (which is blank by default) pull=dict(choices=['always', 'missing', 'never'], default='missing'), ) @@ -99,7 +99,7 @@ def main(): state = module.params.get('state') pull = module.params.get('pull') - existing_item = module.get_one('execution_environments', name_or_id=name) + existing_item = module.get_one('execution_environments', name_or_id=name, check_exists=(state == 'exists')) if state == 'absent': module.delete_if_needed(existing_item) diff --git a/awx_collection/plugins/modules/group.py b/awx_collection/plugins/modules/group.py index c91bf164d9..d594f59f60 100644 --- a/awx_collection/plugins/modules/group.py +++ b/awx_collection/plugins/modules/group.py @@ -67,7 +67,7 @@ options: description: - Desired state of the resource. default: "present" - choices: ["present", "absent"] + choices: ["present", "absent", "exists"] type: str new_name: description: @@ -115,7 +115,7 @@ def main(): children=dict(type='list', elements='str', aliases=['groups']), preserve_existing_hosts=dict(type='bool', default=False), preserve_existing_children=dict(type='bool', default=False, aliases=['preserve_existing_groups']), - state=dict(choices=['present', 'absent'], default='present'), + state=dict(choices=['present', 'absent', 'exists'], default='present'), ) # Create a module for ourselves @@ -135,7 +135,7 @@ def main(): inventory_id = module.resolve_name_to_id('inventories', inventory) # Attempt to look up the object based on the provided name and inventory ID - group = module.get_one('groups', name_or_id=name, **{'data': {'inventory': inventory_id}}) + group = module.get_one('groups', name_or_id=name, check_exists=(state == 'exists'), **{'data': {'inventory': inventory_id}}) if state == 'absent': # If the state was absent we can let the module delete it if needed, the module will handle exiting from this diff --git a/awx_collection/plugins/modules/host.py b/awx_collection/plugins/modules/host.py index 21d063f39e..7bdc6bf8ce 100644 --- a/awx_collection/plugins/modules/host.py +++ b/awx_collection/plugins/modules/host.py @@ -50,7 +50,7 @@ options: state: description: - Desired state of the resource. - choices: ["present", "absent"] + choices: ["present", "absent", "exists"] default: "present" type: str extends_documentation_fragment: awx.awx.auth @@ -83,7 +83,7 @@ def main(): inventory=dict(required=True), enabled=dict(type='bool'), variables=dict(type='dict'), - state=dict(choices=['present', 'absent'], default='present'), + state=dict(choices=['present', 'absent', 'exists'], default='present'), ) # Create a module for ourselves @@ -102,7 +102,7 @@ def main(): inventory_id = module.resolve_name_to_id('inventories', inventory) # Attempt to look up host based on the provided name and inventory ID - host = module.get_one('hosts', name_or_id=name, **{'data': {'inventory': inventory_id}}) + host = module.get_one('hosts', name_or_id=name, check_exists=(state == 'exists'), **{'data': {'inventory': inventory_id}}) if state == 'absent': # If the state was absent we can let the module delete it if needed, the module will handle exiting from this diff --git a/awx_collection/plugins/modules/instance_group.py b/awx_collection/plugins/modules/instance_group.py index dc993f8b5b..e9d3a9fa53 100644 --- a/awx_collection/plugins/modules/instance_group.py +++ b/awx_collection/plugins/modules/instance_group.py @@ -81,7 +81,7 @@ options: state: description: - Desired state of the resource. - choices: ["present", "absent"] + choices: ["present", "absent", "exists"] default: "present" type: str extends_documentation_fragment: awx.awx.auth @@ -107,7 +107,7 @@ def main(): policy_instance_list=dict(type='list', elements='str'), pod_spec_override=dict(), instances=dict(required=False, type="list", elements='str'), - state=dict(choices=['present', 'absent'], default='present'), + state=dict(choices=['present', 'absent', 'exists'], default='present'), ) # Create a module for ourselves @@ -128,7 +128,7 @@ def main(): state = module.params.get('state') # Attempt to look up an existing item based on the provided data - existing_item = module.get_one('instance_groups', name_or_id=name) + existing_item = module.get_one('instance_groups', name_or_id=name, check_exists=(state == 'exists')) if state == 'absent': # If the state was absent we can let the module delete it if needed, the module will handle exiting from this diff --git a/awx_collection/plugins/modules/inventory.py b/awx_collection/plugins/modules/inventory.py index 3de24bf1ba..f1f0b36a49 100644 --- a/awx_collection/plugins/modules/inventory.py +++ b/awx_collection/plugins/modules/inventory.py @@ -78,7 +78,7 @@ options: description: - Desired state of the resource. default: "present" - choices: ["present", "absent"] + choices: ["present", "absent", "exists"] type: str extends_documentation_fragment: awx.awx.auth ''' @@ -149,7 +149,7 @@ def main(): host_filter=dict(), instance_groups=dict(type="list", elements='str'), prevent_instance_group_fallback=dict(type='bool'), - state=dict(choices=['present', 'absent'], default='present'), + state=dict(choices=['present', 'absent', 'exists'], default='present'), input_inventories=dict(type='list', elements='str'), ) @@ -172,7 +172,7 @@ def main(): org_id = module.resolve_name_to_id('organizations', organization) # Attempt to look up inventory based on the provided name and org ID - inventory = module.get_one('inventories', name_or_id=name, **{'data': {'organization': org_id}}) + inventory = module.get_one('inventories', name_or_id=name, check_exists=(state == 'exists'), **{'data': {'organization': org_id}}) # Attempt to look up credential to copy based on the provided name if copy_from: diff --git a/awx_collection/plugins/modules/inventory_source.py b/awx_collection/plugins/modules/inventory_source.py index 04ee423a3b..538e866bbd 100644 --- a/awx_collection/plugins/modules/inventory_source.py +++ b/awx_collection/plugins/modules/inventory_source.py @@ -118,7 +118,7 @@ options: description: - Desired state of the resource. default: "present" - choices: ["present", "absent"] + choices: ["present", "absent", "exists"] type: str notification_templates_started: description: @@ -192,7 +192,7 @@ def main(): notification_templates_started=dict(type="list", elements='str'), notification_templates_success=dict(type="list", elements='str'), notification_templates_error=dict(type="list", elements='str'), - state=dict(choices=['present', 'absent'], default='present'), + state=dict(choices=['present', 'absent', 'exists'], default='present'), ) # Create a module for ourselves @@ -219,6 +219,7 @@ def main(): inventory_source_object = module.get_one( 'inventory_sources', name_or_id=name, + check_exists=(state == 'exists'), **{ 'data': { 'inventory': inventory_object['id'], diff --git a/awx_collection/plugins/modules/job_template.py b/awx_collection/plugins/modules/job_template.py index ef4b2d8aca..5e2c6455f6 100644 --- a/awx_collection/plugins/modules/job_template.py +++ b/awx_collection/plugins/modules/job_template.py @@ -295,7 +295,7 @@ options: description: - Desired state of the resource. default: "present" - choices: ["present", "absent"] + choices: ["present", "absent", "exists"] type: str notification_templates_started: description: @@ -444,7 +444,7 @@ def main(): notification_templates_success=dict(type="list", elements='str'), notification_templates_error=dict(type="list", elements='str'), prevent_instance_group_fallback=dict(type="bool"), - state=dict(choices=['present', 'absent'], default='present'), + state=dict(choices=['present', 'absent', 'exists'], default='present'), ) # Create a module for ourselves @@ -484,7 +484,7 @@ def main(): new_fields['execution_environment'] = module.resolve_name_to_id('execution_environments', ee) # Attempt to look up an existing item based on the provided data - existing_item = module.get_one('job_templates', name_or_id=name, **{'data': search_fields}) + existing_item = module.get_one('job_templates', name_or_id=name, check_exists=(state == 'exists'), **{'data': search_fields}) # Attempt to look up credential to copy based on the provided name if copy_from: diff --git a/awx_collection/plugins/modules/label.py b/awx_collection/plugins/modules/label.py index b17d58beec..026d37f44c 100644 --- a/awx_collection/plugins/modules/label.py +++ b/awx_collection/plugins/modules/label.py @@ -41,7 +41,7 @@ options: description: - Desired state of the resource. default: "present" - choices: ["present"] + choices: ["present", "exists"] type: str extends_documentation_fragment: awx.awx.auth ''' @@ -62,7 +62,7 @@ def main(): name=dict(required=True), new_name=dict(), organization=dict(required=True), - state=dict(choices=['present'], default='present'), + state=dict(choices=['present', 'exists'], default='present'), ) # Create a module for ourselves @@ -72,6 +72,7 @@ def main(): name = module.params.get('name') new_name = module.params.get("new_name") organization = module.params.get('organization') + state = module.params.get("state") # Attempt to look up the related items the user specified (these will fail the module if not found) organization_id = None @@ -82,6 +83,7 @@ def main(): existing_item = module.get_one( 'labels', name_or_id=name, + check_exists=(state == 'exists'), **{ 'data': { 'organization': organization_id, diff --git a/awx_collection/plugins/modules/notification_template.py b/awx_collection/plugins/modules/notification_template.py index 6da77c6599..3ad2ed282f 100644 --- a/awx_collection/plugins/modules/notification_template.py +++ b/awx_collection/plugins/modules/notification_template.py @@ -97,7 +97,7 @@ options: description: - Desired state of the resource. default: "present" - choices: ["present", "absent"] + choices: ["present", "absent", "exists"] type: str extends_documentation_fragment: awx.awx.auth ''' @@ -222,7 +222,7 @@ def main(): notification_type=dict(choices=['email', 'grafana', 'irc', 'mattermost', 'pagerduty', 'rocketchat', 'slack', 'twilio', 'webhook']), notification_configuration=dict(type='dict'), messages=dict(type='dict'), - state=dict(choices=['present', 'absent'], default='present'), + state=dict(choices=['present', 'absent', 'exists'], default='present'), ) # Create a module for ourselves @@ -248,6 +248,7 @@ def main(): existing_item = module.get_one( 'notification_templates', name_or_id=name, + check_exists=(state == 'exists'), **{ 'data': { 'organization': organization_id, diff --git a/awx_collection/plugins/modules/organization.py b/awx_collection/plugins/modules/organization.py index de78eb228b..a32a66c733 100644 --- a/awx_collection/plugins/modules/organization.py +++ b/awx_collection/plugins/modules/organization.py @@ -52,7 +52,7 @@ options: description: - Desired state of the resource. default: "present" - choices: ["present", "absent"] + choices: ["present", "absent", "exists"] type: str instance_groups: description: @@ -130,7 +130,7 @@ def main(): notification_templates_error=dict(type="list", elements='str'), notification_templates_approvals=dict(type="list", elements='str'), galaxy_credentials=dict(type="list", elements='str'), - state=dict(choices=['present', 'absent'], default='present'), + state=dict(choices=['present', 'absent', 'exists'], default='present'), ) # Create a module for ourselves @@ -146,7 +146,7 @@ def main(): state = module.params.get('state') # Attempt to look up organization based on the provided name - organization = module.get_one('organizations', name_or_id=name) + organization = module.get_one('organizations', name_or_id=name, check_exists=(state == 'exists')) if state == 'absent': # If the state was absent we can let the module delete it if needed, the module will handle exiting from this diff --git a/awx_collection/plugins/modules/project.py b/awx_collection/plugins/modules/project.py index 877340a111..f3a9ac9d6f 100644 --- a/awx_collection/plugins/modules/project.py +++ b/awx_collection/plugins/modules/project.py @@ -122,7 +122,7 @@ options: description: - Desired state of the resource. default: "present" - choices: ["present", "absent"] + choices: ["present", "absent", "exists"] type: str wait: description: @@ -272,7 +272,7 @@ def main(): notification_templates_started=dict(type="list", elements='str'), notification_templates_success=dict(type="list", elements='str'), notification_templates_error=dict(type="list", elements='str'), - state=dict(choices=['present', 'absent'], default='present'), + state=dict(choices=['present', 'absent', 'exists'], default='present'), wait=dict(type='bool', default=True), update_project=dict(default=False, type='bool'), interval=dict(default=2.0, type='float'), @@ -313,7 +313,7 @@ def main(): lookup_data['organization'] = org_id # Attempt to look up project based on the provided name and org ID - project = module.get_one('projects', name_or_id=name, data=lookup_data) + project = module.get_one('projects', name_or_id=name, check_exists=(state == 'exists'), data=lookup_data) # Attempt to look up credential to copy based on the provided name if copy_from: diff --git a/awx_collection/plugins/modules/schedule.py b/awx_collection/plugins/modules/schedule.py index 5bf821025d..7a6a184413 100644 --- a/awx_collection/plugins/modules/schedule.py +++ b/awx_collection/plugins/modules/schedule.py @@ -146,7 +146,7 @@ options: state: description: - Desired state of the resource. - choices: ["present", "absent"] + choices: ["present", "absent", "exists"] default: "present" type: str extends_documentation_fragment: awx.awx.auth @@ -220,7 +220,7 @@ def main(): unified_job_template=dict(), organization=dict(), enabled=dict(type='bool'), - state=dict(choices=['present', 'absent'], default='present'), + state=dict(choices=['present', 'absent', 'exists'], default='present'), ) # Create a module for ourselves @@ -265,8 +265,13 @@ def main(): search_fields['name'] = unified_job_template unified_job_template_id = module.get_one('unified_job_templates', **{'data': search_fields})['id'] sched_search_fields['unified_job_template'] = unified_job_template_id + # Attempt to look up an existing item based on the provided data - existing_item = module.get_one('schedules', name_or_id=name, **{'data': sched_search_fields}) + existing_item = module.get_one('schedules', name_or_id=name, check_exists=(state == 'exists'), **{'data': sched_search_fields}) + + if state == 'absent': + # If the state was absent we can let the module delete it if needed, the module will handle exiting from this + module.delete_if_needed(existing_item) association_fields = {} @@ -343,18 +348,14 @@ def main(): else: new_fields['execution_environment'] = ee['id'] - if state == 'absent': - # If the state was absent we can let the module delete it if needed, the module will handle exiting from this - module.delete_if_needed(existing_item) - elif state == 'present': - # If the state was present and we can let the module build or update the existing item, this will return on its own - module.create_or_update_if_needed( - existing_item, - new_fields, - endpoint='schedules', - item_type='schedule', - associations=association_fields, - ) + # If the state was present and we can let the module build or update the existing item, this will return on its own + module.create_or_update_if_needed( + existing_item, + new_fields, + endpoint='schedules', + item_type='schedule', + associations=association_fields, + ) if __name__ == '__main__': diff --git a/awx_collection/plugins/modules/team.py b/awx_collection/plugins/modules/team.py index 5482b4c850..12b91d3170 100644 --- a/awx_collection/plugins/modules/team.py +++ b/awx_collection/plugins/modules/team.py @@ -42,7 +42,7 @@ options: state: description: - Desired state of the resource. - choices: ["present", "absent"] + choices: ["present", "absent", "exists"] default: "present" type: str extends_documentation_fragment: awx.awx.auth @@ -69,7 +69,7 @@ def main(): new_name=dict(), description=dict(), organization=dict(required=True), - state=dict(choices=['present', 'absent'], default='present'), + state=dict(choices=['present', 'absent', 'exists'], default='present'), ) # Create a module for ourselves @@ -86,7 +86,7 @@ def main(): org_id = module.resolve_name_to_id('organizations', organization) # Attempt to look up team based on the provided name and org ID - team = module.get_one('teams', name_or_id=name, **{'data': {'organization': org_id}}) + team = module.get_one('teams', name_or_id=name, check_exists=(state == 'exists'), **{'data': {'organization': org_id}}) if state == 'absent': # If the state was absent we can let the module delete it if needed, the module will handle exiting from this diff --git a/awx_collection/plugins/modules/user.py b/awx_collection/plugins/modules/user.py index 49a6f216a6..57f783c9cc 100644 --- a/awx_collection/plugins/modules/user.py +++ b/awx_collection/plugins/modules/user.py @@ -69,7 +69,7 @@ options: state: description: - Desired state of the resource. - choices: ["present", "absent"] + choices: ["present", "absent", "exists"] default: "present" type: str extends_documentation_fragment: awx.awx.auth @@ -137,7 +137,7 @@ def main(): password=dict(no_log=True), update_secrets=dict(type='bool', default=True, no_log=False), organization=dict(), - state=dict(choices=['present', 'absent'], default='present'), + state=dict(choices=['present', 'absent', 'exists'], default='present'), ) # Create a module for ourselves @@ -158,7 +158,7 @@ def main(): # Attempt to look up the related items the user specified (these will fail the module if not found) # Attempt to look up an existing item based on the provided data - existing_item = module.get_one('users', name_or_id=username) + existing_item = module.get_one('users', name_or_id=username, check_exists=(state == 'exists')) if state == 'absent': # If the state was absent we can let the module delete it if needed, the module will handle exiting from this diff --git a/awx_collection/plugins/modules/workflow_job_template.py b/awx_collection/plugins/modules/workflow_job_template.py index db9c646fda..27748ed988 100644 --- a/awx_collection/plugins/modules/workflow_job_template.py +++ b/awx_collection/plugins/modules/workflow_job_template.py @@ -144,6 +144,7 @@ options: choices: - present - absent + - exists default: "present" type: str notification_templates_started: @@ -667,8 +668,7 @@ def create_workflow_nodes(module, response, workflow_nodes, workflow_id): inv_lookup_data = {} if 'organization' in workflow_node['inventory']: inv_lookup_data['organization'] = module.resolve_name_to_id('organizations', workflow_node['inventory']['organization']['name']) - workflow_node_fields['inventory'] = module.get_one( - 'inventories', name_or_id=workflow_node['inventory']['name'], data=inv_lookup_data)['id'] + workflow_node_fields['inventory'] = module.get_one('inventories', name_or_id=workflow_node['inventory']['name'], data=inv_lookup_data)['id'] else: workflow_node_fields['inventory'] = module.get_one('inventories', name_or_id=workflow_node['inventory'])['id'] @@ -843,7 +843,7 @@ def main(): notification_templates_approvals=dict(type="list", elements='str'), workflow_nodes=dict(type='list', elements='dict', aliases=['schema']), destroy_current_nodes=dict(type='bool', default=False, aliases=['destroy_current_schema']), - state=dict(choices=['present', 'absent'], default='present'), + state=dict(choices=['present', 'absent', 'exists'], default='present'), ) # Create a module for ourselves @@ -871,7 +871,7 @@ def main(): search_fields['organization'] = new_fields['organization'] = organization_id # Attempt to look up an existing item based on the provided data - existing_item = module.get_one('workflow_job_templates', name_or_id=name, **{'data': search_fields}) + existing_item = module.get_one('workflow_job_templates', name_or_id=name, check_exists=(state == 'exists'), **{'data': search_fields}) # Attempt to look up credential to copy based on the provided name if copy_from: diff --git a/awx_collection/plugins/modules/workflow_job_template_node.py b/awx_collection/plugins/modules/workflow_job_template_node.py index a59cd33f9c..747ebbeeb3 100644 --- a/awx_collection/plugins/modules/workflow_job_template_node.py +++ b/awx_collection/plugins/modules/workflow_job_template_node.py @@ -179,7 +179,7 @@ options: state: description: - Desired state of the resource. - choices: ["present", "absent"] + choices: ["present", "absent", "exists"] default: "present" type: str extends_documentation_fragment: awx.awx.auth @@ -285,7 +285,7 @@ def main(): job_slice_count=dict(type='int'), labels=dict(type='list', elements='str'), timeout=dict(type='int'), - state=dict(choices=['present', 'absent'], default='present'), + state=dict(choices=['present', 'absent', 'exists'], default='present'), ) mutually_exclusive = [("unified_job_template", "approval_node")] required_if = [ @@ -327,7 +327,7 @@ def main(): search_fields['workflow_job_template'] = new_fields['workflow_job_template'] = workflow_job_template_id # Attempt to look up an existing item based on the provided data - existing_item = module.get_one('workflow_job_template_nodes', **{'data': search_fields}) + existing_item = module.get_one('workflow_job_template_nodes', check_exists=(state == 'exists'), **{'data': search_fields}) if state == 'absent': # If the state was absent we can let the module delete it if needed, the module will handle exiting from this diff --git a/awx_collection/tests/integration/targets/application/tasks/main.yml b/awx_collection/tests/integration/targets/application/tasks/main.yml index 54fbe47b34..864d4d0ac2 100644 --- a/awx_collection/tests/integration/targets/application/tasks/main.yml +++ b/awx_collection/tests/integration/targets/application/tasks/main.yml @@ -24,6 +24,43 @@ that: - "result is changed" + - name: Run an application with exists + application: + name: "{{ app1_name }}" + authorization_grant_type: "password" + client_type: "public" + organization: "Default" + state: exists + register: result + + - assert: + that: + - "result is not changed" + + - name: Delete our application + application: + name: "{{ app1_name }}" + organization: "Default" + state: absent + register: result + + - assert: + that: + - "result is changed" + + - name: Run an application with exists + application: + name: "{{ app1_name }}" + authorization_grant_type: "password" + client_type: "public" + organization: "Default" + state: exists + register: result + + - assert: + that: + - "result is changed" + - name: Delete our application application: name: "{{ app1_name }}" diff --git a/awx_collection/tests/integration/targets/credential/tasks/main.yml b/awx_collection/tests/integration/targets/credential/tasks/main.yml index 42d22d8bc7..428eab5f8b 100644 --- a/awx_collection/tests/integration/targets/credential/tasks/main.yml +++ b/awx_collection/tests/integration/targets/credential/tasks/main.yml @@ -47,6 +47,42 @@ that: - "result is changed" +- name: Create an Org-specific credential with an ID with exists + credential: + name: "{{ ssh_cred_name1 }}" + organization: Default + credential_type: Machine + state: exists + register: result + +- assert: + that: + - "result is not changed" + +- name: Delete an Org-specific credential with an ID + credential: + name: "{{ ssh_cred_name1 }}" + organization: Default + credential_type: Machine + state: absent + register: result + +- assert: + that: + - "result is changed" + +- name: Create an Org-specific credential with an ID with exists + credential: + name: "{{ ssh_cred_name1 }}" + organization: Default + credential_type: Machine + state: exists + register: result + +- assert: + that: + - "result is changed" + - name: Delete a Org-specific credential credential: name: "{{ ssh_cred_name1 }}" diff --git a/awx_collection/tests/integration/targets/credential_input_source/tasks/main.yml b/awx_collection/tests/integration/targets/credential_input_source/tasks/main.yml index e9066a0f2a..1d56d5e4b1 100644 --- a/awx_collection/tests/integration/targets/credential_input_source/tasks/main.yml +++ b/awx_collection/tests/integration/targets/credential_input_source/tasks/main.yml @@ -54,6 +54,51 @@ that: - "result is changed" + - name: Add credential Input Source with exists + credential_input_source: + input_field_name: password + target_credential: "{{ target_cred_result.id }}" + source_credential: "{{ src_cred_result.id }}" + metadata: + object_query: "Safe=MY_SAFE;Object=AWX-user" + object_query_format: "Exact" + state: exists + register: result + + - assert: + that: + - "result is not changed" + + - name: Delete credential Input Source + credential_input_source: + input_field_name: password + target_credential: "{{ target_cred_result.id }}" + source_credential: "{{ src_cred_result.id }}" + metadata: + object_query: "Safe=MY_SAFE;Object=AWX-user" + object_query_format: "Exact" + state: absent + register: result + + - assert: + that: + - "result is changed" + + - name: Add credential Input Source with exists + credential_input_source: + input_field_name: password + target_credential: "{{ target_cred_result.id }}" + source_credential: "{{ src_cred_result.id }}" + metadata: + object_query: "Safe=MY_SAFE;Object=AWX-user" + object_query_format: "Exact" + state: exists + register: result + + - assert: + that: + - "result is changed" + - name: Add Second credential Lookup credential: description: Credential for Testing Source Change diff --git a/awx_collection/tests/integration/targets/credential_type/tasks/main.yml b/awx_collection/tests/integration/targets/credential_type/tasks/main.yml index ee6045ea50..11bd6f9045 100644 --- a/awx_collection/tests/integration/targets/credential_type/tasks/main.yml +++ b/awx_collection/tests/integration/targets/credential_type/tasks/main.yml @@ -22,6 +22,48 @@ that: - "result is changed" + - name: Add Tower credential type with exists + credential_type: + description: Credential type for Test + name: "{{ cred_type_name }}" + kind: cloud + inputs: {"fields": [{"type": "string", "id": "username", "label": "Username"}, {"secret": true, "type": "string", "id": "password", "label": "Password"}], "required": ["username", "password"]} + injectors: {"extra_vars": {"test": "foo"}} + state: exists + register: result + + - assert: + that: + - "result is not changed" + + - name: Delete the credential type + credential_type: + description: Credential type for Test + name: "{{ cred_type_name }}" + kind: cloud + inputs: {"fields": [{"type": "string", "id": "username", "label": "Username"}, {"secret": true, "type": "string", "id": "password", "label": "Password"}], "required": ["username", "password"]} + injectors: {"extra_vars": {"test": "foo"}} + state: absent + register: result + + - assert: + that: + - "result is changed" + + - name: Add Tower credential type with exists + credential_type: + description: Credential type for Test + name: "{{ cred_type_name }}" + kind: cloud + inputs: {"fields": [{"type": "string", "id": "username", "label": "Username"}, {"secret": true, "type": "string", "id": "password", "label": "Password"}], "required": ["username", "password"]} + injectors: {"extra_vars": {"test": "foo"}} + state: exists + register: result + + - assert: + that: + - "result is changed" + - name: Rename Tower credential type credential_type: name: "{{ cred_type_name }}" diff --git a/awx_collection/tests/integration/targets/execution_environment/tasks/main.yml b/awx_collection/tests/integration/targets/execution_environment/tasks/main.yml index 0cb2fd90b3..f1758bd1b3 100644 --- a/awx_collection/tests/integration/targets/execution_environment/tasks/main.yml +++ b/awx_collection/tests/integration/targets/execution_environment/tasks/main.yml @@ -22,6 +22,48 @@ that: - "result is changed" + - name: Add an EE with exists + execution_environment: + name: "{{ ee_name }}" + description: "EE for Testing" + image: quay.io/ansible/awx-ee + pull: always + organization: Default + state: exists + register: result + + - assert: + that: + - "result is not changed" + + - name: Delete an EE + execution_environment: + name: "{{ ee_name }}" + description: "EE for Testing" + image: quay.io/ansible/awx-ee + pull: always + organization: Default + state: absent + register: result + + - assert: + that: + - "result is changed" + + - name: Add an EE with exists + execution_environment: + name: "{{ ee_name }}" + description: "EE for Testing" + image: quay.io/ansible/awx-ee + pull: always + organization: Default + state: exists + register: result + + - assert: + that: + - "result is changed" + - name: Associate the Test EE with Default Org (this should fail) execution_environment: name: "{{ ee_name }}" diff --git a/awx_collection/tests/integration/targets/group/tasks/main.yml b/awx_collection/tests/integration/targets/group/tasks/main.yml index 3200acbb96..f88729f56e 100644 --- a/awx_collection/tests/integration/targets/group/tasks/main.yml +++ b/awx_collection/tests/integration/targets/group/tasks/main.yml @@ -19,9 +19,9 @@ name: "{{ inv_name }}" organization: Default state: present - register: result + registuer: result -- name: Create a Group +- name: Create Group 1 group: name: "{{ group_name1 }}" inventory: "{{ result.id }}" @@ -34,7 +34,46 @@ that: - "result is changed" -- name: Create a Group +- name: Create Group 1 with exists + group: + name: "{{ group_name1 }}" + inventory: "{{ inv_name }}" + state: exists + variables: + foo: bar + register: result + +- assert: + that: + - "result is not changed" + +- name: Delete Group 1 + group: + name: "{{ group_name1 }}" + inventory: "{{ inv_name }}" + state: absent + variables: + foo: bar + register: result + +- assert: + that: + - "result is changed" + +- name: Create Group 1 with exists + group: + name: "{{ group_name1 }}" + inventory: "{{ inv_name }}" + state: exists + variables: + foo: bar + register: result + +- assert: + that: + - "result is changed" + +- name: Create Group 2 group: name: "{{ group_name2 }}" inventory: "{{ inv_name }}" @@ -47,7 +86,7 @@ that: - "result is changed" -- name: Create a Group +- name: Create Group 3 group: name: "{{ group_name3 }}" inventory: "{{ inv_name }}" @@ -69,7 +108,7 @@ - "{{ host_name2 }}" - "{{ host_name3 }}" -- name: Create a Group with hosts and sub group +- name: Create Group 1 with hosts and sub group of Group 2 group: name: "{{ group_name1 }}" inventory: "{{ inv_name }}" @@ -83,7 +122,7 @@ foo: bar register: result -- name: Create a Group with hosts and sub group +- name: Create Group 1 with hosts and sub group group: name: "{{ group_name1 }}" inventory: "{{ inv_name }}" @@ -104,18 +143,7 @@ that: - group1_host_count == "3" -- name: Delete a Group - group: - name: "{{ group_name1 }}" - inventory: "{{ inv_name }}" - state: absent - register: result - -- assert: - that: - - "result is changed" - -- name: Delete a Group +- name: Delete Group 2 group: name: "{{ group_name2 }}" inventory: "{{ inv_name }}" @@ -127,7 +155,7 @@ that: - "result is not changed" -- name: Delete a Group +- name: Delete Group 3 group: name: "{{ group_name3 }}" inventory: "{{ inv_name }}" @@ -136,7 +164,19 @@ - assert: that: - - "result is not changed" + - "result is changed" + +# If we delete group 1 first it will delete group 2 and 3 +- name: Delete Group 1 + group: + name: "{{ group_name1 }}" + inventory: "{{ inv_name }}" + state: absent + register: result + +- assert: + that: + - "result is changed" - name: Check module fails with correct msg group: diff --git a/awx_collection/tests/integration/targets/host/tasks/main.yml b/awx_collection/tests/integration/targets/host/tasks/main.yml index 6c1fefdf91..d7752ca661 100644 --- a/awx_collection/tests/integration/targets/host/tasks/main.yml +++ b/awx_collection/tests/integration/targets/host/tasks/main.yml @@ -29,6 +29,45 @@ that: - "result is changed" +- name: Create a Host with exists + host: + name: "{{ host_name }}" + inventory: "{{ inv_name }}" + state: exists + variables: + foo: bar + register: result + +- assert: + that: + - "result is not changed" + +- name: Delete a Host + host: + name: "{{ host_name }}" + inventory: "{{ inv_name }}" + state: absent + variables: + foo: bar + register: result + +- assert: + that: + - "result is changed" + +- name: Create a Host with exists + host: + name: "{{ host_name }}" + inventory: "{{ inv_name }}" + state: exists + variables: + foo: bar + register: result + +- assert: + that: + - "result is changed" + - name: Delete a Host host: name: "{{ result.id }}" diff --git a/awx_collection/tests/integration/targets/instance_group/tasks/main.yml b/awx_collection/tests/integration/targets/instance_group/tasks/main.yml index e360cf933a..1ef9d2da38 100644 --- a/awx_collection/tests/integration/targets/instance_group/tasks/main.yml +++ b/awx_collection/tests/integration/targets/instance_group/tasks/main.yml @@ -38,6 +38,42 @@ that: - "result is changed" + - name: Create an Instance Group with exists + instance_group: + name: "{{ group_name1 }}" + policy_instance_percentage: 34 + policy_instance_minimum: 12 + state: exists + register: result + + - assert: + that: + - "result is not changed" + + - name: Delete an Instance Group + instance_group: + name: "{{ group_name1 }}" + policy_instance_percentage: 34 + policy_instance_minimum: 12 + state: absent + register: result + + - assert: + that: + - "result is changed" + + - name: Create an Instance Group with exists + instance_group: + name: "{{ group_name1 }}" + policy_instance_percentage: 34 + policy_instance_minimum: 12 + state: exists + register: result + + - assert: + that: + - "result is changed" + - name: Update an Instance Group instance_group: name: "{{ result.id }}" diff --git a/awx_collection/tests/integration/targets/inventory/tasks/main.yml b/awx_collection/tests/integration/targets/inventory/tasks/main.yml index cec0c6b27a..dbaf7dbcec 100644 --- a/awx_collection/tests/integration/targets/inventory/tasks/main.yml +++ b/awx_collection/tests/integration/targets/inventory/tasks/main.yml @@ -51,6 +51,45 @@ that: - "result is changed" + - name: Create an Inventory with exists + inventory: + name: "{{ inv_name1 }}" + organization: Default + instance_groups: + - "{{ group_name1 }}" + state: exists + register: result + + - assert: + that: + - "result is not changed" + + - name: Delete an Inventory + inventory: + name: "{{ inv_name1 }}" + organization: Default + instance_groups: + - "{{ group_name1 }}" + state: absent + register: result + + - assert: + that: + - "result is changed" + + - name: Create an Inventory with exists + inventory: + name: "{{ inv_name1 }}" + organization: Default + instance_groups: + - "{{ group_name1 }}" + state: exists + register: result + + - assert: + that: + - "result is changed" + - name: Test Inventory module idempotency inventory: name: "{{ result.id }}" diff --git a/awx_collection/tests/integration/targets/inventory_source/tasks/main.yml b/awx_collection/tests/integration/targets/inventory_source/tasks/main.yml index 40ffd8c9b2..a0442eeaf2 100644 --- a/awx_collection/tests/integration/targets/inventory_source/tasks/main.yml +++ b/awx_collection/tests/integration/targets/inventory_source/tasks/main.yml @@ -30,7 +30,7 @@ organization: Default name: "{{ openstack_inv }}" -- name: Create a source inventory +- name: Create an source inventory inventory_source: name: "{{ openstack_inv_source }}" description: Source for Test inventory @@ -47,6 +47,60 @@ that: - "result is changed" +- name: Create an source inventory with exists + inventory_source: + name: "{{ openstack_inv_source }}" + description: Source for Test inventory + inventory: "{{ openstack_inv }}" + credential: "{{ credential_result.id }}" + overwrite: true + update_on_launch: true + source_vars: + private: false + source: openstack + state: exists + register: result + +- assert: + that: + - "result is not changed" + +- name: Delete an source inventory + inventory_source: + name: "{{ openstack_inv_source }}" + description: Source for Test inventory + inventory: "{{ openstack_inv }}" + credential: "{{ credential_result.id }}" + overwrite: true + update_on_launch: true + source_vars: + private: false + source: openstack + state: absent + register: result + +- assert: + that: + - "result is changed" + +- name: Create an source inventory with exists + inventory_source: + name: "{{ openstack_inv_source }}" + description: Source for Test inventory + inventory: "{{ openstack_inv }}" + credential: "{{ credential_result.id }}" + overwrite: true + update_on_launch: true + source_vars: + private: false + source: openstack + state: exists + register: result + +- assert: + that: + - "result is changed" + - name: Delete the inventory source with an invalid cred and source_project specified inventory_source: name: "{{ result.id }}" diff --git a/awx_collection/tests/integration/targets/job_template/tasks/main.yml b/awx_collection/tests/integration/targets/job_template/tasks/main.yml index 6269a0b142..b8e5130090 100644 --- a/awx_collection/tests/integration/targets/job_template/tasks/main.yml +++ b/awx_collection/tests/integration/targets/job_template/tasks/main.yml @@ -10,6 +10,7 @@ cred1: "AWX-Collection-tests-job_template-cred1-{{ test_id }}" cred2: "AWX-Collection-tests-job_template-cred2-{{ test_id }}" cred3: "AWX-Collection-tests-job_template-cred3-{{ test_id }}" + inv1: "AWX-Collection-tests-job_template-inv-{{ test_id }}" proj1: "AWX-Collection-tests-job_template-proj-{{ test_id }}" jt1: "AWX-Collection-tests-job_template-jt1-{{ test_id }}" jt2: "AWX-Collection-tests-job_template-jt2-{{ test_id }}" @@ -25,6 +26,11 @@ - Ansible Galaxy register: result +- name: Create an inventory + inventory: + name: "{{ inv1 }}" + organization: "{{ org_name }}" + - name: Create a Demo Project project: name: "{{ proj1 }}" @@ -104,7 +110,7 @@ job_template: name: "{{ jt1 }}" project: "{{ proj1 }}" - inventory: Demo Inventory + inventory: "{{ inv1 }}" playbook: hello_world.yml credentials: - "{{ cred1 }}" @@ -119,6 +125,63 @@ that: - "jt1_result is changed" +- name: Create Job Template 1 with exists + job_template: + name: "{{ jt1 }}" + project: "{{ proj1 }}" + inventory: "{{ inv1 }}" + playbook: hello_world.yml + credentials: + - "{{ cred1 }}" + - "{{ cred2 }}" + instance_groups: + - "{{ group_name1 }}" + job_type: run + state: exists + register: jt1_result + +- assert: + that: + - "jt1_result is not changed" + +- name: Delete Job Template 1 + job_template: + name: "{{ jt1 }}" + project: "{{ proj1 }}" + inventory: "{{ inv1 }}" + playbook: hello_world.yml + credentials: + - "{{ cred1 }}" + - "{{ cred2 }}" + instance_groups: + - "{{ group_name1 }}" + job_type: run + state: absent + register: jt1_result + +- assert: + that: + - "jt1_result is changed" + +- name: Create Job Template 1 with exists + job_template: + name: "{{ jt1 }}" + project: "{{ proj1 }}" + inventory: "{{ inv1 }}" + playbook: hello_world.yml + credentials: + - "{{ cred1 }}" + - "{{ cred2 }}" + instance_groups: + - "{{ group_name1 }}" + job_type: run + state: exists + register: jt1_result + +- assert: + that: + - "jt1_result is changed" + - name: Add a credential to this JT job_template: name: "{{ jt1 }}" @@ -218,7 +281,7 @@ name: "{{ jt2 }}" organization: Default project: "{{ proj1 }}" - inventory: Demo Inventory + inventory: "{{ inv1 }}" playbook: hello_world.yml credential: "{{ cred3 }}" job_type: run @@ -236,7 +299,7 @@ name: "{{ jt2 }}" organization: Default project: "{{ proj1 }}" - inventory: Demo Inventory + inventory: "{{ inv1 }}" playbook: hello_world.yml credential: "{{ cred3 }}" job_type: run @@ -384,7 +447,7 @@ job_template: name: "{{ jt2 }}" project: "{{ proj1 }}" - inventory: Demo Inventory + inventory: "{{ inv1 }}" playbook: hello_world.yml credential: "{{ cred3 }}" job_type: run @@ -444,6 +507,12 @@ organization: Default state: absent +- name: Delete an inventory + inventory: + name: "{{ inv1 }}" + organization: "{{ org_name }}" + state: absent + - name: "Remove the organization" organization: name: "{{ org_name }}" diff --git a/awx_collection/tests/integration/targets/label/tasks/main.yml b/awx_collection/tests/integration/targets/label/tasks/main.yml index 8b3f29171d..a98594d5cf 100644 --- a/awx_collection/tests/integration/targets/label/tasks/main.yml +++ b/awx_collection/tests/integration/targets/label/tasks/main.yml @@ -13,6 +13,22 @@ name: "{{ label_name }}" organization: Default state: present + register: results + +- assert: + that: + - "results is changed" + +- name: Create a Label with exists + label: + name: "{{ label_name }}" + organization: Default + state: exists + register: results + +- assert: + that: + - "results is not changed" - name: Check module fails with correct msg label: diff --git a/awx_collection/tests/integration/targets/notification_template/tasks/main.yml b/awx_collection/tests/integration/targets/notification_template/tasks/main.yml index b909a98299..483024e2f8 100644 --- a/awx_collection/tests/integration/targets/notification_template/tasks/main.yml +++ b/awx_collection/tests/integration/targets/notification_template/tasks/main.yml @@ -36,6 +36,75 @@ that: - result is changed +- name: Create Slack notification with custom messages with exists + notification_template: + name: "{{ slack_not }}" + organization: Default + notification_type: slack + notification_configuration: + token: a_token + channels: + - general + messages: + started: + message: "{{ '{{' }} job_friendly_name {{' }}' }} {{ '{{' }} job.id {{' }}' }} started" + success: + message: "{{ '{{' }} job_friendly_name {{ '}}' }} completed in {{ '{{' }} job.elapsed {{ '}}' }} seconds" + error: + message: "{{ '{{' }} job_friendly_name {{ '}}' }} FAILED! Please look at {{ '{{' }} job.url {{ '}}' }}" + state: exists + register: result + +- assert: + that: + - result is not changed + +- name: Delete Slack notification with custom messages + notification_template: + name: "{{ slack_not }}" + organization: Default + notification_type: slack + notification_configuration: + token: a_token + channels: + - general + messages: + started: + message: "{{ '{{' }} job_friendly_name {{' }}' }} {{ '{{' }} job.id {{' }}' }} started" + success: + message: "{{ '{{' }} job_friendly_name {{ '}}' }} completed in {{ '{{' }} job.elapsed {{ '}}' }} seconds" + error: + message: "{{ '{{' }} job_friendly_name {{ '}}' }} FAILED! Please look at {{ '{{' }} job.url {{ '}}' }}" + state: absent + register: result + +- assert: + that: + - result is changed + +- name: Create Slack notification with custom messages with exists + notification_template: + name: "{{ slack_not }}" + organization: Default + notification_type: slack + notification_configuration: + token: a_token + channels: + - general + messages: + started: + message: "{{ '{{' }} job_friendly_name {{' }}' }} {{ '{{' }} job.id {{' }}' }} started" + success: + message: "{{ '{{' }} job_friendly_name {{ '}}' }} completed in {{ '{{' }} job.elapsed {{ '}}' }} seconds" + error: + message: "{{ '{{' }} job_friendly_name {{ '}}' }} FAILED! Please look at {{ '{{' }} job.url {{ '}}' }}" + state: exists + register: result + +- assert: + that: + - result is changed + - name: Delete Slack notification notification_template: name: "{{ slack_not }}" diff --git a/awx_collection/tests/integration/targets/organization/tasks/main.yml b/awx_collection/tests/integration/targets/organization/tasks/main.yml index baa7fb733f..57af83c473 100644 --- a/awx_collection/tests/integration/targets/organization/tasks/main.yml +++ b/awx_collection/tests/integration/targets/organization/tasks/main.yml @@ -25,6 +25,39 @@ - assert: that: "result is changed" +- name: "Create a new organization with exists" + organization: + name: "{{ org_name }}" + galaxy_credentials: + - Ansible Galaxy + state: exists + register: result + +- assert: + that: "result is not changed" + +- name: "Delete a new organization" + organization: + name: "{{ org_name }}" + galaxy_credentials: + - Ansible Galaxy + state: absent + register: result + +- assert: + that: "result is changed" + +- name: "Create a new organization with exists" + organization: + name: "{{ org_name }}" + galaxy_credentials: + - Ansible Galaxy + state: exists + register: result + +- assert: + that: "result is changed" + - name: "Make sure making the same org is not a change" organization: name: "{{ org_name }}" diff --git a/awx_collection/tests/integration/targets/project/tasks/main.yml b/awx_collection/tests/integration/targets/project/tasks/main.yml index 89d6f36885..f622af8e6f 100644 --- a/awx_collection/tests/integration/targets/project/tasks/main.yml +++ b/awx_collection/tests/integration/targets/project/tasks/main.yml @@ -39,6 +39,48 @@ that: - result is changed + - name: Create a git project without credentials and wait with exists + project: + name: "{{ project_name1 }}" + organization: Default + scm_type: git + scm_url: https://github.com/ansible/test-playbooks + wait: true + state: exists + register: result + + - assert: + that: + - result is not changed + + - name: Delete a git project without credentials and wait + project: + name: "{{ project_name1 }}" + organization: Default + scm_type: git + scm_url: https://github.com/ansible/test-playbooks + wait: true + state: absent + register: result + + - assert: + that: + - result is changed + + - name: Create a git project without credentials and wait with exists + project: + name: "{{ project_name1 }}" + organization: Default + scm_type: git + scm_url: https://github.com/ansible/test-playbooks + wait: true + state: exists + register: result + + - assert: + that: + - result is changed + - name: Recreate the project to validate not changed project: name: "{{ project_name1 }}" diff --git a/awx_collection/tests/integration/targets/schedule/tasks/main.yml b/awx_collection/tests/integration/targets/schedule/tasks/main.yml index 6bde226fcc..b02c7f72dd 100644 --- a/awx_collection/tests/integration/targets/schedule/tasks/main.yml +++ b/awx_collection/tests/integration/targets/schedule/tasks/main.yml @@ -76,6 +76,42 @@ that: - result is changed + - name: Build a real schedule with exists + schedule: + name: "{{ sched1 }}" + state: exists + unified_job_template: "Demo Job Template" + rrule: "DTSTART:20191219T130551Z RRULE:FREQ=WEEKLY;INTERVAL=1;COUNT=1" + register: result + + - assert: + that: + - result is not changed + + - name: Delete a real schedule + schedule: + name: "{{ sched1 }}" + state: absent + unified_job_template: "Demo Job Template" + rrule: "DTSTART:20191219T130551Z RRULE:FREQ=WEEKLY;INTERVAL=1;COUNT=1" + register: result + + - assert: + that: + - result is changed + + - name: Build a real schedule with exists + schedule: + name: "{{ sched1 }}" + state: exists + unified_job_template: "Demo Job Template" + rrule: "DTSTART:20191219T130551Z RRULE:FREQ=WEEKLY;INTERVAL=1;COUNT=1" + register: result + + - assert: + that: + - result is changed + - name: Rebuild the same schedule schedule: name: "{{ sched1 }}" diff --git a/awx_collection/tests/integration/targets/team/tasks/main.yml b/awx_collection/tests/integration/targets/team/tasks/main.yml index 390a888610..52f02b067f 100644 --- a/awx_collection/tests/integration/targets/team/tasks/main.yml +++ b/awx_collection/tests/integration/targets/team/tasks/main.yml @@ -34,6 +34,39 @@ that: - "result is changed" +- name: Create a team with exists + team: + name: "{{ team_name }}" + organization: Default + state: exists + register: result + +- assert: + that: + - "result is not changed" + +- name: Delete a team + team: + name: "{{ team_name }}" + organization: Default + state: absent + register: result + +- assert: + that: + - "result is changed" + +- name: Create a team with exists + team: + name: "{{ team_name }}" + organization: Default + state: exists + register: result + +- assert: + that: + - "result is changed" + - name: Delete a team team: name: "{{ team_name }}" diff --git a/awx_collection/tests/integration/targets/user/tasks/main.yml b/awx_collection/tests/integration/targets/user/tasks/main.yml index 6d2b290dd6..2d88bb199a 100644 --- a/awx_collection/tests/integration/targets/user/tasks/main.yml +++ b/awx_collection/tests/integration/targets/user/tasks/main.yml @@ -20,6 +20,42 @@ that: - "result is changed" +- name: Create a User with exists + user: + username: "{{ username }}" + first_name: Joe + password: "{{ 65535 | random | to_uuid }}" + state: exists + register: result + +- assert: + that: + - "result is not changed" + +- name: Delete a User + user: + username: "{{ username }}" + first_name: Joe + password: "{{ 65535 | random | to_uuid }}" + state: absent + register: result + +- assert: + that: + - "result is changed" + +- name: Create a User with exists + user: + username: "{{ username }}" + first_name: Joe + password: "{{ 65535 | random | to_uuid }}" + state: exists + register: result + +- assert: + that: + - "result is changed" + - name: Change a User by ID user: username: "{{ result.id }}" diff --git a/awx_collection/tests/integration/targets/workflow_job_template/tasks/main.yml b/awx_collection/tests/integration/targets/workflow_job_template/tasks/main.yml index dc15afb428..b5d6663c33 100644 --- a/awx_collection/tests/integration/targets/workflow_job_template/tasks/main.yml +++ b/awx_collection/tests/integration/targets/workflow_job_template/tasks/main.yml @@ -254,6 +254,65 @@ that: - "result is changed" + - name: Create a workflow job template with exists + workflow_job_template: + name: "{{ wfjt_name }}" + organization: Default + inventory: Demo Inventory + extra_vars: {'foo': 'bar', 'another-foo': {'barz': 'bar2'}} + labels: + - "{{ lab1 }}" + ask_inventory_on_launch: true + ask_scm_branch_on_launch: true + ask_limit_on_launch: true + ask_tags_on_launch: true + ask_variables_on_launch: true + state: exists + register: result + + - assert: + that: + - "result is not changed" + + - name: Delete a workflow job template + workflow_job_template: + name: "{{ wfjt_name }}" + organization: Default + inventory: Demo Inventory + extra_vars: {'foo': 'bar', 'another-foo': {'barz': 'bar2'}} + labels: + - "{{ lab1 }}" + ask_inventory_on_launch: true + ask_scm_branch_on_launch: true + ask_limit_on_launch: true + ask_tags_on_launch: true + ask_variables_on_launch: true + state: absent + register: result + + - assert: + that: + - "result is changed" + + - name: Create a workflow job template with exists + workflow_job_template: + name: "{{ wfjt_name }}" + organization: Default + inventory: Demo Inventory + extra_vars: {'foo': 'bar', 'another-foo': {'barz': 'bar2'}} + # We don't try with the label here because after we delete the first WFJT the label is delete with it because it has no references + ask_inventory_on_launch: true + ask_scm_branch_on_launch: true + ask_limit_on_launch: true + ask_tags_on_launch: true + ask_variables_on_launch: true + state: exists + register: result + + - assert: + that: + - "result is changed" + - name: Create a workflow job template with bad label workflow_job_template: name: "{{ wfjt_name }}"