diff --git a/borgmatic/actions/check.py b/borgmatic/actions/check.py index 580c70ef..f3572395 100644 --- a/borgmatic/actions/check.py +++ b/borgmatic/actions/check.py @@ -1,6 +1,7 @@ import logging import borgmatic.borg.check +import borgmatic.config.validate import borgmatic.hooks.command logger = logging.getLogger(__name__) @@ -23,6 +24,11 @@ def run_check( ''' Run the "check" action for the given repository. ''' + if check_arguments.repository and not borgmatic.config.validate.repositories_match( + repository, check_arguments.repository + ): + return + borgmatic.hooks.command.execute_hook( hooks.get('before_check'), hooks.get('umask'), diff --git a/borgmatic/actions/compact.py b/borgmatic/actions/compact.py index 00585b0b..7a25b829 100644 --- a/borgmatic/actions/compact.py +++ b/borgmatic/actions/compact.py @@ -2,6 +2,7 @@ import logging import borgmatic.borg.compact import borgmatic.borg.feature +import borgmatic.config.validate import borgmatic.hooks.command logger = logging.getLogger(__name__) @@ -24,6 +25,11 @@ def run_compact( ''' Run the "compact" action for the given repository. ''' + if compact_arguments.repository and not borgmatic.config.validate.repositories_match( + repository, compact_arguments.repository + ): + return + borgmatic.hooks.command.execute_hook( hooks.get('before_compact'), hooks.get('umask'), diff --git a/borgmatic/actions/create.py b/borgmatic/actions/create.py index c882032a..96a48521 100644 --- a/borgmatic/actions/create.py +++ b/borgmatic/actions/create.py @@ -2,6 +2,7 @@ import json import logging import borgmatic.borg.create +import borgmatic.config.validate import borgmatic.hooks.command import borgmatic.hooks.dispatch import borgmatic.hooks.dump @@ -28,6 +29,11 @@ def run_create( If create_arguments.json is True, yield the JSON output from creating the archive. ''' + if create_arguments.repository and not borgmatic.config.validate.repositories_match( + repository, create_arguments.repository + ): + return + borgmatic.hooks.command.execute_hook( hooks.get('before_backup'), hooks.get('umask'), diff --git a/borgmatic/actions/prune.py b/borgmatic/actions/prune.py index 2d214a18..ca098ce4 100644 --- a/borgmatic/actions/prune.py +++ b/borgmatic/actions/prune.py @@ -1,6 +1,7 @@ import logging import borgmatic.borg.prune +import borgmatic.config.validate import borgmatic.hooks.command logger = logging.getLogger(__name__) @@ -23,6 +24,11 @@ def run_prune( ''' Run the "prune" action for the given repository. ''' + if prune_arguments.repository and not borgmatic.config.validate.repositories_match( + repository, prune_arguments.repository + ): + return + borgmatic.hooks.command.execute_hook( hooks.get('before_prune'), hooks.get('umask'), diff --git a/borgmatic/commands/arguments.py b/borgmatic/commands/arguments.py index 3f56b7cd..d5dc6af4 100644 --- a/borgmatic/commands/arguments.py +++ b/borgmatic/commands/arguments.py @@ -333,6 +333,10 @@ def make_parsers(): add_help=False, ) prune_group = prune_parser.add_argument_group('prune arguments') + prune_group.add_argument( + '--repository', + help='Path of specific existing repository to prune (must be already specified in a borgmatic configuration file)', + ) prune_group.add_argument( '--stats', dest='stats', @@ -353,6 +357,10 @@ def make_parsers(): add_help=False, ) compact_group = compact_parser.add_argument_group('compact arguments') + compact_group.add_argument( + '--repository', + help='Path of specific existing repository to compact (must be already specified in a borgmatic configuration file)', + ) compact_group.add_argument( '--progress', dest='progress', @@ -385,6 +393,10 @@ def make_parsers(): add_help=False, ) create_group = create_parser.add_argument_group('create arguments') + create_group.add_argument( + '--repository', + help='Path of specific existing repository to backup to (must be already specified in a borgmatic configuration file)', + ) create_group.add_argument( '--progress', dest='progress', @@ -415,6 +427,10 @@ def make_parsers(): add_help=False, ) check_group = check_parser.add_argument_group('check arguments') + check_group.add_argument( + '--repository', + help='Path of specific existing repository to check (must be already specified in a borgmatic configuration file)', + ) check_group.add_argument( '--progress', dest='progress', diff --git a/tests/integration/commands/test_arguments.py b/tests/integration/commands/test_arguments.py index cb001c10..754564b2 100644 --- a/tests/integration/commands/test_arguments.py +++ b/tests/integration/commands/test_arguments.py @@ -254,13 +254,6 @@ def test_parse_arguments_allows_init_and_create(): module.parse_arguments('--config', 'myconfig', 'init', '--encryption', 'repokey', 'create') -def test_parse_arguments_disallows_repository_unless_action_consumes_it(): - flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default']) - - with pytest.raises(SystemExit): - module.parse_arguments('--config', 'myconfig', '--repository', 'test.borg') - - def test_parse_arguments_allows_repository_with_extract(): flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default']) diff --git a/tests/unit/actions/test_check.py b/tests/unit/actions/test_check.py index 0007ee3f..bd0cf1c7 100644 --- a/tests/unit/actions/test_check.py +++ b/tests/unit/actions/test_check.py @@ -3,15 +3,78 @@ from flexmock import flexmock from borgmatic.actions import check as module -def test_run_check_calls_hooks(): +def test_run_check_calls_hooks_for_configured_repository(): flexmock(module.logger).answer = lambda message: None flexmock(module.borgmatic.config.checks).should_receive( 'repository_enabled_for_checks' ).and_return(True) - flexmock(module.borgmatic.borg.check).should_receive('check_archives') + flexmock(module.borgmatic.config.validate).should_receive('repositories_match').never() + flexmock(module.borgmatic.borg.check).should_receive('check_archives').once() flexmock(module.borgmatic.hooks.command).should_receive('execute_hook').times(2) check_arguments = flexmock( - progress=flexmock(), repair=flexmock(), only=flexmock(), force=flexmock() + repository=None, progress=flexmock(), repair=flexmock(), only=flexmock(), force=flexmock(), + ) + global_arguments = flexmock(monitoring_verbosity=1, dry_run=False) + + module.run_check( + config_filename='test.yaml', + repository='repo', + location={'repositories': ['repo']}, + storage={}, + consistency={}, + hooks={}, + hook_context={}, + local_borg_version=None, + check_arguments=check_arguments, + global_arguments=global_arguments, + local_path=None, + remote_path=None, + ) + + +def test_run_check_runs_with_select_repository(): + flexmock(module.logger).answer = lambda message: None + flexmock(module.borgmatic.config.validate).should_receive( + 'repositories_match' + ).once().and_return(True) + flexmock(module.borgmatic.borg.check).should_receive('check_archives').once() + check_arguments = flexmock( + repository=flexmock(), + progress=flexmock(), + repair=flexmock(), + only=flexmock(), + force=flexmock(), + ) + global_arguments = flexmock(monitoring_verbosity=1, dry_run=False) + + module.run_check( + config_filename='test.yaml', + repository=flexmock(), + location={'repositories': ['repo']}, + storage={}, + consistency={}, + hooks={}, + hook_context={}, + local_borg_version=None, + check_arguments=check_arguments, + global_arguments=global_arguments, + local_path=None, + remote_path=None, + ) + + +def test_run_check_bails_if_repository_does_not_match(): + flexmock(module.logger).answer = lambda message: None + flexmock(module.borgmatic.config.validate).should_receive( + 'repositories_match' + ).once().and_return(False) + flexmock(module.borgmatic.borg.check).should_receive('check_archives').never() + check_arguments = flexmock( + repository=flexmock(), + progress=flexmock(), + repair=flexmock(), + only=flexmock(), + force=flexmock(), ) global_arguments = flexmock(monitoring_verbosity=1, dry_run=False) diff --git a/tests/unit/actions/test_compact.py b/tests/unit/actions/test_compact.py index bc2b9406..97f3d5b7 100644 --- a/tests/unit/actions/test_compact.py +++ b/tests/unit/actions/test_compact.py @@ -3,13 +3,70 @@ from flexmock import flexmock from borgmatic.actions import compact as module -def test_compact_actions_calls_hooks(): +def test_compact_actions_calls_hooks_for_configured_repository(): flexmock(module.logger).answer = lambda message: None flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) - flexmock(module.borgmatic.borg.compact).should_receive('compact_segments') + flexmock(module.borgmatic.config.validate).should_receive('repositories_match').never() + flexmock(module.borgmatic.borg.compact).should_receive('compact_segments').once() flexmock(module.borgmatic.hooks.command).should_receive('execute_hook').times(2) compact_arguments = flexmock( - progress=flexmock(), cleanup_commits=flexmock(), threshold=flexmock() + repository=None, progress=flexmock(), cleanup_commits=flexmock(), threshold=flexmock() + ) + global_arguments = flexmock(monitoring_verbosity=1, dry_run=False) + + module.run_compact( + config_filename='test.yaml', + repository='repo', + storage={}, + retention={}, + hooks={}, + hook_context={}, + local_borg_version=None, + compact_arguments=compact_arguments, + global_arguments=global_arguments, + dry_run_label='', + local_path=None, + remote_path=None, + ) + + +def test_compact_runs_with_select_repository(): + flexmock(module.logger).answer = lambda message: None + flexmock(module.borgmatic.config.validate).should_receive( + 'repositories_match' + ).once().and_return(True) + flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) + flexmock(module.borgmatic.borg.compact).should_receive('compact_segments').once() + compact_arguments = flexmock( + repository=flexmock(), progress=flexmock(), cleanup_commits=flexmock(), threshold=flexmock() + ) + global_arguments = flexmock(monitoring_verbosity=1, dry_run=False) + + module.run_compact( + config_filename='test.yaml', + repository='repo', + storage={}, + retention={}, + hooks={}, + hook_context={}, + local_borg_version=None, + compact_arguments=compact_arguments, + global_arguments=global_arguments, + dry_run_label='', + local_path=None, + remote_path=None, + ) + + +def test_compact_bails_if_repository_does_not_match(): + flexmock(module.logger).answer = lambda message: None + flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) + flexmock(module.borgmatic.config.validate).should_receive( + 'repositories_match' + ).once().and_return(False) + flexmock(module.borgmatic.borg.compact).should_receive('compact_segments').never() + compact_arguments = flexmock( + repository=flexmock(), progress=flexmock(), cleanup_commits=flexmock(), threshold=flexmock() ) global_arguments = flexmock(monitoring_verbosity=1, dry_run=False) diff --git a/tests/unit/actions/test_create.py b/tests/unit/actions/test_create.py index 915f0ae1..6eb18c2c 100644 --- a/tests/unit/actions/test_create.py +++ b/tests/unit/actions/test_create.py @@ -3,16 +3,87 @@ from flexmock import flexmock from borgmatic.actions import create as module -def test_run_create_executes_and_calls_hooks(): +def test_run_create_executes_and_calls_hooks_for_configured_repository(): flexmock(module.logger).answer = lambda message: None - flexmock(module.borgmatic.borg.create).should_receive('create_archive') + flexmock(module.borgmatic.config.validate).should_receive('repositories_match').never() + flexmock(module.borgmatic.borg.create).should_receive('create_archive').once() flexmock(module.borgmatic.hooks.command).should_receive('execute_hook').times(2) flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').and_return({}) flexmock(module.borgmatic.hooks.dispatch).should_receive( 'call_hooks_even_if_unconfigured' ).and_return({}) create_arguments = flexmock( - progress=flexmock(), stats=flexmock(), json=flexmock(), list_files=flexmock() + repository=None, + progress=flexmock(), + stats=flexmock(), + json=flexmock(), + list_files=flexmock(), + ) + global_arguments = flexmock(monitoring_verbosity=1, dry_run=False) + + list( + module.run_create( + config_filename='test.yaml', + repository='repo', + location={}, + storage={}, + hooks={}, + hook_context={}, + local_borg_version=None, + create_arguments=create_arguments, + global_arguments=global_arguments, + dry_run_label='', + local_path=None, + remote_path=None, + ) + ) + + +def test_run_create_runs_with_select_repository(): + flexmock(module.logger).answer = lambda message: None + flexmock(module.borgmatic.config.validate).should_receive( + 'repositories_match' + ).once().and_return(True) + flexmock(module.borgmatic.borg.create).should_receive('create_archive').once() + create_arguments = flexmock( + repository=flexmock(), + progress=flexmock(), + stats=flexmock(), + json=flexmock(), + list_files=flexmock(), + ) + global_arguments = flexmock(monitoring_verbosity=1, dry_run=False) + + list( + module.run_create( + config_filename='test.yaml', + repository='repo', + location={}, + storage={}, + hooks={}, + hook_context={}, + local_borg_version=None, + create_arguments=create_arguments, + global_arguments=global_arguments, + dry_run_label='', + local_path=None, + remote_path=None, + ) + ) + + +def test_run_create_bails_if_repository_does_not_match(): + flexmock(module.logger).answer = lambda message: None + flexmock(module.borgmatic.config.validate).should_receive( + 'repositories_match' + ).once().and_return(False) + flexmock(module.borgmatic.borg.create).should_receive('create_archive').never() + create_arguments = flexmock( + repository=flexmock(), + progress=flexmock(), + stats=flexmock(), + json=flexmock(), + list_files=flexmock(), ) global_arguments = flexmock(monitoring_verbosity=1, dry_run=False) diff --git a/tests/unit/actions/test_prune.py b/tests/unit/actions/test_prune.py index d34a9c84..b9119898 100644 --- a/tests/unit/actions/test_prune.py +++ b/tests/unit/actions/test_prune.py @@ -3,11 +3,62 @@ from flexmock import flexmock from borgmatic.actions import prune as module -def test_run_prune_calls_hooks(): +def test_run_prune_calls_hooks_of_configured_repository(): flexmock(module.logger).answer = lambda message: None - flexmock(module.borgmatic.borg.prune).should_receive('prune_archives') + flexmock(module.borgmatic.config.validate).should_receive('repositories_match').never() + flexmock(module.borgmatic.borg.prune).should_receive('prune_archives').once() flexmock(module.borgmatic.hooks.command).should_receive('execute_hook').times(2) - prune_arguments = flexmock(stats=flexmock(), list_archives=flexmock()) + prune_arguments = flexmock(repository=None, stats=flexmock(), list_archives=flexmock()) + global_arguments = flexmock(monitoring_verbosity=1, dry_run=False) + + module.run_prune( + config_filename='test.yaml', + repository='repo', + storage={}, + retention={}, + hooks={}, + hook_context={}, + local_borg_version=None, + prune_arguments=prune_arguments, + global_arguments=global_arguments, + dry_run_label='', + local_path=None, + remote_path=None, + ) + + +def test_run_prune_runs_with_select_repository(): + flexmock(module.logger).answer = lambda message: None + flexmock(module.borgmatic.config.validate).should_receive( + 'repositories_match' + ).once().and_return(True) + flexmock(module.borgmatic.borg.prune).should_receive('prune_archives').once() + prune_arguments = flexmock(repository=flexmock(), stats=flexmock(), list_archives=flexmock()) + global_arguments = flexmock(monitoring_verbosity=1, dry_run=False) + + module.run_prune( + config_filename='test.yaml', + repository='repo', + storage={}, + retention={}, + hooks={}, + hook_context={}, + local_borg_version=None, + prune_arguments=prune_arguments, + global_arguments=global_arguments, + dry_run_label='', + local_path=None, + remote_path=None, + ) + + +def test_run_prune_bails_if_repository_does_not_match(): + flexmock(module.logger).answer = lambda message: None + flexmock(module.borgmatic.config.validate).should_receive( + 'repositories_match' + ).once().and_return(False) + flexmock(module.borgmatic.borg.prune).should_receive('prune_archives').never() + prune_arguments = flexmock(repository=flexmock(), stats=flexmock(), list_archives=flexmock()) global_arguments = flexmock(monitoring_verbosity=1, dry_run=False) module.run_prune(