Run "compact" action by default when no actions are specified (#394).
This commit is contained in:
parent
4498671233
commit
b525e70e1c
17 changed files with 239 additions and 77 deletions
4
NEWS
4
NEWS
|
@ -1,5 +1,7 @@
|
||||||
1.5.23.dev0
|
1.5.23.dev0
|
||||||
* #394: Compact repository segments with new "borgmatic compact" action. Borg 1.2+ only.
|
* #394: Compact repository segments and free space with new "borgmatic compact" action. Borg 1.2+
|
||||||
|
only. Also run "compact" by default when no actions are specified, as "prune" in Borg 1.2 no
|
||||||
|
longer frees up space unless "compact" is run.
|
||||||
* #480, #482: Fix traceback when a YAML validation error occurs.
|
* #480, #482: Fix traceback when a YAML validation error occurs.
|
||||||
|
|
||||||
1.5.22
|
1.5.22
|
||||||
|
|
|
@ -38,4 +38,4 @@ def compact_segments(
|
||||||
+ (repository,)
|
+ (repository,)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute_command(full_command, output_log_level=logging.WARNING, borg_local_path=local_path)
|
execute_command(full_command, output_log_level=logging.INFO, borg_local_path=local_path)
|
||||||
|
|
20
borgmatic/borg/feature.py
Normal file
20
borgmatic/borg/feature.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
from pkg_resources import parse_version
|
||||||
|
|
||||||
|
|
||||||
|
class Feature(Enum):
|
||||||
|
COMPACT = 1
|
||||||
|
|
||||||
|
|
||||||
|
FEATURE_TO_MINIMUM_BORG_VERSION = {
|
||||||
|
Feature.COMPACT: parse_version('1.2.0a2'),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def available(feature, borg_version):
|
||||||
|
'''
|
||||||
|
Given a Borg Feature constant and a Borg version string, return whether that feature is
|
||||||
|
available in that version of Borg.
|
||||||
|
'''
|
||||||
|
return FEATURE_TO_MINIMUM_BORG_VERSION[feature] <= parse_version(borg_version)
|
25
borgmatic/borg/version.py
Normal file
25
borgmatic/borg/version.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from borgmatic.execute import execute_command
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def local_borg_version(local_path='borg'):
|
||||||
|
'''
|
||||||
|
Given a local Borg binary path, return a version string for it.
|
||||||
|
|
||||||
|
Raise OSError or CalledProcessError if there is a problem running Borg.
|
||||||
|
Raise ValueError if the version cannot be parsed.
|
||||||
|
'''
|
||||||
|
full_command = (
|
||||||
|
(local_path, '--version')
|
||||||
|
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
||||||
|
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
|
||||||
|
)
|
||||||
|
output = execute_command(full_command, output_log_level=None, borg_local_path=local_path)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return output.split(' ')[1].strip()
|
||||||
|
except IndexError:
|
||||||
|
raise ValueError('Could not parse Borg version string')
|
|
@ -63,9 +63,9 @@ def parse_subparser_arguments(unparsed_arguments, subparsers):
|
||||||
|
|
||||||
arguments[canonical_name] = parsed
|
arguments[canonical_name] = parsed
|
||||||
|
|
||||||
# If no actions are explicitly requested, assume defaults: prune, create, and check.
|
# If no actions are explicitly requested, assume defaults: prune, compact, create, and check.
|
||||||
if not arguments and '--help' not in unparsed_arguments and '-h' not in unparsed_arguments:
|
if not arguments and '--help' not in unparsed_arguments and '-h' not in unparsed_arguments:
|
||||||
for subparser_name in ('prune', 'create', 'check'):
|
for subparser_name in ('prune', 'compact', 'create', 'check'):
|
||||||
subparser = subparsers[subparser_name]
|
subparser = subparsers[subparser_name]
|
||||||
parsed, unused_remaining = subparser.parse_known_args(unparsed_arguments)
|
parsed, unused_remaining = subparser.parse_known_args(unparsed_arguments)
|
||||||
arguments[subparser_name] = parsed
|
arguments[subparser_name] = parsed
|
||||||
|
@ -200,8 +200,8 @@ def parse_arguments(*unparsed_arguments):
|
||||||
top_level_parser = ArgumentParser(
|
top_level_parser = ArgumentParser(
|
||||||
description='''
|
description='''
|
||||||
Simple, configuration-driven backup software for servers and workstations. If none of
|
Simple, configuration-driven backup software for servers and workstations. If none of
|
||||||
the action options are given, then borgmatic defaults to: prune, create, and check
|
the action options are given, then borgmatic defaults to: prune, compact, create, and
|
||||||
archives.
|
check.
|
||||||
''',
|
''',
|
||||||
parents=[global_parser],
|
parents=[global_parser],
|
||||||
)
|
)
|
||||||
|
@ -209,7 +209,7 @@ def parse_arguments(*unparsed_arguments):
|
||||||
subparsers = top_level_parser.add_subparsers(
|
subparsers = top_level_parser.add_subparsers(
|
||||||
title='actions',
|
title='actions',
|
||||||
metavar='',
|
metavar='',
|
||||||
help='Specify zero or more actions. Defaults to prune, create, and check. Use --help with action for details:',
|
help='Specify zero or more actions. Defaults to prune, compact, create, and check. Use --help with action for details:',
|
||||||
)
|
)
|
||||||
init_parser = subparsers.add_parser(
|
init_parser = subparsers.add_parser(
|
||||||
'init',
|
'init',
|
||||||
|
@ -242,8 +242,8 @@ def parse_arguments(*unparsed_arguments):
|
||||||
prune_parser = subparsers.add_parser(
|
prune_parser = subparsers.add_parser(
|
||||||
'prune',
|
'prune',
|
||||||
aliases=SUBPARSER_ALIASES['prune'],
|
aliases=SUBPARSER_ALIASES['prune'],
|
||||||
help='Prune archives according to the retention policy',
|
help='Prune archives according to the retention policy (with Borg 1.2+, run compact afterwards to actually free space)',
|
||||||
description='Prune archives according to the retention policy',
|
description='Prune archives according to the retention policy (with Borg 1.2+, run compact afterwards to actually free space)',
|
||||||
add_help=False,
|
add_help=False,
|
||||||
)
|
)
|
||||||
prune_group = prune_parser.add_argument_group('prune arguments')
|
prune_group = prune_parser.add_argument_group('prune arguments')
|
||||||
|
|
|
@ -18,12 +18,14 @@ from borgmatic.borg import create as borg_create
|
||||||
from borgmatic.borg import environment as borg_environment
|
from borgmatic.borg import environment as borg_environment
|
||||||
from borgmatic.borg import export_tar as borg_export_tar
|
from borgmatic.borg import export_tar as borg_export_tar
|
||||||
from borgmatic.borg import extract as borg_extract
|
from borgmatic.borg import extract as borg_extract
|
||||||
|
from borgmatic.borg import feature as borg_feature
|
||||||
from borgmatic.borg import info as borg_info
|
from borgmatic.borg import info as borg_info
|
||||||
from borgmatic.borg import init as borg_init
|
from borgmatic.borg import init as borg_init
|
||||||
from borgmatic.borg import list as borg_list
|
from borgmatic.borg import list as borg_list
|
||||||
from borgmatic.borg import mount as borg_mount
|
from borgmatic.borg import mount as borg_mount
|
||||||
from borgmatic.borg import prune as borg_prune
|
from borgmatic.borg import prune as borg_prune
|
||||||
from borgmatic.borg import umount as borg_umount
|
from borgmatic.borg import umount as borg_umount
|
||||||
|
from borgmatic.borg import version as borg_version
|
||||||
from borgmatic.commands.arguments import parse_arguments
|
from borgmatic.commands.arguments import parse_arguments
|
||||||
from borgmatic.config import checks, collect, convert, validate
|
from borgmatic.config import checks, collect, convert, validate
|
||||||
from borgmatic.hooks import command, dispatch, dump, monitor
|
from borgmatic.hooks import command, dispatch, dump, monitor
|
||||||
|
@ -39,8 +41,8 @@ LEGACY_CONFIG_PATH = '/etc/borgmatic/config'
|
||||||
def run_configuration(config_filename, config, arguments):
|
def run_configuration(config_filename, config, arguments):
|
||||||
'''
|
'''
|
||||||
Given a config filename, the corresponding parsed config dict, and command-line arguments as a
|
Given a config filename, the corresponding parsed config dict, and command-line arguments as a
|
||||||
dict from subparser name to a namespace of parsed arguments, execute its defined pruning,
|
dict from subparser name to a namespace of parsed arguments, execute the defined prune, compact,
|
||||||
backups, consistency checks, and/or other actions.
|
create, check, and/or other actions.
|
||||||
|
|
||||||
Yield a combination of:
|
Yield a combination of:
|
||||||
|
|
||||||
|
@ -60,11 +62,19 @@ def run_configuration(config_filename, config, arguments):
|
||||||
borg_environment.initialize(storage)
|
borg_environment.initialize(storage)
|
||||||
encountered_error = None
|
encountered_error = None
|
||||||
error_repository = ''
|
error_repository = ''
|
||||||
prune_create_or_check = {'prune', 'create', 'check'}.intersection(arguments)
|
using_primary_action = {'prune', 'compact', 'create', 'check'}.intersection(arguments)
|
||||||
monitoring_log_level = verbosity_to_log_level(global_arguments.monitoring_verbosity)
|
monitoring_log_level = verbosity_to_log_level(global_arguments.monitoring_verbosity)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if prune_create_or_check:
|
local_borg_version = borg_version.local_borg_version(local_path)
|
||||||
|
except (OSError, CalledProcessError, ValueError) as error:
|
||||||
|
yield from make_error_log_records(
|
||||||
|
'{}: Error getting local Borg version'.format(config_filename), error
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
if using_primary_action:
|
||||||
dispatch.call_hooks(
|
dispatch.call_hooks(
|
||||||
'initialize_monitor',
|
'initialize_monitor',
|
||||||
hooks,
|
hooks,
|
||||||
|
@ -113,7 +123,7 @@ def run_configuration(config_filename, config, arguments):
|
||||||
'pre-extract',
|
'pre-extract',
|
||||||
global_arguments.dry_run,
|
global_arguments.dry_run,
|
||||||
)
|
)
|
||||||
if prune_create_or_check:
|
if using_primary_action:
|
||||||
dispatch.call_hooks(
|
dispatch.call_hooks(
|
||||||
'ping_monitor',
|
'ping_monitor',
|
||||||
hooks,
|
hooks,
|
||||||
|
@ -153,6 +163,7 @@ def run_configuration(config_filename, config, arguments):
|
||||||
hooks=hooks,
|
hooks=hooks,
|
||||||
local_path=local_path,
|
local_path=local_path,
|
||||||
remote_path=remote_path,
|
remote_path=remote_path,
|
||||||
|
local_borg_version=local_borg_version,
|
||||||
repository_path=repository_path,
|
repository_path=repository_path,
|
||||||
)
|
)
|
||||||
except (OSError, CalledProcessError, ValueError) as error:
|
except (OSError, CalledProcessError, ValueError) as error:
|
||||||
|
@ -218,7 +229,7 @@ def run_configuration(config_filename, config, arguments):
|
||||||
'post-extract',
|
'post-extract',
|
||||||
global_arguments.dry_run,
|
global_arguments.dry_run,
|
||||||
)
|
)
|
||||||
if prune_create_or_check:
|
if using_primary_action:
|
||||||
dispatch.call_hooks(
|
dispatch.call_hooks(
|
||||||
'ping_monitor',
|
'ping_monitor',
|
||||||
hooks,
|
hooks,
|
||||||
|
@ -245,7 +256,7 @@ def run_configuration(config_filename, config, arguments):
|
||||||
'{}: Error running post hook'.format(config_filename), error
|
'{}: Error running post hook'.format(config_filename), error
|
||||||
)
|
)
|
||||||
|
|
||||||
if encountered_error and prune_create_or_check:
|
if encountered_error and using_primary_action:
|
||||||
try:
|
try:
|
||||||
command.execute_hook(
|
command.execute_hook(
|
||||||
hooks.get('on_error'),
|
hooks.get('on_error'),
|
||||||
|
@ -293,12 +304,13 @@ def run_actions(
|
||||||
hooks,
|
hooks,
|
||||||
local_path,
|
local_path,
|
||||||
remote_path,
|
remote_path,
|
||||||
|
local_borg_version,
|
||||||
repository_path,
|
repository_path,
|
||||||
): # pragma: no cover
|
): # pragma: no cover
|
||||||
'''
|
'''
|
||||||
Given parsed command-line arguments as an argparse.ArgumentParser instance, several different
|
Given parsed command-line arguments as an argparse.ArgumentParser instance, several different
|
||||||
configuration dicts, local and remote paths to Borg, and a repository name, run all actions
|
configuration dicts, local and remote paths to Borg, a local Borg version string, and a
|
||||||
from the command-line arguments on the given repository.
|
repository name, run all actions from the command-line arguments on the given repository.
|
||||||
|
|
||||||
Yield JSON output strings from executing any actions that produce JSON.
|
Yield JSON output strings from executing any actions that produce JSON.
|
||||||
|
|
||||||
|
@ -332,17 +344,22 @@ def run_actions(
|
||||||
files=arguments['prune'].files,
|
files=arguments['prune'].files,
|
||||||
)
|
)
|
||||||
if 'compact' in arguments:
|
if 'compact' in arguments:
|
||||||
logger.info('{}: Compacting segments{}'.format(repository, dry_run_label))
|
if borg_feature.available(borg_feature.Feature.COMPACT, local_borg_version):
|
||||||
borg_compact.compact_segments(
|
logger.info('{}: Compacting segments{}'.format(repository, dry_run_label))
|
||||||
global_arguments.dry_run,
|
borg_compact.compact_segments(
|
||||||
repository,
|
global_arguments.dry_run,
|
||||||
storage,
|
repository,
|
||||||
local_path=local_path,
|
storage,
|
||||||
remote_path=remote_path,
|
local_path=local_path,
|
||||||
progress=arguments['compact'].progress,
|
remote_path=remote_path,
|
||||||
cleanup_commits=arguments['compact'].cleanup_commits,
|
progress=arguments['compact'].progress,
|
||||||
threshold=arguments['compact'].threshold,
|
cleanup_commits=arguments['compact'].cleanup_commits,
|
||||||
)
|
threshold=arguments['compact'].threshold,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logger.info(
|
||||||
|
'{}: Skipping compact (only available/needed in Borg 1.2+)'.format(repository)
|
||||||
|
)
|
||||||
if 'create' in arguments:
|
if 'create' in arguments:
|
||||||
logger.info('{}: Creating archive{}'.format(repository, dry_run_label))
|
logger.info('{}: Creating archive{}'.format(repository, dry_run_label))
|
||||||
dispatch.call_hooks(
|
dispatch.call_hooks(
|
||||||
|
|
|
@ -605,10 +605,11 @@ properties:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: |
|
||||||
List of one or more shell commands or scripts to execute
|
List of one or more shell commands or scripts to execute
|
||||||
when an exception occurs during a "prune", "create", or
|
when an exception occurs during a "prune", "compact",
|
||||||
"check" action or an associated before/after hook.
|
"create", or "check" action or an associated before/after
|
||||||
|
hook.
|
||||||
example:
|
example:
|
||||||
- echo "Error during prune/create/check."
|
- echo "Error during prune/compact/create/check."
|
||||||
before_everything:
|
before_everything:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
|
|
|
@ -33,9 +33,9 @@ configuration file, right before the `create` action. `after_backup` hooks run
|
||||||
afterwards, but not if an error occurs in a previous hook or in the backups
|
afterwards, but not if an error occurs in a previous hook or in the backups
|
||||||
themselves.
|
themselves.
|
||||||
|
|
||||||
There are additional hooks for the `prune` and `check` actions as well.
|
There are additional hooks that run before/after other actions as well. For
|
||||||
`before_prune` and `after_prune` run if there are any `prune` actions, while
|
instance, `before_prune` runs before a `prune` action, while `after_prune`
|
||||||
`before_check` and `after_check` run if there are any `check` actions.
|
runs after it.
|
||||||
|
|
||||||
You can also use `before_everything` and `after_everything` hooks to perform
|
You can also use `before_everything` and `after_everything` hooks to perform
|
||||||
global setup or cleanup:
|
global setup or cleanup:
|
||||||
|
|
|
@ -115,6 +115,6 @@ There are some caveats you should be aware of with this feature.
|
||||||
* The soft failure doesn't have to apply to a repository. You can even perform
|
* The soft failure doesn't have to apply to a repository. You can even perform
|
||||||
a test to make sure that individual source directories are mounted and
|
a test to make sure that individual source directories are mounted and
|
||||||
available. Use your imagination!
|
available. Use your imagination!
|
||||||
* The soft failure feature also works for `before_prune`, `after_prune`,
|
* The soft failure feature also works for before/after hooks for other
|
||||||
`before_check`, and `after_check` hooks. But it is not implemented for
|
actions as well. But it is not implemented for `before_everything` or
|
||||||
`before_everything` or `after_everything`.
|
`after_everything`.
|
||||||
|
|
|
@ -9,19 +9,20 @@ eleventyNavigation:
|
||||||
|
|
||||||
Borg itself is great for efficiently de-duplicating data across successive
|
Borg itself is great for efficiently de-duplicating data across successive
|
||||||
backup archives, even when dealing with very large repositories. But you may
|
backup archives, even when dealing with very large repositories. But you may
|
||||||
find that while borgmatic's default mode of "prune, create, and check" works
|
find that while borgmatic's default mode of `prune`, `compact`, `create`, and
|
||||||
well on small repositories, it's not so great on larger ones. That's because
|
`check` works well on small repositories, it's not so great on larger ones.
|
||||||
running the default pruning and consistency checks take a long time on large
|
That's because running the default pruning, compact, and consistency checks
|
||||||
repositories.
|
take a long time on large repositories.
|
||||||
|
|
||||||
### A la carte actions
|
### A la carte actions
|
||||||
|
|
||||||
If you find yourself in this situation, you have some options. First, you can
|
If you find yourself in this situation, you have some options. First, you can
|
||||||
run borgmatic's pruning, creating, or checking actions separately. For
|
run borgmatic's `prune`, `compact`, `create`, or `check` actions separately.
|
||||||
instance, the following optional actions are available:
|
For instance, the following optional actions are available:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
borgmatic prune
|
borgmatic prune
|
||||||
|
borgmatic compact
|
||||||
borgmatic create
|
borgmatic create
|
||||||
borgmatic check
|
borgmatic check
|
||||||
```
|
```
|
||||||
|
@ -32,7 +33,7 @@ borgmatic check
|
||||||
You can run with only one of these actions provided, or you can mix and match
|
You can run with only one of these actions provided, or you can mix and match
|
||||||
any number of them in a single borgmatic run. This supports approaches like
|
any number of them in a single borgmatic run. This supports approaches like
|
||||||
skipping certain actions while running others. For instance, this skips
|
skipping certain actions while running others. For instance, this skips
|
||||||
`prune` and only runs `create` and `check`:
|
`prune` and `compact` and only runs `create` and `check`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
borgmatic create check
|
borgmatic create check
|
||||||
|
|
|
@ -83,10 +83,10 @@ tests](https://torsion.org/borgmatic/docs/how-to/extract-a-backup/).
|
||||||
|
|
||||||
## Error hooks
|
## Error hooks
|
||||||
|
|
||||||
When an error occurs during a `prune`, `create`, or `check` action, borgmatic
|
When an error occurs during a `prune`, `compact`, `create`, or `check` action,
|
||||||
can run configurable shell commands to fire off custom error notifications or
|
borgmatic can run configurable shell commands to fire off custom error
|
||||||
take other actions, so you can get alerted as soon as something goes wrong.
|
notifications or take other actions, so you can get alerted as soon as
|
||||||
Here's a not-so-useful example:
|
something goes wrong. Here's a not-so-useful example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
hooks:
|
hooks:
|
||||||
|
@ -117,9 +117,9 @@ here:
|
||||||
* `output`: output of the command that failed (may be blank if an error
|
* `output`: output of the command that failed (may be blank if an error
|
||||||
occurred without running a command)
|
occurred without running a command)
|
||||||
|
|
||||||
Note that borgmatic runs the `on_error` hooks only for `prune`, `create`, or
|
Note that borgmatic runs the `on_error` hooks only for `prune`, `compact`,
|
||||||
`check` actions or hooks in which an error occurs, and not other actions.
|
`create`, or `check` actions or hooks in which an error occurs, and not other
|
||||||
borgmatic does not run `on_error` hooks if an error occurs within a
|
actions. borgmatic does not run `on_error` hooks if an error occurs within a
|
||||||
`before_everything` or `after_everything` hook. For more about hooks, see the
|
`before_everything` or `after_everything` hook. For more about hooks, see the
|
||||||
[borgmatic hooks
|
[borgmatic hooks
|
||||||
documentation](https://torsion.org/borgmatic/docs/how-to/add-preparation-and-cleanup-steps-to-backups/),
|
documentation](https://torsion.org/borgmatic/docs/how-to/add-preparation-and-cleanup-steps-to-backups/),
|
||||||
|
@ -144,7 +144,7 @@ With this hook in place, borgmatic pings your Healthchecks project when a
|
||||||
backup begins, ends, or errors. Specifically, after the <a
|
backup begins, ends, or errors. Specifically, after the <a
|
||||||
href="https://torsion.org/borgmatic/docs/how-to/add-preparation-and-cleanup-steps-to-backups/">`before_backup`
|
href="https://torsion.org/borgmatic/docs/how-to/add-preparation-and-cleanup-steps-to-backups/">`before_backup`
|
||||||
hooks</a> run, borgmatic lets Healthchecks know that it has started if any of
|
hooks</a> run, borgmatic lets Healthchecks know that it has started if any of
|
||||||
the `prune`, `create`, or `check` actions are run.
|
the `prune`, `compact`, `create`, or `check` actions are run.
|
||||||
|
|
||||||
Then, if the actions complete successfully, borgmatic notifies Healthchecks of
|
Then, if the actions complete successfully, borgmatic notifies Healthchecks of
|
||||||
the success after the `after_backup` hooks run, and includes borgmatic logs in
|
the success after the `after_backup` hooks run, and includes borgmatic logs in
|
||||||
|
@ -155,7 +155,7 @@ in the Healthchecks UI, although be aware that Healthchecks currently has a
|
||||||
If an error occurs during any action or hook, borgmatic notifies Healthchecks
|
If an error occurs during any action or hook, borgmatic notifies Healthchecks
|
||||||
after the `on_error` hooks run, also tacking on logs including the error
|
after the `on_error` hooks run, also tacking on logs including the error
|
||||||
itself. But the logs are only included for errors that occur when a `prune`,
|
itself. But the logs are only included for errors that occur when a `prune`,
|
||||||
`create`, or `check` action is run.
|
`compact`, `create`, or `check` action is run.
|
||||||
|
|
||||||
You can customize the verbosity of the logs that are sent to Healthchecks with
|
You can customize the verbosity of the logs that are sent to Healthchecks with
|
||||||
borgmatic's `--monitoring-verbosity` flag. The `--files` and `--stats` flags
|
borgmatic's `--monitoring-verbosity` flag. The `--files` and `--stats` flags
|
||||||
|
@ -184,8 +184,8 @@ With this hook in place, borgmatic pings your Cronitor monitor when a backup
|
||||||
begins, ends, or errors. Specifically, after the <a
|
begins, ends, or errors. Specifically, after the <a
|
||||||
href="https://torsion.org/borgmatic/docs/how-to/add-preparation-and-cleanup-steps-to-backups/">`before_backup`
|
href="https://torsion.org/borgmatic/docs/how-to/add-preparation-and-cleanup-steps-to-backups/">`before_backup`
|
||||||
hooks</a> run, borgmatic lets Cronitor know that it has started if any of the
|
hooks</a> run, borgmatic lets Cronitor know that it has started if any of the
|
||||||
`prune`, `create`, or `check` actions are run. Then, if the actions complete
|
`prune`, `compact`, `create`, or `check` actions are run. Then, if the actions
|
||||||
successfully, borgmatic notifies Cronitor of the success after the
|
complete successfully, borgmatic notifies Cronitor of the success after the
|
||||||
`after_backup` hooks run. And if an error occurs during any action or hook,
|
`after_backup` hooks run. And if an error occurs during any action or hook,
|
||||||
borgmatic notifies Cronitor after the `on_error` hooks run.
|
borgmatic notifies Cronitor after the `on_error` hooks run.
|
||||||
|
|
||||||
|
@ -212,8 +212,8 @@ With this hook in place, borgmatic pings your Cronhub monitor when a backup
|
||||||
begins, ends, or errors. Specifically, after the <a
|
begins, ends, or errors. Specifically, after the <a
|
||||||
href="https://torsion.org/borgmatic/docs/how-to/add-preparation-and-cleanup-steps-to-backups/">`before_backup`
|
href="https://torsion.org/borgmatic/docs/how-to/add-preparation-and-cleanup-steps-to-backups/">`before_backup`
|
||||||
hooks</a> run, borgmatic lets Cronhub know that it has started if any of the
|
hooks</a> run, borgmatic lets Cronhub know that it has started if any of the
|
||||||
`prune`, `create`, or `check` actions are run. Then, if the actions complete
|
`prune`, `compact`, `create`, or `check` actions are run. Then, if the actions
|
||||||
successfully, borgmatic notifies Cronhub of the success after the
|
complete successfully, borgmatic notifies Cronhub of the success after the
|
||||||
`after_backup` hooks run. And if an error occurs during any action or hook,
|
`after_backup` hooks run. And if an error occurs during any action or hook,
|
||||||
borgmatic notifies Cronhub after the `on_error` hooks run.
|
borgmatic notifies Cronhub after the `on_error` hooks run.
|
||||||
|
|
||||||
|
@ -252,9 +252,9 @@ hooks:
|
||||||
|
|
||||||
With this hook in place, borgmatic creates a PagerDuty event for your service
|
With this hook in place, borgmatic creates a PagerDuty event for your service
|
||||||
whenever backups fail. Specifically, if an error occurs during a `create`,
|
whenever backups fail. Specifically, if an error occurs during a `create`,
|
||||||
`prune`, or `check` action, borgmatic sends an event to PagerDuty before the
|
`prune`, `compact`, or `check` action, borgmatic sends an event to PagerDuty
|
||||||
`on_error` hooks run. Note that borgmatic does not contact PagerDuty when a
|
before the `on_error` hooks run. Note that borgmatic does not contact
|
||||||
backup starts or ends without error.
|
PagerDuty when a backup starts or ends without error.
|
||||||
|
|
||||||
You can configure PagerDuty to notify you by a [variety of
|
You can configure PagerDuty to notify you by a [variety of
|
||||||
mechanisms](https://support.pagerduty.com/docs/notifications) when backups
|
mechanisms](https://support.pagerduty.com/docs/notifications) when backups
|
||||||
|
|
|
@ -227,8 +227,8 @@ sudo borgmatic --verbosity 1 --files
|
||||||
borgmatic. So try leaving it out, or upgrade borgmatic!)
|
borgmatic. So try leaving it out, or upgrade borgmatic!)
|
||||||
|
|
||||||
By default, this will also prune any old backups as per the configured
|
By default, this will also prune any old backups as per the configured
|
||||||
retention policy, and check backups for consistency problems due to things
|
retention policy, compact segments to free up space (with Borg 1.2+), and
|
||||||
like file damage.
|
check backups for consistency problems due to things like file damage.
|
||||||
|
|
||||||
The verbosity flag makes borgmatic show the steps it's performing. And the
|
The verbosity flag makes borgmatic show the steps it's performing. And the
|
||||||
files flag lists each file that's new or changed since the last backup.
|
files flag lists each file that's new or changed since the last backup.
|
||||||
|
|
13
tests/integration/borg/test_feature.py
Normal file
13
tests/integration/borg/test_feature.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
from borgmatic.borg import feature as module
|
||||||
|
|
||||||
|
|
||||||
|
def test_available_true_for_new_enough_borg_version():
|
||||||
|
assert module.available(module.Feature.COMPACT, '1.3.7')
|
||||||
|
|
||||||
|
|
||||||
|
def test_available_true_for_borg_version_introducing_feature():
|
||||||
|
assert module.available(module.Feature.COMPACT, '1.2.0a2')
|
||||||
|
|
||||||
|
|
||||||
|
def test_available_false_for_too_old_borg_version():
|
||||||
|
assert not module.available(module.Feature.COMPACT, '1.1.5')
|
|
@ -17,33 +17,33 @@ COMPACT_COMMAND = ('borg', 'compact')
|
||||||
|
|
||||||
|
|
||||||
def test_compact_segments_calls_borg_with_parameters():
|
def test_compact_segments_calls_borg_with_parameters():
|
||||||
insert_execute_command_mock(COMPACT_COMMAND + ('repo',), logging.WARNING)
|
insert_execute_command_mock(COMPACT_COMMAND + ('repo',), logging.INFO)
|
||||||
|
|
||||||
module.compact_segments(dry_run=False, repository='repo', storage_config={})
|
module.compact_segments(dry_run=False, repository='repo', storage_config={})
|
||||||
|
|
||||||
|
|
||||||
def test_compact_segments_with_log_info_calls_borg_with_info_parameter():
|
def test_compact_segments_with_log_info_calls_borg_with_info_parameter():
|
||||||
insert_execute_command_mock(COMPACT_COMMAND + ('--info', 'repo'), logging.WARNING)
|
insert_execute_command_mock(COMPACT_COMMAND + ('--info', 'repo'), logging.INFO)
|
||||||
insert_logging_mock(logging.INFO)
|
insert_logging_mock(logging.INFO)
|
||||||
|
|
||||||
module.compact_segments(repository='repo', storage_config={}, dry_run=False)
|
module.compact_segments(repository='repo', storage_config={}, dry_run=False)
|
||||||
|
|
||||||
|
|
||||||
def test_compact_segments_with_log_debug_calls_borg_with_debug_parameter():
|
def test_compact_segments_with_log_debug_calls_borg_with_debug_parameter():
|
||||||
insert_execute_command_mock(COMPACT_COMMAND + ('--debug', '--show-rc', 'repo'), logging.WARNING)
|
insert_execute_command_mock(COMPACT_COMMAND + ('--debug', '--show-rc', 'repo'), logging.INFO)
|
||||||
insert_logging_mock(logging.DEBUG)
|
insert_logging_mock(logging.DEBUG)
|
||||||
|
|
||||||
module.compact_segments(repository='repo', storage_config={}, dry_run=False)
|
module.compact_segments(repository='repo', storage_config={}, dry_run=False)
|
||||||
|
|
||||||
|
|
||||||
def test_compact_segments_with_dry_run_calls_borg_with_dry_run_parameter():
|
def test_compact_segments_with_dry_run_calls_borg_with_dry_run_parameter():
|
||||||
insert_execute_command_mock(COMPACT_COMMAND + ('--dry-run', 'repo'), logging.WARNING)
|
insert_execute_command_mock(COMPACT_COMMAND + ('--dry-run', 'repo'), logging.INFO)
|
||||||
|
|
||||||
module.compact_segments(repository='repo', storage_config={}, dry_run=True)
|
module.compact_segments(repository='repo', storage_config={}, dry_run=True)
|
||||||
|
|
||||||
|
|
||||||
def test_compact_segments_with_local_path_calls_borg_via_local_path():
|
def test_compact_segments_with_local_path_calls_borg_via_local_path():
|
||||||
insert_execute_command_mock(('borg1',) + COMPACT_COMMAND[1:] + ('repo',), logging.WARNING)
|
insert_execute_command_mock(('borg1',) + COMPACT_COMMAND[1:] + ('repo',), logging.INFO)
|
||||||
|
|
||||||
module.compact_segments(
|
module.compact_segments(
|
||||||
dry_run=False, repository='repo', storage_config={}, local_path='borg1',
|
dry_run=False, repository='repo', storage_config={}, local_path='borg1',
|
||||||
|
@ -51,9 +51,7 @@ def test_compact_segments_with_local_path_calls_borg_via_local_path():
|
||||||
|
|
||||||
|
|
||||||
def test_compact_segments_with_remote_path_calls_borg_with_remote_path_parameters():
|
def test_compact_segments_with_remote_path_calls_borg_with_remote_path_parameters():
|
||||||
insert_execute_command_mock(
|
insert_execute_command_mock(COMPACT_COMMAND + ('--remote-path', 'borg1', 'repo'), logging.INFO)
|
||||||
COMPACT_COMMAND + ('--remote-path', 'borg1', 'repo'), logging.WARNING
|
|
||||||
)
|
|
||||||
|
|
||||||
module.compact_segments(
|
module.compact_segments(
|
||||||
dry_run=False, repository='repo', storage_config={}, remote_path='borg1',
|
dry_run=False, repository='repo', storage_config={}, remote_path='borg1',
|
||||||
|
@ -61,7 +59,7 @@ def test_compact_segments_with_remote_path_calls_borg_with_remote_path_parameter
|
||||||
|
|
||||||
|
|
||||||
def test_compact_segments_with_progress_calls_borg_with_progress_parameter():
|
def test_compact_segments_with_progress_calls_borg_with_progress_parameter():
|
||||||
insert_execute_command_mock(COMPACT_COMMAND + ('--progress', 'repo'), logging.WARNING)
|
insert_execute_command_mock(COMPACT_COMMAND + ('--progress', 'repo'), logging.INFO)
|
||||||
|
|
||||||
module.compact_segments(
|
module.compact_segments(
|
||||||
dry_run=False, repository='repo', storage_config={}, progress=True,
|
dry_run=False, repository='repo', storage_config={}, progress=True,
|
||||||
|
@ -69,7 +67,7 @@ def test_compact_segments_with_progress_calls_borg_with_progress_parameter():
|
||||||
|
|
||||||
|
|
||||||
def test_compact_segments_with_cleanup_commits_calls_borg_with_cleanup_commits_parameter():
|
def test_compact_segments_with_cleanup_commits_calls_borg_with_cleanup_commits_parameter():
|
||||||
insert_execute_command_mock(COMPACT_COMMAND + ('--cleanup-commits', 'repo'), logging.WARNING)
|
insert_execute_command_mock(COMPACT_COMMAND + ('--cleanup-commits', 'repo'), logging.INFO)
|
||||||
|
|
||||||
module.compact_segments(
|
module.compact_segments(
|
||||||
dry_run=False, repository='repo', storage_config={}, cleanup_commits=True,
|
dry_run=False, repository='repo', storage_config={}, cleanup_commits=True,
|
||||||
|
@ -77,7 +75,7 @@ def test_compact_segments_with_cleanup_commits_calls_borg_with_cleanup_commits_p
|
||||||
|
|
||||||
|
|
||||||
def test_compact_segments_with_threshold_calls_borg_with_threshold_parameter():
|
def test_compact_segments_with_threshold_calls_borg_with_threshold_parameter():
|
||||||
insert_execute_command_mock(COMPACT_COMMAND + ('--threshold', '20', 'repo'), logging.WARNING)
|
insert_execute_command_mock(COMPACT_COMMAND + ('--threshold', '20', 'repo'), logging.INFO)
|
||||||
|
|
||||||
module.compact_segments(
|
module.compact_segments(
|
||||||
dry_run=False, repository='repo', storage_config={}, threshold=20,
|
dry_run=False, repository='repo', storage_config={}, threshold=20,
|
||||||
|
@ -86,7 +84,7 @@ def test_compact_segments_with_threshold_calls_borg_with_threshold_parameter():
|
||||||
|
|
||||||
def test_compact_segments_with_umask_calls_borg_with_umask_parameters():
|
def test_compact_segments_with_umask_calls_borg_with_umask_parameters():
|
||||||
storage_config = {'umask': '077'}
|
storage_config = {'umask': '077'}
|
||||||
insert_execute_command_mock(COMPACT_COMMAND + ('--umask', '077', 'repo'), logging.WARNING)
|
insert_execute_command_mock(COMPACT_COMMAND + ('--umask', '077', 'repo'), logging.INFO)
|
||||||
|
|
||||||
module.compact_segments(
|
module.compact_segments(
|
||||||
dry_run=False, repository='repo', storage_config=storage_config,
|
dry_run=False, repository='repo', storage_config=storage_config,
|
||||||
|
@ -95,7 +93,7 @@ def test_compact_segments_with_umask_calls_borg_with_umask_parameters():
|
||||||
|
|
||||||
def test_compact_segments_with_lock_wait_calls_borg_with_lock_wait_parameters():
|
def test_compact_segments_with_lock_wait_calls_borg_with_lock_wait_parameters():
|
||||||
storage_config = {'lock_wait': 5}
|
storage_config = {'lock_wait': 5}
|
||||||
insert_execute_command_mock(COMPACT_COMMAND + ('--lock-wait', '5', 'repo'), logging.WARNING)
|
insert_execute_command_mock(COMPACT_COMMAND + ('--lock-wait', '5', 'repo'), logging.INFO)
|
||||||
|
|
||||||
module.compact_segments(
|
module.compact_segments(
|
||||||
dry_run=False, repository='repo', storage_config=storage_config,
|
dry_run=False, repository='repo', storage_config=storage_config,
|
||||||
|
@ -103,7 +101,7 @@ def test_compact_segments_with_lock_wait_calls_borg_with_lock_wait_parameters():
|
||||||
|
|
||||||
|
|
||||||
def test_compact_segments_with_extra_borg_options_calls_borg_with_extra_options():
|
def test_compact_segments_with_extra_borg_options_calls_borg_with_extra_options():
|
||||||
insert_execute_command_mock(COMPACT_COMMAND + ('--extra', '--options', 'repo'), logging.WARNING)
|
insert_execute_command_mock(COMPACT_COMMAND + ('--extra', '--options', 'repo'), logging.INFO)
|
||||||
|
|
||||||
module.compact_segments(
|
module.compact_segments(
|
||||||
dry_run=False,
|
dry_run=False,
|
||||||
|
|
49
tests/unit/borg/test_version.py
Normal file
49
tests/unit/borg/test_version.py
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from flexmock import flexmock
|
||||||
|
|
||||||
|
from borgmatic.borg import version as module
|
||||||
|
|
||||||
|
from ..test_verbosity import insert_logging_mock
|
||||||
|
|
||||||
|
VERSION = '1.2.3'
|
||||||
|
|
||||||
|
|
||||||
|
def insert_execute_command_mock(command, borg_local_path='borg', version_output=f'borg {VERSION}'):
|
||||||
|
flexmock(module).should_receive('execute_command').with_args(
|
||||||
|
command, output_log_level=None, borg_local_path=borg_local_path
|
||||||
|
).once().and_return(version_output)
|
||||||
|
|
||||||
|
|
||||||
|
def test_local_borg_version_calls_borg_with_required_parameters():
|
||||||
|
insert_execute_command_mock(('borg', '--version'))
|
||||||
|
|
||||||
|
assert module.local_borg_version() == VERSION
|
||||||
|
|
||||||
|
|
||||||
|
def test_local_borg_version_with_log_info_calls_borg_with_info_parameter():
|
||||||
|
insert_execute_command_mock(('borg', '--version', '--info'))
|
||||||
|
insert_logging_mock(logging.INFO)
|
||||||
|
|
||||||
|
assert module.local_borg_version() == VERSION
|
||||||
|
|
||||||
|
|
||||||
|
def test_local_borg_version_with_log_debug_calls_borg_with_debug_parameters():
|
||||||
|
insert_execute_command_mock(('borg', '--version', '--debug', '--show-rc'))
|
||||||
|
insert_logging_mock(logging.DEBUG)
|
||||||
|
|
||||||
|
assert module.local_borg_version() == VERSION
|
||||||
|
|
||||||
|
|
||||||
|
def test_local_borg_version_with_local_borg_path_calls_borg_with_it():
|
||||||
|
insert_execute_command_mock(('borg1', '--version'), borg_local_path='borg1')
|
||||||
|
|
||||||
|
assert module.local_borg_version('borg1') == VERSION
|
||||||
|
|
||||||
|
|
||||||
|
def test_local_borg_version_with_invalid_version_raises():
|
||||||
|
insert_execute_command_mock(('borg', '--version'), version_output='wtf')
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
module.local_borg_version()
|
|
@ -72,12 +72,14 @@ def test_parse_subparser_arguments_consumes_multiple_subparser_arguments():
|
||||||
|
|
||||||
def test_parse_subparser_arguments_applies_default_subparsers():
|
def test_parse_subparser_arguments_applies_default_subparsers():
|
||||||
prune_namespace = flexmock()
|
prune_namespace = flexmock()
|
||||||
|
compact_namespace = flexmock()
|
||||||
create_namespace = flexmock(progress=True)
|
create_namespace = flexmock(progress=True)
|
||||||
check_namespace = flexmock()
|
check_namespace = flexmock()
|
||||||
subparsers = {
|
subparsers = {
|
||||||
'prune': flexmock(
|
'prune': flexmock(
|
||||||
parse_known_args=lambda arguments: (prune_namespace, ['prune', '--progress'])
|
parse_known_args=lambda arguments: (prune_namespace, ['prune', '--progress'])
|
||||||
),
|
),
|
||||||
|
'compact': flexmock(parse_known_args=lambda arguments: (compact_namespace, [])),
|
||||||
'create': flexmock(parse_known_args=lambda arguments: (create_namespace, [])),
|
'create': flexmock(parse_known_args=lambda arguments: (create_namespace, [])),
|
||||||
'check': flexmock(parse_known_args=lambda arguments: (check_namespace, [])),
|
'check': flexmock(parse_known_args=lambda arguments: (check_namespace, [])),
|
||||||
'other': flexmock(),
|
'other': flexmock(),
|
||||||
|
@ -87,6 +89,7 @@ def test_parse_subparser_arguments_applies_default_subparsers():
|
||||||
|
|
||||||
assert arguments == {
|
assert arguments == {
|
||||||
'prune': prune_namespace,
|
'prune': prune_namespace,
|
||||||
|
'compact': compact_namespace,
|
||||||
'create': create_namespace,
|
'create': create_namespace,
|
||||||
'check': check_namespace,
|
'check': check_namespace,
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ from borgmatic.commands import borgmatic as module
|
||||||
|
|
||||||
def test_run_configuration_runs_actions_for_each_repository():
|
def test_run_configuration_runs_actions_for_each_repository():
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
expected_results = [flexmock(), flexmock()]
|
expected_results = [flexmock(), flexmock()]
|
||||||
flexmock(module).should_receive('run_actions').and_return(expected_results[:1]).and_return(
|
flexmock(module).should_receive('run_actions').and_return(expected_results[:1]).and_return(
|
||||||
expected_results[1:]
|
expected_results[1:]
|
||||||
|
@ -22,8 +23,21 @@ def test_run_configuration_runs_actions_for_each_repository():
|
||||||
assert results == expected_results
|
assert results == expected_results
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_configuration_with_invalid_borg_version_errors():
|
||||||
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_raise(ValueError)
|
||||||
|
flexmock(module.command).should_receive('execute_hook').never()
|
||||||
|
flexmock(module.dispatch).should_receive('call_hooks').never()
|
||||||
|
flexmock(module).should_receive('run_actions').never()
|
||||||
|
config = {'location': {'repositories': ['foo']}}
|
||||||
|
arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'prune': flexmock()}
|
||||||
|
|
||||||
|
list(module.run_configuration('test.yaml', config, arguments))
|
||||||
|
|
||||||
|
|
||||||
def test_run_configuration_calls_hooks_for_prune_action():
|
def test_run_configuration_calls_hooks_for_prune_action():
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
flexmock(module.command).should_receive('execute_hook').twice()
|
flexmock(module.command).should_receive('execute_hook').twice()
|
||||||
flexmock(module.dispatch).should_receive('call_hooks').at_least().twice()
|
flexmock(module.dispatch).should_receive('call_hooks').at_least().twice()
|
||||||
flexmock(module).should_receive('run_actions').and_return([])
|
flexmock(module).should_receive('run_actions').and_return([])
|
||||||
|
@ -35,6 +49,7 @@ def test_run_configuration_calls_hooks_for_prune_action():
|
||||||
|
|
||||||
def test_run_configuration_calls_hooks_for_compact_action():
|
def test_run_configuration_calls_hooks_for_compact_action():
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
flexmock(module.command).should_receive('execute_hook').twice()
|
flexmock(module.command).should_receive('execute_hook').twice()
|
||||||
flexmock(module).should_receive('run_actions').and_return([])
|
flexmock(module).should_receive('run_actions').and_return([])
|
||||||
config = {'location': {'repositories': ['foo']}}
|
config = {'location': {'repositories': ['foo']}}
|
||||||
|
@ -45,6 +60,7 @@ def test_run_configuration_calls_hooks_for_compact_action():
|
||||||
|
|
||||||
def test_run_configuration_executes_and_calls_hooks_for_create_action():
|
def test_run_configuration_executes_and_calls_hooks_for_create_action():
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
flexmock(module.command).should_receive('execute_hook').twice()
|
flexmock(module.command).should_receive('execute_hook').twice()
|
||||||
flexmock(module.dispatch).should_receive('call_hooks').at_least().twice()
|
flexmock(module.dispatch).should_receive('call_hooks').at_least().twice()
|
||||||
flexmock(module).should_receive('run_actions').and_return([])
|
flexmock(module).should_receive('run_actions').and_return([])
|
||||||
|
@ -56,6 +72,7 @@ def test_run_configuration_executes_and_calls_hooks_for_create_action():
|
||||||
|
|
||||||
def test_run_configuration_calls_hooks_for_check_action():
|
def test_run_configuration_calls_hooks_for_check_action():
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
flexmock(module.command).should_receive('execute_hook').twice()
|
flexmock(module.command).should_receive('execute_hook').twice()
|
||||||
flexmock(module.dispatch).should_receive('call_hooks').at_least().twice()
|
flexmock(module.dispatch).should_receive('call_hooks').at_least().twice()
|
||||||
flexmock(module).should_receive('run_actions').and_return([])
|
flexmock(module).should_receive('run_actions').and_return([])
|
||||||
|
@ -67,6 +84,7 @@ def test_run_configuration_calls_hooks_for_check_action():
|
||||||
|
|
||||||
def test_run_configuration_calls_hooks_for_extract_action():
|
def test_run_configuration_calls_hooks_for_extract_action():
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
flexmock(module.command).should_receive('execute_hook').twice()
|
flexmock(module.command).should_receive('execute_hook').twice()
|
||||||
flexmock(module.dispatch).should_receive('call_hooks').never()
|
flexmock(module.dispatch).should_receive('call_hooks').never()
|
||||||
flexmock(module).should_receive('run_actions').and_return([])
|
flexmock(module).should_receive('run_actions').and_return([])
|
||||||
|
@ -78,6 +96,7 @@ def test_run_configuration_calls_hooks_for_extract_action():
|
||||||
|
|
||||||
def test_run_configuration_does_not_trigger_hooks_for_list_action():
|
def test_run_configuration_does_not_trigger_hooks_for_list_action():
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
flexmock(module.command).should_receive('execute_hook').never()
|
flexmock(module.command).should_receive('execute_hook').never()
|
||||||
flexmock(module.dispatch).should_receive('call_hooks').never()
|
flexmock(module.dispatch).should_receive('call_hooks').never()
|
||||||
flexmock(module).should_receive('run_actions').and_return([])
|
flexmock(module).should_receive('run_actions').and_return([])
|
||||||
|
@ -89,6 +108,7 @@ def test_run_configuration_does_not_trigger_hooks_for_list_action():
|
||||||
|
|
||||||
def test_run_configuration_logs_actions_error():
|
def test_run_configuration_logs_actions_error():
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
flexmock(module.command).should_receive('execute_hook')
|
flexmock(module.command).should_receive('execute_hook')
|
||||||
flexmock(module.dispatch).should_receive('call_hooks')
|
flexmock(module.dispatch).should_receive('call_hooks')
|
||||||
expected_results = [flexmock()]
|
expected_results = [flexmock()]
|
||||||
|
@ -104,6 +124,7 @@ def test_run_configuration_logs_actions_error():
|
||||||
|
|
||||||
def test_run_configuration_logs_pre_hook_error():
|
def test_run_configuration_logs_pre_hook_error():
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
flexmock(module.command).should_receive('execute_hook').and_raise(OSError).and_return(None)
|
flexmock(module.command).should_receive('execute_hook').and_raise(OSError).and_return(None)
|
||||||
expected_results = [flexmock()]
|
expected_results = [flexmock()]
|
||||||
flexmock(module).should_receive('make_error_log_records').and_return(expected_results)
|
flexmock(module).should_receive('make_error_log_records').and_return(expected_results)
|
||||||
|
@ -118,6 +139,7 @@ def test_run_configuration_logs_pre_hook_error():
|
||||||
|
|
||||||
def test_run_configuration_bails_for_pre_hook_soft_failure():
|
def test_run_configuration_bails_for_pre_hook_soft_failure():
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
|
error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
|
||||||
flexmock(module.command).should_receive('execute_hook').and_raise(error).and_return(None)
|
flexmock(module.command).should_receive('execute_hook').and_raise(error).and_return(None)
|
||||||
flexmock(module).should_receive('make_error_log_records').never()
|
flexmock(module).should_receive('make_error_log_records').never()
|
||||||
|
@ -132,6 +154,7 @@ def test_run_configuration_bails_for_pre_hook_soft_failure():
|
||||||
|
|
||||||
def test_run_configuration_logs_post_hook_error():
|
def test_run_configuration_logs_post_hook_error():
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
flexmock(module.command).should_receive('execute_hook').and_return(None).and_raise(
|
flexmock(module.command).should_receive('execute_hook').and_return(None).and_raise(
|
||||||
OSError
|
OSError
|
||||||
).and_return(None)
|
).and_return(None)
|
||||||
|
@ -149,6 +172,7 @@ def test_run_configuration_logs_post_hook_error():
|
||||||
|
|
||||||
def test_run_configuration_bails_for_post_hook_soft_failure():
|
def test_run_configuration_bails_for_post_hook_soft_failure():
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
|
error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
|
||||||
flexmock(module.command).should_receive('execute_hook').and_return(None).and_raise(
|
flexmock(module.command).should_receive('execute_hook').and_return(None).and_raise(
|
||||||
error
|
error
|
||||||
|
@ -166,6 +190,7 @@ def test_run_configuration_bails_for_post_hook_soft_failure():
|
||||||
|
|
||||||
def test_run_configuration_logs_on_error_hook_error():
|
def test_run_configuration_logs_on_error_hook_error():
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
flexmock(module.command).should_receive('execute_hook').and_raise(OSError)
|
flexmock(module.command).should_receive('execute_hook').and_raise(OSError)
|
||||||
expected_results = [flexmock(), flexmock()]
|
expected_results = [flexmock(), flexmock()]
|
||||||
flexmock(module).should_receive('make_error_log_records').and_return(
|
flexmock(module).should_receive('make_error_log_records').and_return(
|
||||||
|
@ -182,6 +207,7 @@ def test_run_configuration_logs_on_error_hook_error():
|
||||||
|
|
||||||
def test_run_configuration_bails_for_on_error_hook_soft_failure():
|
def test_run_configuration_bails_for_on_error_hook_soft_failure():
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
|
error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
|
||||||
flexmock(module.command).should_receive('execute_hook').and_return(None).and_raise(error)
|
flexmock(module.command).should_receive('execute_hook').and_return(None).and_raise(error)
|
||||||
expected_results = [flexmock()]
|
expected_results = [flexmock()]
|
||||||
|
@ -198,6 +224,7 @@ def test_run_configuration_bails_for_on_error_hook_soft_failure():
|
||||||
def test_run_configuration_retries_soft_error():
|
def test_run_configuration_retries_soft_error():
|
||||||
# Run action first fails, second passes
|
# Run action first fails, second passes
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
flexmock(module.command).should_receive('execute_hook')
|
flexmock(module.command).should_receive('execute_hook')
|
||||||
flexmock(module).should_receive('run_actions').and_raise(OSError).and_return([])
|
flexmock(module).should_receive('run_actions').and_raise(OSError).and_return([])
|
||||||
expected_results = [flexmock()]
|
expected_results = [flexmock()]
|
||||||
|
@ -211,6 +238,7 @@ def test_run_configuration_retries_soft_error():
|
||||||
def test_run_configuration_retries_hard_error():
|
def test_run_configuration_retries_hard_error():
|
||||||
# Run action fails twice
|
# Run action fails twice
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
flexmock(module.command).should_receive('execute_hook')
|
flexmock(module.command).should_receive('execute_hook')
|
||||||
flexmock(module).should_receive('run_actions').and_raise(OSError).times(2)
|
flexmock(module).should_receive('run_actions').and_raise(OSError).times(2)
|
||||||
expected_results = [flexmock(), flexmock()]
|
expected_results = [flexmock(), flexmock()]
|
||||||
|
@ -229,6 +257,7 @@ def test_run_configuration_retries_hard_error():
|
||||||
|
|
||||||
def test_run_repos_ordered():
|
def test_run_repos_ordered():
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
flexmock(module.command).should_receive('execute_hook')
|
flexmock(module.command).should_receive('execute_hook')
|
||||||
flexmock(module).should_receive('run_actions').and_raise(OSError).times(2)
|
flexmock(module).should_receive('run_actions').and_raise(OSError).times(2)
|
||||||
expected_results = [flexmock(), flexmock()]
|
expected_results = [flexmock(), flexmock()]
|
||||||
|
@ -246,6 +275,7 @@ def test_run_repos_ordered():
|
||||||
|
|
||||||
def test_run_configuration_retries_round_robbin():
|
def test_run_configuration_retries_round_robbin():
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
flexmock(module.command).should_receive('execute_hook')
|
flexmock(module.command).should_receive('execute_hook')
|
||||||
flexmock(module).should_receive('run_actions').and_raise(OSError).times(4)
|
flexmock(module).should_receive('run_actions').and_raise(OSError).times(4)
|
||||||
expected_results = [flexmock(), flexmock(), flexmock(), flexmock()]
|
expected_results = [flexmock(), flexmock(), flexmock(), flexmock()]
|
||||||
|
@ -269,6 +299,7 @@ def test_run_configuration_retries_round_robbin():
|
||||||
|
|
||||||
def test_run_configuration_retries_one_passes():
|
def test_run_configuration_retries_one_passes():
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
flexmock(module.command).should_receive('execute_hook')
|
flexmock(module.command).should_receive('execute_hook')
|
||||||
flexmock(module).should_receive('run_actions').and_raise(OSError).and_raise(OSError).and_return(
|
flexmock(module).should_receive('run_actions').and_raise(OSError).and_raise(OSError).and_return(
|
||||||
[]
|
[]
|
||||||
|
@ -291,6 +322,7 @@ def test_run_configuration_retries_one_passes():
|
||||||
|
|
||||||
def test_run_configuration_retry_wait():
|
def test_run_configuration_retry_wait():
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
flexmock(module.command).should_receive('execute_hook')
|
flexmock(module.command).should_receive('execute_hook')
|
||||||
flexmock(module).should_receive('run_actions').and_raise(OSError).times(4)
|
flexmock(module).should_receive('run_actions').and_raise(OSError).times(4)
|
||||||
expected_results = [flexmock(), flexmock(), flexmock(), flexmock()]
|
expected_results = [flexmock(), flexmock(), flexmock(), flexmock()]
|
||||||
|
@ -320,6 +352,7 @@ def test_run_configuration_retry_wait():
|
||||||
|
|
||||||
def test_run_configuration_retries_timeout_multiple_repos():
|
def test_run_configuration_retries_timeout_multiple_repos():
|
||||||
flexmock(module.borg_environment).should_receive('initialize')
|
flexmock(module.borg_environment).should_receive('initialize')
|
||||||
|
flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
|
||||||
flexmock(module.command).should_receive('execute_hook')
|
flexmock(module.command).should_receive('execute_hook')
|
||||||
flexmock(module).should_receive('run_actions').and_raise(OSError).and_raise(OSError).and_return(
|
flexmock(module).should_receive('run_actions').and_raise(OSError).and_raise(OSError).and_return(
|
||||||
[]
|
[]
|
||||||
|
|
Loading…
Reference in a new issue