Skip to content

Commit b42d3da

Browse files
authored
feat: Improve OS version filtering for Pub/Sub tasks (#5026)
### Description: **Summary:** This PR addresses a flaw in the client-side OS version filtering for Pub/Sub tasks. Legacy bots, which do not have the `BASE_OS_VERSION` environment variable set, were not correctly skipping tasks intended for specific, newer OS versions. This could lead to bots attempting to execute incompatible tasks, causing errors and wasting resources, particularly during OS version migrations. This change makes the task handling more resilient by ensuring that bots only process tasks compatible with their environment. **Changes:** - **Modified `_filter_task_for_os_mismatch`:** The core filtering logic in `src/clusterfuzz/_internal/base/tasks/__init__.py` has been updated. It now correctly skips a task if the incoming message has a `base_os_version` attribute and the bot's own OS version is either different or not set (`None`). - **Updated Unit Tests:** The corresponding unit tests in `src/clusterfuzz/_internal/tests/core/base/tasks/tasks_test.py` have been updated to validate the corrected logic and ensure there are no regressions. - **Improved Documentation:** The docstring for the filtering function has been refined to be more explicit and conform to the Google Python Style Guide. Redundant inline comments were removed to improve code clarity. **Testing:** All changes are covered by unit tests. The relevant test suite passes successfully.
1 parent 6e8b7b6 commit b42d3da

File tree

2 files changed

+39
-26
lines changed

2 files changed

+39
-26
lines changed

src/clusterfuzz/_internal/base/tasks/__init__.py

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -586,40 +586,43 @@ def lease(self, _event=None): # pylint: disable=arguments-differ
586586

587587

588588
def _filter_task_for_os_mismatch(message, queue) -> bool:
589-
"""Filters a Pub/Sub message if its OS version does not match the bot's OS.
589+
"""Filters a Pub/Sub message if its OS version mismatches the bot's OS.
590590
591-
This function checks the `base_os_version` attribute in the incoming message
592-
against the bot's `BASE_OS_VERSION` environment variable. This handles cases
593-
where a message is misrouted or received from a legacy subscription without
594-
OS-specific filters.
591+
This function compares the `base_os_version` attribute from the message
592+
against the bot's `BASE_OS_VERSION` environment variable.
595593
596-
If an OS version mismatch is detected, the function logs a warning and
597-
acknowledges (`ack()`) the message. Acknowledging the message permanently
598-
removes it from the current subscription, effectively skipping it for this
599-
bot. This assumes the message was also correctly delivered to another,
600-
properly filtered subscription for processing.
594+
A task is skipped (and the message is acknowledged) if the message specifies
595+
an OS version, and the bot's OS is different. This includes the legacy
596+
scenario where a bot does not have `BASE_OS_VERSION` set (evaluating to
597+
`None`), preventing it from processing tasks meant for newer OS versions.
598+
599+
If the message does not specify an OS version, it can be processed by any
600+
bot. If the versions match, it is also processed.
601601
602602
Args:
603-
message: The `pubsub.Message` object to check.
604-
queue: The name of the queue from which the message was pulled.
603+
message (pubsub.Message): The message object to check.
604+
queue (str): The name of the queue from which the message was pulled.
605605
606606
Returns:
607-
True if the message had a mismatch and was acknowledged; False otherwise.
607+
bool: True if the message had a mismatch and was acknowledged, otherwise
608+
False.
608609
"""
609610
base_os_version = environment.get_value('BASE_OS_VERSION')
610611
message_base_os_version = message.attributes.get('base_os_version')
611612

612-
if not (message_base_os_version and base_os_version and
613-
message_base_os_version != base_os_version):
613+
if not message_base_os_version:
614614
return False
615615

616-
logs.warning(
617-
'Skipping task for different OS.',
618-
queue=queue,
619-
message_os_version=message_base_os_version,
620-
base_os_version=base_os_version)
621-
message.ack()
622-
return True
616+
if message_base_os_version != base_os_version:
617+
logs.warning(
618+
'Skipping task for different OS.',
619+
queue=queue,
620+
message_os_version=message_base_os_version,
621+
base_os_version=base_os_version)
622+
message.ack()
623+
return True
624+
625+
return False
623626

624627

625628
def get_task_from_message(message, queue=None, can_defer=True,

src/clusterfuzz/_internal/tests/core/base/tasks/tasks_test.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@ def test_no_message(self):
280280

281281
def test_success(self):
282282
"""Test successful task creation from a message."""
283+
self.mock_env_get.return_value = None
284+
self.mock_message.attributes = {}
283285
self.assertEqual(
284286
tasks.get_task_from_message(self.mock_message), self.mock_task)
285287

@@ -297,6 +299,8 @@ def test_defer(self):
297299
def test_set_queue(self):
298300
"""Tests the set_queue method of a task."""
299301
mock_queue = mock.Mock()
302+
self.mock_env_get.return_value = None
303+
self.mock_message.attributes = {}
300304
task = tasks.get_task_from_message(self.mock_message, queue=mock_queue)
301305
task.set_queue.assert_called_with(mock_queue)
302306

@@ -336,15 +340,21 @@ def test_bot_has_os_message_does_not(self):
336340
self.assertEqual(result, self.mock_task)
337341
self.mock_message.ack.assert_not_called()
338342

339-
def test_bot_has_no_os_message_does(self):
340-
"""Test that a message is processed if the message has an OS but the bot does not."""
343+
@mock.patch('clusterfuzz._internal.metrics.logs.warning')
344+
def test_bot_has_no_os_message_does(self, mock_log_warning):
345+
"""Test that a message is skipped if it has an OS but the bot does not."""
341346
self.mock_env_get.return_value = None
342347
self.mock_message.attributes = {'base_os_version': 'ubuntu-24-04'}
343348

344349
result = tasks.get_task_from_message(self.mock_message)
345350

346-
self.assertEqual(result, self.mock_task)
347-
self.mock_message.ack.assert_not_called()
351+
self.assertIsNone(result)
352+
self.mock_message.ack.assert_called_once()
353+
mock_log_warning.assert_called_with(
354+
'Skipping task for different OS.',
355+
queue=None,
356+
message_os_version='ubuntu-24-04',
357+
base_os_version=None)
348358

349359

350360
@test_utils.with_cloud_emulators('datastore')

0 commit comments

Comments
 (0)