Change the default action order to: "create", "prune", "compact", "check" (#304).

This commit is contained in:
Dan Helfman 2023-03-08 14:05:06 -08:00
parent 9db31bd1e9
commit b343363bb8
7 changed files with 79 additions and 72 deletions

3
NEWS
View file

@ -1,5 +1,8 @@
1.7.9.dev0 1.7.9.dev0
* #295: Add a SQLite database dump/restore hook. * #295: Add a SQLite database dump/restore hook.
* #304: Change the default action order when no actions are specified on the command-line to:
"create", "prune", "compact", "check". If you'd like to retain the old ordering ("prune" and
"compact" first), then specify actions explicitly on the command-line.
* #304: Run any command-line actions in the order specified instead of using a fixed ordering. * #304: Run any command-line actions in the order specified instead of using a fixed ordering.
* #628: Add a Healthchecks "log" state to send borgmatic logs to Healthchecks without signalling * #628: Add a Healthchecks "log" state to send borgmatic logs to Healthchecks without signalling
success or failure. success or failure.

View file

@ -68,9 +68,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, compact, create, and check. # If no actions are explicitly requested, assume defaults.
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', 'compact', 'create', 'check'): for subparser_name in ('create', 'prune', 'compact', '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
@ -216,7 +216,7 @@ def make_parsers():
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, compact, create, and the action options are given, then borgmatic defaults to: create, prune, compact, and
check. check.
''', ''',
parents=[global_parser], parents=[global_parser],
@ -225,7 +225,7 @@ def make_parsers():
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, compact, create, and check. Use --help with action for details:', help='Specify zero or more actions. Defaults to creat, prune, compact, and check. Use --help with action for details:',
) )
rcreate_parser = subparsers.add_parser( rcreate_parser = subparsers.add_parser(
'rcreate', 'rcreate',

View file

@ -44,8 +44,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 the defined prune, compact, dict from subparser name to a namespace of parsed arguments, execute the defined create, prune,
create, check, and/or other actions. compact, check, and/or other actions.
Yield a combination of: Yield a combination of:
@ -64,7 +64,7 @@ def run_configuration(config_filename, config, arguments):
retry_wait = storage.get('retry_wait', 0) retry_wait = storage.get('retry_wait', 0)
encountered_error = None encountered_error = None
error_repository = '' error_repository = ''
using_primary_action = {'prune', 'compact', 'create', 'check'}.intersection(arguments) using_primary_action = {'create', 'prune', 'compact', '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:
@ -302,6 +302,21 @@ def run_actions(
local_path, local_path,
remote_path, remote_path,
) )
elif action_name == 'create':
yield from borgmatic.actions.create.run_create(
config_filename,
repository,
location,
storage,
hooks,
hook_context,
local_borg_version,
action_arguments,
global_arguments,
dry_run_label,
local_path,
remote_path,
)
elif action_name == 'prune': elif action_name == 'prune':
borgmatic.actions.prune.run_prune( borgmatic.actions.prune.run_prune(
config_filename, config_filename,
@ -332,21 +347,6 @@ def run_actions(
local_path, local_path,
remote_path, remote_path,
) )
elif action_name == 'create':
yield from borgmatic.actions.create.run_create(
config_filename,
repository,
location,
storage,
hooks,
hook_context,
local_borg_version,
action_arguments,
global_arguments,
dry_run_label,
local_path,
remote_path,
)
elif action_name == 'check': elif action_name == 'check':
if checks.repository_enabled_for_checks(repository, consistency): if checks.repository_enabled_for_checks(repository, consistency):
borgmatic.actions.check.run_check( borgmatic.actions.check.run_check(

View file

@ -369,6 +369,11 @@ properties:
description: | description: |
Extra command-line options to pass to "borg init". Extra command-line options to pass to "borg init".
example: "--extra-option" example: "--extra-option"
create:
type: string
description: |
Extra command-line options to pass to "borg create".
example: "--extra-option"
prune: prune:
type: string type: string
description: | description: |
@ -379,11 +384,6 @@ properties:
description: | description: |
Extra command-line options to pass to "borg compact". Extra command-line options to pass to "borg compact".
example: "--extra-option" example: "--extra-option"
create:
type: string
description: |
Extra command-line options to pass to "borg create".
example: "--extra-option"
check: check:
type: string type: string
description: | description: |
@ -663,11 +663,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", "compact", when an exception occurs during a "create", "prune",
"create", or "check" action or an associated before/after "compact", or "check" action or an associated before/after
hook. hook.
example: example:
- echo "Error during prune/compact/create/check." - echo "Error during create/prune/compact/check."
before_everything: before_everything:
type: array type: array
items: items:

View file

@ -9,28 +9,32 @@ 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`, `compact`, `create`, and find that while borgmatic's default actions of `create`, `prune`, `compact`,
`check` works well on small repositories, it's not so great on larger ones. and `check` works well on small repositories, it's not so great on larger
That's because running the default pruning, compact, and consistency checks ones. That's because running the default pruning, compact, and consistency
take a long time on large repositories. checks take a long time on large repositories.
<span class="minilink minilink-addedin">Prior to version 1.7.9</span> The
default action ordering was `prune`, `compact`, `create`, and `check`.
### 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 wanting to customize the actions, you have some options.
run borgmatic's `prune`, `compact`, `create`, or `check` actions separately. First, you can run borgmatic's `prune`, `compact`, `create`, or `check`
For instance, the following optional actions are available: actions separately. For instance, the following optional actions are
available (among others):
```bash ```bash
borgmatic create
borgmatic prune borgmatic prune
borgmatic compact borgmatic compact
borgmatic create
borgmatic check borgmatic check
``` ```
You can run with only one of these actions provided, or you can mix and match You can run borgmatic with only one of these actions provided, or you can mix
any number of them in a single borgmatic run. This supports approaches like and match any number of them in a single borgmatic run. This supports
skipping certain actions while running others. For instance, this skips approaches like skipping certain actions while running others. For instance,
`prune` and `compact` and only runs `create` and `check`: this skips `prune` and `compact` and only runs `create` and `check`:
```bash ```bash
borgmatic create check borgmatic create check

View file

@ -83,7 +83,7 @@ tests](https://torsion.org/borgmatic/docs/how-to/extract-a-backup/).
## Error hooks ## Error hooks
When an error occurs during a `prune`, `compact`, `create`, or `check` action, When an error occurs during a `create`, `prune`, `compact`, or `check` action,
borgmatic can run configurable shell commands to fire off custom error borgmatic can run configurable shell commands to fire off custom error
notifications or take other actions, so you can get alerted as soon as notifications or take other actions, so you can get alerted as soon as
something goes wrong. Here's a not-so-useful example: something goes wrong. Here's a not-so-useful example:
@ -116,8 +116,8 @@ the repository. Here's the full set of supported variables you can use 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`, `compact`, Note that borgmatic runs the `on_error` hooks only for `create`, `prune`,
`create`, or `check` actions or hooks in which an error occurs, and not other `compact`, or `check` actions or hooks in which an error occurs, and not other
actions. 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
@ -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`, `compact`, `create`, or `check` actions are run. the `create`, `prune`, `compact`, 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
@ -154,8 +154,8 @@ 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 `create`,
`compact`, `create`, or `check` action is run. `prune`, `compact`, 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 `--list` and `--stats` flags borgmatic's `--monitoring-verbosity` flag. The `--list` and `--stats` flags

View file

@ -436,6 +436,30 @@ def test_run_actions_runs_transfer():
) )
def test_run_actions_runs_create():
flexmock(module).should_receive('add_custom_log_levels')
flexmock(module.command).should_receive('execute_hook')
expected = flexmock()
flexmock(borgmatic.actions.create).should_receive('run_create').and_yield(expected).once()
result = tuple(
module.run_actions(
arguments={'global': flexmock(dry_run=False), 'create': flexmock()},
config_filename=flexmock(),
location={'repositories': []},
storage=flexmock(),
retention=flexmock(),
consistency=flexmock(),
hooks={},
local_path=flexmock(),
remote_path=flexmock(),
local_borg_version=flexmock(),
repository_path='repo',
)
)
assert result == (expected,)
def test_run_actions_runs_prune(): def test_run_actions_runs_prune():
flexmock(module).should_receive('add_custom_log_levels') flexmock(module).should_receive('add_custom_log_levels')
flexmock(module.command).should_receive('execute_hook') flexmock(module.command).should_receive('execute_hook')
@ -480,30 +504,6 @@ def test_run_actions_runs_compact():
) )
def test_run_actions_runs_create():
flexmock(module).should_receive('add_custom_log_levels')
flexmock(module.command).should_receive('execute_hook')
expected = flexmock()
flexmock(borgmatic.actions.create).should_receive('run_create').and_yield(expected).once()
result = tuple(
module.run_actions(
arguments={'global': flexmock(dry_run=False), 'create': flexmock()},
config_filename=flexmock(),
location={'repositories': []},
storage=flexmock(),
retention=flexmock(),
consistency=flexmock(),
hooks={},
local_path=flexmock(),
remote_path=flexmock(),
local_borg_version=flexmock(),
repository_path='repo',
)
)
assert result == (expected,)
def test_run_actions_runs_check_when_repository_enabled_for_checks(): def test_run_actions_runs_check_when_repository_enabled_for_checks():
flexmock(module).should_receive('add_custom_log_levels') flexmock(module).should_receive('add_custom_log_levels')
flexmock(module.command).should_receive('execute_hook') flexmock(module.command).should_receive('execute_hook')