From b0f5d2f82d276a6e871084cf82338580ee367789 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Fri, 21 Jul 2017 16:18:40 -0400 Subject: [PATCH] allow Job Templates to launch with *only* a vault credential see: https://github.com/ansible/ansible-tower/issues/7252 --- awx/api/serializers.py | 7 +++- awx/main/models/jobs.py | 6 ++- .../functional/api/test_job_runtime_params.py | 41 +++++++++++++++++++ .../multi-credential.directive.js | 4 +- 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 62a52ae703..63e6025211 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -2460,12 +2460,17 @@ class JobTemplateSerializer(JobTemplateMixin, UnifiedJobTemplateSerializer, JobO inventory = get_field_from_model_or_attrs('inventory') credential = get_field_from_model_or_attrs('credential') + vault_credential = get_field_from_model_or_attrs('vault_credential') project = get_field_from_model_or_attrs('project') prompting_error_message = _("Must either set a default value or ask to prompt on launch.") if project is None: raise serializers.ValidationError({'project': _("Job types 'run' and 'check' must have assigned a project.")}) - elif credential is None and not get_field_from_model_or_attrs('ask_credential_on_launch'): + elif all([ + credential is None, + vault_credential is None, + not get_field_from_model_or_attrs('ask_credential_on_launch'), + ]): raise serializers.ValidationError({'credential': prompting_error_message}) elif inventory is None and not get_field_from_model_or_attrs('ask_inventory_on_launch'): raise serializers.ValidationError({'inventory': prompting_error_message}) diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py index a32cb1dae8..34141765eb 100644 --- a/awx/main/models/jobs.py +++ b/awx/main/models/jobs.py @@ -314,10 +314,12 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour resources_needed_to_start.append('inventory') if not self.ask_inventory_on_launch: validation_errors['inventory'] = [_("Job Template must provide 'inventory' or allow prompting for it."),] - if self.credential is None: + if self.credential is None and self.vault_credential is None: resources_needed_to_start.append('credential') if not self.ask_credential_on_launch: validation_errors['credential'] = [_("Job Template must provide 'credential' or allow prompting for it."),] + elif self.credential is None and self.ask_credential_on_launch: + resources_needed_to_start.append('credential') # Job type dependent checks if self.project is None: @@ -695,7 +697,7 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin): if not super(Job, self).can_start: return False - if not (self.credential): + if not (self.credential) and not (self.vault_credential): return False return True diff --git a/awx/main/tests/functional/api/test_job_runtime_params.py b/awx/main/tests/functional/api/test_job_runtime_params.py index b75ad32672..4d7f1e3ecd 100644 --- a/awx/main/tests/functional/api/test_job_runtime_params.py +++ b/awx/main/tests/functional/api/test_job_runtime_params.py @@ -316,6 +316,47 @@ def test_job_launch_JT_enforces_unique_extra_credential_kinds(machine_credential assert validated is False +@pytest.mark.django_db +def test_job_launch_with_no_credentials(deploy_jobtemplate): + deploy_jobtemplate.credential = None + deploy_jobtemplate.vault_credential = None + serializer = JobLaunchSerializer( + instance=deploy_jobtemplate, data={}, + context={'obj': deploy_jobtemplate, 'data': {}, 'passwords': {}}) + validated = serializer.is_valid() + assert validated is False + assert serializer.errors['credential'] == ["Job Template 'credential' is missing or undefined."] + + +@pytest.mark.django_db +def test_job_launch_with_only_vault_credential(vault_credential, deploy_jobtemplate): + deploy_jobtemplate.credential = None + deploy_jobtemplate.vault_credential = vault_credential + serializer = JobLaunchSerializer( + instance=deploy_jobtemplate, data={}, + context={'obj': deploy_jobtemplate, 'data': {}, 'passwords': {}}) + validated = serializer.is_valid() + assert validated + + prompted_fields, ignored_fields = deploy_jobtemplate._accept_or_ignore_job_kwargs(**{}) + job_obj = deploy_jobtemplate.create_unified_job(**prompted_fields) + + assert job_obj.vault_credential.pk == vault_credential.pk + + +@pytest.mark.django_db +def test_job_launch_with_vault_credential_ask_for_machine(vault_credential, deploy_jobtemplate): + deploy_jobtemplate.credential = None + deploy_jobtemplate.ask_credential_on_launch = True + deploy_jobtemplate.vault_credential = vault_credential + serializer = JobLaunchSerializer( + instance=deploy_jobtemplate, data={}, + context={'obj': deploy_jobtemplate, 'data': {}, 'passwords': {}}) + validated = serializer.is_valid() + assert validated is False + assert serializer.errors['credential'] == ["Job Template 'credential' is missing or undefined."] + + @pytest.mark.django_db def test_job_launch_JT_with_default_vault_credential(machine_credential, vault_credential, deploy_jobtemplate): deploy_jobtemplate.credential = machine_credential diff --git a/awx/ui/client/src/templates/job_templates/multi-credential/multi-credential.directive.js b/awx/ui/client/src/templates/job_templates/multi-credential/multi-credential.directive.js index f575c7fd44..a2cf5ea85d 100644 --- a/awx/ui/client/src/templates/job_templates/multi-credential/multi-credential.directive.js +++ b/awx/ui/client/src/templates/job_templates/multi-credential/multi-credential.directive.js @@ -21,6 +21,7 @@ export default ['templateUrl', '$compile', if (!$scope.selectedCredentials) { $scope.selectedCredentials = { machine: null, + vault: null, extra: [] }; } @@ -39,7 +40,8 @@ export default ['templateUrl', '$compile', } $scope.credentialNotPresent = !$scope.prompt && - $scope.selectedCredentials.machine === null; + $scope.selectedCredentials.machine === null && + $scope.selectedCredentials.vault === null; }); $scope.removeCredential = function(credToRemove) {