From dfff6b218ae036c231a9c35ea92b369c28aeb103 Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Mon, 10 Aug 2015 11:05:27 -0400 Subject: [PATCH] correctly inherit extra_vars * inherit extra_vars from job_template * overwrite with extra vars survey default vars * overwrite with job extra_vars * bypass extra_vars inherit logic for job relaunch * with tests --- awx/api/views.py | 1 + awx/main/models/jobs.py | 24 ++++++++- awx/main/models/unified_jobs.py | 1 + awx/main/tests/jobs/__init__.py | 1 + awx/main/tests/jobs/job_relaunch.py | 75 +++++++++++++++++++++++++++++ awx/main/tests/jobs/start_cancel.py | 24 --------- 6 files changed, 100 insertions(+), 26 deletions(-) create mode 100644 awx/main/tests/jobs/job_relaunch.py diff --git a/awx/api/views.py b/awx/api/views.py index a0d383d758..9598d21b42 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -2207,6 +2207,7 @@ class JobRelaunch(RetrieveAPIView, GenericAPIView): if not serializer.is_valid(): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + obj.launch_type = 'relaunch' new_job = obj.copy() result = new_job.signal_start(**request.DATA) if not result: diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py index 52b2ccd281..ebdca5a469 100644 --- a/awx/main/models/jobs.py +++ b/awx/main/models/jobs.py @@ -284,15 +284,35 @@ class JobTemplate(UnifiedJobTemplate, JobOptions): return errors def _update_unified_job_kwargs(self, **kwargs): - # Overwrite job extra_vars with job template extra vars + if 'launch_type' in kwargs and kwargs['launch_type'] == 'relaunch': + return kwargs + + # Job Template extra_vars extra_vars = self.extra_vars_dict - # Overwrite with job template survey default vars + # Overwrite with job template extra vars with survey default vars if self.survey_enabled and 'spec' in self.survey_spec: for survey_element in self.survey_spec.get("spec", []): if survey_element['default']: extra_vars[survey_element['variable']] = survey_element['default'] + # transform to dict + if 'extra_vars' in kwargs: + kwargs_extra_vars = kwargs['extra_vars'] + if not isinstance(kwargs_extra_vars, dict): + try: + kwargs_extra_vars = json.loads(kwargs_extra_vars) + except Exception: + try: + yaml.safe_load(kwargs_extra_vars) + except: + kwargs_extra_vars = {} + else: + kwargs_extra_vars = {} + + # Overwrite job template extra vars with explicit job extra vars + # and add on job extra vars + extra_vars.update(kwargs_extra_vars) kwargs['extra_vars'] = json.dumps(extra_vars) return kwargs diff --git a/awx/main/models/unified_jobs.py b/awx/main/models/unified_jobs.py index 00deb50691..e9b5e4bfd2 100644 --- a/awx/main/models/unified_jobs.py +++ b/awx/main/models/unified_jobs.py @@ -343,6 +343,7 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique LAUNCH_TYPE_CHOICES = [ ('manual', _('Manual')), # Job was started manually by a user. + ('relaunch', _('Relaunch')), # Job was started via relaunch. ('callback', _('Callback')), # Job was started via host callback. ('scheduled', _('Scheduled')), # Job was started from a schedule. ('dependency', _('Dependency')), # Job was started as a dependency of another job. 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..04ae95ba75 --- /dev/null +++ b/awx/main/tests/jobs/job_relaunch.py @@ -0,0 +1,75 @@ +# Copyright (c) 2015 Ansible, Inc. +# All Rights Reserved + +# Python +from __future__ import absolute_import +import json + +# Django +from django.core.urlresolvers import reverse + +# AWX +from awx.main.models import * # noqa +from awx.main.tests.base import BaseLiveServerTest +from .base import BaseJobTestMixin + +__all__ = ['JobRelaunchTest',] + +class JobRelaunchTest(BaseJobTestMixin, BaseLiveServerTest): + + def test_job_relaunch(self): + job = self.make_job(self.jt_ops_east_run, self.user_sue, 'success') + url = reverse('api:job_relaunch', args=(job.pk,)) + with self.current_user(self.user_sue): + response = self.post(url, {}, expect=201) + j = Job.objects.get(pk=response['job']) + self.assertTrue(j.status == 'successful') + self.assertEqual(j.launch_type, 'relaunch') + # Test with a job that prompts for SSH and sudo passwords. + job = self.make_job(self.jt_sup_run, self.user_sue, 'success') + url = reverse('api:job_start', args=(job.pk,)) + with self.current_user(self.user_sue): + response = self.get(url) + self.assertEqual(set(response['passwords_needed_to_start']), + set(['ssh_password', 'become_password'])) + data = dict() + response = self.post(url, data, expect=400) + data['ssh_password'] = 'sshpass' + response = self.post(url, data, expect=400) + data2 = dict(become_password='sudopass') + response = self.post(url, data2, expect=400) + data.update(data2) + response = self.post(url, data, expect=202) + job = Job.objects.get(pk=job.pk) + + # Create jt with no extra_vars + # Launch j1 with runtime extra_vars + # Assign extra_vars to jt backing job + # Relaunch j1 + # j2 should not contain jt extra_vars + def test_relaunch_job_does_not_inherit_jt_extra_vars(self): + jt_extra_vars = { + "hello": "world" + } + j_extra_vars = { + "goodbye": "cruel universe" + } + job = self.make_job(self.jt_ops_east_run, self.user_sue, 'success', extra_vars=j_extra_vars) + url = reverse('api:job_relaunch', args=(job.pk,)) + with self.current_user(self.user_sue): + response = self.post(url, {}, expect=201) + j = Job.objects.get(pk=response['job']) + self.assertTrue(j.status == 'successful') + + self.jt_ops_east_run.extra_vars = jt_extra_vars + self.jt_ops_east_run.save() + + response = self.post(url, {}, expect=201) + j = Job.objects.get(pk=response['job']) + self.assertTrue(j.status == 'successful') + + resp_extra_vars = json.loads(response['extra_vars']) + self.assertNotIn("hello", resp_extra_vars) + self.assertEqual(resp_extra_vars, j_extra_vars) + + diff --git a/awx/main/tests/jobs/start_cancel.py b/awx/main/tests/jobs/start_cancel.py index 8aae66b402..54ff2255d9 100644 --- a/awx/main/tests/jobs/start_cancel.py +++ b/awx/main/tests/jobs/start_cancel.py @@ -115,30 +115,6 @@ class JobStartCancelTest(BaseJobTestMixin, BaseLiveServerTest): # FIXME: Test with other users, test when passwords are required. - def test_job_relaunch(self): - job = self.make_job(self.jt_ops_east_run, self.user_sue, 'success') - url = reverse('api:job_relaunch', args=(job.pk,)) - with self.current_user(self.user_sue): - response = self.post(url, {}, expect=201) - j = Job.objects.get(pk=response['job']) - self.assertTrue(j.status == 'successful') - # Test with a job that prompts for SSH and sudo passwords. - job = self.make_job(self.jt_sup_run, self.user_sue, 'success') - url = reverse('api:job_start', args=(job.pk,)) - with self.current_user(self.user_sue): - response = self.get(url) - self.assertEqual(set(response['passwords_needed_to_start']), - set(['ssh_password', 'become_password'])) - data = dict() - response = self.post(url, data, expect=400) - data['ssh_password'] = 'sshpass' - response = self.post(url, data, expect=400) - data2 = dict(become_password='sudopass') - response = self.post(url, data2, expect=400) - data.update(data2) - response = self.post(url, data, expect=202) - job = Job.objects.get(pk=job.pk) - def test_job_cancel(self): #job = self.job_ops_east_run job = self.make_job(self.jt_ops_east_run, self.user_sue, 'new')