mirror of
https://github.com/borgmatic-collective/borgmatic.git
synced 2026-07-25 03:13:02 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6cdc92bd0c | ||
|
|
612e1fea67 | ||
|
|
0a9f4e8708 | ||
|
|
781fac3266 | ||
|
|
4c38810a32 |
@@ -28,4 +28,8 @@ Use `sudo borg --version`
|
||||
|
||||
Use `python3 --version`
|
||||
|
||||
**Database version (if applicable):** [version here]
|
||||
|
||||
Use `psql --version` or `mysql --version` on client and server.
|
||||
|
||||
**operating system and version:** [OS here]
|
||||
|
||||
@@ -11,4 +11,4 @@ Robin `ypid` Schneider: Support additional options of Borg and add validate-borg
|
||||
Scott Squires: Custom archive names
|
||||
Thomas LÉVEIL: Support for a keep_minutely prune option. Support for the --json option
|
||||
|
||||
Any many others! See the output of "git log".
|
||||
And many others! See the output of "git log".
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
1.4.10
|
||||
* #246: Fix for "borgmatic restore" showing success and incorrectly extracting archive files, even
|
||||
when no databases are configured to restore. As this can overwrite files from the archive and
|
||||
lead to data loss, please upgrade to get the fix before using "borgmatic restore".
|
||||
* Reopen the file given by "--log-file" flag if an external program rotates the log file while
|
||||
borgmatic is running.
|
||||
|
||||
1.4.9
|
||||
* #228: Database dump hooks for MySQL/MariaDB, so you can easily dump your databases before backups
|
||||
run.
|
||||
|
||||
@@ -18,7 +18,7 @@ from borgmatic.borg import list as borg_list
|
||||
from borgmatic.borg import prune as borg_prune
|
||||
from borgmatic.commands.arguments import parse_arguments
|
||||
from borgmatic.config import checks, collect, convert, validate
|
||||
from borgmatic.hooks import command, cronhub, cronitor, dispatch, dump, healthchecks
|
||||
from borgmatic.hooks import command, dispatch, dump, monitor
|
||||
from borgmatic.logger import configure_logging, should_do_markup
|
||||
from borgmatic.signals import configure_signals
|
||||
from borgmatic.verbosity import verbosity_to_log_level
|
||||
@@ -53,14 +53,13 @@ def run_configuration(config_filename, config, arguments):
|
||||
|
||||
if 'create' in arguments:
|
||||
try:
|
||||
healthchecks.ping_healthchecks(
|
||||
hooks.get('healthchecks'), config_filename, global_arguments.dry_run, 'start'
|
||||
)
|
||||
cronitor.ping_cronitor(
|
||||
hooks.get('cronitor'), config_filename, global_arguments.dry_run, 'run'
|
||||
)
|
||||
cronhub.ping_cronhub(
|
||||
hooks.get('cronhub'), config_filename, global_arguments.dry_run, 'start'
|
||||
dispatch.call_hooks(
|
||||
'ping_monitor',
|
||||
hooks,
|
||||
config_filename,
|
||||
monitor.MONITOR_HOOK_NAMES,
|
||||
monitor.State.START,
|
||||
global_arguments.dry_run,
|
||||
)
|
||||
command.execute_hook(
|
||||
hooks.get('before_backup'),
|
||||
@@ -119,14 +118,13 @@ def run_configuration(config_filename, config, arguments):
|
||||
'post-backup',
|
||||
global_arguments.dry_run,
|
||||
)
|
||||
healthchecks.ping_healthchecks(
|
||||
hooks.get('healthchecks'), config_filename, global_arguments.dry_run
|
||||
)
|
||||
cronitor.ping_cronitor(
|
||||
hooks.get('cronitor'), config_filename, global_arguments.dry_run, 'complete'
|
||||
)
|
||||
cronhub.ping_cronhub(
|
||||
hooks.get('cronhub'), config_filename, global_arguments.dry_run, 'finish'
|
||||
dispatch.call_hooks(
|
||||
'ping_monitor',
|
||||
hooks,
|
||||
config_filename,
|
||||
monitor.MONITOR_HOOK_NAMES,
|
||||
monitor.State.FINISH,
|
||||
global_arguments.dry_run,
|
||||
)
|
||||
except (OSError, CalledProcessError) as error:
|
||||
encountered_error = error
|
||||
@@ -146,14 +144,13 @@ def run_configuration(config_filename, config, arguments):
|
||||
error=encountered_error,
|
||||
output=getattr(encountered_error, 'output', ''),
|
||||
)
|
||||
healthchecks.ping_healthchecks(
|
||||
hooks.get('healthchecks'), config_filename, global_arguments.dry_run, 'fail'
|
||||
)
|
||||
cronitor.ping_cronitor(
|
||||
hooks.get('cronitor'), config_filename, global_arguments.dry_run, 'fail'
|
||||
)
|
||||
cronhub.ping_cronhub(
|
||||
hooks.get('cronhub'), config_filename, global_arguments.dry_run, 'fail'
|
||||
dispatch.call_hooks(
|
||||
'ping_monitor',
|
||||
hooks,
|
||||
config_filename,
|
||||
monitor.MONITOR_HOOK_NAMES,
|
||||
monitor.State.FAIL,
|
||||
global_arguments.dry_run,
|
||||
)
|
||||
except (OSError, CalledProcessError) as error:
|
||||
yield from make_error_log_records(
|
||||
@@ -269,12 +266,13 @@ def run_actions(
|
||||
dump.DATABASE_HOOK_NAMES,
|
||||
restore_names,
|
||||
)
|
||||
|
||||
borg_extract.extract_archive(
|
||||
global_arguments.dry_run,
|
||||
repository,
|
||||
arguments['restore'].archive,
|
||||
dump.convert_glob_patterns_to_borg_patterns(
|
||||
[pattern for patterns in dump_patterns.values() for pattern in patterns]
|
||||
dump.flatten_dump_patterns(dump_patterns, restore_names)
|
||||
),
|
||||
location,
|
||||
storage,
|
||||
|
||||
@@ -2,23 +2,29 @@ import logging
|
||||
|
||||
import requests
|
||||
|
||||
from borgmatic.hooks import monitor
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
MONITOR_STATE_TO_CRONHUB = {
|
||||
monitor.State.START: 'start',
|
||||
monitor.State.FINISH: 'finish',
|
||||
monitor.State.FAIL: 'fail',
|
||||
}
|
||||
|
||||
def ping_cronhub(ping_url, config_filename, dry_run, state):
|
||||
|
||||
def ping_monitor(ping_url, config_filename, state, dry_run):
|
||||
'''
|
||||
Ping the given Cronhub URL, substituting in the state string. Use the given configuration
|
||||
Ping the given Cronhub URL, modified with the monitor.State. Use the given configuration
|
||||
filename in any log entries. If this is a dry run, then don't actually ping anything.
|
||||
'''
|
||||
if not ping_url:
|
||||
logger.debug('{}: No Cronhub hook set'.format(config_filename))
|
||||
return
|
||||
|
||||
dry_run_label = ' (dry run; not actually pinging)' if dry_run else ''
|
||||
formatted_state = '/{}/'.format(state)
|
||||
formatted_state = '/{}/'.format(MONITOR_STATE_TO_CRONHUB[state])
|
||||
ping_url = ping_url.replace('/start/', formatted_state).replace('/ping/', formatted_state)
|
||||
|
||||
logger.info('{}: Pinging Cronhub {}{}'.format(config_filename, state, dry_run_label))
|
||||
logger.info(
|
||||
'{}: Pinging Cronhub {}{}'.format(config_filename, state.name.lower(), dry_run_label)
|
||||
)
|
||||
logger.debug('{}: Using Cronhub ping URL {}'.format(config_filename, ping_url))
|
||||
|
||||
if not dry_run:
|
||||
|
||||
+16
-10
@@ -2,22 +2,28 @@ import logging
|
||||
|
||||
import requests
|
||||
|
||||
from borgmatic.hooks import monitor
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
MONITOR_STATE_TO_CRONITOR = {
|
||||
monitor.State.START: 'run',
|
||||
monitor.State.FINISH: 'complete',
|
||||
monitor.State.FAIL: 'fail',
|
||||
}
|
||||
|
||||
def ping_cronitor(ping_url, config_filename, dry_run, append):
|
||||
'''
|
||||
Ping the given Cronitor URL, appending the append string. Use the given configuration filename
|
||||
in any log entries. If this is a dry run, then don't actually ping anything.
|
||||
'''
|
||||
if not ping_url:
|
||||
logger.debug('{}: No Cronitor hook set'.format(config_filename))
|
||||
return
|
||||
|
||||
def ping_monitor(ping_url, config_filename, state, dry_run):
|
||||
'''
|
||||
Ping the given Cronitor URL, modified with the monitor.State. Use the given configuration
|
||||
filename in any log entries. If this is a dry run, then don't actually ping anything.
|
||||
'''
|
||||
dry_run_label = ' (dry run; not actually pinging)' if dry_run else ''
|
||||
ping_url = '{}/{}'.format(ping_url, append)
|
||||
ping_url = '{}/{}'.format(ping_url, MONITOR_STATE_TO_CRONITOR[state])
|
||||
|
||||
logger.info('{}: Pinging Cronitor {}{}'.format(config_filename, append, dry_run_label))
|
||||
logger.info(
|
||||
'{}: Pinging Cronitor {}{}'.format(config_filename, state.name.lower(), dry_run_label)
|
||||
)
|
||||
logger.debug('{}: Using Cronitor ping URL {}'.format(config_filename, ping_url))
|
||||
|
||||
if not dry_run:
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import logging
|
||||
|
||||
from borgmatic.hooks import mysql, postgresql
|
||||
from borgmatic.hooks import cronhub, cronitor, healthchecks, mysql, postgresql
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
HOOK_NAME_TO_MODULE = {'postgresql_databases': postgresql, 'mysql_databases': mysql}
|
||||
HOOK_NAME_TO_MODULE = {
|
||||
'healthchecks': healthchecks,
|
||||
'cronitor': cronitor,
|
||||
'cronhub': cronhub,
|
||||
'postgresql_databases': postgresql,
|
||||
'mysql_databases': mysql,
|
||||
}
|
||||
|
||||
|
||||
def call_hook(function_name, hooks, log_prefix, hook_name, *args, **kwargs):
|
||||
|
||||
+25
-1
@@ -20,6 +20,26 @@ def make_database_dump_filename(dump_path, name, hostname=None):
|
||||
return os.path.join(os.path.expanduser(dump_path), hostname or 'localhost', name)
|
||||
|
||||
|
||||
def flatten_dump_patterns(dump_patterns, names):
|
||||
'''
|
||||
Given a dict from a database hook name to glob patterns matching the dumps for the named
|
||||
databases, flatten out all the glob patterns into a single sequence, and return it.
|
||||
|
||||
Raise ValueError if there are no resulting glob patterns, which indicates that databases are not
|
||||
configured in borgmatic's configuration.
|
||||
'''
|
||||
flattened = [pattern for patterns in dump_patterns.values() for pattern in patterns]
|
||||
|
||||
if not flattened:
|
||||
raise ValueError(
|
||||
'Cannot restore database(s) {} missing from borgmatic\'s configuration'.format(
|
||||
', '.join(names) or '"all"'
|
||||
)
|
||||
)
|
||||
|
||||
return flattened
|
||||
|
||||
|
||||
def remove_database_dumps(dump_path, databases, database_type_name, log_prefix, dry_run):
|
||||
'''
|
||||
Remove the database dumps for the given databases in the dump directory path. The databases are
|
||||
@@ -109,7 +129,6 @@ def get_per_hook_database_configurations(hooks, names, dump_patterns):
|
||||
Raise ValueError if one of the database names cannot be matched to a database in borgmatic's
|
||||
database configuration.
|
||||
'''
|
||||
# TODO: Need to filter names by database type? Maybe take a database --type argument to disambiguate.
|
||||
hook_databases = {
|
||||
hook_name: list(
|
||||
get_database_configurations(
|
||||
@@ -122,6 +141,11 @@ def get_per_hook_database_configurations(hooks, names, dump_patterns):
|
||||
}
|
||||
|
||||
if not names or 'all' in names:
|
||||
if not any(hook_databases.values()):
|
||||
raise ValueError(
|
||||
'Cannot restore database "all", as there are no database dumps in the archive'
|
||||
)
|
||||
|
||||
return hook_databases
|
||||
|
||||
found_names = {
|
||||
|
||||
@@ -2,19 +2,23 @@ import logging
|
||||
|
||||
import requests
|
||||
|
||||
from borgmatic.hooks import monitor
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
MONITOR_STATE_TO_HEALTHCHECKS = {
|
||||
monitor.State.START: 'start',
|
||||
monitor.State.FINISH: None, # Healthchecks doesn't append to the URL for the finished state.
|
||||
monitor.State.FAIL: 'fail',
|
||||
}
|
||||
|
||||
def ping_healthchecks(ping_url_or_uuid, config_filename, dry_run, append=None):
|
||||
|
||||
def ping_monitor(ping_url_or_uuid, config_filename, state, dry_run):
|
||||
'''
|
||||
Ping the given Healthchecks URL or UUID, appending the append string if any. Use the given
|
||||
Ping the given Healthchecks URL or UUID, modified with the monitor.State. Use the given
|
||||
configuration filename in any log entries. If this is a dry run, then don't actually ping
|
||||
anything.
|
||||
'''
|
||||
if not ping_url_or_uuid:
|
||||
logger.debug('{}: No Healthchecks hook set'.format(config_filename))
|
||||
return
|
||||
|
||||
ping_url = (
|
||||
ping_url_or_uuid
|
||||
if ping_url_or_uuid.startswith('http')
|
||||
@@ -22,13 +26,12 @@ def ping_healthchecks(ping_url_or_uuid, config_filename, dry_run, append=None):
|
||||
)
|
||||
dry_run_label = ' (dry run; not actually pinging)' if dry_run else ''
|
||||
|
||||
if append:
|
||||
ping_url = '{}/{}'.format(ping_url, append)
|
||||
healthchecks_state = MONITOR_STATE_TO_HEALTHCHECKS.get(state)
|
||||
if healthchecks_state:
|
||||
ping_url = '{}/{}'.format(ping_url, healthchecks_state)
|
||||
|
||||
logger.info(
|
||||
'{}: Pinging Healthchecks{}{}'.format(
|
||||
config_filename, ' ' + append if append else '', dry_run_label
|
||||
)
|
||||
'{}: Pinging Healthchecks {}{}'.format(config_filename, state.name.lower(), dry_run_label)
|
||||
)
|
||||
logger.debug('{}: Using Healthchecks ping URL {}'.format(config_filename, ping_url))
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
from enum import Enum
|
||||
|
||||
MONITOR_HOOK_NAMES = ('healthchecks', 'cronitor', 'cronhub')
|
||||
|
||||
|
||||
class State(Enum):
|
||||
START = 1
|
||||
FINISH = 2
|
||||
FAIL = 3
|
||||
+1
-1
@@ -104,7 +104,7 @@ def configure_logging(
|
||||
syslog_handler.setLevel(syslog_log_level)
|
||||
handlers = (console_handler, syslog_handler)
|
||||
elif log_file:
|
||||
file_handler = logging.FileHandler(log_file)
|
||||
file_handler = logging.handlers.WatchedFileHandler(log_file)
|
||||
file_handler.setFormatter(logging.Formatter('[%(asctime)s] %(levelname)s: %(message)s'))
|
||||
file_handler.setLevel(log_file_log_level)
|
||||
handlers = (console_handler, file_handler)
|
||||
|
||||
@@ -70,6 +70,21 @@ Or to increase syslog logging to include debug spew:
|
||||
borgmatic --syslog-verbosity 2
|
||||
```
|
||||
|
||||
### Rate limiting
|
||||
|
||||
If you are using rsyslog or systemd's journal, be aware that by default they
|
||||
both throttle the rate at which logging occurs. So you may need to change
|
||||
either [the global rate
|
||||
limit](https://www.rootusers.com/how-to-change-log-rate-limiting-in-linux/) or
|
||||
[the per-service rate
|
||||
limit](https://www.freedesktop.org/software/systemd/man/journald.conf.html#RateLimitIntervalSec=)
|
||||
if you're finding that borgmatic logs are missing.
|
||||
|
||||
Note that the [sample borgmatic systemd service
|
||||
file](https://torsion.org/borgmatic/docs/how-to/set-up-backups/#systemd)
|
||||
already has this rate limit disabled for systemd's journal.
|
||||
|
||||
|
||||
### Logging to file
|
||||
|
||||
If you don't want to use syslog, and you'd rather borgmatic log to a plain
|
||||
@@ -84,20 +99,6 @@ the log file so it doesn't grow too large. Also, there is a
|
||||
`--log-file-verbosity` flag to customize the log file's log level.
|
||||
|
||||
|
||||
### systemd journal
|
||||
|
||||
If your local syslog daemon is systemd's journal, be aware that journald by
|
||||
default throttles the rate at which a particular program can log. So you may
|
||||
need to [change the journald rate
|
||||
limit](https://www.freedesktop.org/software/systemd/man/journald.conf.html#RateLimitIntervalSec=)
|
||||
in `/etc/systemd/journald.conf` if you're finding that borgmatic journald logs
|
||||
are missing.
|
||||
|
||||
Note that the [sample borgmatic systemd service
|
||||
file](https://torsion.org/borgmatic/docs/how-to/set-up-backups/#systemd)
|
||||
already has this rate limit disabled.
|
||||
|
||||
|
||||
## Related documentation
|
||||
|
||||
* [Set up backups with borgmatic](https://torsion.org/borgmatic/docs/how-to/set-up-backups/)
|
||||
|
||||
@@ -39,9 +39,9 @@ generate-borgmatic-config --source config.yaml --destination config-new.yaml
|
||||
New options start as commented out, so you can edit the file and decide
|
||||
whether you want to use each one.
|
||||
|
||||
There are a few caveats to this process, however. First, when generating the
|
||||
new configuration file, `generate-borgmatic-config` replaces any comments
|
||||
you've written in your original configuration file with the newest generated
|
||||
There are a few caveats to this process. First, when generating the new
|
||||
configuration file, `generate-borgmatic-config` replaces any comments you've
|
||||
written in your original configuration file with the newest generated
|
||||
comments. Second, the script adds back any options you had originally deleted,
|
||||
although it does so with the options commented out. And finally, any YAML
|
||||
includes you've used in the source configuration get flattened out into a
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
VERSION = '1.4.9'
|
||||
VERSION = '1.4.10'
|
||||
|
||||
|
||||
setup(
|
||||
|
||||
@@ -23,10 +23,7 @@ def test_run_configuration_runs_actions_for_each_repository():
|
||||
def test_run_configuration_executes_hooks_for_create_action():
|
||||
flexmock(module.borg_environment).should_receive('initialize')
|
||||
flexmock(module.command).should_receive('execute_hook').twice()
|
||||
flexmock(module.dispatch).should_receive('call_hooks').twice()
|
||||
flexmock(module.healthchecks).should_receive('ping_healthchecks').twice()
|
||||
flexmock(module.cronitor).should_receive('ping_cronitor').twice()
|
||||
flexmock(module.cronhub).should_receive('ping_cronhub').twice()
|
||||
flexmock(module.dispatch).should_receive('call_hooks').at_least().twice()
|
||||
flexmock(module).should_receive('run_actions').and_return([])
|
||||
config = {'location': {'repositories': ['foo']}}
|
||||
arguments = {'global': flexmock(dry_run=False), 'create': flexmock()}
|
||||
@@ -38,9 +35,6 @@ def test_run_configuration_logs_actions_error():
|
||||
flexmock(module.borg_environment).should_receive('initialize')
|
||||
flexmock(module.command).should_receive('execute_hook')
|
||||
flexmock(module.dispatch).should_receive('call_hooks')
|
||||
flexmock(module.healthchecks).should_receive('ping_healthchecks')
|
||||
flexmock(module.cronitor).should_receive('ping_cronitor')
|
||||
flexmock(module.cronhub).should_receive('ping_cronhub')
|
||||
expected_results = [flexmock()]
|
||||
flexmock(module).should_receive('make_error_log_records').and_return(expected_results)
|
||||
flexmock(module).should_receive('run_actions').and_raise(OSError)
|
||||
@@ -72,9 +66,6 @@ def test_run_configuration_logs_post_hook_error():
|
||||
OSError
|
||||
).and_return(None)
|
||||
flexmock(module.dispatch).should_receive('call_hooks')
|
||||
flexmock(module.healthchecks).should_receive('ping_healthchecks')
|
||||
flexmock(module.cronitor).should_receive('ping_cronitor')
|
||||
flexmock(module.cronhub).should_receive('ping_cronhub')
|
||||
expected_results = [flexmock()]
|
||||
flexmock(module).should_receive('make_error_log_records').and_return(expected_results)
|
||||
flexmock(module).should_receive('run_actions').and_return([])
|
||||
|
||||
@@ -3,30 +3,36 @@ from flexmock import flexmock
|
||||
from borgmatic.hooks import cronhub as module
|
||||
|
||||
|
||||
def test_ping_cronhub_hits_ping_url_with_start_state():
|
||||
def test_ping_monitor_rewrites_ping_url_for_start_state():
|
||||
ping_url = 'https://example.com/start/abcdef'
|
||||
state = 'bork'
|
||||
flexmock(module.requests).should_receive('get').with_args('https://example.com/bork/abcdef')
|
||||
flexmock(module.requests).should_receive('get').with_args('https://example.com/start/abcdef')
|
||||
|
||||
module.ping_cronhub(ping_url, 'config.yaml', dry_run=False, state=state)
|
||||
module.ping_monitor(ping_url, 'config.yaml', module.monitor.State.START, dry_run=False)
|
||||
|
||||
|
||||
def test_ping_cronhub_hits_ping_url_with_ping_state():
|
||||
def test_ping_monitor_rewrites_ping_url_and_state_for_start_state():
|
||||
ping_url = 'https://example.com/ping/abcdef'
|
||||
state = 'bork'
|
||||
flexmock(module.requests).should_receive('get').with_args('https://example.com/bork/abcdef')
|
||||
flexmock(module.requests).should_receive('get').with_args('https://example.com/start/abcdef')
|
||||
|
||||
module.ping_cronhub(ping_url, 'config.yaml', dry_run=False, state=state)
|
||||
module.ping_monitor(ping_url, 'config.yaml', module.monitor.State.START, dry_run=False)
|
||||
|
||||
|
||||
def test_ping_cronhub_without_ping_url_does_not_raise():
|
||||
flexmock(module.requests).should_receive('get').never()
|
||||
def test_ping_monitor_rewrites_ping_url_for_finish_state():
|
||||
ping_url = 'https://example.com/start/abcdef'
|
||||
flexmock(module.requests).should_receive('get').with_args('https://example.com/finish/abcdef')
|
||||
|
||||
module.ping_cronhub(ping_url=None, config_filename='config.yaml', dry_run=False, state='oops')
|
||||
module.ping_monitor(ping_url, 'config.yaml', module.monitor.State.FINISH, dry_run=False)
|
||||
|
||||
|
||||
def test_ping_cronhub_dry_run_does_not_hit_ping_url():
|
||||
def test_ping_monitor_rewrites_ping_url_for_fail_state():
|
||||
ping_url = 'https://example.com/start/abcdef'
|
||||
flexmock(module.requests).should_receive('get').with_args('https://example.com/fail/abcdef')
|
||||
|
||||
module.ping_monitor(ping_url, 'config.yaml', module.monitor.State.FAIL, dry_run=False)
|
||||
|
||||
|
||||
def test_ping_monitor_dry_run_does_not_hit_ping_url():
|
||||
ping_url = 'https://example.com'
|
||||
flexmock(module.requests).should_receive('get').never()
|
||||
|
||||
module.ping_cronhub(ping_url, 'config.yaml', dry_run=True, state='yay')
|
||||
module.ping_monitor(ping_url, 'config.yaml', module.monitor.State.START, dry_run=True)
|
||||
|
||||
@@ -3,22 +3,29 @@ from flexmock import flexmock
|
||||
from borgmatic.hooks import cronitor as module
|
||||
|
||||
|
||||
def test_ping_cronitor_hits_ping_url():
|
||||
def test_ping_monitor_hits_ping_url_for_start_state():
|
||||
ping_url = 'https://example.com'
|
||||
append = 'failed-so-hard'
|
||||
flexmock(module.requests).should_receive('get').with_args('{}/{}'.format(ping_url, append))
|
||||
flexmock(module.requests).should_receive('get').with_args('{}/{}'.format(ping_url, 'run'))
|
||||
|
||||
module.ping_cronitor(ping_url, 'config.yaml', dry_run=False, append=append)
|
||||
module.ping_monitor(ping_url, 'config.yaml', module.monitor.State.START, dry_run=False)
|
||||
|
||||
|
||||
def test_ping_cronitor_without_ping_url_does_not_raise():
|
||||
flexmock(module.requests).should_receive('get').never()
|
||||
def test_ping_monitor_hits_ping_url_for_finish_state():
|
||||
ping_url = 'https://example.com'
|
||||
flexmock(module.requests).should_receive('get').with_args('{}/{}'.format(ping_url, 'complete'))
|
||||
|
||||
module.ping_cronitor(ping_url=None, config_filename='config.yaml', dry_run=False, append='oops')
|
||||
module.ping_monitor(ping_url, 'config.yaml', module.monitor.State.FINISH, dry_run=False)
|
||||
|
||||
|
||||
def test_ping_cronitor_dry_run_does_not_hit_ping_url():
|
||||
def test_ping_monitor_hits_ping_url_for_fail_state():
|
||||
ping_url = 'https://example.com'
|
||||
flexmock(module.requests).should_receive('get').with_args('{}/{}'.format(ping_url, 'fail'))
|
||||
|
||||
module.ping_monitor(ping_url, 'config.yaml', module.monitor.State.FAIL, dry_run=False)
|
||||
|
||||
|
||||
def test_ping_monitor_dry_run_does_not_hit_ping_url():
|
||||
ping_url = 'https://example.com'
|
||||
flexmock(module.requests).should_receive('get').never()
|
||||
|
||||
module.ping_cronitor(ping_url, 'config.yaml', dry_run=True, append='yay')
|
||||
module.ping_monitor(ping_url, 'config.yaml', module.monitor.State.START, dry_run=True)
|
||||
|
||||
@@ -26,6 +26,27 @@ def test_make_database_dump_filename_with_invalid_name_raises():
|
||||
module.make_database_dump_filename('databases', 'invalid/name')
|
||||
|
||||
|
||||
def test_flatten_dump_patterns_produces_list_of_all_patterns():
|
||||
dump_patterns = {'postgresql_databases': ['*/glob', 'glob/*'], 'mysql_databases': ['*/*/*']}
|
||||
expected_patterns = dump_patterns['postgresql_databases'] + dump_patterns['mysql_databases']
|
||||
|
||||
assert module.flatten_dump_patterns(dump_patterns, ('bob',)) == expected_patterns
|
||||
|
||||
|
||||
def test_flatten_dump_patterns_with_no_patterns_errors():
|
||||
dump_patterns = {'postgresql_databases': [], 'mysql_databases': []}
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
assert module.flatten_dump_patterns(dump_patterns, ('bob',))
|
||||
|
||||
|
||||
def test_flatten_dump_patterns_with_no_hooks_errors():
|
||||
dump_patterns = {}
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
assert module.flatten_dump_patterns(dump_patterns, ('bob',))
|
||||
|
||||
|
||||
def test_remove_database_dumps_removes_dump_for_each_database():
|
||||
databases = [{'name': 'foo'}, {'name': 'bar'}]
|
||||
flexmock(module).should_receive('make_database_dump_filename').and_return(
|
||||
@@ -134,3 +155,15 @@ def test_get_per_hook_database_configurations_with_unknown_database_name_raises(
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
module.get_per_hook_database_configurations(hooks, names, dump_patterns)
|
||||
|
||||
|
||||
def test_get_per_hook_database_configurations_with_all_and_no_archive_dumps_raises():
|
||||
hooks = {'postgresql_databases': [flexmock()]}
|
||||
names = ('foo', 'all')
|
||||
dump_patterns = flexmock()
|
||||
flexmock(module).should_receive('get_database_configurations').with_args(
|
||||
hooks['postgresql_databases'], names
|
||||
).and_return([])
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
module.get_per_hook_database_configurations(hooks, names, dump_patterns)
|
||||
|
||||
@@ -3,38 +3,38 @@ from flexmock import flexmock
|
||||
from borgmatic.hooks import healthchecks as module
|
||||
|
||||
|
||||
def test_ping_healthchecks_hits_ping_url():
|
||||
def test_ping_monitor_hits_ping_url_for_start_state():
|
||||
ping_url = 'https://example.com'
|
||||
flexmock(module.requests).should_receive('get').with_args('{}/{}'.format(ping_url, 'start'))
|
||||
|
||||
module.ping_monitor(ping_url, 'config.yaml', state=module.monitor.State.START, dry_run=False)
|
||||
|
||||
|
||||
def test_ping_monitor_hits_ping_url_for_finish_state():
|
||||
ping_url = 'https://example.com'
|
||||
flexmock(module.requests).should_receive('get').with_args(ping_url)
|
||||
|
||||
module.ping_healthchecks(ping_url, 'config.yaml', dry_run=False)
|
||||
module.ping_monitor(ping_url, 'config.yaml', state=module.monitor.State.FINISH, dry_run=False)
|
||||
|
||||
|
||||
def test_ping_healthchecks_without_ping_url_does_not_raise():
|
||||
flexmock(module.requests).should_receive('get').never()
|
||||
def test_ping_monitor_hits_ping_url_for_fail_state():
|
||||
ping_url = 'https://example.com'
|
||||
flexmock(module.requests).should_receive('get').with_args('{}/{}'.format(ping_url, 'fail'))
|
||||
|
||||
module.ping_healthchecks(ping_url_or_uuid=None, config_filename='config.yaml', dry_run=False)
|
||||
module.ping_monitor(ping_url, 'config.yaml', state=module.monitor.State.FAIL, dry_run=False)
|
||||
|
||||
|
||||
def test_ping_healthchecks_with_ping_uuid_hits_corresponding_url():
|
||||
def test_ping_monitor_with_ping_uuid_hits_corresponding_url():
|
||||
ping_uuid = 'abcd-efgh-ijkl-mnop'
|
||||
flexmock(module.requests).should_receive('get').with_args(
|
||||
'https://hc-ping.com/{}'.format(ping_uuid)
|
||||
)
|
||||
|
||||
module.ping_healthchecks(ping_uuid, 'config.yaml', dry_run=False)
|
||||
module.ping_monitor(ping_uuid, 'config.yaml', state=module.monitor.State.FINISH, dry_run=False)
|
||||
|
||||
|
||||
def test_ping_healthchecks_hits_ping_url_with_append():
|
||||
ping_url = 'https://example.com'
|
||||
append = 'failed-so-hard'
|
||||
flexmock(module.requests).should_receive('get').with_args('{}/{}'.format(ping_url, append))
|
||||
|
||||
module.ping_healthchecks(ping_url, 'config.yaml', dry_run=False, append=append)
|
||||
|
||||
|
||||
def test_ping_healthchecks_dry_run_does_not_hit_ping_url():
|
||||
def test_ping_monitor_dry_run_does_not_hit_ping_url():
|
||||
ping_url = 'https://example.com'
|
||||
flexmock(module.requests).should_receive('get').never()
|
||||
|
||||
module.ping_healthchecks(ping_url, 'config.yaml', dry_run=True)
|
||||
module.ping_monitor(ping_url, 'config.yaml', state=module.monitor.State.START, dry_run=True)
|
||||
|
||||
@@ -203,10 +203,10 @@ def test_configure_logging_to_logfile_instead_of_syslog():
|
||||
)
|
||||
flexmock(module.os.path).should_receive('exists').with_args('/dev/log').and_return(True)
|
||||
flexmock(module.logging.handlers).should_receive('SysLogHandler').never()
|
||||
file_handler = logging.FileHandler('/tmp/logfile')
|
||||
flexmock(module.logging).should_receive('FileHandler').with_args('/tmp/logfile').and_return(
|
||||
file_handler
|
||||
).once()
|
||||
file_handler = logging.handlers.WatchedFileHandler('/tmp/logfile')
|
||||
flexmock(module.logging.handlers).should_receive('WatchedFileHandler').with_args(
|
||||
'/tmp/logfile'
|
||||
).and_return(file_handler).once()
|
||||
|
||||
module.configure_logging(
|
||||
console_log_level=logging.INFO, log_file_log_level=logging.DEBUG, log_file='/tmp/logfile'
|
||||
@@ -214,12 +214,12 @@ def test_configure_logging_to_logfile_instead_of_syslog():
|
||||
|
||||
|
||||
def test_configure_logging_skips_logfile_if_argument_is_none():
|
||||
# No FileHandler added if argument --log-file is None
|
||||
# No WatchedFileHandler added if argument --log-file is None
|
||||
flexmock(module).should_receive('interactive_console').and_return(False)
|
||||
flexmock(module.logging).should_receive('basicConfig').with_args(
|
||||
level=logging.INFO, handlers=tuple
|
||||
)
|
||||
flexmock(module.os.path).should_receive('exists').and_return(False)
|
||||
flexmock(module.logging).should_receive('FileHandler').never()
|
||||
flexmock(module.logging.handlers).should_receive('WatchedFileHandler').never()
|
||||
|
||||
module.configure_logging(console_log_level=logging.INFO, log_file=None)
|
||||
|
||||
Reference in New Issue
Block a user