Merge pull request #36 from ansible/devel

Rebase
This commit is contained in:
Sean Sullivan
2021-02-03 18:40:12 -06:00
committed by GitHub
604 changed files with 29364 additions and 13070 deletions

View File

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

View File

@@ -38,7 +38,7 @@ RETURN = ''' # '''
EXAMPLES = '''
- name: Set the license using a file
license:
tower_license:
manifest: "/tmp/my_manifest.zip"
eula_accepted: True
'''

View File

@@ -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=''),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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