From 370b1ff03ad32205e00625ebddda45894d022f57 Mon Sep 17 00:00:00 2001 From: Mike McLean Date: Jan 30 2026 10:41:13 +0000 Subject: [PATCH 1/2] handler disabled hosts in scheduler Fixes https://pagure.io/koji/issue/4504 --- diff --git a/kojihub/scheduler.py b/kojihub/scheduler.py index 9f0757c..0fd4437 100644 --- a/kojihub/scheduler.py +++ b/kojihub/scheduler.py @@ -348,7 +348,7 @@ class TaskScheduler(object): host = self.hosts.get(task['host_id']) if not host: # not showing as ready - # TODO log and deal with this condition + # we'll address this later in check_active_tasks continue host.setdefault('_load', 0.0) if not task['waiting']: @@ -435,10 +435,18 @@ class TaskScheduler(object): continue host = self.hosts.get(task['host_id']) + if not host: - # host disabled? - # TODO - continue + # host most likely disabled + # simulate the data with get_host + host = kojihub.get_host(task['host_id'], strict=False) + if not host: + # should not be possible + log_both('Active task with nonexistent host', task_id=task['task_id'], level=logging.ERROR) + kojihub.Task(task['task_id']).free() + continue + host['data'] = {} + host['channels'] = [] taskruns = runs.get(task['task_id'], []) if not taskruns: @@ -455,6 +463,12 @@ class TaskScheduler(object): # TODO fix if task['state'] == koji.TASK_STATES['ASSIGNED']: + if not host.get('enabled', True): + # if the host is disabled there is no need to wait for a timeout + log_both('Task assigned to disabled host', task_id=task['task_id'], + host_id=host['id']) + kojihub.Task(task['task_id']).free() + continue assign_ts = min([r['create_ts'] for r in taskruns]) age = time.time() - assign_ts if age > self.assign_timeout: diff --git a/tests/test_hub/test_scheduler.py b/tests/test_hub/test_scheduler.py index 656076f..9704961 100644 --- a/tests/test_hub/test_scheduler.py +++ b/tests/test_hub/test_scheduler.py @@ -265,10 +265,11 @@ class TestDoSchedule(BaseTest): self.assertEqual(t_assigned, list(range(3,5))) self.assertEqual(h_used, list(range(3,5))) -class TestCheckActiveRuns(BaseTest): + +class TestCheckActiveTasks(BaseTest): def setUp(self): - super(TestCheckActiveRuns, self).setUp() + super(TestCheckActiveTasks, self).setUp() self.sched = scheduler.TaskScheduler() self.get_active_runs = mock.MagicMock() @@ -285,6 +286,7 @@ class TestCheckActiveRuns(BaseTest): self.log_db = mock.MagicMock() mock.patch('kojihub.scheduler.log_db', new=self.log_db).start() self.set_refusal = mock.patch('kojihub.scheduler.set_refusal').start() + self.get_host = mock.patch('kojihub.kojihub.get_host').start() def test_check_no_active(self): self.assertEqual(self.sched.active_tasks, []) # set by init @@ -309,6 +311,21 @@ class TestCheckActiveRuns(BaseTest): update = self.updates[0] self.assertEqual(update.table, 'scheduler_task_runs') + def test_check_nonexistent_host(self): + self.sched.active_tasks = [{'task_id': 99, 'host_id': 12345}] + self.sched.hosts = {} + self.get_host.return_value = None + + self.sched.check_active_tasks() + + self.log_db.assert_called_once_with('Active task with nonexistent host', 99, None) + self.get_active_runs.assert_called_once() + self.assertEqual(self.frees, [99]) + self.assertEqual(self.assigns, []) + self.assertEqual(len(self.updates), 1) + update = self.updates[0] + self.assertEqual(update.table, 'scheduler_task_runs') + def test_check_override(self): # 'Override task assignment' case self.sched.active_tasks = [{'task_id': 99, 'host_id': 23, 'state': koji.TASK_STATES['ASSIGNED']}] @@ -350,6 +367,29 @@ class TestCheckActiveRuns(BaseTest): update = self.updates[0] self.assertEqual(update.table, 'scheduler_task_runs') + def test_check_assign_to_disabled(self): + # task assigned to disabled host (non-override) + create_ts = 1000 + now = 1000000 + update_ts = now # host is checking in + self.sched.active_tasks = [{'task_id': 99, 'host_id': 23, 'state': koji.TASK_STATES['ASSIGNED']}] + self.sched.hosts = {} # disabled host not in our list + self.get_host.return_value = {'id': 23, 'name': 'test host 23', 'update_ts': update_ts, 'enabled': False} + self.sched.get_active_runs.return_value = {99: [{'create_ts': create_ts}]} + + with mock.patch('time.time', return_value=now): + self.sched.check_active_tasks() + + self.get_active_runs.assert_called_once() + self.set_refusal.assert_not_called() + self.log_db.assert_called_once_with('Task assigned to disabled host', 99, 23) + # we should free such tasks + self.assertEqual(self.frees, [99]) + self.assertEqual(self.assigns, []) + self.assertEqual(len(self.updates), 1) + update = self.updates[0] + self.assertEqual(update.table, 'scheduler_task_runs') + def test_check_implicit_refusal(self): # 'Task assignment timeout' case create_ts = 1000 @@ -390,3 +430,6 @@ class TestCheckActiveRuns(BaseTest): self.assertEqual(len(self.updates), 1) update = self.updates[0] self.assertEqual(update.table, 'scheduler_task_runs') + + +# the end From f5aa99ff1dc4f95aa56c53fc255c8285c1d2e283 Mon Sep 17 00:00:00 2001 From: Mike McLean Date: Jan 30 2026 10:41:13 +0000 Subject: [PATCH 2/2] flake8 --- diff --git a/kojihub/scheduler.py b/kojihub/scheduler.py index 0fd4437..7639cb9 100644 --- a/kojihub/scheduler.py +++ b/kojihub/scheduler.py @@ -442,7 +442,8 @@ class TaskScheduler(object): host = kojihub.get_host(task['host_id'], strict=False) if not host: # should not be possible - log_both('Active task with nonexistent host', task_id=task['task_id'], level=logging.ERROR) + log_both('Active task with nonexistent host', task_id=task['task_id'], + level=logging.ERROR) kojihub.Task(task['task_id']).free() continue host['data'] = {}