Add a Pushover monitoring hook.

Merge pull request #86 from tony1661/pushover-branch.
This commit is contained in:
Dan Helfman 2024-11-18 20:17:28 -08:00 committed by GitHub
commit 9807549f88
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 957 additions and 0 deletions

View File

@ -66,6 +66,7 @@ borgmatic is powered by [Borg Backup](https://www.borgbackup.org/).
<a href="https://cronitor.io/"><img src="docs/static/cronitor.png" alt="Cronitor" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
<a href="https://cronhub.io/"><img src="docs/static/cronhub.png" alt="Cronhub" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
<a href="https://www.pagerduty.com/"><img src="docs/static/pagerduty.png" alt="PagerDuty" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
<a href="https://www.pushover.net/"><img src="docs/static/pushover.png" alt="Pushover" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
<a href="https://ntfy.sh/"><img src="docs/static/ntfy.png" alt="ntfy" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
<a href="https://grafana.com/oss/loki/"><img src="docs/static/loki.png" alt="Loki" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>
<a href="https://github.com/caronc/apprise/wiki"><img src="docs/static/apprise.png" alt="Apprise" height="60px" style="margin-bottom:20px; margin-right:20px;"></a>

View File

@ -1626,6 +1626,265 @@ properties:
example:
- start
- finish
pushover:
type: object
required: ['token', 'user']
additionalProperties: false
properties:
token:
type: string
description: |
Your application's API token.
example: 7ms6TXHpTokTou2P6x4SodDeentHRa
user:
type: string
description: |
Your user/group key (or that of your target user), viewable
when logged into your dashboard: often referred to as
USER_KEY in Pushover documentation and code examples.
example: hwRwoWsXMBWwgrSecfa9EfPey55WSN
start:
type: object
properties:
message:
type: string
description: |
Message to be sent to the user or group. If omitted
the default is the name of the state.
example: A backup job has started.
priority:
type: integer
description: |
A value of -2, -1, 0 (default), 1 or 2 that
indicates the message priority.
example: 0
expire:
type: integer
description: |
How many seconds your notification will continue
to be retried (every retry seconds). Defaults to
600. This settings only applies to priority 2
notifications.
example: 600
retry:
type: integer
description: |
The retry parameter specifies how often
(in seconds) the Pushover servers will send the
same notification to the user. Defaults to 30. This
settings only applies to priority 2 notifications.
example: 30
device:
type: string
description: |
The name of one of your devices to send just to
that device instead of all devices.
example: pixel8
html:
type: boolean
description: |
Set to True to enable HTML parsing of the message.
Set to False for plain text.
example: True
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
example: bike
title:
type: string
description: |
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.
value of 2.
example: 3600
url:
type: string
description: |
A supplementary URL to show with your message.
example: https://pushover.net/apps/xxxxx-borgbackup
url_title:
type: string
description: |
A title for the URL specified as the url parameter,
otherwise just the URL is shown.
example: Pushover Link
finish:
type: object
type: object
properties:
message:
type: string
description: |
Message to be sent to the user or group. If omitted
the default is the name of the state.
example: A backup job has finished.
priority:
type: integer
description: |
A value of -2, -1, 0 (default), 1 or 2 that
indicates the message priority.
example: 0
expire:
type: integer
description: |
How many seconds your notification will continue
to be retried (every retry seconds). Defaults to
600. This settings only applies to priority 2
notifications.
example: 600
retry:
type: integer
description: |
The retry parameter specifies how often
(in seconds) the Pushover servers will send the
same notification to the user. Defaults to 30. This
settings only applies to priority 2 notifications.
example: 30
device:
type: string
description: |
The name of one of your devices to send just to
that device instead of all devices.
example: pixel8
html:
type: boolean
description: |
Set to True to enable HTML parsing of the message.
Set to False for plain text.
example: True
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
example: bike
title:
type: string
description: |
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.
value of 2.
example: 3600
url:
type: string
description: |
A supplementary URL to show with your message.
example: https://pushover.net/apps/xxxxx-borgbackup
url_title:
type: string
description: |
A title for the URL specified as the url parameter,
otherwise just the URL is shown.
example: Pushover Link
fail:
type: object
properties:
message:
type: string
description: |
Message to be sent to the user or group. If omitted
the default is the name of the state.
example: A backup job has failed.
priority:
type: integer
description: |
A value of -2, -1, 0 (default), 1 or 2 that
indicates the message priority.
example: 0
expire:
type: integer
description: |
How many seconds your notification will continue
to be retried (every retry seconds). Defaults to
600. This settings only applies to priority 2
notifications.
example: 600
retry:
type: integer
description: |
The retry parameter specifies how often
(in seconds) the Pushover servers will send the
same notification to the user. Defaults to 30. This
settings only applies to priority 2 notifications.
example: 30
device:
type: string
description: |
The name of one of your devices to send just to
that device instead of all devices.
example: pixel8
html:
type: boolean
description: |
Set to True to enable HTML parsing of the message.
Set to False for plain text.
example: True
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
example: bike
title:
type: string
description: |
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.
value of 2.
example: 3600
url:
type: string
description: |
A supplementary URL to show with your message.
example: https://pushover.net/apps/xxxxx-borgbackup
url_title:
type: string
description: |
A title for the URL specified as the url parameter,
otherwise just the URL is shown.
example: Pushover Link
states:
type: array
items:
type: string
enum:
- start
- finish
- fail
uniqueItems: true
description: |
List of one or more monitoring states to ping for: "start",
"finish", and/or "fail". Defaults to pinging for failure
only.
example:
- start
- finish
zabbix:
type: object
additionalProperties: false

View File

@ -12,6 +12,7 @@ from borgmatic.hooks import (
ntfy,
pagerduty,
postgresql,
pushover,
sqlite,
uptimekuma,
zabbix,
@ -31,6 +32,7 @@ HOOK_NAME_TO_MODULE = {
'ntfy': ntfy,
'pagerduty': pagerduty,
'postgresql_databases': postgresql,
'pushover': pushover,
'sqlite_databases': sqlite,
'uptime_kuma': uptimekuma,
'zabbix': zabbix,

View File

@ -8,6 +8,7 @@ MONITOR_HOOK_NAMES = (
'loki',
'ntfy',
'pagerduty',
'pushover',
'uptime_kuma',
'zabbix',
)

View File

@ -0,0 +1,86 @@
import logging
import requests
logger = logging.getLogger(__name__)
EMERGENCY_PRIORITY = 2
def initialize_monitor(
ping_url, config, config_filename, monitoring_log_level, dry_run
): # pragma: no cover
'''
No initialization is necessary for this monitor.
'''
pass
def ping_monitor(hook_config, config, config_filename, state, monitoring_log_level, dry_run):
'''
Post a message to the configured Pushover application.
If this is a dry run, then don't actually update anything.
'''
run_states = hook_config.get('states', ['fail'])
if state.name.lower() not in run_states:
return
dry_run_label = ' (dry run; not actually updating)' if dry_run else ''
state_config = hook_config.get(state.name.lower(), {})
token = hook_config.get('token')
user = hook_config.get('user')
logger.info(f'{config_filename}: Updating Pushover{dry_run_label}')
if state_config.get('priority') == EMERGENCY_PRIORITY:
if 'expire' not in state_config:
logger.info(f'{config_filename}: Setting expire to default (10min).')
state_config['expire'] = 600
if 'retry' not in state_config:
logger.info(f'{config_filename}: Setting retry to default (30sec).')
state_config['retry'] = 30
else:
if 'expire' in state_config or 'retry' in state_config:
raise ValueError(
'The configuration parameters retry and expire should not be set when priority is not equal to 2. Please remove them from the configuration.'
)
state_config = {
key: (int(value) if key in 'html' else value) for key, value in state_config.items()
}
data = dict(
{
'token': token,
'user': user,
'message': state.name.lower(), # default to state name. Can be overwritten in state_config loop below.
},
**state_config,
)
if not dry_run:
logging.getLogger('urllib3').setLevel(logging.ERROR)
try:
response = requests.post(
'https://api.pushover.net/1/messages.json',
headers={'Content-type': 'application/x-www-form-urlencoded'},
data=data,
)
if not response.ok:
response.raise_for_status()
except requests.exceptions.RequestException as error:
logger.warning(f'{config_filename}: Pushover error: {error}')
def destroy_monitor(
ping_url_or_uuid, config, config_filename, monitoring_log_level, dry_run
): # pragma: no cover
'''
No destruction is necessary for this monitor.
'''
pass

View File

@ -46,6 +46,7 @@ them as backups happen:
* [Healthchecks](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#healthchecks-hook)
* [ntfy](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#ntfy-hook)
* [PagerDuty](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#pagerduty-hook)
* [Pushover](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#pushover-hook)
* [Uptime Kuma](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#uptime-kuma-hook)
* [Zabbix](https://torsion.org/borgmatic/docs/how-to/monitor-your-backups/#zabbix-hook)
@ -290,6 +291,76 @@ If you have any issues with the integration, [please contact
us](https://torsion.org/borgmatic/#support-and-contributing).
## Pushover hook
<span class="minilink minilink-addedin">New in version 1.9.2</span>
[Pushover](https://pushover.net) makes it easy to get real-time notifications
on your Android, iPhone, iPad, and Desktop (Android Wear and Apple Watch,
too!).
First, create a Pushover account and login on your mobile device. Create an
Application in your Pushover dashboard.
Then, configure borgmatic with your user's unique "User Key" found in your
Pushover dashboard and the unique "API Token" from the created Application.
Here's a basic example:
```yaml
pushover:
token: 7ms6TXHpTokTou2P6x4SodDeentHRa
user: hwRwoWsXMBWwgrSecfa9EfPey55WSN
```
With this configuration, borgmatic creates a Pushover event for your service
whenever borgmatic fails, but only when any of the `create`, `prune`, `compact`,
or `check` actions are run. Note that borgmatic does not contact Pushover
when a backup starts or when it ends without error by default.
You can configure Pushover to have custom parameters declared for borgmatic's
`start`, `fail` and `finish` hooks states.
Here's a more advanced example:
```yaml
pushover:
token: 7ms6TXHpTokTou2P6x4SodDeentHRa
user: hwRwoWsXMBWwgrSecfa9EfPey55WSN
start:
message: "Backup <b>Started</b>"
priority: -2
title: "Backup Started"
html: True
ttl: 10 # Message will be deleted after 10 seconds.
fail:
message: "Backup <font color='#ff6961'>Failed</font>"
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 Failed"
html: True
sound: "siren"
url: "https://ticketing-system.example.com/login"
url_title: "Login to ticketing system"
finish:
message: "Backup <font color='#77dd77'>Finished</font>"
priority: 0
title: "Backup Finished"
html: True
ttl: 60
url: "https://ticketing-system.example.com/login"
url_title: "Login to ticketing system"
states:
- start
- finish
- fail
```
## ntfy hook
<span class="minilink minilink-addedin">New in version 1.6.3</span>

BIN
docs/static/pushover.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -0,0 +1,537 @@
import pytest
from flexmock import flexmock
import borgmatic.hooks.monitor
from borgmatic.hooks import pushover as module
def test_ping_monitor_config_with_minimum_config_fail_state_backup_successfully_send_to_pushover():
'''
This test should be the minimum working configuration. The "message"
should be auto populated with the default value which is the state name.
'''
hook_config = {'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui'}
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': 'fail',
},
).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_config_with_minimum_config_start_state_backup_not_send_to_pushover_exit_early():
'''
This test should exit early since the hook config does not specify the
'start' state. Only the 'fail' state is enabled by default.
'''
hook_config = {'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui'}
flexmock(module.logger).should_receive('warning').never()
flexmock(module.requests).should_receive('post').never()
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_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.
'''
hook_config = {
'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
'user': '983hfe0of902lkjfa2amanfgui',
'states': {'start', 'fail', 'finish'},
}
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',
},
).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_custom_message_successfully_send_to_pushover():
'''
This test should send a notification to Pushover on backup start
since the state has been configured. It should send a custom
'message' since it is explicitly declared in the state config.
'''
hook_config = {
'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
'user': '983hfe0of902lkjfa2amanfgui',
'states': {'start', 'fail', 'finish'},
'start': {'message': 'custom start message'},
}
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': 'custom start message',
},
).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_uses_expire_and_retry_defaults():
'''
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': 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': 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_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, 'retry': 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': 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_high_declared_expire_and_retry_ignored_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 a ValueError is
raised.
'''
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').never()
with pytest.raises(ValueError):
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_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 <b>Started</b>',
'priority': -2,
'title': 'Backup Started',
'html': 1,
'ttl': 10,
},
'fail': {
'message': 'Backup <font color="#ff6961">Failed</font>',
'priority': 2,
'expire': 600,
'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 <font color="#77dd77">Finished</font>',
'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 <b>Started</b>',
'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 <b>Started</b>',
'priority': -2,
'title': 'Backup Started',
'html': 1,
'ttl': 10,
},
'fail': {
'message': 'Backup <font color="#ff6961">Failed</font>',
'priority': 2,
'expire': 600,
'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 <font color="#77dd77">Finished</font>',
'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 <font color="#ff6961">Failed</font>',
'priority': 2,
'expire': 600,
'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 <b>Started</b>',
'priority': -2,
'title': 'Backup Started',
'html': 1,
'ttl': 10,
},
'fail': {
'message': 'Backup <font color="#ff6961">Failed</font>',
'priority': 2,
'expire': 600,
'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 <font color="#77dd77">Finished</font>',
'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 <font color="#77dd77">Finished</font>',
'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,
)
def test_ping_monitor_config_with_minimum_config_fail_state_backup_successfully_send_to_pushover_dryrun():
'''
This test should be the minimum working configuration. The "message"
should be auto populated with the default value which is the state name.
'''
hook_config = {'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui'}
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': 'fail',
},
).and_return(flexmock(ok=True)).never()
module.ping_monitor(
hook_config,
{},
'config.yaml',
borgmatic.hooks.monitor.State.FAIL,
monitoring_log_level=1,
dry_run=True,
)
def test_ping_monitor_config_incorrect_state_exit_early():
'''
This test should exit early since the start state is not declared in the configuration.
'''
hook_config = {
'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
'user': '983hfe0of902lkjfa2amanfgui',
}
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',
},
).and_return(flexmock(ok=True)).never()
module.ping_monitor(
hook_config,
{},
'config.yaml',
borgmatic.hooks.monitor.State.START,
monitoring_log_level=1,
dry_run=True,
)
def test_ping_monitor_push_post_error_exits_early():
'''
This test simulates the Pushover servers not responding with a 200 OK. We
should raise for status and warn then exit.
'''
hook_config = hook_config = {
'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
'user': '983hfe0of902lkjfa2amanfgui',
}
push_response = flexmock(ok=False)
push_response.should_receive('raise_for_status').and_raise(
module.requests.ConnectionError
).once()
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': 'fail',
},
).and_return(push_response).once()
flexmock(module.logger).should_receive('warning').once()
module.ping_monitor(
hook_config,
{},
'config.yaml',
borgmatic.hooks.monitor.State.FAIL,
monitoring_log_level=1,
dry_run=False,
)