From db6cc7c50b1d775eeaf9bcf1660a3da99858057a Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Fri, 11 May 2018 09:05:25 -0400 Subject: [PATCH] Add exception to allow relaunching callback jobs allows for execute_role level users to directly relaunch callback-type jobs, even though limit has changed from JT, it is a down-selection --- awx/main/access.py | 2 +- awx/main/models/jobs.py | 2 ++ awx/main/models/unified_jobs.py | 12 +++++++----- awx/main/tests/functional/api/test_job.py | 2 +- awx/main/tests/functional/test_rbac_job.py | 11 +++++++++++ 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/awx/main/access.py b/awx/main/access.py index 67bb01905d..47534928bd 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -1507,7 +1507,7 @@ class JobAccess(BaseAccess): elif not jt_access: return False - org_access = obj.inventory and self.user in obj.inventory.organization.inventory_admin_role + org_access = bool(obj.inventory) and self.user in obj.inventory.organization.inventory_admin_role project_access = obj.project is None or self.user in obj.project.admin_role credential_access = all([self.user in cred.use_role for cred in obj.credentials.all()]) diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py index 80280636cc..dd05e2359a 100644 --- a/awx/main/models/jobs.py +++ b/awx/main/models/jobs.py @@ -975,6 +975,8 @@ class JobLaunchConfig(LaunchTimeConfig): return True for field_name, ask_field_name in ask_mapping.items(): if field_name in prompts and not getattr(template, ask_field_name): + if field_name == 'limit' and self.job and self.job.launch_type == 'callback': + continue # exception for relaunching callbacks return True else: return False diff --git a/awx/main/models/unified_jobs.py b/awx/main/models/unified_jobs.py index f758a230a9..fd3d6f4082 100644 --- a/awx/main/models/unified_jobs.py +++ b/awx/main/models/unified_jobs.py @@ -814,7 +814,7 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique # Done. return result - def copy_unified_job(self, limit=None): + def copy_unified_job(self, _eager_fields=None, **new_prompts): ''' Returns saved object, including related fields. Create a copy of this unified job for the purpose of relaunch @@ -824,12 +824,14 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique parent_field_name = unified_job_class._get_parent_field_name() fields = unified_jt_class._get_unified_job_field_names() | set([parent_field_name]) - create_data = {"launch_type": "relaunch"} - if limit: - create_data["limit"] = limit + create_data = {} + if _eager_fields: + create_data = _eager_fields.copy() + create_data["launch_type"] = "relaunch" prompts = self.launch_prompts() - if self.unified_job_template and prompts: + if self.unified_job_template and (prompts is not None): + prompts.update(new_prompts) prompts['_eager_fields'] = create_data unified_job = self.unified_job_template.create_unified_job(**prompts) else: diff --git a/awx/main/tests/functional/api/test_job.py b/awx/main/tests/functional/api/test_job.py index db5da93c2f..0e939269b6 100644 --- a/awx/main/tests/functional/api/test_job.py +++ b/awx/main/tests/functional/api/test_job.py @@ -75,7 +75,7 @@ def test_job_relaunch_on_failed_hosts(post, inventory, project, machine_credenti project=project ) jt.credentials.add(machine_credential) - job = jt.create_unified_job(_eager_fields={'status': 'failed', 'limit': 'host1,host2,host3'}) + job = jt.create_unified_job(_eager_fields={'status': 'failed'}, limit='host1,host2,host3') job.job_events.create(event='playbook_on_stats') job.job_host_summaries.create(host=h1, failed=False, ok=1, changed=0, failures=0, host_name=h1.name) job.job_host_summaries.create(host=h2, failed=False, ok=0, changed=1, failures=0, host_name=h2.name) diff --git a/awx/main/tests/functional/test_rbac_job.py b/awx/main/tests/functional/test_rbac_job.py index 55785f9c73..9c4b2b100a 100644 --- a/awx/main/tests/functional/test_rbac_job.py +++ b/awx/main/tests/functional/test_rbac_job.py @@ -223,6 +223,17 @@ class TestJobRelaunchAccess: job.credentials.add(net_credential) assert not rando.can_access(Job, 'start', job, validate_license=False) + @pytest.mark.job_runtime_vars + def test_callback_relaunchable_by_user(self, job_template, rando): + job = job_template.create_unified_job( + _eager_fields={'launch_type': 'callback'}, + limit='host2' + ) + assert 'limit' in job.launch_config.prompts_dict() # sanity assertion + job_template.execute_role.members.add(rando) + can_access, messages = rando.can_access_with_errors(Job, 'start', job, validate_license=False) + assert can_access, messages + @pytest.mark.django_db class TestJobAndUpdateCancels: