From 4de4d32aa61d44b1be7fb15f4653204389183c50 Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Thu, 30 Apr 2015 09:30:29 -0400 Subject: [PATCH 1/6] refactored job launch serializer * inherit from jobserializer ** validate password logic moved to job serializer to be shared * Added job relaunch test cases --- awx/api/serializers.py | 51 ++++++++---------------- awx/main/tests/jobs/__init__.py | 1 + awx/main/tests/jobs/job_relaunch.py | 61 +++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 35 deletions(-) create mode 100644 awx/main/tests/jobs/job_relaunch.py 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) From 4ecea7dc5b0abbe1c5700b6bf4182c14df8a04aa Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Thu, 30 Apr 2015 11:50:52 -0400 Subject: [PATCH 2/6] moved job launch to be near job relaunch and cancel --- awx/api/serializers.py | 144 ++++++++++++++++++++--------------------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index eab1db1ab0..c3ba2a6a8f 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -1518,6 +1518,78 @@ class JobCancelSerializer(JobSerializer): fields = ('can_cancel',) +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') + survey_enabled = serializers.SerializerMethodField('get_survey_enabled') + + class Meta: + 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',) + write_only_fields = ('credential','extra_vars',) + + def to_native(self, obj): + res = super(JobLaunchSerializer, self).to_native(obj) + view = self.context.get('view', None) + if obj and hasattr(view, '_raw_data_form_marker'): + if obj.passwords_needed_to_start: + password_keys = dict([(p, u'') for p in obj.passwords_needed_to_start]) + res.update(password_keys) + if self.get_credential_needed_to_start(obj) is True: + res.update(dict(credential='')) + return res + + def get_credential_needed_to_start(self, obj): + return not (obj and obj.credential and obj.credential.active) + + def get_survey_enabled(self, obj): + if obj: + return obj.survey_enabled and 'spec' in obj.survey_spec + return False + + def validate_credential(self, attrs, source): + obj = self.context.get('obj') + credential = attrs.get(source, None) or (obj and obj.credential) + if not credential or not credential.active: + raise serializers.ValidationError('Credential not provided') + attrs[source] = credential + return attrs + + def validate(self, attrs): + obj = self.context.get('obj') + extra_vars = attrs.get('extra_vars', {}) + try: + extra_vars = literal_eval(extra_vars) + extra_vars = json.dumps(extra_vars) + except Exception: + pass + + try: + extra_vars = json.loads(extra_vars) + except (ValueError, TypeError): + try: + extra_vars = yaml.safe_load(extra_vars) + except (yaml.YAMLError, TypeError, AttributeError): + raise serializers.ValidationError(dict(extra_vars=['Must be valid JSON or YAML'])) + + if not isinstance(extra_vars, dict): + extra_vars = {} + + if self.get_survey_enabled(obj): + validation_errors = obj.survey_variable_validation(extra_vars) + if validation_errors: + raise serializers.ValidationError(dict(variables_needed_to_start=validation_errors)) + + if obj.job_type != PERM_INVENTORY_SCAN and (obj.project is None or not obj.project.active): + raise serializers.ValidationError(dict(errors=["Job Template Project is missing or undefined"])) + if obj.inventory is None or not obj.inventory.active: + raise serializers.ValidationError(dict(errors=["Job Template Inventory is missing or undefined"])) + + return attrs + + class JobRelaunchSerializer(JobSerializer): class Meta: @@ -1732,78 +1804,6 @@ class AdHocCommandEventSerializer(BaseSerializer): res['host'] = reverse('api:host_detail', args=(obj.host.pk,)) return res -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') - survey_enabled = serializers.SerializerMethodField('get_survey_enabled') - - class Meta: - model = JobTemplate - 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',) - write_only_fields = ('credential','extra_vars',) - - def to_native(self, obj): - res = super(JobLaunchSerializer, self).to_native(obj) - view = self.context.get('view', None) - if obj and hasattr(view, '_raw_data_form_marker'): - if obj.passwords_needed_to_start: - password_keys = dict([(p, u'') for p in obj.passwords_needed_to_start]) - res.update(password_keys) - if self.get_credential_needed_to_start(obj) is True: - res.update(dict(credential='')) - return res - - def get_credential_needed_to_start(self, obj): - return not (obj and obj.credential and obj.credential.active) - - def get_survey_enabled(self, obj): - if obj: - return obj.survey_enabled and 'spec' in obj.survey_spec - return False - - def validate_credential(self, attrs, source): - obj = self.context.get('obj') - credential = attrs.get(source, None) or (obj and obj.credential) - if not credential or not credential.active: - raise serializers.ValidationError('Credential not provided') - attrs[source] = credential - return attrs - - def validate(self, attrs): - obj = self.context.get('obj') - extra_vars = attrs.get('extra_vars', {}) - try: - extra_vars = literal_eval(extra_vars) - extra_vars = json.dumps(extra_vars) - except Exception: - pass - - try: - extra_vars = json.loads(extra_vars) - except (ValueError, TypeError): - try: - extra_vars = yaml.safe_load(extra_vars) - except (yaml.YAMLError, TypeError, AttributeError): - raise serializers.ValidationError(dict(extra_vars=['Must be valid JSON or YAML'])) - - if not isinstance(extra_vars, dict): - extra_vars = {} - - if self.get_survey_enabled(obj): - validation_errors = obj.survey_variable_validation(extra_vars) - if validation_errors: - raise serializers.ValidationError(dict(variables_needed_to_start=validation_errors)) - - if obj.job_type != PERM_INVENTORY_SCAN and (obj.project is None or not obj.project.active): - raise serializers.ValidationError(dict(errors=["Job Template Project is missing or undefined"])) - if obj.inventory is None or not obj.inventory.active: - raise serializers.ValidationError(dict(errors=["Job Template Inventory is missing or undefined"])) - - return attrs - class ScheduleSerializer(BaseSerializer): class Meta: From a29868c55152256ffee6e37cfd30e7329eafe8e1 Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Thu, 30 Apr 2015 12:42:24 -0400 Subject: [PATCH 3/6] moved passwords_needed_to_start to job options. have job launch inherit from jobtemplate and job options --- awx/api/serializers.py | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index c3ba2a6a8f..7de42cdcce 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -1321,12 +1321,13 @@ class CredentialSerializer(BaseSerializer): class JobOptionsSerializer(BaseSerializer): + passwords_needed_to_start = serializers.Field(source='passwords_needed_to_start') class Meta: fields = ('*', 'job_type', 'inventory', 'project', 'playbook', 'credential', 'cloud_credential', 'forks', 'limit', 'verbosity', 'extra_vars', 'job_tags', 'force_handlers', - 'skip_tags', 'start_at_task') + 'skip_tags', 'start_at_task', 'passwords_needed_to_start',) def get_related(self, obj): res = super(JobOptionsSerializer, self).get_related(obj) @@ -1372,6 +1373,21 @@ class JobOptionsSerializer(BaseSerializer): raise serializers.ValidationError('Must select playbook for project') 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 + class JobTemplateSerializer(UnifiedJobTemplateSerializer, JobOptionsSerializer): @@ -1418,12 +1434,11 @@ class JobTemplateSerializer(UnifiedJobTemplateSerializer, JobOptionsSerializer): class JobSerializer(UnifiedJobSerializer, JobOptionsSerializer): - passwords_needed_to_start = serializers.Field(source='passwords_needed_to_start') ask_variables_on_launch = serializers.Field(source='ask_variables_on_launch') class Meta: model = Job - fields = ('*', 'job_template', 'passwords_needed_to_start', 'ask_variables_on_launch') + fields = ('*', 'job_template', 'ask_variables_on_launch') def get_related(self, obj): res = super(JobSerializer, self).get_related(obj) @@ -1494,22 +1509,6 @@ 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): can_cancel = serializers.BooleanField(source='can_cancel', read_only=True) @@ -1518,7 +1517,7 @@ class JobCancelSerializer(JobSerializer): fields = ('can_cancel',) -class JobLaunchSerializer(JobSerializer): +class JobLaunchSerializer(JobTemplateSerializer, JobOptionsSerializer): 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') From c3cb99b2d2441e0772a6e4e0856ac1c1b10255d0 Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Thu, 30 Apr 2015 18:41:27 -0400 Subject: [PATCH 4/6] moved passwords_needed_to_start to it's own class --- awx/api/serializers.py | 63 ++++++++++++++++++++++++++++-------------- awx/api/views.py | 4 +-- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 7de42cdcce..5ffca45ddb 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -1321,13 +1321,12 @@ class CredentialSerializer(BaseSerializer): class JobOptionsSerializer(BaseSerializer): - passwords_needed_to_start = serializers.Field(source='passwords_needed_to_start') class Meta: fields = ('*', 'job_type', 'inventory', 'project', 'playbook', 'credential', 'cloud_credential', 'forks', 'limit', 'verbosity', 'extra_vars', 'job_tags', 'force_handlers', - 'skip_tags', 'start_at_task', 'passwords_needed_to_start',) + 'skip_tags', 'start_at_task',) def get_related(self, obj): res = super(JobOptionsSerializer, self).get_related(obj) @@ -1373,21 +1372,6 @@ class JobOptionsSerializer(BaseSerializer): raise serializers.ValidationError('Must select playbook for project') 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 - class JobTemplateSerializer(UnifiedJobTemplateSerializer, JobOptionsSerializer): @@ -1395,7 +1379,7 @@ class JobTemplateSerializer(UnifiedJobTemplateSerializer, JobOptionsSerializer): class Meta: model = JobTemplate - fields = ('*', 'host_config_key', 'ask_variables_on_launch', 'survey_enabled', 'become_enabled') + fields = ('*', 'host_config_key', 'ask_variables_on_launch', 'survey_enabled', 'become_enabled',) def get_related(self, obj): res = super(JobTemplateSerializer, self).get_related(obj) @@ -1509,6 +1493,42 @@ class JobSerializer(UnifiedJobSerializer, JobOptionsSerializer): pass return ret +''' +This Serializer requires that model be set to something with a 'passwords_needed_to_start' +variable. +Inheriting classes should add 'passwords_needed_to_start' to their list of fields +to take advantage of the validate functionality. +''' +class PasswordsNeededToStartMixin(BaseSerializer): + + passwords_needed_to_start = serializers.Field(source='passwords_needed_to_start') + + def validate_passwords_needed_to_start(self, attrs, source): + obj = self.context.get('obj') + passwords = {} + + 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] = self.init_data[p] + except KeyError: + raise serializers.ValidationError(credential.passwords_needed) + + # Use the context dict to allow the view to access the passwords + self.context['passwords'] = passwords + return attrs + + +class JobTemplateSerializerWithPasswordsNeededToStart(JobTemplateSerializer, PasswordsNeededToStartMixin): + pass + + +class JobSerializerWithPasswordsNeededToStart(JobSerializer, PasswordsNeededToStartMixin): + pass + + class JobCancelSerializer(JobSerializer): can_cancel = serializers.BooleanField(source='can_cancel', read_only=True) @@ -1517,7 +1537,8 @@ class JobCancelSerializer(JobSerializer): fields = ('can_cancel',) -class JobLaunchSerializer(JobTemplateSerializer, JobOptionsSerializer): +class JobLaunchSerializer(JobTemplateSerializerWithPasswordsNeededToStart): + 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') @@ -1589,7 +1610,7 @@ class JobLaunchSerializer(JobTemplateSerializer, JobOptionsSerializer): return attrs -class JobRelaunchSerializer(JobSerializer): +class JobRelaunchSerializer(JobSerializerWithPasswordsNeededToStart): class Meta: fields = ('passwords_needed_to_start',) @@ -1598,7 +1619,7 @@ class JobRelaunchSerializer(JobSerializer): res = super(JobRelaunchSerializer, self).to_native(obj) view = self.context.get('view', None) if hasattr(view, '_raw_data_form_marker'): - password_keys = dict([(p, u'') for p in self.get_passwords_needed_to_start(obj)]) + password_keys = dict([(p, u'') for p in obj.passwords_needed_to_start]) res.update(password_keys) return res diff --git a/awx/api/views.py b/awx/api/views.py index e52dd77009..751c9da7a9 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -1449,7 +1449,7 @@ class JobTemplateLaunch(RetrieveAPIView, GenericAPIView): request.DATA['credential'] = request.DATA['credential_id'] passwords = {} - serializer = self.serializer_class(data=request.DATA, context={'obj': obj, 'data': request.DATA, 'passwords': passwords}) + serializer = self.serializer_class(data=request.DATA, context={'obj': obj}) if not serializer.is_valid(): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @@ -1458,7 +1458,7 @@ class JobTemplateLaunch(RetrieveAPIView, GenericAPIView): } if 'extra_vars' in request.DATA: kv['extra_vars'] = request.DATA['extra_vars'] - kv.update(passwords) + kv.update(serializer.context['passwords']) new_job = obj.create_unified_job(**kv) result = new_job.signal_start(**kv) From 52cf7920731747cc9b8b3c30140560e3c7e586d1 Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Mon, 4 May 2015 07:30:03 -0400 Subject: [PATCH 5/6] flake8 --- awx/api/views.py | 1 - awx/main/tests/jobs/job_relaunch.py | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/awx/api/views.py b/awx/api/views.py index 751c9da7a9..4bce22b70f 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -1448,7 +1448,6 @@ class JobTemplateLaunch(RetrieveAPIView, GenericAPIView): if 'credential' not in request.DATA and 'credential_id' in request.DATA: request.DATA['credential'] = request.DATA['credential_id'] - passwords = {} serializer = self.serializer_class(data=request.DATA, context={'obj': obj}) if not serializer.is_valid(): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) diff --git a/awx/main/tests/jobs/job_relaunch.py b/awx/main/tests/jobs/job_relaunch.py index 4d8389a523..3068b8ee55 100644 --- a/awx/main/tests/jobs/job_relaunch.py +++ b/awx/main/tests/jobs/job_relaunch.py @@ -38,24 +38,24 @@ class JobRelaunchTest(BaseJobTestMixin, django.test.TestCase): def test_relaunch_job(self): with self.current_user(self.user_sue): - response = self.post(self.relaunch_url, {}, expect=201) + 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) + 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) + 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) + 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) + self.post(self.relaunch_url, {}, expect=400) From 475cac044b17a625bab2078da79697babe9f3c9c Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Mon, 4 May 2015 08:01:18 -0400 Subject: [PATCH 6/6] job launch tests --- awx/main/tests/jobs/job_launch.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/awx/main/tests/jobs/job_launch.py b/awx/main/tests/jobs/job_launch.py index 26124b1f61..7d078e88bb 100644 --- a/awx/main/tests/jobs/job_launch.py +++ b/awx/main/tests/jobs/job_launch.py @@ -148,6 +148,18 @@ class JobTemplateLaunchTest(BaseJobTestMixin, django.test.TestCase): with self.current_user(self.user_sue): self.post(self.launch_url, {}, expect=400) + # pass in a credential NOT viewable by the current logged in user + def test_explicit_credential_permission_denied(self): + #self.cred_sue.mark_inactive() + with self.current_user(self.user_doug): + self.post(self.launch_url, {'credential': self.cred_sue.pk}, expect=403) + + def test_explicit_deleted_credential(self): + self.cred_sue.mark_inactive() + with self.current_user(self.user_alex): + self.post(self.launch_url, {'credential': self.cred_sue.pk}, expect=400) + + class JobTemplateLaunchPasswordsTest(BaseJobTestMixin, django.test.TestCase): def setUp(self): super(JobTemplateLaunchPasswordsTest, self).setUp()