From 2e4c0cc7e72a5521b76fb459e41f01f85a6ec1ba Mon Sep 17 00:00:00 2001 From: estebanthi Date: Fri, 19 Apr 2024 10:36:40 +0200 Subject: [PATCH 1/4] Support for healthchecks auto provisionning --- borgmatic/config/schema.yaml | 7 ++++ borgmatic/hooks/healthchecks.py | 6 ++++ tests/unit/hooks/test_healthchecks.py | 52 +++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/borgmatic/config/schema.yaml b/borgmatic/config/schema.yaml index dacc4105..7ca317a2 100644 --- a/borgmatic/config/schema.yaml +++ b/borgmatic/config/schema.yaml @@ -1662,6 +1662,13 @@ properties: states. example: - finish + create_slug: + type: boolean + description: | + Create the check if it does not exist. Only works with + the slug URL scheme (https://hc-ping.com// + as opposed to https://hc-ping.com/). Defaults to false. + example: true description: | Configuration for a monitoring integration with Healthchecks. Create an account at https://healthchecks.io (or self-host Healthchecks) if diff --git a/borgmatic/hooks/healthchecks.py b/borgmatic/hooks/healthchecks.py index 8c63f217..c36143d2 100644 --- a/borgmatic/hooks/healthchecks.py +++ b/borgmatic/hooks/healthchecks.py @@ -1,4 +1,5 @@ import logging +import re import requests @@ -59,10 +60,15 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev ) return + ping_url_is_uuid = re.match(r'(\w{4}-?){4}$', hook_config['ping_url']) + healthchecks_state = MONITOR_STATE_TO_HEALTHCHECKS.get(state) if healthchecks_state: ping_url = f'{ping_url}/{healthchecks_state}' + if hook_config.get('create_slug') and not ping_url_is_uuid: + ping_url = f'{ping_url}?create=1' + logger.info(f'{config_filename}: Pinging Healthchecks {state.name.lower()}{dry_run_label}') logger.debug(f'{config_filename}: Using Healthchecks ping URL {ping_url}') diff --git a/tests/unit/hooks/test_healthchecks.py b/tests/unit/hooks/test_healthchecks.py index 479b0e34..f53c0f59 100644 --- a/tests/unit/hooks/test_healthchecks.py +++ b/tests/unit/hooks/test_healthchecks.py @@ -264,6 +264,58 @@ def test_ping_monitor_hits_ping_url_when_states_matching(): ) +def test_ping_monitor_adds_create_query_parameter_when_create_slug_true(): + flexmock(module.borgmatic.hooks.logs).should_receive('Forgetful_buffering_handler').never() + hook_config = {'ping_url': 'https://example.com', 'create_slug': True} + flexmock(module.requests).should_receive('post').with_args( + 'https://example.com/start?create=1', data=''.encode('utf-8'), verify=True + ).and_return(flexmock(ok=True)) + + module.ping_monitor( + hook_config, + {}, + 'config.yaml', + state=module.monitor.State.START, + monitoring_log_level=1, + dry_run=False, + ) + + +def test_ping_monitor_does_not_add_create_query_parameter_when_create_slug_false(): + flexmock(module.borgmatic.hooks.logs).should_receive('Forgetful_buffering_handler').never() + hook_config = {'ping_url': 'https://example.com', 'create_slug': False} + flexmock(module.requests).should_receive('post').with_args( + 'https://example.com/start', data=''.encode('utf-8'), verify=True + ).and_return(flexmock(ok=True)) + + module.ping_monitor( + hook_config, + {}, + 'config.yaml', + state=module.monitor.State.START, + monitoring_log_level=1, + dry_run=False, + ) + + +def test_ping_monitor_does_not_add_create_query_parameter_when_ping_url_is_uuid(): + hook_config = {'ping_url': 'abcd-efgh-ijkl-mnop', 'create_slug': True} + flexmock(module.requests).should_receive('post').with_args( + f"https://hc-ping.com/{hook_config['ping_url']}", + data=''.encode('utf-8'), + verify=True, + ).and_return(flexmock(ok=True)) + + module.ping_monitor( + hook_config, + {}, + 'config.yaml', + state=module.monitor.State.FINISH, + monitoring_log_level=1, + dry_run=False, + ) + + def test_ping_monitor_with_connection_error_logs_warning(): flexmock(module.borgmatic.hooks.logs).should_receive('Forgetful_buffering_handler').never() hook_config = {'ping_url': 'https://example.com'} From cfdc0a1f2a8d5c0f42c239b73c0f8cfbfb4fc73b Mon Sep 17 00:00:00 2001 From: estebanthilliez Date: Mon, 22 Apr 2024 20:44:20 +0200 Subject: [PATCH 2/4] Fix Healthchecks UUID regex --- borgmatic/hooks/healthchecks.py | 2 +- tests/unit/hooks/test_healthchecks.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/borgmatic/hooks/healthchecks.py b/borgmatic/hooks/healthchecks.py index c36143d2..65afc027 100644 --- a/borgmatic/hooks/healthchecks.py +++ b/borgmatic/hooks/healthchecks.py @@ -60,7 +60,7 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev ) return - ping_url_is_uuid = re.match(r'(\w{4}-?){4}$', hook_config['ping_url']) + ping_url_is_uuid = re.search(r'\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$', ping_url) healthchecks_state = MONITOR_STATE_TO_HEALTHCHECKS.get(state) if healthchecks_state: diff --git a/tests/unit/hooks/test_healthchecks.py b/tests/unit/hooks/test_healthchecks.py index f53c0f59..74afe40e 100644 --- a/tests/unit/hooks/test_healthchecks.py +++ b/tests/unit/hooks/test_healthchecks.py @@ -299,7 +299,7 @@ def test_ping_monitor_does_not_add_create_query_parameter_when_create_slug_false def test_ping_monitor_does_not_add_create_query_parameter_when_ping_url_is_uuid(): - hook_config = {'ping_url': 'abcd-efgh-ijkl-mnop', 'create_slug': True} + hook_config = {'ping_url': 'b3611b24-df9c-4d36-9203-fa292820bf2a', 'create_slug': True} flexmock(module.requests).should_receive('post').with_args( f"https://hc-ping.com/{hook_config['ping_url']}", data=''.encode('utf-8'), From 4b7f7bba049c598976a45d905a5deaf42beddc34 Mon Sep 17 00:00:00 2001 From: estebanthilliez Date: Mon, 22 Apr 2024 20:45:36 +0200 Subject: [PATCH 3/4] Issue warning if using UUID URL scheme with create_slug --- borgmatic/hooks/healthchecks.py | 9 +++++++-- tests/unit/hooks/test_healthchecks.py | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/borgmatic/hooks/healthchecks.py b/borgmatic/hooks/healthchecks.py index 65afc027..f7daf677 100644 --- a/borgmatic/hooks/healthchecks.py +++ b/borgmatic/hooks/healthchecks.py @@ -66,8 +66,13 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev if healthchecks_state: ping_url = f'{ping_url}/{healthchecks_state}' - if hook_config.get('create_slug') and not ping_url_is_uuid: - ping_url = f'{ping_url}?create=1' + if hook_config.get('create_slug'): + if ping_url_is_uuid: + logger.warning( + f'{config_filename}: Healthchecks UUIDs do not support auto provisionning; ignoring' + ) + else: + ping_url = f'{ping_url}?create=1' logger.info(f'{config_filename}: Pinging Healthchecks {state.name.lower()}{dry_run_label}') logger.debug(f'{config_filename}: Using Healthchecks ping URL {ping_url}') diff --git a/tests/unit/hooks/test_healthchecks.py b/tests/unit/hooks/test_healthchecks.py index 74afe40e..1e8a2a1e 100644 --- a/tests/unit/hooks/test_healthchecks.py +++ b/tests/unit/hooks/test_healthchecks.py @@ -316,6 +316,20 @@ def test_ping_monitor_does_not_add_create_query_parameter_when_ping_url_is_uuid( ) +def test_ping_monitor_issues_warning_when_ping_url_is_uuid_and_create_slug_true(): + hook_config = {'ping_url': 'b3611b24-df9c-4d36-9203-fa292820bf2a', 'create_slug': True} + flexmock(module.logger).should_receive('warning').once() + + module.ping_monitor( + hook_config, + {}, + 'config.yaml', + state=module.monitor.State.FINISH, + monitoring_log_level=1, + dry_run=False, + ) + + def test_ping_monitor_with_connection_error_logs_warning(): flexmock(module.borgmatic.hooks.logs).should_receive('Forgetful_buffering_handler').never() hook_config = {'ping_url': 'https://example.com'} From 407bb33359925673cbc0d84120b7003cf48a3eac Mon Sep 17 00:00:00 2001 From: estebanthilliez Date: Mon, 22 Apr 2024 20:47:03 +0200 Subject: [PATCH 4/4] Fix schema.yaml to comply with maximum line length --- borgmatic/config/schema.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/borgmatic/config/schema.yaml b/borgmatic/config/schema.yaml index 7ca317a2..538c468d 100644 --- a/borgmatic/config/schema.yaml +++ b/borgmatic/config/schema.yaml @@ -1667,7 +1667,8 @@ properties: description: | Create the check if it does not exist. Only works with the slug URL scheme (https://hc-ping.com// - as opposed to https://hc-ping.com/). Defaults to false. + as opposed to https://hc-ping.com/). + Defaults to false. example: true description: | Configuration for a monitoring integration with Healthchecks. Create