Compare commits

...

4 Commits

Author SHA1 Message Date
Shane McDonald
05e9b29460 Merge pull request #13963 from Akasurde/doc_fix
Minor typo fix in docs
2023-05-10 08:33:01 -04:00
John Westcott IV
7f020052db Make state exists universal in collection (#13890)
Make state: exists available for all API modules

Make state:exists return the ID just like it would if it created the resource
2023-05-10 09:05:29 -03:00
Rick Elrod
53260213ba Issue template: Remind people to use security@ (#13971)
Signed-off-by: Rick Elrod <rick@elrod.me>
2023-05-09 11:00:02 -05:00
Abhijeet Kasurde
7d1ee37689 Minor typo fix in docs
Signed-off-by: Abhijeet Kasurde <akasurde@redhat.com>
2023-05-08 07:47:07 -07:00
42 changed files with 921 additions and 136 deletions

View File

@@ -19,6 +19,8 @@ body:
required: true
- label: I understand that AWX is open source software provided for free and that I might not receive a timely response.
required: true
- label: I am **NOT** reporting a (potential) security vulnerability. (These should be emailed to `security@ansible.com` instead.)
required: true
- type: textarea
id: summary

View File

@@ -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(

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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'],

View File

@@ -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:

View File

@@ -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,

View File

@@ -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,

View File

@@ -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

View File

@@ -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:

View File

@@ -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__':

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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 }}"

View File

@@ -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 }}"

View File

@@ -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

View File

@@ -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 }}"

View File

@@ -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 }}"

View File

@@ -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:

View File

@@ -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 }}"

View File

@@ -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 }}"

View File

@@ -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 }}"

View File

@@ -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 }}"

View File

@@ -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 }}"

View File

@@ -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:

View File

@@ -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 }}"

View File

@@ -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 }}"

View File

@@ -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 }}"

View File

@@ -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 }}"

View File

@@ -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 }}"

View File

@@ -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 }}"

View File

@@ -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 }}"

View File

@@ -110,7 +110,7 @@ processes it spawns. This allows Ansible events to be captured and persisted
into the AWX database; this process is what drives the "streaming" web UI
you'll see if you launch a job from the AWX web interface and watch its results
appears on the screen. AWX relies on stability in this plugin interface, the
heirarchy of emitted events based on strategy, and _especially_ the structure
hierarchy of emitted events based on strategy, and _especially_ the structure
of event data to work across Ansible releases:
![Event Data Diagram](https://user-images.githubusercontent.com/722880/35641610-ae7f1dea-068e-11e8-84fb-0f96043d53e4.png)