From 563cb8441e0e1da7d56d7391f80ee86ab5db9946 Mon Sep 17 00:00:00 2001 From: Dan Helfman Date: Mon, 1 Sep 2025 16:51:33 -0700 Subject: [PATCH 1/2] When making HTTP requests in monitoring hooks, set "borgmatic" as the user agent (#1139). --- NEWS | 1 + borgmatic/hooks/monitoring/cronhub.py | 6 +++++- borgmatic/hooks/monitoring/cronitor.py | 6 +++++- borgmatic/hooks/monitoring/healthchecks.py | 1 + borgmatic/hooks/monitoring/loki.py | 9 +++++---- borgmatic/hooks/monitoring/ntfy.py | 3 ++- borgmatic/hooks/monitoring/pagerduty.py | 1 + borgmatic/hooks/monitoring/pushover.py | 5 ++++- borgmatic/hooks/monitoring/sentry.py | 6 +++++- borgmatic/hooks/monitoring/uptime_kuma.py | 1 + borgmatic/hooks/monitoring/zabbix.py | 2 +- tests/unit/hooks/monitoring/test_cronhub.py | 5 +++++ tests/unit/hooks/monitoring/test_cronitor.py | 4 ++++ .../hooks/monitoring/test_healthchecks.py | 13 ++++++++++++ tests/unit/hooks/monitoring/test_ntfy.py | 2 ++ tests/unit/hooks/monitoring/test_pushover.py | 20 +++++++++---------- tests/unit/hooks/monitoring/test_sentry.py | 2 ++ .../unit/hooks/monitoring/test_uptimekuma.py | 8 ++++++++ tests/unit/hooks/monitoring/test_zabbix.py | 2 ++ 19 files changed, 77 insertions(+), 20 deletions(-) diff --git a/NEWS b/NEWS index 6dcede31..717e9c48 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ use. * #1126: Create LVM snapshots as read-write to avoid an error when snapshotting ext4 filesystems with orphaned files that need recovery. + * #1139: When making HTTP requests in monitoring hooks, set "borgmatic" as the user agent. * When running tests, use Ruff for faster and more comprehensive code linting and formatting, replacing Flake8, Black, isort, etc. * Switch from pipx to uv for installing development tools, and added tox-uv for speeding up test diff --git a/borgmatic/hooks/monitoring/cronhub.py b/borgmatic/hooks/monitoring/cronhub.py index 6fbd83c1..fa651095 100644 --- a/borgmatic/hooks/monitoring/cronhub.py +++ b/borgmatic/hooks/monitoring/cronhub.py @@ -49,7 +49,11 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev if not dry_run: logging.getLogger('urllib3').setLevel(logging.ERROR) try: - response = requests.get(ping_url, timeout=TIMEOUT_SECONDS) + response = requests.get( + ping_url, + timeout=TIMEOUT_SECONDS, + headers={'User-Agent': 'borgmatic'}, + ) if not response.ok: response.raise_for_status() except requests.exceptions.RequestException as error: diff --git a/borgmatic/hooks/monitoring/cronitor.py b/borgmatic/hooks/monitoring/cronitor.py index 89c89999..ad331957 100644 --- a/borgmatic/hooks/monitoring/cronitor.py +++ b/borgmatic/hooks/monitoring/cronitor.py @@ -44,7 +44,11 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev if not dry_run: logging.getLogger('urllib3').setLevel(logging.ERROR) try: - response = requests.get(ping_url, timeout=TIMEOUT_SECONDS) + response = requests.get( + ping_url, + timeout=TIMEOUT_SECONDS, + headers={'User-Agent': 'borgmatic'}, + ) if not response.ok: response.raise_for_status() except requests.exceptions.RequestException as error: diff --git a/borgmatic/hooks/monitoring/healthchecks.py b/borgmatic/hooks/monitoring/healthchecks.py index 2caa3947..eeb09dea 100644 --- a/borgmatic/hooks/monitoring/healthchecks.py +++ b/borgmatic/hooks/monitoring/healthchecks.py @@ -91,6 +91,7 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev data=payload.encode('utf-8'), verify=hook_config.get('verify_tls', True), timeout=TIMEOUT_SECONDS, + headers={'User-Agent': 'borgmatic'}, ) if not response.ok: response.raise_for_status() diff --git a/borgmatic/hooks/monitoring/loki.py b/borgmatic/hooks/monitoring/loki.py index 7024edd5..c60c7520 100644 --- a/borgmatic/hooks/monitoring/loki.py +++ b/borgmatic/hooks/monitoring/loki.py @@ -65,16 +65,17 @@ class Loki_log_buffer: # Skip as there are not logs to send yet return - request_body = self.to_request() self.root['streams'][0]['values'] = [] - request_header = {'Content-Type': 'application/json'} try: result = requests.post( self.url, - headers=request_header, - data=request_body, + data=self.to_request(), timeout=TIMEOUT_SECONDS, + headers={ + 'Content-Type': 'application/json', + 'User-Agent': 'borgmatic', + }, ) result.raise_for_status() except requests.RequestException: diff --git a/borgmatic/hooks/monitoring/ntfy.py b/borgmatic/hooks/monitoring/ntfy.py index b11ff5ee..a197d0a7 100644 --- a/borgmatic/hooks/monitoring/ntfy.py +++ b/borgmatic/hooks/monitoring/ntfy.py @@ -49,6 +49,7 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev logger.debug(f'Using Ntfy ping URL {base_url}/{topic}') headers = { + 'User-Agent': 'borgmatic', 'X-Title': state_config.get('title'), 'X-Message': state_config.get('message'), 'X-Priority': state_config.get('priority'), @@ -94,9 +95,9 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev try: response = requests.post( f'{base_url}/{topic}', - headers=headers, auth=auth, timeout=TIMEOUT_SECONDS, + headers=headers, ) if not response.ok: response.raise_for_status() diff --git a/borgmatic/hooks/monitoring/pagerduty.py b/borgmatic/hooks/monitoring/pagerduty.py index 69cf4133..e995531a 100644 --- a/borgmatic/hooks/monitoring/pagerduty.py +++ b/borgmatic/hooks/monitoring/pagerduty.py @@ -102,6 +102,7 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev EVENTS_API_URL, data=payload.encode('utf-8'), timeout=TIMEOUT_SECONDS, + headers={'User-Agent': 'borgmatic'}, ) if not response.ok: response.raise_for_status() diff --git a/borgmatic/hooks/monitoring/pushover.py b/borgmatic/hooks/monitoring/pushover.py index 0fd96e60..7d70aad0 100644 --- a/borgmatic/hooks/monitoring/pushover.py +++ b/borgmatic/hooks/monitoring/pushover.py @@ -82,9 +82,12 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev try: response = requests.post( 'https://api.pushover.net/1/messages.json', - headers={'Content-type': 'application/x-www-form-urlencoded'}, data=data, timeout=TIMEOUT_SECONDS, + headers={ + 'Content-type': 'application/x-www-form-urlencoded', + 'User-Agent': 'borgmatic', + }, ) if not response.ok: response.raise_for_status() diff --git a/borgmatic/hooks/monitoring/sentry.py b/borgmatic/hooks/monitoring/sentry.py index c245c8e9..e52d61b1 100644 --- a/borgmatic/hooks/monitoring/sentry.py +++ b/borgmatic/hooks/monitoring/sentry.py @@ -67,7 +67,11 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev logging.getLogger('urllib3').setLevel(logging.ERROR) try: - response = requests.post(f'{cron_url}?status={status}', timeout=TIMEOUT_SECONDS) + response = requests.post( + f'{cron_url}?status={status}', + timeout=TIMEOUT_SECONDS, + headers={'User-Agent': 'borgmatic'}, + ) if not response.ok: response.raise_for_status() except requests.exceptions.RequestException as error: diff --git a/borgmatic/hooks/monitoring/uptime_kuma.py b/borgmatic/hooks/monitoring/uptime_kuma.py index 2887d388..cae33f9a 100644 --- a/borgmatic/hooks/monitoring/uptime_kuma.py +++ b/borgmatic/hooks/monitoring/uptime_kuma.py @@ -47,6 +47,7 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev f'{push_url}?{query}', verify=hook_config.get('verify_tls', True), timeout=TIMEOUT_SECONDS, + headers={'User-Agent': 'borgmatic'}, ) if not response.ok: response.raise_for_status() diff --git a/borgmatic/hooks/monitoring/zabbix.py b/borgmatic/hooks/monitoring/zabbix.py index ddb66a8d..9230d9be 100644 --- a/borgmatic/hooks/monitoring/zabbix.py +++ b/borgmatic/hooks/monitoring/zabbix.py @@ -101,7 +101,7 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev host = hook_config.get('host') key = hook_config.get('key') value = state_config.get('value') - headers = {'Content-Type': 'application/json-rpc'} + headers = {'Content-Type': 'application/json-rpc', 'User-Agent': 'borgmatic'} logger.info(f'Pinging Zabbix{dry_run_label}') logger.debug(f'Using Zabbix URL: {server}') diff --git a/tests/unit/hooks/monitoring/test_cronhub.py b/tests/unit/hooks/monitoring/test_cronhub.py index 3b5e1480..9c6881ec 100644 --- a/tests/unit/hooks/monitoring/test_cronhub.py +++ b/tests/unit/hooks/monitoring/test_cronhub.py @@ -8,6 +8,7 @@ def test_ping_monitor_rewrites_ping_url_for_start_state(): flexmock(module.requests).should_receive('get').with_args( 'https://example.com/start/abcdef', timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)) module.ping_monitor( @@ -25,6 +26,7 @@ def test_ping_monitor_rewrites_ping_url_and_state_for_start_state(): flexmock(module.requests).should_receive('get').with_args( 'https://example.com/start/abcdef', timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)) module.ping_monitor( @@ -42,6 +44,7 @@ def test_ping_monitor_rewrites_ping_url_for_finish_state(): flexmock(module.requests).should_receive('get').with_args( 'https://example.com/finish/abcdef', timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)) module.ping_monitor( @@ -59,6 +62,7 @@ def test_ping_monitor_rewrites_ping_url_for_fail_state(): flexmock(module.requests).should_receive('get').with_args( 'https://example.com/fail/abcdef', timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)) module.ping_monitor( @@ -111,6 +115,7 @@ def test_ping_monitor_with_other_error_logs_warning(): flexmock(module.requests).should_receive('get').with_args( 'https://example.com/start/abcdef', timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(response) flexmock(module.logger).should_receive('warning').once() diff --git a/tests/unit/hooks/monitoring/test_cronitor.py b/tests/unit/hooks/monitoring/test_cronitor.py index eac86f92..c0e9305d 100644 --- a/tests/unit/hooks/monitoring/test_cronitor.py +++ b/tests/unit/hooks/monitoring/test_cronitor.py @@ -8,6 +8,7 @@ def test_ping_monitor_hits_ping_url_for_start_state(): flexmock(module.requests).should_receive('get').with_args( 'https://example.com/run', timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)) module.ping_monitor( @@ -25,6 +26,7 @@ def test_ping_monitor_hits_ping_url_for_finish_state(): flexmock(module.requests).should_receive('get').with_args( 'https://example.com/complete', timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)) module.ping_monitor( @@ -42,6 +44,7 @@ def test_ping_monitor_hits_ping_url_for_fail_state(): flexmock(module.requests).should_receive('get').with_args( 'https://example.com/fail', timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)) module.ping_monitor( @@ -94,6 +97,7 @@ def test_ping_monitor_with_other_error_logs_warning(): flexmock(module.requests).should_receive('get').with_args( 'https://example.com/run', timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(response) flexmock(module.logger).should_receive('warning').once() diff --git a/tests/unit/hooks/monitoring/test_healthchecks.py b/tests/unit/hooks/monitoring/test_healthchecks.py index 1cb54532..db4469e2 100644 --- a/tests/unit/hooks/monitoring/test_healthchecks.py +++ b/tests/unit/hooks/monitoring/test_healthchecks.py @@ -99,6 +99,7 @@ def test_ping_monitor_hits_ping_url_for_start_state(): data=b'', verify=True, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)) module.ping_monitor( @@ -123,6 +124,7 @@ def test_ping_monitor_hits_ping_url_for_finish_state(): data=payload.encode('utf-8'), verify=True, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)) module.ping_monitor( @@ -147,6 +149,7 @@ def test_ping_monitor_hits_ping_url_for_fail_state(): data=payload.encode('utf'), verify=True, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)) module.ping_monitor( @@ -171,6 +174,7 @@ def test_ping_monitor_hits_ping_url_for_log_state(): data=payload.encode('utf'), verify=True, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)) module.ping_monitor( @@ -195,6 +199,7 @@ def test_ping_monitor_with_ping_uuid_hits_corresponding_url(): data=payload.encode('utf-8'), verify=True, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)) module.ping_monitor( @@ -219,6 +224,7 @@ def test_ping_monitor_skips_ssl_verification_when_verify_tls_false(): data=payload.encode('utf-8'), verify=False, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)) module.ping_monitor( @@ -243,6 +249,7 @@ def test_ping_monitor_executes_ssl_verification_when_verify_tls_true(): data=payload.encode('utf-8'), verify=True, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)) module.ping_monitor( @@ -299,6 +306,7 @@ def test_ping_monitor_hits_ping_url_when_states_matching(): data=b'', verify=True, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)) module.ping_monitor( @@ -321,6 +329,7 @@ def test_ping_monitor_adds_create_query_parameter_when_create_slug_true(): data=b'', verify=True, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)) module.ping_monitor( @@ -343,6 +352,7 @@ def test_ping_monitor_does_not_add_create_query_parameter_when_create_slug_false data=b'', verify=True, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)) module.ping_monitor( @@ -362,6 +372,7 @@ def test_ping_monitor_does_not_add_create_query_parameter_when_ping_url_is_uuid( data=b'', verify=True, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)) module.ping_monitor( @@ -401,6 +412,7 @@ def test_ping_monitor_with_connection_error_logs_warning(): data=b'', verify=True, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_raise(module.requests.exceptions.ConnectionError) flexmock(module.logger).should_receive('warning').once() @@ -428,6 +440,7 @@ def test_ping_monitor_with_other_error_logs_warning(): data=b'', verify=True, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(response) flexmock(module.logger).should_receive('warning').once() diff --git a/tests/unit/hooks/monitoring/test_ntfy.py b/tests/unit/hooks/monitoring/test_ntfy.py index fd140d63..4c2c65a0 100644 --- a/tests/unit/hooks/monitoring/test_ntfy.py +++ b/tests/unit/hooks/monitoring/test_ntfy.py @@ -17,6 +17,7 @@ custom_message_config = { } custom_message_headers = { + 'User-Agent': 'borgmatic', 'X-Title': custom_message_config['title'], 'X-Message': custom_message_config['message'], 'X-Priority': custom_message_config['priority'], @@ -26,6 +27,7 @@ custom_message_headers = { def return_default_message_headers(state=Enum): return { + 'User-Agent': 'borgmatic', 'X-Title': f'A borgmatic {state.name} event happened', 'X-Message': f'A borgmatic {state.name} event happened', 'X-Priority': 'default', diff --git a/tests/unit/hooks/monitoring/test_pushover.py b/tests/unit/hooks/monitoring/test_pushover.py index 41cf9c2c..2a75528c 100644 --- a/tests/unit/hooks/monitoring/test_pushover.py +++ b/tests/unit/hooks/monitoring/test_pushover.py @@ -17,7 +17,7 @@ def test_ping_monitor_config_with_minimum_config_fail_state_backup_successfully_ flexmock(module.logger).should_receive('warning').never() flexmock(module.requests).should_receive('post').with_args( 'https://api.pushover.net/1/messages.json', - headers={'Content-type': 'application/x-www-form-urlencoded'}, + headers={'Content-type': 'application/x-www-form-urlencoded', 'User-Agent': 'borgmatic'}, data={ 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui', @@ -76,7 +76,7 @@ def test_ping_monitor_start_state_backup_default_message_successfully_send_to_pu flexmock(module.logger).should_receive('warning').never() flexmock(module.requests).should_receive('post').with_args( 'https://api.pushover.net/1/messages.json', - headers={'Content-type': 'application/x-www-form-urlencoded'}, + headers={'Content-type': 'application/x-www-form-urlencoded', 'User-Agent': 'borgmatic'}, data={ 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui', @@ -113,7 +113,7 @@ def test_ping_monitor_start_state_backup_custom_message_successfully_send_to_pus flexmock(module.logger).should_receive('warning').never() flexmock(module.requests).should_receive('post').with_args( 'https://api.pushover.net/1/messages.json', - headers={'Content-type': 'application/x-www-form-urlencoded'}, + headers={'Content-type': 'application/x-www-form-urlencoded', 'User-Agent': 'borgmatic'}, data={ 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui', @@ -149,7 +149,7 @@ def test_ping_monitor_start_state_backup_default_message_with_priority_emergency flexmock(module.logger).should_receive('warning').never() flexmock(module.requests).should_receive('post').with_args( 'https://api.pushover.net/1/messages.json', - headers={'Content-type': 'application/x-www-form-urlencoded'}, + headers={'Content-type': 'application/x-www-form-urlencoded', 'User-Agent': 'borgmatic'}, data={ 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui', @@ -188,7 +188,7 @@ def test_ping_monitor_start_state_backup_default_message_with_priority_emergency flexmock(module.logger).should_receive('warning').never() flexmock(module.requests).should_receive('post').with_args( 'https://api.pushover.net/1/messages.json', - headers={'Content-type': 'application/x-www-form-urlencoded'}, + headers={'Content-type': 'application/x-www-form-urlencoded', 'User-Agent': 'borgmatic'}, data={ 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui', @@ -227,7 +227,7 @@ def test_ping_monitor_start_state_backup_default_message_with_priority_emergency flexmock(module.logger).should_receive('warning').never() flexmock(module.requests).should_receive('post').with_args( 'https://api.pushover.net/1/messages.json', - headers={'Content-type': 'application/x-www-form-urlencoded'}, + headers={'Content-type': 'application/x-www-form-urlencoded', 'User-Agent': 'borgmatic'}, data={ 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui', @@ -324,7 +324,7 @@ def test_ping_monitor_start_state_backup_based_on_documentation_advanced_example flexmock(module.logger).should_receive('warning').never() flexmock(module.requests).should_receive('post').with_args( 'https://api.pushover.net/1/messages.json', - headers={'Content-type': 'application/x-www-form-urlencoded'}, + headers={'Content-type': 'application/x-www-form-urlencoded', 'User-Agent': 'borgmatic'}, data={ 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui', @@ -391,7 +391,7 @@ def test_ping_monitor_fail_state_backup_based_on_documentation_advanced_example_ flexmock(module.logger).should_receive('warning').never() flexmock(module.requests).should_receive('post').with_args( 'https://api.pushover.net/1/messages.json', - headers={'Content-type': 'application/x-www-form-urlencoded'}, + headers={'Content-type': 'application/x-www-form-urlencoded', 'User-Agent': 'borgmatic'}, data={ 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui', @@ -463,7 +463,7 @@ def test_ping_monitor_finish_state_backup_based_on_documentation_advanced_exampl flexmock(module.logger).should_receive('warning').never() flexmock(module.requests).should_receive('post').with_args( 'https://api.pushover.net/1/messages.json', - headers={'Content-type': 'application/x-www-form-urlencoded'}, + headers={'Content-type': 'application/x-www-form-urlencoded', 'User-Agent': 'borgmatic'}, data={ 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui', @@ -553,7 +553,7 @@ def test_ping_monitor_push_post_error_bails(): ).once() flexmock(module.requests).should_receive('post').with_args( 'https://api.pushover.net/1/messages.json', - headers={'Content-type': 'application/x-www-form-urlencoded'}, + headers={'Content-type': 'application/x-www-form-urlencoded', 'User-Agent': 'borgmatic'}, data={ 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui', diff --git a/tests/unit/hooks/monitoring/test_sentry.py b/tests/unit/hooks/monitoring/test_sentry.py index 970549d5..87f62a1a 100644 --- a/tests/unit/hooks/monitoring/test_sentry.py +++ b/tests/unit/hooks/monitoring/test_sentry.py @@ -31,6 +31,7 @@ def test_ping_monitor_constructs_cron_url_and_pings_it(state, configured_states, flexmock(module.requests).should_receive('post').with_args( f'https://o294220.ingest.us.sentry.io/api/203069/cron/test/5f80ec/?status={expected_status}', timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)).once() module.ping_monitor( @@ -137,6 +138,7 @@ def test_ping_monitor_with_network_error_does_not_raise(): flexmock(module.requests).should_receive('post').with_args( 'https://o294220.ingest.us.sentry.io/api/203069/cron/test/5f80ec/?status=in_progress', timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(response).once() module.ping_monitor( diff --git a/tests/unit/hooks/monitoring/test_uptimekuma.py b/tests/unit/hooks/monitoring/test_uptimekuma.py index da49d974..ae0a77f1 100644 --- a/tests/unit/hooks/monitoring/test_uptimekuma.py +++ b/tests/unit/hooks/monitoring/test_uptimekuma.py @@ -13,6 +13,7 @@ def test_ping_monitor_hits_default_uptimekuma_on_fail(): f'{DEFAULT_PUSH_URL}?status=down&msg=fail', verify=True, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)).once() module.ping_monitor( @@ -31,6 +32,7 @@ def test_ping_monitor_hits_custom_uptimekuma_on_fail(): f'{CUSTOM_PUSH_URL}?status=down&msg=fail', verify=True, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)).once() module.ping_monitor( @@ -49,6 +51,7 @@ def test_ping_monitor_custom_uptimekuma_on_start(): f'{CUSTOM_PUSH_URL}?status=up&msg=start', verify=True, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)).once() module.ping_monitor( @@ -67,6 +70,7 @@ def test_ping_monitor_custom_uptimekuma_on_finish(): f'{CUSTOM_PUSH_URL}?status=up&msg=finish', verify=True, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)).once() module.ping_monitor( @@ -127,6 +131,7 @@ def test_ping_monitor_with_connection_error_logs_warning(): f'{CUSTOM_PUSH_URL}?status=down&msg=fail', verify=True, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_raise(module.requests.exceptions.ConnectionError) flexmock(module.logger).should_receive('warning').once() @@ -150,6 +155,7 @@ def test_ping_monitor_with_other_error_logs_warning(): f'{CUSTOM_PUSH_URL}?status=down&msg=fail', verify=True, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(response) flexmock(module.logger).should_receive('warning').once() @@ -183,6 +189,7 @@ def test_ping_monitor_skips_ssl_verification_when_verify_tls_false(): f'{CUSTOM_PUSH_URL}?status=down&msg=fail', verify=False, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)).once() module.ping_monitor( @@ -201,6 +208,7 @@ def test_ping_monitor_executes_ssl_verification_when_verify_tls_true(): f'{CUSTOM_PUSH_URL}?status=down&msg=fail', verify=True, timeout=int, + headers={'User-Agent': 'borgmatic'}, ).and_return(flexmock(ok=True)).once() module.ping_monitor( diff --git a/tests/unit/hooks/monitoring/test_zabbix.py b/tests/unit/hooks/monitoring/test_zabbix.py index d9777e5f..ab329d9b 100644 --- a/tests/unit/hooks/monitoring/test_zabbix.py +++ b/tests/unit/hooks/monitoring/test_zabbix.py @@ -56,11 +56,13 @@ DATA_USER_LOGOUT = { AUTH_HEADERS_LOGIN = { 'Content-Type': 'application/json-rpc', + 'User-Agent': 'borgmatic', } AUTH_HEADERS = { 'Content-Type': 'application/json-rpc', 'Authorization': f'Bearer {API_KEY}', + 'User-Agent': 'borgmatic', } From f1a008ee18ddcf15c7668accf1f77139d89b0d29 Mon Sep 17 00:00:00 2001 From: Dan Helfman Date: Tue, 2 Sep 2025 09:47:53 -0700 Subject: [PATCH 2/2] Rephrase NEWS entry for clarity (#1139). --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 717e9c48..1de700b2 100644 --- a/NEWS +++ b/NEWS @@ -4,7 +4,7 @@ use. * #1126: Create LVM snapshots as read-write to avoid an error when snapshotting ext4 filesystems with orphaned files that need recovery. - * #1139: When making HTTP requests in monitoring hooks, set "borgmatic" as the user agent. + * #1139: Set "borgmatic" as the user agent when connecting to monitoring services. * When running tests, use Ruff for faster and more comprehensive code linting and formatting, replacing Flake8, Black, isort, etc. * Switch from pipx to uv for installing development tools, and added tox-uv for speeding up test