From 94db527500a1878c60f41154702442d011266b5e Mon Sep 17 00:00:00 2001 From: Antonio Fernandez Date: Wed, 30 Oct 2024 15:43:06 -0400 Subject: [PATCH] finalized support for Pushover --- README.md | 1 + borgmatic/config/schema.yaml | 126 +++++++----- borgmatic/hooks/pushover.py | 17 +- docs/how-to/monitor-your-backups.md | 20 +- docs/static/pushover.png | Bin 0 -> 6488 bytes tests/unit/hooks/test_pushover.py | 302 +++++++++++++++++++++++++++- 6 files changed, 397 insertions(+), 69 deletions(-) create mode 100644 docs/static/pushover.png diff --git a/README.md b/README.md index 45b48a4e..01151e70 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ borgmatic is powered by [Borg Backup](https://www.borgbackup.org/). Cronitor Cronhub PagerDuty +Pushover ntfy Loki Apprise diff --git a/borgmatic/config/schema.yaml b/borgmatic/config/schema.yaml index d7668124..222a141d 100644 --- a/borgmatic/config/schema.yaml +++ b/borgmatic/config/schema.yaml @@ -1637,40 +1637,48 @@ properties: priority: type: integer description: | - A value of -2, -1, 0 (default), 1 or 2 that - indicates the message priority. - example: "0" + A value of -2, -1, 0 (default), 1 or 2 that indicates the message priority. + example: 0 + expire: + type: integer + description: | + he expire parameter specifies how many seconds + your notification will continue to be retried + for (every retry seconds). + example: 1200 + retry: + type: integer + description: | + The retry parameter specifies how often + (in seconds) the Pushover servers will send the + same notification to the user. + example: 30 device: type: string description: | - The name of one of your devices to send just to that - device instead of all devices. + The name of one of your devices to send just to that device instead of all devices. example: pixel8 html: type: integer description: | - Set to 1 to enable HTML parsing of the message. Set - to 0 for plain text. + Set to 1 to enable HTML parsing of the message. Set to 0 for plain text. example: 1 sound: type: string description: | - The name of a supported sound to override your - default sound choice. All options can be - found here: https://pushover.net/api#sounds + The name of a supported sound to override your default sound choice. All + options can be found here: https://pushover.net/api#sounds example: bike title: type: string description: | - Your message's title, otherwise your app's - name is used. + Your message's title, otherwise your app's name is used. example: A backup job has started. ttl: type: integer description: | - The number of seconds that the message will live, - before being deleted automatically. The ttl - parameter is ignored for messages with a priority + The number of seconds that the message will live, before being deleted + automatically. The ttl parameter is ignored for messages with a priority value of 2. example: 3600 url: @@ -1681,8 +1689,8 @@ properties: url_title: type: string description: | - A title for the URL specified as the url parameter, - otherwise just the URL is shown. + A title for the URL specified as the url parameter, otherwise just + the URL is shown. example: Pushover Link finish: type: object @@ -1696,40 +1704,48 @@ properties: priority: type: integer description: | - A value of -2, -1, 0 (default), 1 or 2 that - indicates the message priority. - example: "0" + A value of -2, -1, 0 (default), 1 or 2 that indicates the message priority. + example: 0 + expire: + type: integer + description: | + he expire parameter specifies how many seconds + your notification will continue to be retried + for (every retry seconds). + example: 1200 + retry: + type: integer + description: | + The retry parameter specifies how often + (in seconds) the Pushover servers will send the + same notification to the user. + example: 30 device: type: string description: | - The name of one of your devices to send just to that - device instead of all devices. + The name of one of your devices to send just to that device instead of all devices. example: pixel8 html: type: integer description: | - Set to 1 to enable HTML parsing of the message. Set - to 0 for plain text. + Set to 1 to enable HTML parsing of the message. Set to 0 for plain text. example: 1 sound: type: string description: | - The name of a supported sound to override your - default sound choice. All options can be - found here: https://pushover.net/api#sounds + The name of a supported sound to override your default sound choice. All + options can be found here: https://pushover.net/api#sounds example: bike title: type: string description: | - Your message's title, otherwise your app's - name is used. + Your message's title, otherwise your app's name is used. example: A backup job has started. ttl: type: integer description: | - The number of seconds that the message will live, - before being deleted automatically. The ttl - parameter is ignored for messages with a priority + The number of seconds that the message will live, before being deleted + automatically. The ttl parameter is ignored for messages with a priority value of 2. example: 3600 url: @@ -1740,8 +1756,8 @@ properties: url_title: type: string description: | - A title for the URL specified as the url parameter, - otherwise just the URL is shown. + A title for the URL specified as the url parameter, otherwise just + the URL is shown. example: Pushover Link fail: type: object @@ -1754,40 +1770,48 @@ properties: priority: type: integer description: | - A value of -2, -1, 0 (default), 1 or 2 that - indicates the message priority. - example: "0" + A value of -2, -1, 0 (default), 1 or 2 that indicates the message priority. + example: 0 + expire: + type: integer + description: | + he expire parameter specifies how many seconds + your notification will continue to be retried + for (every retry seconds). + example: 1200 + retry: + type: integer + description: | + The retry parameter specifies how often + (in seconds) the Pushover servers will send the + same notification to the user. + example: 30 device: type: string description: | - The name of one of your devices to send just to that - device instead of all devices. + The name of one of your devices to send just to that device instead of all devices. example: pixel8 html: type: integer description: | - Set to 1 to enable HTML parsing of the message. Set - to 0 for plain text. + Set to 1 to enable HTML parsing of the message. Set to 0 for plain text. example: 1 sound: type: string description: | - The name of a supported sound to override your - default sound choice. All options can be - found here: https://pushover.net/api#sounds + The name of a supported sound to override your default sound choice. All + options can be found here: https://pushover.net/api#sounds example: bike title: type: string description: | - Your message's title, otherwise your app's - name is used. + Your message's title, otherwise your app's name is used. example: A backup job has started. ttl: type: integer description: | - The number of seconds that the message will live, - before being deleted automatically. The ttl - parameter is ignored for messages with a priority + The number of seconds that the message will live, before being deleted + automatically. The ttl parameter is ignored for messages with a priority value of 2. example: 3600 url: @@ -1798,8 +1822,8 @@ properties: url_title: type: string description: | - A title for the URL specified as the url parameter, - otherwise just the URL is shown. + A title for the URL specified as the url parameter, otherwise just + the URL is shown. example: Pushover Link states: type: array diff --git a/borgmatic/hooks/pushover.py b/borgmatic/hooks/pushover.py index 73a01c89..ecd6ce28 100644 --- a/borgmatic/hooks/pushover.py +++ b/borgmatic/hooks/pushover.py @@ -46,18 +46,25 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev logger.warning(f'{config_filename}: User missing for Pushover') return + if 'priority' in state_config and state_config['priority'] == 2: + if 'expire' not in state_config: + logger.info(f'{config_filename}: Setting expire to default (10min).') + state_config['expire'] = 1200 + if 'retry' not in state_config: + logger.info(f'{config_filename}: Setting retry to default (30sec).') + state_config['retry'] = 30 + else: + state_config.pop('expire', None) + state_config.pop('retry', None) + data = { 'token': token, 'user': user, 'message': state.name.lower(), # default to state name. Can be overwritten in state_config loop below. } - + for key in state_config: data[key] = state_config[key] - if key == 'priority': - if data['priority'] == 2: - data['expire'] = 30 - data['retry'] = 30 if not dry_run: logging.getLogger('urllib3').setLevel(logging.ERROR) diff --git a/docs/how-to/monitor-your-backups.md b/docs/how-to/monitor-your-backups.md index 9069610f..82de8c28 100644 --- a/docs/how-to/monitor-your-backups.md +++ b/docs/how-to/monitor-your-backups.md @@ -331,20 +331,28 @@ pushover: start: message: "Backup Started" priority: -2 - device: "pixel8" title: "Backup Started" html: 1 - sound: "bike" - ttl: 10 + ttl: 10 # Message will be deleted after 10 seconds. fail: - message: "Backup Failed" - priority: -2 + message: "Backup Failed" + priority: 2 # Requests acknowledgement for messages. + expire: 1200 # Used only for priority 2. Default is 1200 seconds. + retry: 30 # Used only for priority 2. Default is 30 seconds. device: "pixel8" - title: "Backup Started" + title: "Backup Failed" html: 1 sound: "siren" url: "https://ticketing-system.example.com/login" url_title: "Login to ticketing system" + finish: + message: "Backup Finished" + priority: 0 + title: "Backup Finished" + html: 1 + ttl: 60 + url: "https://ticketing-system.example.com/login" + url_title: "Login to ticketing system" states: - start - finish diff --git a/docs/static/pushover.png b/docs/static/pushover.png new file mode 100644 index 0000000000000000000000000000000000000000..e0ef72a9c069e3d53a658102678966e83c757108 GIT binary patch literal 6488 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 literal 0 HcmV?d00001 diff --git a/tests/unit/hooks/test_pushover.py b/tests/unit/hooks/test_pushover.py index 8484f5ab..b709ae4d 100644 --- a/tests/unit/hooks/test_pushover.py +++ b/tests/unit/hooks/test_pushover.py @@ -142,17 +142,115 @@ def test_ping_monitor_start_state_backup_custom_message_successfully_send_to_pus ) -def test_ping_monitor_start_state_backup_default_message_with_priority_declared_successfully_send_to_pushover(): - # This test should send a notification to Pushover on backup start - # since the state has been configured. It should default to sending - # the name of the state as the 'message' since it is not - # explicitly declared in the state config. It should also send - # with a priority of 1 (high). +def test_ping_monitor_start_state_backup_default_message_with_priority_emergency_declared_no_expiry_or_retry_success(): + # This simulates priority level 2 being set but expiry and retry are + # not declared. This should set retry and expiry to their defaults. hook_config = { 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui', 'states': {'start', 'fail', 'finish'}, - 'start': {'priority': 1}, + 'start': {'priority': 2}, + } + 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'}, + data={ + 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', + 'user': '983hfe0of902lkjfa2amanfgui', + 'message': 'start', + 'priority': 2, + 'retry': 30, + 'expire': 1200, + }, + ).and_return(flexmock(ok=True)).once() + + module.ping_monitor( + hook_config, + {}, + 'config.yaml', + borgmatic.hooks.monitor.State.START, + monitoring_log_level=1, + dry_run=False, + ) + + +def test_ping_monitor_start_state_backup_default_message_with_priority_emergency_declared_with_expire_no_retry_success(): + # This simulates priority level 2 and expiry being set but retry is + # not declared. This should set retry to the default. + hook_config = { + 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', + 'user': '983hfe0of902lkjfa2amanfgui', + 'states': {'start', 'fail', 'finish'}, + 'start': {'priority': 2, 'expire': 600}, + } + 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'}, + data={ + 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', + 'user': '983hfe0of902lkjfa2amanfgui', + 'message': 'start', + 'priority': 2, + 'retry': 30, + 'expire': 600, + }, + ).and_return(flexmock(ok=True)).once() + + module.ping_monitor( + hook_config, + {}, + 'config.yaml', + borgmatic.hooks.monitor.State.START, + monitoring_log_level=1, + dry_run=False, + ) + + +def test_ping_monitor_start_state_backup_default_message_with_priority_emergency_declared_no_expire_with_retry_success(): + # This simulates priority level 2 and retry being set but expire is + # not declared. This should set expire to the default. + hook_config = { + 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', + 'user': '983hfe0of902lkjfa2amanfgui', + 'states': {'start', 'fail', 'finish'}, + 'start': {'priority': 2, 'expire': 30}, + } + 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'}, + data={ + 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', + 'user': '983hfe0of902lkjfa2amanfgui', + 'message': 'start', + 'priority': 2, + 'retry': 30, + 'expire': 30, + }, + ).and_return(flexmock(ok=True)).once() + + module.ping_monitor( + hook_config, + {}, + 'config.yaml', + borgmatic.hooks.monitor.State.START, + monitoring_log_level=1, + dry_run=False, + ) + + +def test_ping_monitor_start_state_backup_default_message_with_priority_high_declared_expire_and_retry_delared_success(): + # This simulates priority level 1, retry and expiry being set. Since expire + # and retry are only used for priority level 2, they should not be included + # in the request sent to Pushover. This test verifies that those are + # stripped from the request. + hook_config = { + 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', + 'user': '983hfe0of902lkjfa2amanfgui', + 'states': {'start', 'fail', 'finish'}, + 'start': {'priority': 1, 'expire': 30, 'retry': 30}, } flexmock(module.logger).should_receive('warning').never() flexmock(module.requests).should_receive('post').with_args( @@ -174,3 +272,193 @@ def test_ping_monitor_start_state_backup_default_message_with_priority_declared_ monitoring_log_level=1, dry_run=False, ) + + +def test_ping_monitor_start_state_backup_based_on_documentation_advanced_example_success(): + # Here is a test of what is provided in the monitor-your-backups.md file + # as an 'advanced example'. This test runs the start state. + hook_config = { + 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', + 'user': '983hfe0of902lkjfa2amanfgui', + 'states': {'start', 'fail', 'finish'}, + 'start': { + 'message': 'Backup Started', + 'priority': -2, + 'title': 'Backup Started', + 'html': 1, + 'ttl': 10, + }, + 'fail': { + 'message': 'Backup Failed', + 'priority': 2, + 'expire': 1200, + 'retry': 30, + 'device': 'pixel8', + 'title': 'Backup Failed', + 'html': 1, + 'sound': 'siren', + 'url': 'https://ticketing-system.example.com/login', + 'url_title': 'Login to ticketing system', + }, + 'finish': { + 'message': 'Backup Finished', + 'priority': 0, + 'title': 'Backup Finished', + 'html': 1, + 'ttl': 60, + 'url': 'https://ticketing-system.example.com/login', + 'url_title': 'Login to ticketing system', + }, + } + 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'}, + data={ + 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', + 'user': '983hfe0of902lkjfa2amanfgui', + 'message': 'Backup Started', + 'priority': -2, + 'title': 'Backup Started', + 'html': 1, + 'ttl': 10, + }, + ).and_return(flexmock(ok=True)).once() + + module.ping_monitor( + hook_config, + {}, + 'config.yaml', + borgmatic.hooks.monitor.State.START, + monitoring_log_level=1, + dry_run=False, + ) + + +def test_ping_monitor_fail_state_backup_based_on_documentation_advanced_example_success(): + # Here is a test of what is provided in the monitor-your-backups.md file + # as an 'advanced example'. This test runs the fail state. + hook_config = { + 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', + 'user': '983hfe0of902lkjfa2amanfgui', + 'states': {'start', 'fail', 'finish'}, + 'start': { + 'message': 'Backup Started', + 'priority': -2, + 'title': 'Backup Started', + 'html': 1, + 'ttl': 10, + }, + 'fail': { + 'message': 'Backup Failed', + 'priority': 2, + 'expire': 1200, + 'retry': 30, + 'device': 'pixel8', + 'title': 'Backup Failed', + 'html': 1, + 'sound': 'siren', + 'url': 'https://ticketing-system.example.com/login', + 'url_title': 'Login to ticketing system', + }, + 'finish': { + 'message': 'Backup Finished', + 'priority': 0, + 'title': 'Backup Finished', + 'html': 1, + 'ttl': 60, + 'url': 'https://ticketing-system.example.com/login', + 'url_title': 'Login to ticketing system', + }, + } + 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'}, + data={ + 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', + 'user': '983hfe0of902lkjfa2amanfgui', + 'message': 'Backup Failed', + 'priority': 2, + 'expire': 1200, + 'retry': 30, + 'device': 'pixel8', + 'title': 'Backup Failed', + 'html': 1, + 'sound': 'siren', + 'url': 'https://ticketing-system.example.com/login', + 'url_title': 'Login to ticketing system', + }, + ).and_return(flexmock(ok=True)).once() + + module.ping_monitor( + hook_config, + {}, + 'config.yaml', + borgmatic.hooks.monitor.State.FAIL, + monitoring_log_level=1, + dry_run=False, + ) + + +def test_ping_monitor_finish_state_backup_based_on_documentation_advanced_example_success(): + # Here is a test of what is provided in the monitor-your-backups.md file + # as an 'advanced example'. This test runs the finish state. + hook_config = { + 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', + 'user': '983hfe0of902lkjfa2amanfgui', + 'states': {'start', 'fail', 'finish'}, + 'start': { + 'message': 'Backup Started', + 'priority': -2, + 'title': 'Backup Started', + 'html': 1, + 'ttl': 10, + }, + 'fail': { + 'message': 'Backup Failed', + 'priority': 2, + 'expire': 1200, + 'retry': 30, + 'device': 'pixel8', + 'title': 'Backup Failed', + 'html': 1, + 'sound': 'siren', + 'url': 'https://ticketing-system.example.com/login', + 'url_title': 'Login to ticketing system', + }, + 'finish': { + 'message': 'Backup Finished', + 'priority': 0, + 'title': 'Backup Finished', + 'html': 1, + 'ttl': 60, + 'url': 'https://ticketing-system.example.com/login', + 'url_title': 'Login to ticketing system', + }, + } + 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'}, + data={ + 'token': 'ksdjfwoweijfvwoeifvjmwghagy92', + 'user': '983hfe0of902lkjfa2amanfgui', + 'message': 'Backup Finished', + 'priority': 0, + 'title': 'Backup Finished', + 'html': 1, + 'ttl': 60, + 'url': 'https://ticketing-system.example.com/login', + 'url_title': 'Login to ticketing system', + }, + ).and_return(flexmock(ok=True)).once() + + module.ping_monitor( + hook_config, + {}, + 'config.yaml', + borgmatic.hooks.monitor.State.FINISH, + monitoring_log_level=1, + dry_run=False, + )