From b343363bb8cbe37d75e37e086ba4e11fb8d13461 Mon Sep 17 00:00:00 2001 From: Dan Helfman Date: Wed, 8 Mar 2023 14:05:06 -0800 Subject: [PATCH] Change the default action order to: "create", "prune", "compact", "check" (#304). --- NEWS | 3 ++ borgmatic/commands/arguments.py | 8 ++-- borgmatic/commands/borgmatic.py | 36 ++++++++-------- borgmatic/config/schema.yaml | 16 +++---- docs/how-to/deal-with-very-large-backups.md | 28 ++++++------ docs/how-to/monitor-your-backups.md | 12 +++--- tests/unit/commands/test_borgmatic.py | 48 ++++++++++----------- 7 files changed, 79 insertions(+), 72 deletions(-) diff --git a/NEWS b/NEWS index aa62d5e..4e05e07 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,8 @@ 1.7.9.dev0 * #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. * #628: Add a Healthchecks "log" state to send borgmatic logs to Healthchecks without signalling success or failure. diff --git a/borgmatic/commands/arguments.py b/borgmatic/commands/arguments.py index 9b88271..cc9431d 100644 --- a/borgmatic/commands/arguments.py +++ b/borgmatic/commands/arguments.py @@ -68,9 +68,9 @@ def parse_subparser_arguments(unparsed_arguments, subparsers): 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: - for subparser_name in ('prune', 'compact', 'create', 'check'): + for subparser_name in ('create', 'prune', 'compact', 'check'): subparser = subparsers[subparser_name] parsed, unused_remaining = subparser.parse_known_args(unparsed_arguments) arguments[subparser_name] = parsed @@ -216,7 +216,7 @@ def make_parsers(): top_level_parser = ArgumentParser( description=''' 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. ''', parents=[global_parser], @@ -225,7 +225,7 @@ def make_parsers(): subparsers = top_level_parser.add_subparsers( title='actions', 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', diff --git a/borgmatic/commands/borgmatic.py b/borgmatic/commands/borgmatic.py index 4cf6011..fe07981 100644 --- a/borgmatic/commands/borgmatic.py +++ b/borgmatic/commands/borgmatic.py @@ -44,8 +44,8 @@ LEGACY_CONFIG_PATH = '/etc/borgmatic/config' def run_configuration(config_filename, config, arguments): ''' 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, - create, check, and/or other actions. + dict from subparser name to a namespace of parsed arguments, execute the defined create, prune, + compact, check, and/or other actions. Yield a combination of: @@ -64,7 +64,7 @@ def run_configuration(config_filename, config, arguments): retry_wait = storage.get('retry_wait', 0) encountered_error = None 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) try: @@ -302,6 +302,21 @@ def run_actions( local_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': borgmatic.actions.prune.run_prune( config_filename, @@ -332,21 +347,6 @@ def run_actions( local_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': if checks.repository_enabled_for_checks(repository, consistency): borgmatic.actions.check.run_check( diff --git a/borgmatic/config/schema.yaml b/borgmatic/config/schema.yaml index 480c1dc..f716156 100644 --- a/borgmatic/config/schema.yaml +++ b/borgmatic/config/schema.yaml @@ -369,6 +369,11 @@ properties: description: | Extra command-line options to pass to "borg init". example: "--extra-option" + create: + type: string + description: | + Extra command-line options to pass to "borg create". + example: "--extra-option" prune: type: string description: | @@ -379,11 +384,6 @@ properties: description: | Extra command-line options to pass to "borg compact". example: "--extra-option" - create: - type: string - description: | - Extra command-line options to pass to "borg create". - example: "--extra-option" check: type: string description: | @@ -663,11 +663,11 @@ properties: type: string description: | List of one or more shell commands or scripts to execute - when an exception occurs during a "prune", "compact", - "create", or "check" action or an associated before/after + when an exception occurs during a "create", "prune", + "compact", or "check" action or an associated before/after hook. example: - - echo "Error during prune/compact/create/check." + - echo "Error during create/prune/compact/check." before_everything: type: array items: diff --git a/docs/how-to/deal-with-very-large-backups.md b/docs/how-to/deal-with-very-large-backups.md index 343f692..c02f718 100644 --- a/docs/how-to/deal-with-very-large-backups.md +++ b/docs/how-to/deal-with-very-large-backups.md @@ -9,28 +9,32 @@ eleventyNavigation: Borg itself is great for efficiently de-duplicating data across successive backup archives, even when dealing with very large repositories. But you may -find that while borgmatic's default mode of `prune`, `compact`, `create`, and -`check` works well on small repositories, it's not so great on larger ones. -That's because running the default pruning, compact, and consistency checks -take a long time on large repositories. +find that while borgmatic's default actions of `create`, `prune`, `compact`, +and `check` works well on small repositories, it's not so great on larger +ones. That's because running the default pruning, compact, and consistency +checks take a long time on large repositories. + +Prior to version 1.7.9 The +default action ordering was `prune`, `compact`, `create`, and `check`. ### A la carte actions -If you find yourself in this situation, you have some options. First, you can -run borgmatic's `prune`, `compact`, `create`, or `check` actions separately. -For instance, the following optional actions are available: +If you find yourself wanting to customize the actions, you have some options. +First, you can run borgmatic's `prune`, `compact`, `create`, or `check` +actions separately. For instance, the following optional actions are +available (among others): ```bash +borgmatic create borgmatic prune borgmatic compact -borgmatic create borgmatic check ``` -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 -skipping certain actions while running others. For instance, this skips -`prune` and `compact` and only runs `create` and `check`: +You can run borgmatic 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 skipping certain actions while running others. For instance, +this skips `prune` and `compact` and only runs `create` and `check`: ```bash borgmatic create check diff --git a/docs/how-to/monitor-your-backups.md b/docs/how-to/monitor-your-backups.md index 575f4af..eb7a620 100644 --- a/docs/how-to/monitor-your-backups.md +++ b/docs/how-to/monitor-your-backups.md @@ -83,7 +83,7 @@ tests](https://torsion.org/borgmatic/docs/how-to/extract-a-backup/). ## 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 notifications or take other actions, so you can get alerted as soon as 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 occurred without running a command) -Note that borgmatic runs the `on_error` hooks only for `prune`, `compact`, -`create`, or `check` actions or hooks in which an error occurs, and not other +Note that borgmatic runs the `on_error` hooks only for `create`, `prune`, +`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 `before_everything` or `after_everything` hook. For more about hooks, see the [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 `before_backup` hooks 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 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 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`, -`compact`, `create`, or `check` action is run. +itself. But the logs are only included for errors that occur when a `create`, +`prune`, `compact`, or `check` action is run. You can customize the verbosity of the logs that are sent to Healthchecks with borgmatic's `--monitoring-verbosity` flag. The `--list` and `--stats` flags diff --git a/tests/unit/commands/test_borgmatic.py b/tests/unit/commands/test_borgmatic.py index a6899d9..19ac00d 100644 --- a/tests/unit/commands/test_borgmatic.py +++ b/tests/unit/commands/test_borgmatic.py @@ -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(): flexmock(module).should_receive('add_custom_log_levels') 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(): flexmock(module).should_receive('add_custom_log_levels') flexmock(module.command).should_receive('execute_hook')