Skip to content

Commit 522abef

Browse files
authored
Read timezone for Crons monitors from celery_schedule if existing (#2497)
1 parent 36c2650 commit 522abef

File tree

2 files changed

+138
-14
lines changed

2 files changed

+138
-14
lines changed

sentry_sdk/integrations/celery.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,15 @@ def _get_monitor_config(celery_schedule, app, monitor_name):
444444
if schedule_unit is not None:
445445
monitor_config["schedule"]["unit"] = schedule_unit
446446

447-
monitor_config["timezone"] = app.conf.timezone or "UTC"
447+
monitor_config["timezone"] = (
448+
(
449+
hasattr(celery_schedule, "tz")
450+
and celery_schedule.tz is not None
451+
and str(celery_schedule.tz)
452+
)
453+
or app.timezone
454+
or "UTC"
455+
)
448456

449457
return monitor_config
450458

tests/integrations/celery/test_celery_beat_crons.py

Lines changed: 129 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import datetime
2+
import sys
3+
14
import pytest
25

36
from sentry_sdk.integrations.celery import (
@@ -207,25 +210,65 @@ def test_crons_task_retry():
207210

208211
def test_get_monitor_config_crontab():
209212
app = MagicMock()
210-
app.conf = MagicMock()
211-
app.conf.timezone = "Europe/Vienna"
213+
app.timezone = "Europe/Vienna"
212214

215+
# schedule with the default timezone
213216
celery_schedule = crontab(day_of_month="3", hour="12", minute="*/10")
217+
214218
monitor_config = _get_monitor_config(celery_schedule, app, "foo")
215219
assert monitor_config == {
216220
"schedule": {
217221
"type": "crontab",
218222
"value": "*/10 12 3 * *",
219223
},
220-
"timezone": "Europe/Vienna",
224+
"timezone": "UTC", # the default because `crontab` does not know about the app
221225
}
222226
assert "unit" not in monitor_config["schedule"]
223227

228+
# schedule with the timezone from the app
229+
celery_schedule = crontab(day_of_month="3", hour="12", minute="*/10", app=app)
230+
231+
monitor_config = _get_monitor_config(celery_schedule, app, "foo")
232+
assert monitor_config == {
233+
"schedule": {
234+
"type": "crontab",
235+
"value": "*/10 12 3 * *",
236+
},
237+
"timezone": "Europe/Vienna", # the timezone from the app
238+
}
239+
240+
# schedule without a timezone, the celery integration will read the config from the app
241+
celery_schedule = crontab(day_of_month="3", hour="12", minute="*/10")
242+
celery_schedule.tz = None
243+
244+
monitor_config = _get_monitor_config(celery_schedule, app, "foo")
245+
assert monitor_config == {
246+
"schedule": {
247+
"type": "crontab",
248+
"value": "*/10 12 3 * *",
249+
},
250+
"timezone": "Europe/Vienna", # the timezone from the app
251+
}
252+
253+
# schedule without a timezone, and an app without timezone, the celery integration will fall back to UTC
254+
app = MagicMock()
255+
app.timezone = None
256+
257+
celery_schedule = crontab(day_of_month="3", hour="12", minute="*/10")
258+
celery_schedule.tz = None
259+
monitor_config = _get_monitor_config(celery_schedule, app, "foo")
260+
assert monitor_config == {
261+
"schedule": {
262+
"type": "crontab",
263+
"value": "*/10 12 3 * *",
264+
},
265+
"timezone": "UTC", # default timezone from celery integration
266+
}
267+
224268

225269
def test_get_monitor_config_seconds():
226270
app = MagicMock()
227-
app.conf = MagicMock()
228-
app.conf.timezone = "Europe/Vienna"
271+
app.timezone = "Europe/Vienna"
229272

230273
celery_schedule = schedule(run_every=3) # seconds
231274

@@ -243,25 +286,69 @@ def test_get_monitor_config_seconds():
243286

244287
def test_get_monitor_config_minutes():
245288
app = MagicMock()
246-
app.conf = MagicMock()
247-
app.conf.timezone = "Europe/Vienna"
289+
app.timezone = "Europe/Vienna"
290+
291+
# schedule with the default timezone
292+
celery_schedule = schedule(run_every=60) # seconds
293+
294+
monitor_config = _get_monitor_config(celery_schedule, app, "foo")
295+
assert monitor_config == {
296+
"schedule": {
297+
"type": "interval",
298+
"value": 1,
299+
"unit": "minute",
300+
},
301+
"timezone": "UTC",
302+
}
303+
304+
# schedule with the timezone from the app
305+
celery_schedule = schedule(run_every=60, app=app) # seconds
306+
307+
monitor_config = _get_monitor_config(celery_schedule, app, "foo")
308+
assert monitor_config == {
309+
"schedule": {
310+
"type": "interval",
311+
"value": 1,
312+
"unit": "minute",
313+
},
314+
"timezone": "Europe/Vienna", # the timezone from the app
315+
}
316+
317+
# schedule without a timezone, the celery integration will read the config from the app
318+
celery_schedule = schedule(run_every=60) # seconds
319+
celery_schedule.tz = None
320+
321+
monitor_config = _get_monitor_config(celery_schedule, app, "foo")
322+
assert monitor_config == {
323+
"schedule": {
324+
"type": "interval",
325+
"value": 1,
326+
"unit": "minute",
327+
},
328+
"timezone": "Europe/Vienna", # the timezone from the app
329+
}
330+
331+
# schedule without a timezone, and an app without timezone, the celery integration will fall back to UTC
332+
app = MagicMock()
333+
app.timezone = None
248334

249335
celery_schedule = schedule(run_every=60) # seconds
336+
celery_schedule.tz = None
337+
250338
monitor_config = _get_monitor_config(celery_schedule, app, "foo")
251339
assert monitor_config == {
252340
"schedule": {
253341
"type": "interval",
254342
"value": 1,
255343
"unit": "minute",
256344
},
257-
"timezone": "Europe/Vienna",
345+
"timezone": "UTC", # default timezone from celery integration
258346
}
259347

260348

261349
def test_get_monitor_config_unknown():
262350
app = MagicMock()
263-
app.conf = MagicMock()
264-
app.conf.timezone = "Europe/Vienna"
351+
app.timezone = "Europe/Vienna"
265352

266353
unknown_celery_schedule = MagicMock()
267354
monitor_config = _get_monitor_config(unknown_celery_schedule, app, "foo")
@@ -270,16 +357,45 @@ def test_get_monitor_config_unknown():
270357

271358
def test_get_monitor_config_default_timezone():
272359
app = MagicMock()
273-
app.conf = MagicMock()
274-
app.conf.timezone = None
360+
app.timezone = None
275361

276362
celery_schedule = crontab(day_of_month="3", hour="12", minute="*/10")
277363

278-
monitor_config = _get_monitor_config(celery_schedule, app, "foo")
364+
monitor_config = _get_monitor_config(celery_schedule, app, "dummy_monitor_name")
279365

280366
assert monitor_config["timezone"] == "UTC"
281367

282368

369+
def test_get_monitor_config_timezone_in_app_conf():
370+
app = MagicMock()
371+
app.timezone = "Asia/Karachi"
372+
373+
celery_schedule = crontab(day_of_month="3", hour="12", minute="*/10")
374+
celery_schedule.tz = None
375+
376+
monitor_config = _get_monitor_config(celery_schedule, app, "dummy_monitor_name")
377+
378+
assert monitor_config["timezone"] == "Asia/Karachi"
379+
380+
381+
@pytest.mark.skipif(
382+
sys.version_info < (3, 0),
383+
reason="no datetime.timezone for Python 2, so skipping this test.",
384+
)
385+
def test_get_monitor_config_timezone_in_celery_schedule():
386+
app = MagicMock()
387+
app.timezone = "Asia/Karachi"
388+
389+
panama_tz = datetime.timezone(datetime.timedelta(hours=-5), name="America/Panama")
390+
391+
celery_schedule = crontab(day_of_month="3", hour="12", minute="*/10")
392+
celery_schedule.tz = panama_tz
393+
394+
monitor_config = _get_monitor_config(celery_schedule, app, "dummy_monitor_name")
395+
396+
assert monitor_config["timezone"] == str(panama_tz)
397+
398+
283399
@pytest.mark.parametrize(
284400
"task_name,exclude_beat_tasks,task_in_excluded_beat_tasks",
285401
[

0 commit comments

Comments
 (0)