diff --git a/awx/api/serializers.py b/awx/api/serializers.py index ba6f4d8acc..eab1db1ab0 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -1494,6 +1494,21 @@ class JobSerializer(UnifiedJobSerializer, JobOptionsSerializer): pass return ret + def validate_passwords_needed_to_start(self, attrs, source): + obj = self.context.get('obj') + passwords = self.context.get('passwords') + data = self.context.get('data') + + credential = attrs.get('credential', None) or obj.credential + # fill passwords dict with request data passwords + if credential and credential.passwords_needed: + try: + for p in credential.passwords_needed: + passwords[p] = data[p] + except KeyError: + raise serializers.ValidationError(credential.passwords_needed) + return attrs + class JobCancelSerializer(JobSerializer): @@ -1504,7 +1519,6 @@ class JobCancelSerializer(JobSerializer): class JobRelaunchSerializer(JobSerializer): - passwords_needed_to_start = serializers.SerializerMethodField('get_passwords_needed_to_start') class Meta: fields = ('passwords_needed_to_start',) @@ -1517,22 +1531,6 @@ class JobRelaunchSerializer(JobSerializer): res.update(password_keys) return res - def get_passwords_needed_to_start(self, obj): - if obj: - return obj.passwords_needed_to_start - return '' - - def validate_passwords_needed_to_start(self, attrs, source): - obj = self.context.get('obj') - data = self.context.get('data') - - # Check for passwords needed - needed = self.get_passwords_needed_to_start(obj) - provided = dict([(field, data.get(field, '')) for field in needed]) - if not all(provided.values()): - raise serializers.ValidationError(needed) - return attrs - def validate(self, attrs): obj = self.context.get('obj') if not obj.credential or obj.credential.active is False: @@ -1734,8 +1732,7 @@ class AdHocCommandEventSerializer(BaseSerializer): res['host'] = reverse('api:host_detail', args=(obj.host.pk,)) return res -class JobLaunchSerializer(BaseSerializer): - passwords_needed_to_start = serializers.Field(source='passwords_needed_to_start') +class JobLaunchSerializer(JobSerializer): can_start_without_user_input = serializers.Field(source='can_start_without_user_input') variables_needed_to_start = serializers.Field(source='variables_needed_to_start') credential_needed_to_start = serializers.SerializerMethodField('get_credential_needed_to_start') @@ -1746,7 +1743,6 @@ class JobLaunchSerializer(BaseSerializer): fields = ('can_start_without_user_input', 'passwords_needed_to_start', 'extra_vars', 'ask_variables_on_launch', 'survey_enabled', 'variables_needed_to_start', 'credential', 'credential_needed_to_start',) - read_only_fields = ('ask_variables_on_launch',) write_only_fields = ('credential','extra_vars',) def to_native(self, obj): @@ -1776,21 +1772,6 @@ class JobLaunchSerializer(BaseSerializer): attrs[source] = credential return attrs - def validate_passwords_needed_to_start(self, attrs, source): - obj = self.context.get('obj') - passwords = self.context.get('passwords') - data = self.context.get('data') - - credential = attrs.get('credential', None) or obj.credential - # fill passwords dict with request data passwords - if credential and credential.passwords_needed: - try: - for p in credential.passwords_needed: - passwords[p] = data[p] - except KeyError: - raise serializers.ValidationError(credential.passwords_needed) - return attrs - def validate(self, attrs): obj = self.context.get('obj') extra_vars = attrs.get('extra_vars', {}) diff --git a/awx/main/tests/jobs/__init__.py b/awx/main/tests/jobs/__init__.py index bf5eedafe7..092826ccf0 100644 --- a/awx/main/tests/jobs/__init__.py +++ b/awx/main/tests/jobs/__init__.py @@ -5,6 +5,7 @@ from __future__ import absolute_import from .jobs_monolithic import * # noqa from .job_launch import * # noqa +from .job_relaunch import * # noqa from .survey_password import * # noqa from .start_cancel import * # noqa from .base import * # noqa diff --git a/awx/main/tests/jobs/job_relaunch.py b/awx/main/tests/jobs/job_relaunch.py new file mode 100644 index 0000000000..4d8389a523 --- /dev/null +++ b/awx/main/tests/jobs/job_relaunch.py @@ -0,0 +1,61 @@ +# Copyright (c) 2015 Ansible, Inc. +# All Rights Reserved + +# Python +from __future__ import absolute_import + +# Django +import django +from django.core.urlresolvers import reverse + +# AWX +from awx.main.models import * # noqa +from .base import BaseJobTestMixin + +__all__ = ['JobRelaunchTest',] + +class JobRelaunchTest(BaseJobTestMixin, django.test.TestCase): + def setUp(self): + super(JobRelaunchTest, self).setUp() + + self.url = reverse('api:job_template_list') + self.data = dict( + name = 'launched job template', + job_type = PERM_INVENTORY_DEPLOY, + inventory = self.inv_eng.pk, + project = self.proj_dev.pk, + credential = self.cred_sue.pk, + playbook = self.proj_dev.playbooks[0], + ) + + with self.current_user(self.user_sue): + response = self.post(self.url, self.data, expect=201) + self.launch_url = reverse('api:job_template_launch', + args=(response['id'],)) + response = self.post(self.launch_url, {}, expect=202) + self.relaunch_url = reverse('api:job_relaunch', + args=(response['job'],)) + + def test_relaunch_job(self): + with self.current_user(self.user_sue): + response = self.post(self.relaunch_url, {}, expect=201) + + def test_relaunch_inactive_project(self): + self.proj_dev.mark_inactive() + with self.current_user(self.user_sue): + response = self.post(self.relaunch_url, {}, expect=400) + + def test_relaunch_inactive_inventory(self): + self.inv_eng.mark_inactive() + with self.current_user(self.user_sue): + response = self.post(self.relaunch_url, {}, expect=400) + + def test_relaunch_deleted_inventory(self): + self.inv_eng.delete() + with self.current_user(self.user_sue): + response = self.post(self.relaunch_url, {}, expect=400) + + def test_relaunch_deleted_project(self): + self.proj_dev.delete() + with self.current_user(self.user_sue): + response = self.post(self.relaunch_url, {}, expect=400)