From d0b524488892bdc1f95c6b974cd556b35e426a74 Mon Sep 17 00:00:00 2001 From: Tom Janssen Date: Tue, 28 Oct 2025 16:52:50 +0100 Subject: [PATCH 1/4] add environment option --- borgmatic/config/schema.yaml | 6 +++++ borgmatic/hooks/monitoring/sentry.py | 7 +++++- .../configuration/monitoring/sentry.md | 4 ++++ tests/unit/hooks/monitoring/test_sentry.py | 22 ++++++++++++------- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/borgmatic/config/schema.yaml b/borgmatic/config/schema.yaml index 1a964d9e..fcf80ccb 100644 --- a/borgmatic/config/schema.yaml +++ b/borgmatic/config/schema.yaml @@ -3022,6 +3022,12 @@ properties: project monitor. Used along with the data source name URL to construct a cron URL. example: mymonitor + environment: + type: string + description: | + Sentry monitor environment to specify. Used in the call + to sentry of set. + example: production states: type: array items: diff --git a/borgmatic/hooks/monitoring/sentry.py b/borgmatic/hooks/monitoring/sentry.py index e52d61b1..94b7455b 100644 --- a/borgmatic/hooks/monitoring/sentry.py +++ b/borgmatic/hooks/monitoring/sentry.py @@ -40,6 +40,7 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev dry_run_label = ' (dry run; not actually pinging)' if dry_run else '' data_source_name_url = hook_config.get('data_source_name_url') + environment = hook_config.get('environment') monitor_slug = hook_config.get('monitor_slug') match = DATA_SOURCE_NAME_URL_PATTERN.match(data_source_name_url) @@ -65,10 +66,14 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev if dry_run: return + environment_query = '' + if environment: + environment_query = f'&environment={environment}' + logging.getLogger('urllib3').setLevel(logging.ERROR) try: response = requests.post( - f'{cron_url}?status={status}', + f'{cron_url}?status={status}{environment_query}', timeout=TIMEOUT_SECONDS, headers={'User-Agent': 'borgmatic'}, ) diff --git a/docs/reference/configuration/monitoring/sentry.md b/docs/reference/configuration/monitoring/sentry.md index 8c59c089..27286f10 100644 --- a/docs/reference/configuration/monitoring/sentry.md +++ b/docs/reference/configuration/monitoring/sentry.md @@ -26,6 +26,9 @@ sentry: The `monitor_slug` value comes from the "Monitor Slug" under "Cron Details" on the same Sentry monitor page. +The `environment` value is optionally specifies the enviroment that is used in +sentry. + With this configuration, borgmatic pings Sentry whenever borgmatic starts, finishes, or fails, but only when any of the `create`, `prune`, `compact`, or `check` actions are run. You can optionally override the start/finish/fail @@ -36,6 +39,7 @@ Sentry on failure: sentry: data_source_name_url: https://5f80ec@o294220.ingest.us.sentry.io/203069 monitor_slug: mymonitor + environment: myenvironment states: - fail ``` diff --git a/tests/unit/hooks/monitoring/test_sentry.py b/tests/unit/hooks/monitoring/test_sentry.py index 87f62a1a..77fff233 100644 --- a/tests/unit/hooks/monitoring/test_sentry.py +++ b/tests/unit/hooks/monitoring/test_sentry.py @@ -6,20 +6,21 @@ from borgmatic.hooks.monitoring import sentry as module @pytest.mark.parametrize( - 'state,configured_states,expected_status', + 'state,configured_states,configured_environment,expected_status', ( - (borgmatic.hooks.monitoring.monitor.State.START, ['start'], 'in_progress'), + (borgmatic.hooks.monitoring.monitor.State.START, ['start'], None, 'in_progress'), ( borgmatic.hooks.monitoring.monitor.State.START, ['start', 'finish', 'fail'], - 'in_progress', + None, + 'in_progress' ), - (borgmatic.hooks.monitoring.monitor.State.START, None, 'in_progress'), - (borgmatic.hooks.monitoring.monitor.State.FINISH, ['finish'], 'ok'), - (borgmatic.hooks.monitoring.monitor.State.FAIL, ['fail'], 'error'), + (borgmatic.hooks.monitoring.monitor.State.START, None, 'production', 'in_progress'), + (borgmatic.hooks.monitoring.monitor.State.FINISH, ['finish'], 'development', 'ok'), + (borgmatic.hooks.monitoring.monitor.State.FAIL, ['fail'], 'another-environment', 'error'), ), ) -def test_ping_monitor_constructs_cron_url_and_pings_it(state, configured_states, expected_status): +def test_ping_monitor_constructs_cron_url_and_pings_it(state, configured_states, configured_environment, expected_status): hook_config = { 'data_source_name_url': 'https://5f80ec@o294220.ingest.us.sentry.io/203069', 'monitor_slug': 'test', @@ -28,8 +29,13 @@ def test_ping_monitor_constructs_cron_url_and_pings_it(state, configured_states, if configured_states: hook_config['states'] = configured_states + environment_query = '' + if configured_environment: + hook_config['environment'] = configured_environment + environment_query = f'&environment={configured_environment}' + flexmock(module.requests).should_receive('post').with_args( - f'https://o294220.ingest.us.sentry.io/api/203069/cron/test/5f80ec/?status={expected_status}', + f'https://o294220.ingest.us.sentry.io/api/203069/cron/test/5f80ec/?status={expected_status}{environment_query}', timeout=int, headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)).once() From 7cca1358fb06c7d7a53da585516cbf719fb5a2af Mon Sep 17 00:00:00 2001 From: Tom Janssen Date: Wed, 29 Oct 2025 09:50:55 +0100 Subject: [PATCH 2/4] incorporate feedback --- borgmatic/config/schema.yaml | 4 ++-- borgmatic/hooks/monitoring/sentry.py | 5 ++--- docs/reference/configuration/monitoring/sentry.md | 4 ++-- tests/unit/hooks/monitoring/test_sentry.py | 6 ++++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/borgmatic/config/schema.yaml b/borgmatic/config/schema.yaml index fcf80ccb..75a4a2a3 100644 --- a/borgmatic/config/schema.yaml +++ b/borgmatic/config/schema.yaml @@ -3025,8 +3025,8 @@ properties: environment: type: string description: | - Sentry monitor environment to specify. Used in the call - to sentry of set. + Sentry monitor environment used in the call to sentry. If not set, + the Sentry default is used. example: production states: type: array diff --git a/borgmatic/hooks/monitoring/sentry.py b/borgmatic/hooks/monitoring/sentry.py index 94b7455b..c6ec5856 100644 --- a/borgmatic/hooks/monitoring/sentry.py +++ b/borgmatic/hooks/monitoring/sentry.py @@ -1,5 +1,6 @@ import logging import re +import urllib import requests @@ -66,9 +67,7 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev if dry_run: return - environment_query = '' - if environment: - environment_query = f'&environment={environment}' + environment_query = f'&environment={urllib.parse.quote(environment)}' if environment else '' logging.getLogger('urllib3').setLevel(logging.ERROR) try: diff --git a/docs/reference/configuration/monitoring/sentry.md b/docs/reference/configuration/monitoring/sentry.md index 27286f10..e703ca55 100644 --- a/docs/reference/configuration/monitoring/sentry.md +++ b/docs/reference/configuration/monitoring/sentry.md @@ -26,8 +26,8 @@ sentry: The `monitor_slug` value comes from the "Monitor Slug" under "Cron Details" on the same Sentry monitor page. -The `environment` value is optionally specifies the enviroment that is used in -sentry. +The `environment` value optionally specifies the enviroment that is used in +Sentry. With this configuration, borgmatic pings Sentry whenever borgmatic starts, finishes, or fails, but only when any of the `create`, `prune`, `compact`, or diff --git a/tests/unit/hooks/monitoring/test_sentry.py b/tests/unit/hooks/monitoring/test_sentry.py index 77fff233..f85f8f85 100644 --- a/tests/unit/hooks/monitoring/test_sentry.py +++ b/tests/unit/hooks/monitoring/test_sentry.py @@ -13,14 +13,16 @@ from borgmatic.hooks.monitoring import sentry as module borgmatic.hooks.monitoring.monitor.State.START, ['start', 'finish', 'fail'], None, - 'in_progress' + 'in_progress', ), (borgmatic.hooks.monitoring.monitor.State.START, None, 'production', 'in_progress'), (borgmatic.hooks.monitoring.monitor.State.FINISH, ['finish'], 'development', 'ok'), (borgmatic.hooks.monitoring.monitor.State.FAIL, ['fail'], 'another-environment', 'error'), ), ) -def test_ping_monitor_constructs_cron_url_and_pings_it(state, configured_states, configured_environment, expected_status): +def test_ping_monitor_constructs_cron_url_and_pings_it( + state, configured_states, configured_environment, expected_status +): hook_config = { 'data_source_name_url': 'https://5f80ec@o294220.ingest.us.sentry.io/203069', 'monitor_slug': 'test', From 370bc7e7f0f15379ad5408eef009165924f28eb8 Mon Sep 17 00:00:00 2001 From: Tom Janssen Date: Wed, 29 Oct 2025 09:55:03 +0100 Subject: [PATCH 3/4] fix schema length --- borgmatic/config/schema.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/borgmatic/config/schema.yaml b/borgmatic/config/schema.yaml index 75a4a2a3..2e52a638 100644 --- a/borgmatic/config/schema.yaml +++ b/borgmatic/config/schema.yaml @@ -3025,8 +3025,8 @@ properties: environment: type: string description: | - Sentry monitor environment used in the call to sentry. If not set, - the Sentry default is used. + Sentry monitor environment used in the call to sentry. If + not set, the Sentry default is used. example: production states: type: array From 9e649af2a3a5aafa463a24c7626417f5b64f80e1 Mon Sep 17 00:00:00 2001 From: Tom Janssen Date: Wed, 29 Oct 2025 10:13:45 +0100 Subject: [PATCH 4/4] fix typo --- docs/reference/configuration/monitoring/sentry.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/configuration/monitoring/sentry.md b/docs/reference/configuration/monitoring/sentry.md index e703ca55..09cde442 100644 --- a/docs/reference/configuration/monitoring/sentry.md +++ b/docs/reference/configuration/monitoring/sentry.md @@ -26,7 +26,7 @@ sentry: The `monitor_slug` value comes from the "Monitor Slug" under "Cron Details" on the same Sentry monitor page. -The `environment` value optionally specifies the enviroment that is used in +The `environment` value optionally specifies the environment that is used in Sentry. With this configuration, borgmatic pings Sentry whenever borgmatic starts,