From 63e9aed6017de99aa184568db2ca829aab985f5b Mon Sep 17 00:00:00 2001 From: Seth Foster Date: Mon, 16 Dec 2019 13:43:42 -0500 Subject: [PATCH] Prevent running jobs from blocking project updates A running job that has a project update will block that update from running. This fix removes the block. Adds a functional test that sets up a job in "running" state, and starts a project update that is in "pending" state. Assert that the task manager and dependency graph .is_job_blocked methods both return False. issue #5153 --- awx/main/scheduler/dependency_graph.py | 9 ++----- .../task_management/test_scheduler.py | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/awx/main/scheduler/dependency_graph.py b/awx/main/scheduler/dependency_graph.py index bbe2e71ebaf8..2d180bbceddb 100644 --- a/awx/main/scheduler/dependency_graph.py +++ b/awx/main/scheduler/dependency_graph.py @@ -15,7 +15,6 @@ class DependencyGraph(object): INVENTORY_UPDATES = 'inventory_updates' JOB_TEMPLATE_JOBS = 'job_template_jobs' - JOB_PROJECT_IDS = 'job_project_ids' JOB_INVENTORY_IDS = 'job_inventory_ids' SYSTEM_JOB = 'system_job' @@ -41,8 +40,6 @@ def __init__(self, queue): Track runnable job related project and inventory to ensure updates don't run while a job needing those resources is running. ''' - # project_id -> True / False - self.data[self.JOB_PROJECT_IDS] = {} # inventory_id -> True / False self.data[self.JOB_INVENTORY_IDS] = {} @@ -66,7 +63,7 @@ def add_latest_project_update(self, job): def get_now(self): return tz_now() - + def mark_system_job(self): self.data[self.SYSTEM_JOB] = False @@ -81,15 +78,13 @@ def mark_inventory_source_update(self, inventory_source_id): def mark_job_template_job(self, job): self.data[self.JOB_INVENTORY_IDS][job.inventory_id] = False - self.data[self.JOB_PROJECT_IDS][job.project_id] = False self.data[self.JOB_TEMPLATE_JOBS][job.job_template_id] = False def mark_workflow_job(self, job): self.data[self.WORKFLOW_JOB_TEMPLATES_JOBS][job.workflow_job_template_id] = False def can_project_update_run(self, job): - return self.data[self.JOB_PROJECT_IDS].get(job.project_id, True) and \ - self.data[self.PROJECT_UPDATES].get(job.project_id, True) + return self.data[self.PROJECT_UPDATES].get(job.project_id, True) def can_inventory_update_run(self, job): return self.data[self.JOB_INVENTORY_IDS].get(job.inventory_source.inventory_id, True) and \ diff --git a/awx/main/tests/functional/task_management/test_scheduler.py b/awx/main/tests/functional/task_management/test_scheduler.py index 6a554380beb1..f76e998355f2 100644 --- a/awx/main/tests/functional/task_management/test_scheduler.py +++ b/awx/main/tests/functional/task_management/test_scheduler.py @@ -4,6 +4,7 @@ from datetime import timedelta from awx.main.scheduler import TaskManager +from awx.main.scheduler.dependency_graph import DependencyGraph from awx.main.utils import encrypt_field from awx.main.models import WorkflowJobTemplate, JobTemplate @@ -326,3 +327,29 @@ def test_shared_dependencies_launch(default_instance_group, job_template_factory iu = [x for x in ii.inventory_updates.all()] assert len(pu) == 1 assert len(iu) == 1 + + +@pytest.mark.django_db +def test_job_not_blocking_project_update(default_instance_group, job_template_factory): + objects = job_template_factory('jt', organization='org1', project='proj', + inventory='inv', credential='cred', + jobs=["job"]) + job = objects.jobs["job"] + job.instance_group = default_instance_group + job.status = "running" + job.save() + + with mock.patch("awx.main.scheduler.TaskManager.start_task"): + task_manager = TaskManager() + task_manager._schedule() + + proj = objects.project + project_update = proj.create_project_update() + project_update.instance_group = default_instance_group + project_update.status = "pending" + project_update.save() + assert not task_manager.is_job_blocked(project_update) + + dependency_graph = DependencyGraph(None) + dependency_graph.add_job(job) + assert not dependency_graph.is_job_blocked(project_update)