mirror of
https://github.com/ansible/awx.git
synced 2026-03-16 16:37:30 -02:30
@@ -37,6 +37,12 @@ options:
|
||||
description:
|
||||
- Inventory to use for the job, only used if prompt for inventory is set.
|
||||
type: str
|
||||
organization:
|
||||
description:
|
||||
- Organization the job template exists in.
|
||||
- Used to help lookup the object, cannot be modified using this module.
|
||||
- If not provided, will lookup by name only, which does not work with duplicates.
|
||||
type: str
|
||||
credentials:
|
||||
description:
|
||||
- Credential to use for job, only used if prompt for credential is set.
|
||||
@@ -149,6 +155,7 @@ def main():
|
||||
name=dict(required=True, aliases=['job_template']),
|
||||
job_type=dict(choices=['run', 'check']),
|
||||
inventory=dict(default=None),
|
||||
organization=dict(),
|
||||
# Credentials will be a str instead of a list for backwards compatability
|
||||
credentials=dict(type='list', default=None, aliases=['credential'], elements='str'),
|
||||
limit=dict(),
|
||||
@@ -172,6 +179,7 @@ def main():
|
||||
name = module.params.get('name')
|
||||
optional_args['job_type'] = module.params.get('job_type')
|
||||
inventory = module.params.get('inventory')
|
||||
organization = module.params.get('organization')
|
||||
credentials = module.params.get('credentials')
|
||||
optional_args['limit'] = module.params.get('limit')
|
||||
optional_args['tags'] = module.params.get('tags')
|
||||
@@ -201,7 +209,10 @@ def main():
|
||||
post_data['credentials'].append(module.resolve_name_to_id('credentials', credential))
|
||||
|
||||
# Attempt to look up job_template based on the provided name
|
||||
job_template = module.get_one('job_templates', name_or_id=name)
|
||||
lookup_data = {}
|
||||
if organization:
|
||||
lookup_data['organization'] = module.resolve_name_to_id('organizations', organization)
|
||||
job_template = module.get_one('job_templates', name_or_id=name, data=lookup_data)
|
||||
|
||||
if job_template is None:
|
||||
module.fail_json(msg="Unable to find job template by name {0}".format(name))
|
||||
@@ -211,7 +222,6 @@ def main():
|
||||
check_vars_to_prompts = {
|
||||
'scm_branch': 'ask_scm_branch_on_launch',
|
||||
'diff_mode': 'ask_diff_mode_on_launch',
|
||||
'extra_vars': 'ask_variables_on_launch',
|
||||
'limit': 'ask_limit_on_launch',
|
||||
'tags': 'ask_tags_on_launch',
|
||||
'skip_tags': 'ask_skip_tags_on_launch',
|
||||
@@ -225,6 +235,9 @@ def main():
|
||||
for variable_name in check_vars_to_prompts:
|
||||
if module.params.get(variable_name) and not job_template[check_vars_to_prompts[variable_name]]:
|
||||
param_errors.append("The field {0} was specified but the job template does not allow for it to be overridden".format(variable_name))
|
||||
# Check if Either ask_variables_on_launch, or survey_enabled is enabled for use of extra vars.
|
||||
if module.params.get('extra_vars') and not (job_template['ask_variables_on_launch'] or job_template['survey_enabled']):
|
||||
param_errors.append("The field extra_vars was specified but the job template does not allow for it to be overridden")
|
||||
if len(param_errors) > 0:
|
||||
module.fail_json(msg="Parameters specified which can not be passed into job template, see errors for details", **{'errors': param_errors})
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ RETURN = ''' # '''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Set the license using a file
|
||||
license:
|
||||
tower_license:
|
||||
manifest: "/tmp/my_manifest.zip"
|
||||
eula_accepted: True
|
||||
'''
|
||||
|
||||
@@ -34,7 +34,7 @@ options:
|
||||
scm_type:
|
||||
description:
|
||||
- Type of SCM resource.
|
||||
choices: ["manual", "git", "hg", "svn", "insights"]
|
||||
choices: ["manual", "git", "svn", "insights"]
|
||||
default: "manual"
|
||||
type: str
|
||||
scm_url:
|
||||
@@ -168,7 +168,7 @@ EXAMPLES = '''
|
||||
organization: "test"
|
||||
scm_update_on_launch: True
|
||||
scm_update_cache_timeout: 60
|
||||
custom_virtualenv: "/var/lib/awx/venv/ansible-2.2"
|
||||
custom_virtualenv: "/var/lib/awx/var/lib/awx/venv/ansible-2.2"
|
||||
state: present
|
||||
tower_config_file: "~/tower_cli.cfg"
|
||||
'''
|
||||
@@ -220,7 +220,7 @@ def main():
|
||||
argument_spec = dict(
|
||||
name=dict(required=True),
|
||||
description=dict(),
|
||||
scm_type=dict(choices=['manual', 'git', 'hg', 'svn', 'insights'], default='manual'),
|
||||
scm_type=dict(choices=['manual', 'git', 'svn', 'insights'], default='manual'),
|
||||
scm_url=dict(),
|
||||
local_path=dict(),
|
||||
scm_branch=dict(default=''),
|
||||
|
||||
@@ -94,10 +94,12 @@ options:
|
||||
- Setting that variable will prompt the user for job type on the
|
||||
workflow launch.
|
||||
type: bool
|
||||
survey:
|
||||
survey_spec:
|
||||
description:
|
||||
- The definition of the survey associated to the workflow.
|
||||
type: dict
|
||||
aliases:
|
||||
- survey
|
||||
labels:
|
||||
description:
|
||||
- The labels applied to this job template
|
||||
@@ -149,7 +151,15 @@ import json
|
||||
|
||||
def update_survey(module, last_request):
|
||||
spec_endpoint = last_request.get('related', {}).get('survey_spec')
|
||||
module.post_endpoint(spec_endpoint, **{'data': module.params.get('survey')})
|
||||
if module.params.get('survey_spec') == {}:
|
||||
response = module.delete_endpoint(spec_endpoint)
|
||||
if response['status_code'] != 200:
|
||||
# Not sure how to make this actually return a non 200 to test what to dump in the respinse
|
||||
module.fail_json(msg="Failed to delete survey: {0}".format(response['json']))
|
||||
else:
|
||||
response = module.post_endpoint(spec_endpoint, **{'data': module.params.get('survey_spec')})
|
||||
if response['status_code'] != 200:
|
||||
module.fail_json(msg="Failed to update survey: {0}".format(response['json']['error']))
|
||||
module.exit_json(**module.json_output)
|
||||
|
||||
|
||||
@@ -161,7 +171,7 @@ def main():
|
||||
description=dict(),
|
||||
extra_vars=dict(type='dict'),
|
||||
organization=dict(),
|
||||
survey=dict(type='dict'), # special handling
|
||||
survey_spec=dict(type='dict', aliases=['survey']),
|
||||
survey_enabled=dict(type='bool'),
|
||||
allow_simultaneous=dict(type='bool'),
|
||||
ask_variables_on_launch=dict(type='bool'),
|
||||
@@ -266,7 +276,7 @@ def main():
|
||||
# association_fields['labels'].append(label_id)
|
||||
|
||||
on_change = None
|
||||
new_spec = module.params.get('survey')
|
||||
new_spec = module.params.get('survey_spec')
|
||||
if new_spec:
|
||||
existing_spec = None
|
||||
if existing_item:
|
||||
|
||||
@@ -152,15 +152,17 @@ def main():
|
||||
'inventory': 'ask_inventory_on_launch',
|
||||
'limit': 'ask_limit_on_launch',
|
||||
'scm_branch': 'ask_scm_branch_on_launch',
|
||||
'extra_vars': 'ask_variables_on_launch',
|
||||
}
|
||||
|
||||
param_errors = []
|
||||
for variable_name in check_vars_to_prompts:
|
||||
if variable_name in post_data and not workflow_job_template[check_vars_to_prompts[variable_name]]:
|
||||
param_errors.append("The field {0} was specified but the workflow job template does not allow for it to be overridden".format(variable_name))
|
||||
# Check if Either ask_variables_on_launch, or survey_enabled is enabled for use of extra vars.
|
||||
if module.params.get('extra_vars') and not (workflow_job_template['ask_variables_on_launch'] or workflow_job_template['survey_enabled']):
|
||||
param_errors.append("The field extra_vars was specified but the workflow job template does not allow for it to be overridden")
|
||||
if len(param_errors) > 0:
|
||||
module.fail_json(msg="Parameters specified which can not be passed into wotkflow job template, see errors for details", errors=param_errors)
|
||||
module.fail_json(msg="Parameters specified which can not be passed into workflow job template, see errors for details", errors=param_errors)
|
||||
|
||||
# Launch the job
|
||||
result = module.post_endpoint(workflow_job_template['related']['launch'], data=post_data)
|
||||
|
||||
@@ -47,7 +47,7 @@ no_api_parameter_ok = {
|
||||
# Organization is how we are looking up job templates, Approval node is for workflow_approval_templates
|
||||
'tower_workflow_job_template_node': ['organization', 'approval_node'],
|
||||
# Survey is how we handle associations
|
||||
'tower_workflow_job_template': ['survey'],
|
||||
'tower_workflow_job_template': ['survey_spec'],
|
||||
# ad hoc commands support interval and timeout since its more like tower_job_launc
|
||||
'tower_ad_hoc_command': ['interval', 'timeout', 'wait'],
|
||||
}
|
||||
|
||||
@@ -133,10 +133,10 @@ def test_custom_venv_no_op(run_module, admin_user, base_inventory, mocker, proje
|
||||
inventory=base_inventory,
|
||||
source_project=project,
|
||||
source='scm',
|
||||
custom_virtualenv='/venv/foobar/'
|
||||
custom_virtualenv='/var/lib/awx/venv/foobar/'
|
||||
)
|
||||
# mock needed due to API behavior, not incorrect client behavior
|
||||
with mocker.patch('awx.main.models.mixins.get_custom_venv_choices', return_value=['/venv/foobar/']):
|
||||
with mocker.patch('awx.main.models.mixins.get_custom_venv_choices', return_value=['/var/lib/awx/venv/foobar/']):
|
||||
result = run_module('tower_inventory_source', dict(
|
||||
name='foo',
|
||||
description='this is the changed description',
|
||||
@@ -148,7 +148,7 @@ def test_custom_venv_no_op(run_module, admin_user, base_inventory, mocker, proje
|
||||
), admin_user)
|
||||
assert result.pop('changed', None), result
|
||||
inv_src.refresh_from_db()
|
||||
assert inv_src.custom_virtualenv == '/venv/foobar/'
|
||||
assert inv_src.custom_virtualenv == '/var/lib/awx/venv/foobar/'
|
||||
assert inv_src.description == 'this is the changed description'
|
||||
|
||||
|
||||
|
||||
@@ -81,10 +81,11 @@ def test_resets_job_template_values(run_module, admin_user, project, inventory):
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_job_launch_with_prompting(run_module, admin_user, project, inventory, machine_credential):
|
||||
def test_job_launch_with_prompting(run_module, admin_user, project, organization, inventory, machine_credential):
|
||||
JobTemplate.objects.create(
|
||||
name='foo',
|
||||
project=project,
|
||||
organization=organization,
|
||||
playbook='helloworld.yml',
|
||||
ask_variables_on_launch=True,
|
||||
ask_inventory_on_launch=True,
|
||||
@@ -177,6 +178,38 @@ def test_job_template_with_survey_spec(run_module, admin_user, project, inventor
|
||||
assert ActivityStream.objects.count() == prior_ct
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_job_template_with_wrong_survey_spec(run_module, admin_user, project, inventory, survey_spec):
|
||||
result = run_module('tower_job_template', dict(
|
||||
name='foo',
|
||||
playbook='helloworld.yml',
|
||||
project=project.name,
|
||||
inventory=inventory.name,
|
||||
survey_spec=survey_spec,
|
||||
survey_enabled=True
|
||||
), admin_user)
|
||||
assert not result.get('failed', False), result.get('msg', result)
|
||||
assert result.get('changed', False), result
|
||||
jt = JobTemplate.objects.get(pk=result['id'])
|
||||
|
||||
assert jt.survey_spec == survey_spec
|
||||
|
||||
prior_ct = ActivityStream.objects.count()
|
||||
|
||||
del survey_spec['description']
|
||||
|
||||
result = run_module('tower_job_template', dict(
|
||||
name='foo',
|
||||
playbook='helloworld.yml',
|
||||
project=project.name,
|
||||
inventory=inventory.name,
|
||||
survey_spec=survey_spec,
|
||||
survey_enabled=True
|
||||
), admin_user)
|
||||
assert result.get('failed', True)
|
||||
assert result.get('msg') == "Failed to update survey: Field 'description' is missing from survey spec."
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_job_template_with_survey_encrypted_default(run_module, admin_user, project, inventory, silence_warning):
|
||||
spec = {
|
||||
|
||||
@@ -3,7 +3,7 @@ __metaclass__ = type
|
||||
|
||||
import pytest
|
||||
|
||||
from awx.main.models import NotificationTemplate
|
||||
from awx.main.models import NotificationTemplate, Job
|
||||
|
||||
|
||||
def compare_with_encrypted(model_config, param_config):
|
||||
@@ -109,3 +109,32 @@ def test_deprecated_to_modern_no_op(run_module, admin_user, organization):
|
||||
), admin_user)
|
||||
assert not result.get('failed', False), result.get('msg', result)
|
||||
assert not result.pop('changed', None), result
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_build_notification_message_undefined(run_module, admin_user, organization):
|
||||
"""Job notification templates may encounter undefined values in the context when they are
|
||||
rendered. Make sure that accessing attributes or items of an undefined value returns another
|
||||
instance of Undefined, rather than raising an UndefinedError. This enables the use of expressions
|
||||
like "{{ job.created_by.first_name | default('unknown') }}"."""
|
||||
job = Job.objects.create(name='foobar')
|
||||
|
||||
nt_config = {
|
||||
'url': 'http://www.example.com/hook',
|
||||
'headers': {
|
||||
'X-Custom-Header': 'value123'
|
||||
}
|
||||
}
|
||||
custom_start_template = {'body': '{"started_by": "{{ job.summary_fields.created_by.username | default(\'My Placeholder\') }}"}'}
|
||||
messages = {'started': custom_start_template, 'success': None, 'error': None, 'workflow_approval': None}
|
||||
result = run_module('tower_notification_template', dict(
|
||||
name='foo-notification-template',
|
||||
organization=organization.name,
|
||||
notification_type='webhook',
|
||||
notification_configuration=nt_config,
|
||||
messages=messages,
|
||||
), admin_user)
|
||||
nt = NotificationTemplate.objects.get(id=result['id'])
|
||||
|
||||
body = job.build_notification_message(nt, 'running')
|
||||
assert '{"started_by": "My Placeholder"}' in body[1]
|
||||
|
||||
@@ -12,7 +12,7 @@ def test_create_workflow_job_template(run_module, admin_user, organization, surv
|
||||
'name': 'foo-workflow',
|
||||
'organization': organization.name,
|
||||
'extra_vars': {'foo': 'bar', 'another-foo': {'barz': 'bar2'}},
|
||||
'survey': survey_spec,
|
||||
'survey_spec': survey_spec,
|
||||
'survey_enabled': True,
|
||||
'state': 'present'
|
||||
}, admin_user)
|
||||
@@ -72,7 +72,7 @@ def test_survey_spec_only_changed(run_module, admin_user, organization, survey_s
|
||||
result = run_module('tower_workflow_job_template', {
|
||||
'name': 'foo-workflow',
|
||||
'organization': organization.name,
|
||||
'survey': survey_spec,
|
||||
'survey_spec': survey_spec,
|
||||
'state': 'present'
|
||||
}, admin_user)
|
||||
assert not result.get('failed', False), result.get('msg', result)
|
||||
@@ -81,6 +81,34 @@ def test_survey_spec_only_changed(run_module, admin_user, organization, survey_s
|
||||
assert wfjt.survey_spec == survey_spec
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_survey_spec_only_changed(run_module, admin_user, organization, survey_spec):
|
||||
wfjt = WorkflowJobTemplate.objects.create(
|
||||
organization=organization, name='foo-workflow',
|
||||
survey_enabled=True, survey_spec=survey_spec
|
||||
)
|
||||
result = run_module('tower_workflow_job_template', {
|
||||
'name': 'foo-workflow',
|
||||
'organization': organization.name,
|
||||
'state': 'present'
|
||||
}, admin_user)
|
||||
assert not result.get('failed', False), result.get('msg', result)
|
||||
assert not result.get('changed', True), result
|
||||
wfjt.refresh_from_db()
|
||||
assert wfjt.survey_spec == survey_spec
|
||||
|
||||
del survey_spec['description']
|
||||
|
||||
result = run_module('tower_workflow_job_template', {
|
||||
'name': 'foo-workflow',
|
||||
'organization': organization.name,
|
||||
'survey_spec': survey_spec,
|
||||
'state': 'present'
|
||||
}, admin_user)
|
||||
assert result.get('failed', True)
|
||||
assert result.get('msg') == "Failed to update survey: Field 'description' is missing from survey spec."
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_associate_only_on_success(run_module, admin_user, organization, project):
|
||||
wfjt = WorkflowJobTemplate.objects.create(
|
||||
|
||||
@@ -70,12 +70,84 @@
|
||||
scm_type: git
|
||||
scm_url: https://github.com/ansible/test-playbooks
|
||||
|
||||
- name: Create the job template with survey
|
||||
tower_job_template:
|
||||
name: "{{ jt_name2 }}"
|
||||
project: "{{ proj_name }}"
|
||||
playbook: debug.yml
|
||||
job_type: run
|
||||
state: present
|
||||
inventory: "Demo Inventory"
|
||||
survey_enabled: true
|
||||
ask_variables_on_launch: false
|
||||
survey_spec:
|
||||
name: ''
|
||||
description: ''
|
||||
spec:
|
||||
- question_name: Basic Name
|
||||
question_description: Name
|
||||
required: true
|
||||
type: text
|
||||
variable: basic_name
|
||||
min: 0
|
||||
max: 1024
|
||||
default: ''
|
||||
choices: ''
|
||||
new_question: true
|
||||
- question_name: Choose yes or no?
|
||||
question_description: Choosing yes or no.
|
||||
required: false
|
||||
type: multiplechoice
|
||||
variable: option_true_false
|
||||
min:
|
||||
max:
|
||||
default: 'yes'
|
||||
choices: |-
|
||||
yes
|
||||
no
|
||||
new_question: true
|
||||
|
||||
- name: Kick off a job template with survey
|
||||
tower_job_launch:
|
||||
job_template: "{{ jt_name2 }}"
|
||||
extra_vars:
|
||||
basic_name: My First Variable
|
||||
option_true_false: 'no'
|
||||
ignore_errors: true
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is not failed
|
||||
|
||||
- name: Prompt the job templates extra_vars on launch
|
||||
tower_job_template:
|
||||
name: "{{ jt_name2 }}"
|
||||
state: present
|
||||
ask_variables_on_launch: true
|
||||
|
||||
|
||||
- name: Kick off a job template with extra_vars
|
||||
tower_job_launch:
|
||||
job_template: "{{ jt_name2 }}"
|
||||
extra_vars:
|
||||
basic_name: My First Variable
|
||||
var1: My First Variable
|
||||
var2: My Second Variable
|
||||
ignore_errors: true
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is not failed
|
||||
|
||||
- name: Create a Job Template for testing extra_vars
|
||||
tower_job_template:
|
||||
name: "{{ jt_name2 }}"
|
||||
project: "{{ proj_name }}"
|
||||
playbook: debug.yml
|
||||
job_type: run
|
||||
survey_enabled: false
|
||||
state: present
|
||||
inventory: "Demo Inventory"
|
||||
extra_vars:
|
||||
@@ -85,6 +157,7 @@
|
||||
- name: Launch job template with inventory and credential for prompt on launch
|
||||
tower_job_launch:
|
||||
job_template: "{{ jt_name2 }}"
|
||||
organization: Default
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
|
||||
@@ -66,6 +66,66 @@
|
||||
- result is not failed
|
||||
- "'id' in result['job_info']"
|
||||
|
||||
- name: Kick off a workflow with extra_vars but not enabled
|
||||
tower_workflow_launch:
|
||||
workflow_template: "{{ wfjt_name1 }}"
|
||||
extra_vars:
|
||||
var1: My First Variable
|
||||
var2: My Second Variable
|
||||
ignore_errors: true
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is failed
|
||||
- "'The field extra_vars was specified but the workflow job template does not allow for it to be overridden' in result.errors"
|
||||
|
||||
- name: Prompt the workflow's with survey
|
||||
tower_workflow_job_template:
|
||||
name: "{{ wfjt_name1 }}"
|
||||
state: present
|
||||
survey_enabled: true
|
||||
ask_variables_on_launch: false
|
||||
survey:
|
||||
name: ''
|
||||
description: ''
|
||||
spec:
|
||||
- question_name: Basic Name
|
||||
question_description: Name
|
||||
required: true
|
||||
type: text
|
||||
variable: basic_name
|
||||
min: 0
|
||||
max: 1024
|
||||
default: ''
|
||||
choices: ''
|
||||
new_question: true
|
||||
- question_name: Choose yes or no?
|
||||
question_description: Choosing yes or no.
|
||||
required: false
|
||||
type: multiplechoice
|
||||
variable: option_true_false
|
||||
min:
|
||||
max:
|
||||
default: 'yes'
|
||||
choices: |-
|
||||
yes
|
||||
no
|
||||
new_question: true
|
||||
|
||||
- name: Kick off a workflow with survey
|
||||
tower_workflow_launch:
|
||||
workflow_template: "{{ wfjt_name1 }}"
|
||||
extra_vars:
|
||||
basic_name: My First Variable
|
||||
option_true_false: 'no'
|
||||
ignore_errors: true
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is not failed
|
||||
|
||||
- name: Prompt the workflow's extra_vars on launch
|
||||
tower_workflow_job_template:
|
||||
name: "{{ wfjt_name1 }}"
|
||||
@@ -76,6 +136,7 @@
|
||||
tower_workflow_launch:
|
||||
workflow_template: "{{ wfjt_name1 }}"
|
||||
extra_vars:
|
||||
basic_name: My First Variable
|
||||
var1: My First Variable
|
||||
var2: My Second Variable
|
||||
ignore_errors: true
|
||||
|
||||
@@ -31,7 +31,7 @@ with the current AWX version, for example: `awx_collection/awx-awx-9.2.0.tar.gz`
|
||||
Installing the `tar.gz` involves no special instructions.
|
||||
|
||||
{% else %}
|
||||
This collection should be installed from [Content Hub][https://cloud.redhat.com/ansible/automation-hub/ansible/tower/]
|
||||
This collection should be installed from [Content Hub](https://cloud.redhat.com/ansible/automation-hub/ansible/tower/)
|
||||
|
||||
{% endif %}
|
||||
## Running
|
||||
|
||||
Reference in New Issue
Block a user