Add "actions" runtime variable (#657)

This commit adds the "actions" variable to the list of variables
borgmatic can interpolate at runtime.

This variable will contains the list (as a string of comma-separated
elements) of all the actions borgmatic will run in this instance.

This will allow running "intelligent" before_actions and after_actions
hooks capable of behavior dependent of the actual action borgmatic will
perform/has performed.

Original need for this variable was to automatically mount and unmount
an NFS remote folder.

Issue: #657
This commit is contained in:
C-Duv 2023-05-11 01:42:55 +02:00
parent 403ae0f698
commit f17fde399e
4 changed files with 93 additions and 1 deletions

View File

@ -272,6 +272,7 @@ def run_actions(
global_arguments = arguments['global'] global_arguments = arguments['global']
dry_run_label = ' (dry run; not making any changes)' if global_arguments.dry_run else '' dry_run_label = ' (dry run; not making any changes)' if global_arguments.dry_run else ''
hook_context = { hook_context = {
'actions': ','.join(sorted(action for action in arguments.keys() if action != 'global')),
'repository': repository_path, 'repository': repository_path,
# Deprecated: For backwards compatibility with borgmatic < 1.6.0. # Deprecated: For backwards compatibility with borgmatic < 1.6.0.
'repositories': ','.join([repo['path'] for repo in location['repositories']]), 'repositories': ','.join([repo['path'] for repo in location['repositories']]),

View File

@ -64,6 +64,9 @@ values into the hook command: the borgmatic configuration filename and the
paths of the current Borg repository. Here's the full set of supported paths of the current Borg repository. Here's the full set of supported
variables you can use here: variables you can use here:
* `actions`
<span class="minilink minilink-addedin">New in version 1.7.13</span>:
comma-separated list of all the action(s) borgmatic is running
* `configuration_filename`: borgmatic configuration filename in which the * `configuration_filename`: borgmatic configuration filename in which the
hook was defined hook was defined
* `log_file` * `log_file`

View File

@ -108,6 +108,8 @@ In this example, when the error occurs, borgmatic interpolates runtime values
into the hook command: the borgmatic configuration filename, and the path of into the hook command: the borgmatic configuration filename, and the path of
the repository. Here's the full set of supported variables you can use here: the repository. Here's the full set of supported variables you can use here:
* `actions`:
comma-separated list of all the action(s) borgmatic will run
* `configuration_filename`: borgmatic configuration filename in which the * `configuration_filename`: borgmatic configuration filename in which the
error occurred error occurred
* `repository`: path of the repository in which the error occurred (may be * `repository`: path of the repository in which the error occurred (may be

View File

@ -434,7 +434,7 @@ def test_run_actions_adds_log_file_to_hook_context():
location={'repositories': []}, location={'repositories': []},
storage=object, storage=object,
hooks={}, hooks={},
hook_context={'repository': 'repo', 'repositories': '', 'log_file': 'foo'}, hook_context={'repository': 'repo', 'repositories': '', 'log_file': 'foo', 'actions': 'create'},
local_borg_version=object, local_borg_version=object,
create_arguments=object, create_arguments=object,
global_arguments=object, global_arguments=object,
@ -461,6 +461,92 @@ def test_run_actions_adds_log_file_to_hook_context():
assert result == (expected,) assert result == (expected,)
def test_run_actions_adds_action_name_to_hook_context():
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').with_args(
config_filename=object,
repository={'path': 'repo'},
location={'repositories': []},
storage=object,
hooks={},
hook_context={'repository': 'repo', 'repositories': '', 'actions': 'create'},
local_borg_version=object,
create_arguments=object,
global_arguments=object,
dry_run_label='',
local_path=object,
remote_path=object,
).once().and_return(expected)
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_adds_all_sorted_action_names_to_hook_context():
flexmock(module).should_receive('add_custom_log_levels')
flexmock(module.command).should_receive('execute_hook')
expected = flexmock()
flexmock(borgmatic.actions.check).should_receive('run_check').with_args(
config_filename=object,
repository={'path': 'repo'},
location={'repositories': []},
storage=object,
consistency=object,
hooks={},
hook_context={'repository': 'repo', 'repositories': '', 'actions': 'check,prune'},
local_borg_version=object,
check_arguments=object,
global_arguments=object,
local_path=object,
remote_path=object,
).once()
flexmock(borgmatic.actions.prune).should_receive('run_prune').with_args(
config_filename=object,
repository={'path': 'repo'},
storage=object,
retention=object,
hooks={},
hook_context={'repository': 'repo', 'repositories': '', 'actions': 'check,prune'},
local_borg_version=object,
prune_arguments=object,
global_arguments=object,
dry_run_label='',
local_path=object,
remote_path=object,
).once()
module.run_actions(
arguments={'global': flexmock(dry_run=False), 'prune': flexmock(), 'check': 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'},
)
def test_run_actions_runs_transfer(): def test_run_actions_runs_transfer():
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')