From 8ad8a9c422998eab005eaa3da092c5a9d5534d54 Mon Sep 17 00:00:00 2001 From: Dan Helfman Date: Mon, 27 Jan 2020 11:07:07 -0800 Subject: [PATCH] Add per-action hooks: "before_prune", "after_prune", "before_check", and "after_check" (#255). --- NEWS | 3 +- borgmatic/commands/borgmatic.py | 36 ++++++++++++++++- borgmatic/config/schema.yaml | 39 +++++++++++++++++-- ...reparation-and-cleanup-steps-to-backups.md | 4 ++ ...movable-drive-or-an-intermittent-server.md | 5 ++- setup.py | 2 +- 6 files changed, 80 insertions(+), 9 deletions(-) diff --git a/NEWS b/NEWS index bd15bb1b9..151e0d608 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,5 @@ -1.4.23.dev0 +1.5.0 + * #255: Add per-action hooks: "before_prune", "after_prune", "before_check", and "after_check". * #274: Add ~/.config/borgmatic.d as another configuration directory default. * #277: Customize Healthchecks log level via borgmatic "--monitoring-verbosity" flag. * #280: Change "exclude_if_present" option to support multiple filenames that indicate a directory diff --git a/borgmatic/commands/borgmatic.py b/borgmatic/commands/borgmatic.py index 3e3777677..8a2489cd2 100644 --- a/borgmatic/commands/borgmatic.py +++ b/borgmatic/commands/borgmatic.py @@ -66,6 +66,14 @@ def run_configuration(config_filename, config, arguments): monitoring_log_level, global_arguments.dry_run, ) + if 'prune' in arguments: + command.execute_hook( + hooks.get('before_prune'), + hooks.get('umask'), + config_filename, + 'pre-prune', + global_arguments.dry_run, + ) if 'create' in arguments: command.execute_hook( hooks.get('before_backup'), @@ -82,13 +90,21 @@ def run_configuration(config_filename, config, arguments): location, global_arguments.dry_run, ) + if 'check' in arguments: + command.execute_hook( + hooks.get('before_check'), + hooks.get('umask'), + config_filename, + 'pre-check', + global_arguments.dry_run, + ) except (OSError, CalledProcessError) as error: if command.considered_soft_failure(config_filename, error): return encountered_error = error yield from make_error_log_records( - '{}: Error running pre-backup hook'.format(config_filename), error + '{}: Error running pre hook'.format(config_filename), error ) if not encountered_error: @@ -114,6 +130,14 @@ def run_configuration(config_filename, config, arguments): if not encountered_error: try: + if 'prune' in arguments: + command.execute_hook( + hooks.get('after_prune'), + hooks.get('umask'), + config_filename, + 'post-prune', + global_arguments.dry_run, + ) if 'create' in arguments: dispatch.call_hooks( 'remove_database_dumps', @@ -130,6 +154,14 @@ def run_configuration(config_filename, config, arguments): 'post-backup', global_arguments.dry_run, ) + if 'check' in arguments: + command.execute_hook( + hooks.get('after_check'), + hooks.get('umask'), + config_filename, + 'post-check', + global_arguments.dry_run, + ) if {'prune', 'create', 'check'}.intersection(arguments): dispatch.call_hooks( 'ping_monitor', @@ -146,7 +178,7 @@ def run_configuration(config_filename, config, arguments): encountered_error = error yield from make_error_log_records( - '{}: Error running post-backup hook'.format(config_filename), error + '{}: Error running post hook'.format(config_filename), error ) if encountered_error and prune_create_or_check: diff --git a/borgmatic/config/schema.yaml b/borgmatic/config/schema.yaml index 5d00eeba9..3a09fdf57 100644 --- a/borgmatic/config/schema.yaml +++ b/borgmatic/config/schema.yaml @@ -395,6 +395,22 @@ map: backup, run once per configuration file. example: - echo "Starting a backup." + before_prune: + seq: + - type: str + desc: | + List of one or more shell commands or scripts to execute before pruning, run + once per configuration file. + example: + - echo "Starting pruning." + before_check: + seq: + - type: str + desc: | + List of one or more shell commands or scripts to execute before consistency + checks, run once per configuration file. + example: + - echo "Starting checks." after_backup: seq: - type: str @@ -402,15 +418,32 @@ map: List of one or more shell commands or scripts to execute after creating a backup, run once per configuration file. example: - - echo "Created a backup." + - echo "Finished a backup." + after_prune: + seq: + - type: str + desc: | + List of one or more shell commands or scripts to execute after pruning, run once + per configuration file. + example: + - echo "Finished pruning." + after_check: + seq: + - type: str + desc: | + List of one or more shell commands or scripts to execute after consistency + checks, run once per configuration file. + example: + - echo "Finished checks." on_error: seq: - type: str desc: | List of one or more shell commands or scripts to execute when an exception - occurs during a backup or when running a before_backup or after_backup hook. + occurs during a "prune", "create", or "check" action or an associated + before/after hook. example: - - echo "Error while creating a backup or running a backup hook." + - echo "Error during prune/create/check." postgresql_databases: seq: - map: diff --git a/docs/how-to/add-preparation-and-cleanup-steps-to-backups.md b/docs/how-to/add-preparation-and-cleanup-steps-to-backups.md index 1f3b0c3e1..41872ab17 100644 --- a/docs/how-to/add-preparation-and-cleanup-steps-to-backups.md +++ b/docs/how-to/add-preparation-and-cleanup-steps-to-backups.md @@ -29,6 +29,10 @@ 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 themselves. +There are additional hooks for the `prune` and `check` actions as well. +`before_prune` and `after_prune` run if there are any `prune` actions, while +`before_check` and `after_check` run if there are any `check` actions. + You can also use `before_everything` and `after_everything` hooks to perform global setup or cleanup: diff --git a/docs/how-to/backup-to-a-removable-drive-or-an-intermittent-server.md b/docs/how-to/backup-to-a-removable-drive-or-an-intermittent-server.md index e9abfd86f..a38b3dc35 100644 --- a/docs/how-to/backup-to-a-removable-drive-or-an-intermittent-server.md +++ b/docs/how-to/backup-to-a-removable-drive-or-an-intermittent-server.md @@ -95,8 +95,9 @@ 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 a test to make sure that individual source directories are mounted and available. Use your imagination! - * This feature does not apply to `before_everything` or `after_everything` - hooks. + * The soft failure feature also works for `before_prune`, `after_prune`, + `before_check`, and `after_check` hooks. However it is not implemented for + `before_everything` or `after_everything`. ## Related documentation diff --git a/setup.py b/setup.py index c73c9ea77..2cce11ab3 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import find_packages, setup -VERSION = '1.4.23.dev0' +VERSION = '1.5.0' setup(