Skip to content

Commit baf909d

Browse files
authored
Auto monitoring beat update (#1989)
- Small update to support Celery 4 and 5 - Changed the name of the schedule shelf file that we patch to have the suffix `-patched-by-sentry-sdk` instead of `.new` so in case there is an error with this new shelf file somewhere the users know that it is patched by the sentry sdk. - Additionally some minor tweaks to make code more readable
1 parent d8a5a43 commit baf909d

File tree

2 files changed

+44
-25
lines changed

2 files changed

+44
-25
lines changed

sentry_sdk/integrations/celery.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import sys
44
import shutil
55
import functools
6+
import tempfile
67

78
from sentry_sdk.consts import OP
89
from sentry_sdk._compat import reraise
@@ -320,6 +321,11 @@ def sentry_workloop(*args, **kwargs):
320321
def _get_headers(task):
321322
# type: (Task) -> Dict[str, Any]
322323
headers = task.request.get("headers") or {}
324+
325+
if "headers" in headers:
326+
headers.update(headers["headers"])
327+
del headers["headers"]
328+
323329
return headers
324330

325331

@@ -392,9 +398,11 @@ def _reinstall_patched_tasks(app, sender, add_updated_periodic_tasks):
392398
add_updated_periodic_task()
393399

394400
# Start Celery Beat (with new (cloned) schedule, because old one is still in use)
395-
new_schedule_filename = sender.schedule_filename + ".new"
396-
shutil.copy2(sender.schedule_filename, new_schedule_filename)
397-
app.Beat(schedule=new_schedule_filename).run()
401+
cloned_schedule = tempfile.NamedTemporaryFile(suffix="-patched-by-sentry-sdk")
402+
with open(sender.schedule_filename, "rb") as original_schedule:
403+
shutil.copyfileobj(original_schedule, cloned_schedule)
404+
405+
app.Beat(schedule=cloned_schedule.name).run()
398406

399407

400408
# Nested functions do not work as Celery hook receiver,
@@ -480,9 +488,7 @@ def crons_task_before_run(sender, **kwargs):
480488
if "sentry-monitor-slug" not in headers:
481489
return
482490

483-
monitor_config = (
484-
headers["sentry-monitor-config"] if "sentry-monitor-config" in headers else {}
485-
)
491+
monitor_config = headers.get("sentry-monitor-config", {})
486492

487493
start_timestamp_s = now()
488494

@@ -506,9 +512,7 @@ def crons_task_success(sender, **kwargs):
506512
if "sentry-monitor-slug" not in headers:
507513
return
508514

509-
monitor_config = (
510-
headers["sentry-monitor-config"] if "sentry-monitor-config" in headers else {}
511-
)
515+
monitor_config = headers.get("sentry-monitor-config", {})
512516

513517
start_timestamp_s = headers["sentry-monitor-start-timestamp-s"]
514518

@@ -529,9 +533,7 @@ def crons_task_failure(sender, **kwargs):
529533
if "sentry-monitor-slug" not in headers:
530534
return
531535

532-
monitor_config = (
533-
headers["sentry-monitor-config"] if "sentry-monitor-config" in headers else {}
534-
)
536+
monitor_config = headers.get("sentry-monitor-config", {})
535537

536538
start_timestamp_s = headers["sentry-monitor-start-timestamp-s"]
537539

@@ -552,9 +554,7 @@ def crons_task_retry(sender, **kwargs):
552554
if "sentry-monitor-slug" not in headers:
553555
return
554556

555-
monitor_config = (
556-
headers["sentry-monitor-config"] if "sentry-monitor-config" in headers else {}
557-
)
557+
monitor_config = headers.get("sentry-monitor-config", {})
558558

559559
start_timestamp_s = headers["sentry-monitor-start-timestamp-s"]
560560

tests/integrations/celery/test_celery_beat_crons.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import tempfile
12
import mock
23

34
import pytest
@@ -37,6 +38,20 @@ def test_get_headers():
3738

3839
assert _get_headers(fake_task) == {"bla": "blub"}
3940

41+
fake_task.request.update(
42+
{
43+
"headers": {
44+
"headers": {
45+
"tri": "blub",
46+
"bar": "baz",
47+
},
48+
"bla": "blub",
49+
},
50+
}
51+
)
52+
53+
assert _get_headers(fake_task) == {"bla": "blub", "tri": "blub", "bar": "baz"}
54+
4055

4156
@pytest.mark.parametrize(
4257
"seconds, expected_tuple",
@@ -273,16 +288,20 @@ def test_reinstall_patched_tasks():
273288

274289
add_updated_periodic_tasks = [mock.MagicMock(), mock.MagicMock(), mock.MagicMock()]
275290

276-
with mock.patch("sentry_sdk.integrations.celery.shutil.copy2") as mock_copy2:
277-
_reinstall_patched_tasks(app, sender, add_updated_periodic_tasks)
291+
mock_open = mock.Mock(return_value=tempfile.NamedTemporaryFile())
278292

279-
sender.stop.assert_called_once_with()
293+
with mock.patch("sentry_sdk.integrations.celery.open", mock_open):
294+
with mock.patch(
295+
"sentry_sdk.integrations.celery.shutil.copyfileobj"
296+
) as mock_copyfileobj:
297+
_reinstall_patched_tasks(app, sender, add_updated_periodic_tasks)
280298

281-
add_updated_periodic_tasks[0].assert_called_once_with()
282-
add_updated_periodic_tasks[1].assert_called_once_with()
283-
add_updated_periodic_tasks[2].assert_called_once_with()
299+
sender.stop.assert_called_once_with()
284300

285-
mock_copy2.assert_called_once_with(
286-
"test_schedule_filename", "test_schedule_filename.new"
287-
)
288-
fake_beat.run.assert_called_once_with()
301+
add_updated_periodic_tasks[0].assert_called_once_with()
302+
add_updated_periodic_tasks[1].assert_called_once_with()
303+
add_updated_periodic_tasks[2].assert_called_once_with()
304+
305+
mock_copyfileobj.assert_called_once()
306+
307+
fake_beat.run.assert_called_once_with()

0 commit comments

Comments
 (0)