diff --git a/borgmatic/actions/mount.py b/borgmatic/actions/mount.py index a72701e5..cc8a2cbd 100644 --- a/borgmatic/actions/mount.py +++ b/borgmatic/actions/mount.py @@ -40,10 +40,7 @@ def run_mount( local_path, remote_path, ), - mount_arguments.mount_point, - mount_arguments.paths, - mount_arguments.foreground, - mount_arguments.options, + mount_arguments, storage, local_borg_version, global_arguments, diff --git a/borgmatic/actions/prune.py b/borgmatic/actions/prune.py index 422a9d46..f7280840 100644 --- a/borgmatic/actions/prune.py +++ b/borgmatic/actions/prune.py @@ -45,10 +45,9 @@ def run_prune( retention, local_borg_version, global_arguments, + prune_arguments, local_path=local_path, remote_path=remote_path, - stats=prune_arguments.stats, - list_archives=prune_arguments.list_archives, ) borgmatic.hooks.command.execute_hook( hooks.get('after_prune'), diff --git a/borgmatic/borg/mount.py b/borgmatic/borg/mount.py index 6ce01a87..80cfa8d7 100644 --- a/borgmatic/borg/mount.py +++ b/borgmatic/borg/mount.py @@ -9,10 +9,7 @@ logger = logging.getLogger(__name__) def mount_archive( repository_path, archive, - mount_point, - paths, - foreground, - options, + mount_arguments, storage_config, local_borg_version, global_arguments, @@ -36,8 +33,11 @@ def mount_archive( + (('--lock-wait', str(lock_wait)) if lock_wait else ()) + (('--info',) if logger.getEffectiveLevel() == logging.INFO else ()) + (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ()) - + (('--foreground',) if foreground else ()) - + (('-o', options) if options else ()) + + flags.make_flags_from_arguments( + mount_arguments, + excludes=('repository', 'archive', 'mount_point', 'paths', 'options'), + ) + + (('-o', mount_arguments.options) if mount_arguments.options else ()) + ( ( flags.make_repository_flags(repository_path, local_borg_version) @@ -54,14 +54,14 @@ def mount_archive( else flags.make_repository_flags(repository_path, local_borg_version) ) ) - + (mount_point,) - + (tuple(paths) if paths else ()) + + (mount_arguments.mount_point,) + + (tuple(mount_arguments.paths) if mount_arguments.paths else ()) ) borg_environment = environment.make_environment(storage_config) # Don't capture the output when foreground mode is used so that ctrl-C can work properly. - if foreground: + if mount_arguments.foreground: execute_command( full_command, output_file=DO_NOT_CAPTURE, diff --git a/borgmatic/borg/prune.py b/borgmatic/borg/prune.py index b6be75ba..a85cacf9 100644 --- a/borgmatic/borg/prune.py +++ b/borgmatic/borg/prune.py @@ -53,11 +53,10 @@ def prune_archives( storage_config, retention_config, local_borg_version, + prune_arguments, global_arguments, local_path='borg', remote_path=None, - stats=False, - list_archives=False, ): ''' Given dry-run flag, a local or remote repository path, a storage config dict, and a @@ -76,16 +75,20 @@ def prune_archives( + (('--umask', str(umask)) if umask else ()) + (('--log-json',) if global_arguments.log_json else ()) + (('--lock-wait', str(lock_wait)) if lock_wait else ()) - + (('--stats',) if stats and not dry_run else ()) + + (('--stats',) if prune_arguments.stats and not dry_run else ()) + (('--info',) if logger.getEffectiveLevel() == logging.INFO else ()) - + (('--list',) if list_archives else ()) + + flags.make_flags_from_arguments( + prune_arguments, + excludes=('repository', 'stats', 'list_archives'), + ) + + (('--list',) if prune_arguments.list_archives else ()) + (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ()) + (('--dry-run',) if dry_run else ()) + (tuple(extra_borg_options.split(' ')) if extra_borg_options else ()) + flags.make_repository_flags(repository_path, local_borg_version) ) - if stats or list_archives: + if prune_arguments.stats or prune_arguments.list_archives: output_log_level = logging.ANSWER else: output_log_level = logging.INFO diff --git a/borgmatic/commands/arguments.py b/borgmatic/commands/arguments.py index 0812edea..07d1f6d7 100644 --- a/borgmatic/commands/arguments.py +++ b/borgmatic/commands/arguments.py @@ -259,7 +259,7 @@ def make_parsers(): '--source-repository', '--other-repo', metavar='KEY_REPOSITORY', - help='Path to an existing Borg repository whose key material should be reused (Borg 2.x+ only)', + help='Path to an existing Borg repository whose key material should be reused [Borg 2.x+ only]', ) rcreate_group.add_argument( '--repository', @@ -268,7 +268,7 @@ def make_parsers(): rcreate_group.add_argument( '--copy-crypt-key', action='store_true', - help='Copy the crypt key used for authenticated encryption from the source repository, defaults to a new random key (Borg 2.x+ only)', + help='Copy the crypt key used for authenticated encryption from the source repository, defaults to a new random key [Borg 2.x+ only]', ) rcreate_group.add_argument( '--append-only', @@ -291,8 +291,8 @@ def make_parsers(): transfer_parser = subparsers.add_parser( 'transfer', aliases=SUBPARSER_ALIASES['transfer'], - help='Transfer archives from one repository to another, optionally upgrading the transferred data (Borg 2.0+ only)', - description='Transfer archives from one repository to another, optionally upgrading the transferred data (Borg 2.0+ only)', + help='Transfer archives from one repository to another, optionally upgrading the transferred data [Borg 2.0+ only]', + description='Transfer archives from one repository to another, optionally upgrading the transferred data [Borg 2.0+ only]', add_help=False, ) transfer_group = transfer_parser.add_argument_group('transfer arguments') @@ -337,6 +337,26 @@ def make_parsers(): transfer_group.add_argument( '--last', metavar='N', help='Only transfer last N archives after other filters are applied' ) + transfer_group.add_argument( + '--oldest', + metavar='TIMESPAN', + help='Transfer archives within a specified time range starting from the timestamp of the oldest archive (e.g. 7d or 12m) [Borg 2.x+ only]', + ) + transfer_group.add_argument( + '--newest', + metavar='TIMESPAN', + help='Transfer archives within a time range that ends at timestamp of the newest archive and starts a specified time range ago (e.g. 7d or 12m) [Borg 2.x+ only]', + ) + transfer_group.add_argument( + '--older', + metavar='TIMESPAN', + help='Transfer archives that are older than the specified time range (e.g. 7d or 12m) from the current time [Borg 2.x+ only]', + ) + transfer_group.add_argument( + '--newer', + metavar='TIMESPAN', + help='Transfer archives that are newer than the specified time range (e.g. 7d or 12m) from the current time [Borg 2.x+ only]', + ) transfer_group.add_argument( '-h', '--help', action='help', help='Show this help message and exit' ) @@ -363,13 +383,33 @@ def make_parsers(): prune_group.add_argument( '--list', dest='list_archives', action='store_true', help='List archives kept/pruned' ) + prune_group.add_argument( + '--oldest', + metavar='TIMESPAN', + help='Prune archives within a specified time range starting from the timestamp of the oldest archive (e.g. 7d or 12m) [Borg 2.x+ only]', + ) + prune_group.add_argument( + '--newest', + metavar='TIMESPAN', + help='Prune archives within a time range that ends at timestamp of the newest archive and starts a specified time range ago (e.g. 7d or 12m) [Borg 2.x+ only]', + ) + prune_group.add_argument( + '--older', + metavar='TIMESPAN', + help='Prune archives that are older than the specified time range (e.g. 7d or 12m) from the current time [Borg 2.x+ only]', + ) + prune_group.add_argument( + '--newer', + metavar='TIMESPAN', + help='Prune archives that are newer than the specified time range (e.g. 7d or 12m) from the current time [Borg 2.x+ only]', + ) prune_group.add_argument('-h', '--help', action='help', help='Show this help message and exit') compact_parser = subparsers.add_parser( 'compact', aliases=SUBPARSER_ALIASES['compact'], - help='Compact segments to free space (Borg 1.2+, borgmatic 1.5.23+ only)', - description='Compact segments to free space (Borg 1.2+, borgmatic 1.5.23+ only)', + help='Compact segments to free space [Borg 1.2+, borgmatic 1.5.23+ only]', + description='Compact segments to free space [Borg 1.2+, borgmatic 1.5.23+ only]', add_help=False, ) compact_group = compact_parser.add_argument_group('compact arguments') @@ -389,7 +429,7 @@ def make_parsers(): dest='cleanup_commits', default=False, action='store_true', - help='Cleanup commit-only 17-byte segment files left behind by Borg 1.1 (flag in Borg 1.2 only)', + help='Cleanup commit-only 17-byte segment files left behind by Borg 1.1 [flag in Borg 1.2 only]', ) compact_group.add_argument( '--threshold', @@ -603,6 +643,34 @@ def make_parsers(): action='store_true', help='Stay in foreground until ctrl-C is pressed', ) + mount_group.add_argument( + '--first', + metavar='N', + help='Mount first N archives after other filters are applied', + ) + mount_group.add_argument( + '--last', metavar='N', help='Mount last N archives after other filters are applied' + ) + mount_group.add_argument( + '--oldest', + metavar='TIMESPAN', + help='Mount archives within a specified time range starting from the timestamp of the oldest archive (e.g. 7d or 12m) [Borg 2.x+ only]', + ) + mount_group.add_argument( + '--newest', + metavar='TIMESPAN', + help='Mount archives within a time range that ends at timestamp of the newest archive and starts a specified time range ago (e.g. 7d or 12m) [Borg 2.x+ only]', + ) + mount_group.add_argument( + '--older', + metavar='TIMESPAN', + help='Mount archives that are older than the specified time range (e.g. 7d or 12m) from the current time [Borg 2.x+ only]', + ) + mount_group.add_argument( + '--newer', + metavar='TIMESPAN', + help='Mount archives that are newer than the specified time range (e.g. 7d or 12m) from the current time [Borg 2.x+ only]', + ) mount_group.add_argument('--options', dest='options', help='Extra Borg mount options') mount_group.add_argument('-h', '--help', action='help', help='Show this help message and exit') @@ -694,6 +762,26 @@ def make_parsers(): rlist_group.add_argument( '--last', metavar='N', help='List last N archives after other filters are applied' ) + rlist_group.add_argument( + '--oldest', + metavar='TIMESPAN', + help='List archives within a specified time range starting from the timestamp of the oldest archive (e.g. 7d or 12m) [Borg 2.x+ only]', + ) + rlist_group.add_argument( + '--newest', + metavar='TIMESPAN', + help='List archives within a time range that ends at timestamp of the newest archive and starts a specified time range ago (e.g. 7d or 12m) [Borg 2.x+ only]', + ) + rlist_group.add_argument( + '--older', + metavar='TIMESPAN', + help='List archives that are older than the specified time range (e.g. 7d or 12m) from the current time [Borg 2.x+ only]', + ) + rlist_group.add_argument( + '--newer', + metavar='TIMESPAN', + help='List archives that are newer than the specified time range (e.g. 7d or 12m) from the current time [Borg 2.x+ only]', + ) rlist_group.add_argument('-h', '--help', action='help', help='Show this help message and exit') list_parser = subparsers.add_parser( @@ -825,6 +913,26 @@ def make_parsers(): info_group.add_argument( '--last', metavar='N', help='Show info for last N archives after other filters are applied' ) + info_group.add_argument( + '--oldest', + metavar='TIMESPAN', + help='Show info for archives within a specified time range starting from the timestamp of the oldest archive (e.g. 7d or 12m) [Borg 2.x+ only]', + ) + info_group.add_argument( + '--newest', + metavar='TIMESPAN', + help='Show info for archives within a time range that ends at timestamp of the newest archive and starts a specified time range ago (e.g. 7d or 12m) [Borg 2.x+ only]', + ) + info_group.add_argument( + '--older', + metavar='TIMESPAN', + help='Show info for archives that are older than the specified time range (e.g. 7d or 12m) from the current time [Borg 2.x+ only]', + ) + info_group.add_argument( + '--newer', + metavar='TIMESPAN', + help='Show info for archives that are newer than the specified time range (e.g. 7d or 12m) from the current time [Borg 2.x+ only]', + ) info_group.add_argument('-h', '--help', action='help', help='Show this help message and exit') break_lock_parser = subparsers.add_parser( diff --git a/tests/integration/borg/test_commands.py b/tests/integration/borg/test_commands.py index 44403193..3347890c 100644 --- a/tests/integration/borg/test_commands.py +++ b/tests/integration/borg/test_commands.py @@ -124,3 +124,24 @@ def test_display_archives_info_command_does_not_duplicate_flags_or_raise(): fuzz_argument(arguments, argument_name), argparse.Namespace(log_json=False), ) + + +def test_prune_archives_command_does_not_duplicate_flags_or_raise(): + arguments = borgmatic.commands.arguments.parse_arguments('prune')['prune'] + flexmock(borgmatic.borg.prune).should_receive('execute_command').replace_with( + assert_command_does_not_duplicate_flags + ) + + for argument_name in dir(arguments): + if argument_name.startswith('_'): + continue + + borgmatic.borg.prune.prune_archives( + False, + 'repo', + {}, + {}, + '2.3.4', + fuzz_argument(arguments, argument_name), + argparse.Namespace(log_json=False), + ) diff --git a/tests/unit/borg/test_info.py b/tests/unit/borg/test_info.py index 112ef4c5..e7b126a6 100644 --- a/tests/unit/borg/test_info.py +++ b/tests/unit/borg/test_info.py @@ -478,3 +478,53 @@ def test_display_archives_info_passes_through_arguments_to_borg(argument_name): archive=None, json=False, prefix=None, match_archives=None, **{argument_name: 'value'} ), ) + + +def test_display_archives_info_with_date_based_matching_calls_borg_with_date_based_flags(): + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER + flexmock(module.flags).should_receive('make_flags').and_return(()) + flexmock(module.flags).should_receive('make_match_archives_flags').with_args( + None, None, '2.3.4' + ).and_return(()) + flexmock(module.flags).should_receive('make_flags_from_arguments').and_return( + ('--newer', '1d', '--newest', '1y', '--older', '1m', '--oldest', '1w') + ) + flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) + flexmock(module.environment).should_receive('make_environment') + flexmock(module).should_receive('execute_command').with_args( + ( + 'borg', + 'info', + '--newer', + '1d', + '--newest', + '1y', + '--older', + '1m', + '--oldest', + '1w', + '--repo', + 'repo', + ), + output_log_level=module.borgmatic.logger.ANSWER, + borg_local_path='borg', + extra_environment=None, + ) + info_arguments = flexmock( + archive=None, + json=False, + prefix=None, + match_archives=None, + newer='1d', + newest='1y', + older='1m', + oldest='1w', + ) + module.display_archives_info( + repository_path='repo', + storage_config={}, + local_borg_version='2.3.4', + global_arguments=flexmock(log_json=False), + info_arguments=info_arguments, + ) diff --git a/tests/unit/borg/test_mount.py b/tests/unit/borg/test_mount.py index cd177511..bd93cf3d 100644 --- a/tests/unit/borg/test_mount.py +++ b/tests/unit/borg/test_mount.py @@ -21,13 +21,11 @@ def test_mount_archive_calls_borg_with_required_flags(): flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) insert_execute_command_mock(('borg', 'mount', 'repo', '/mnt')) + mount_arguments = flexmock(mount_point='/mnt', options=None, paths=None, foreground=False) module.mount_archive( repository_path='repo', archive=None, - mount_point='/mnt', - paths=None, - foreground=False, - options=None, + mount_arguments=mount_arguments, storage_config={}, local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), @@ -46,13 +44,11 @@ def test_mount_archive_with_borg_features_calls_borg_with_repository_and_match_a ('borg', 'mount', '--repo', 'repo', '--match-archives', 'archive', '/mnt') ) + mount_arguments = flexmock(mount_point='/mnt', options=None, paths=None, foreground=False) module.mount_archive( repository_path='repo', archive='archive', - mount_point='/mnt', - paths=None, - foreground=False, - options=None, + mount_arguments=mount_arguments, storage_config={}, local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), @@ -66,13 +62,11 @@ def test_mount_archive_without_archive_calls_borg_with_repository_flags_only(): ) insert_execute_command_mock(('borg', 'mount', 'repo::archive', '/mnt')) + mount_arguments = flexmock(mount_point='/mnt', options=None, paths=None, foreground=False) module.mount_archive( repository_path='repo', archive='archive', - mount_point='/mnt', - paths=None, - foreground=False, - options=None, + mount_arguments=mount_arguments, storage_config={}, local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), @@ -86,13 +80,13 @@ def test_mount_archive_calls_borg_with_path_flags(): ) insert_execute_command_mock(('borg', 'mount', 'repo::archive', '/mnt', 'path1', 'path2')) + mount_arguments = flexmock( + mount_point='/mnt', options=None, paths=['path1', 'path2'], foreground=False + ) module.mount_archive( repository_path='repo', archive='archive', - mount_point='/mnt', - paths=['path1', 'path2'], - foreground=False, - options=None, + mount_arguments=mount_arguments, storage_config={}, local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), @@ -108,13 +102,11 @@ def test_mount_archive_calls_borg_with_remote_path_flags(): ('borg', 'mount', '--remote-path', 'borg1', 'repo::archive', '/mnt') ) + mount_arguments = flexmock(mount_point='/mnt', options=None, paths=None, foreground=False) module.mount_archive( repository_path='repo', archive='archive', - mount_point='/mnt', - paths=None, - foreground=False, - options=None, + mount_arguments=mount_arguments, storage_config={}, local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), @@ -129,13 +121,11 @@ def test_mount_archive_calls_borg_with_umask_flags(): ) insert_execute_command_mock(('borg', 'mount', '--umask', '0770', 'repo::archive', '/mnt')) + mount_arguments = flexmock(mount_point='/mnt', options=None, paths=None, foreground=False) module.mount_archive( repository_path='repo', archive='archive', - mount_point='/mnt', - paths=None, - foreground=False, - options=None, + mount_arguments=mount_arguments, storage_config={'umask': '0770'}, local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), @@ -149,13 +139,11 @@ def test_mount_archive_calls_borg_with_log_json_flags(): ) insert_execute_command_mock(('borg', 'mount', '--log-json', 'repo::archive', '/mnt')) + mount_arguments = flexmock(mount_point='/mnt', options=None, paths=None, foreground=False) module.mount_archive( repository_path='repo', archive='archive', - mount_point='/mnt', - paths=None, - foreground=False, - options=None, + mount_arguments=mount_arguments, storage_config={}, local_borg_version='1.2.3', global_arguments=flexmock(log_json=True), @@ -169,13 +157,11 @@ def test_mount_archive_calls_borg_with_lock_wait_flags(): ) insert_execute_command_mock(('borg', 'mount', '--lock-wait', '5', 'repo::archive', '/mnt')) + mount_arguments = flexmock(mount_point='/mnt', options=None, paths=None, foreground=False) module.mount_archive( repository_path='repo', archive='archive', - mount_point='/mnt', - paths=None, - foreground=False, - options=None, + mount_arguments=mount_arguments, storage_config={'lock_wait': '5'}, local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), @@ -190,13 +176,11 @@ def test_mount_archive_with_log_info_calls_borg_with_info_parameter(): insert_execute_command_mock(('borg', 'mount', '--info', 'repo::archive', '/mnt')) insert_logging_mock(logging.INFO) + mount_arguments = flexmock(mount_point='/mnt', options=None, paths=None, foreground=False) module.mount_archive( repository_path='repo', archive='archive', - mount_point='/mnt', - paths=None, - foreground=False, - options=None, + mount_arguments=mount_arguments, storage_config={}, local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), @@ -211,13 +195,11 @@ def test_mount_archive_with_log_debug_calls_borg_with_debug_flags(): insert_execute_command_mock(('borg', 'mount', '--debug', '--show-rc', 'repo::archive', '/mnt')) insert_logging_mock(logging.DEBUG) + mount_arguments = flexmock(mount_point='/mnt', options=None, paths=None, foreground=False) module.mount_archive( repository_path='repo', archive='archive', - mount_point='/mnt', - paths=None, - foreground=False, - options=None, + mount_arguments=mount_arguments, storage_config={}, local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), @@ -237,13 +219,11 @@ def test_mount_archive_calls_borg_with_foreground_parameter(): extra_environment=None, ).once() + mount_arguments = flexmock(mount_point='/mnt', options=None, paths=None, foreground=True) module.mount_archive( repository_path='repo', archive='archive', - mount_point='/mnt', - paths=None, - foreground=True, - options=None, + mount_arguments=mount_arguments, storage_config={}, local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), @@ -257,13 +237,74 @@ def test_mount_archive_calls_borg_with_options_flags(): ) insert_execute_command_mock(('borg', 'mount', '-o', 'super_mount', 'repo::archive', '/mnt')) + mount_arguments = flexmock( + mount_point='/mnt', options='super_mount', paths=None, foreground=False + ) module.mount_archive( repository_path='repo', archive='archive', - mount_point='/mnt', - paths=None, - foreground=False, - options='super_mount', + mount_arguments=mount_arguments, + storage_config={}, + local_borg_version='1.2.3', + global_arguments=flexmock(log_json=False), + ) + + +def test_mount_archive_with_date_based_matching_calls_borg_with_date_based_flags(): + flexmock(module.flags).should_receive('make_flags').and_return(()) + flexmock(module.flags).should_receive('make_match_archives_flags').and_return(()) + flexmock(module.flags).should_receive('make_flags_from_arguments').and_return( + ( + '--newer', + '1d', + '--newest', + '1y', + '--older', + '1m', + '--oldest', + '1w', + '--match-archives', + None, + ) + ) + flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) + flexmock(module.environment).should_receive('make_environment') + flexmock(module).should_receive('execute_command').with_args( + ( + 'borg', + 'mount', + '--newer', + '1d', + '--newest', + '1y', + '--older', + '1m', + '--oldest', + '1w', + '--match-archives', + None, + '--repo', + 'repo', + '/mnt', + ), + borg_local_path='borg', + extra_environment=None, + ) + + mount_arguments = flexmock( + mount_point='/mnt', + options=None, + paths=None, + foreground=False, + newer='1d', + newest='1y', + older='1m', + oldest='1w', + ) + module.mount_archive( + repository_path='repo', + archive=None, + mount_arguments=mount_arguments, storage_config={}, local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), diff --git a/tests/unit/borg/test_prune.py b/tests/unit/borg/test_prune.py index 9028a0c7..584d0d50 100644 --- a/tests/unit/borg/test_prune.py +++ b/tests/unit/borg/test_prune.py @@ -117,6 +117,7 @@ def test_prune_archives_calls_borg_with_flags(): flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) insert_execute_command_mock(PRUNE_COMMAND + ('repo',), logging.INFO) + prune_arguments = flexmock(stats=False, list_archives=False) module.prune_archives( dry_run=False, repository_path='repo', @@ -124,6 +125,7 @@ def test_prune_archives_calls_borg_with_flags(): retention_config=flexmock(), local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + prune_arguments=prune_arguments, ) @@ -135,6 +137,7 @@ def test_prune_archives_with_log_info_calls_borg_with_info_flag(): insert_execute_command_mock(PRUNE_COMMAND + ('--info', 'repo'), logging.INFO) insert_logging_mock(logging.INFO) + prune_arguments = flexmock(stats=False, list_archives=False) module.prune_archives( repository_path='repo', storage_config={}, @@ -142,6 +145,7 @@ def test_prune_archives_with_log_info_calls_borg_with_info_flag(): retention_config=flexmock(), local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + prune_arguments=prune_arguments, ) @@ -153,6 +157,7 @@ def test_prune_archives_with_log_debug_calls_borg_with_debug_flag(): insert_execute_command_mock(PRUNE_COMMAND + ('--debug', '--show-rc', 'repo'), logging.INFO) insert_logging_mock(logging.DEBUG) + prune_arguments = flexmock(stats=False, list_archives=False) module.prune_archives( repository_path='repo', storage_config={}, @@ -160,6 +165,7 @@ def test_prune_archives_with_log_debug_calls_borg_with_debug_flag(): retention_config=flexmock(), local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + prune_arguments=prune_arguments, ) @@ -170,6 +176,7 @@ def test_prune_archives_with_dry_run_calls_borg_with_dry_run_flag(): flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) insert_execute_command_mock(PRUNE_COMMAND + ('--dry-run', 'repo'), logging.INFO) + prune_arguments = flexmock(stats=False, list_archives=False) module.prune_archives( repository_path='repo', storage_config={}, @@ -177,6 +184,7 @@ def test_prune_archives_with_dry_run_calls_borg_with_dry_run_flag(): retention_config=flexmock(), local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + prune_arguments=prune_arguments, ) @@ -187,6 +195,7 @@ def test_prune_archives_with_local_path_calls_borg_via_local_path(): flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) insert_execute_command_mock(('borg1',) + PRUNE_COMMAND[1:] + ('repo',), logging.INFO) + prune_arguments = flexmock(stats=False, list_archives=False) module.prune_archives( dry_run=False, repository_path='repo', @@ -195,6 +204,7 @@ def test_prune_archives_with_local_path_calls_borg_via_local_path(): local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), local_path='borg1', + prune_arguments=prune_arguments, ) @@ -205,6 +215,7 @@ def test_prune_archives_with_remote_path_calls_borg_with_remote_path_flags(): flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) insert_execute_command_mock(PRUNE_COMMAND + ('--remote-path', 'borg1', 'repo'), logging.INFO) + prune_arguments = flexmock(stats=False, list_archives=False) module.prune_archives( dry_run=False, repository_path='repo', @@ -213,6 +224,7 @@ def test_prune_archives_with_remote_path_calls_borg_with_remote_path_flags(): local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), remote_path='borg1', + prune_arguments=prune_arguments, ) @@ -223,6 +235,7 @@ def test_prune_archives_with_stats_calls_borg_with_stats_flag_and_answer_output_ flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) insert_execute_command_mock(PRUNE_COMMAND + ('--stats', 'repo'), module.borgmatic.logger.ANSWER) + prune_arguments = flexmock(stats=True, list_archives=False) module.prune_archives( dry_run=False, repository_path='repo', @@ -230,7 +243,7 @@ def test_prune_archives_with_stats_calls_borg_with_stats_flag_and_answer_output_ retention_config=flexmock(), local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), - stats=True, + prune_arguments=prune_arguments, ) @@ -241,6 +254,7 @@ def test_prune_archives_with_files_calls_borg_with_list_flag_and_answer_output_l flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) insert_execute_command_mock(PRUNE_COMMAND + ('--list', 'repo'), module.borgmatic.logger.ANSWER) + prune_arguments = flexmock(stats=False, list_archives=True) module.prune_archives( dry_run=False, repository_path='repo', @@ -248,7 +262,7 @@ def test_prune_archives_with_files_calls_borg_with_list_flag_and_answer_output_l retention_config=flexmock(), local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), - list_archives=True, + prune_arguments=prune_arguments, ) @@ -260,6 +274,7 @@ def test_prune_archives_with_umask_calls_borg_with_umask_flags(): flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) insert_execute_command_mock(PRUNE_COMMAND + ('--umask', '077', 'repo'), logging.INFO) + prune_arguments = flexmock(stats=False, list_archives=False) module.prune_archives( dry_run=False, repository_path='repo', @@ -267,6 +282,7 @@ def test_prune_archives_with_umask_calls_borg_with_umask_flags(): retention_config=flexmock(), local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + prune_arguments=prune_arguments, ) @@ -277,6 +293,7 @@ def test_prune_archives_with_log_json_calls_borg_with_log_json_flag(): flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) insert_execute_command_mock(PRUNE_COMMAND + ('--log-json', 'repo'), logging.INFO) + prune_arguments = flexmock(stats=False, list_archives=False) module.prune_archives( dry_run=False, repository_path='repo', @@ -284,6 +301,7 @@ def test_prune_archives_with_log_json_calls_borg_with_log_json_flag(): retention_config=flexmock(), local_borg_version='1.2.3', global_arguments=flexmock(log_json=True), + prune_arguments=prune_arguments, ) @@ -295,6 +313,7 @@ def test_prune_archives_with_lock_wait_calls_borg_with_lock_wait_flags(): flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) insert_execute_command_mock(PRUNE_COMMAND + ('--lock-wait', '5', 'repo'), logging.INFO) + prune_arguments = flexmock(stats=False, list_archives=False) module.prune_archives( dry_run=False, repository_path='repo', @@ -302,6 +321,7 @@ def test_prune_archives_with_lock_wait_calls_borg_with_lock_wait_flags(): retention_config=flexmock(), local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + prune_arguments=prune_arguments, ) @@ -312,6 +332,7 @@ def test_prune_archives_with_extra_borg_options_calls_borg_with_extra_options(): flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) insert_execute_command_mock(PRUNE_COMMAND + ('--extra', '--options', 'repo'), logging.INFO) + prune_arguments = flexmock(stats=False, list_archives=False) module.prune_archives( dry_run=False, repository_path='repo', @@ -319,4 +340,69 @@ def test_prune_archives_with_extra_borg_options_calls_borg_with_extra_options(): retention_config=flexmock(), local_borg_version='1.2.3', global_arguments=flexmock(log_json=False), + prune_arguments=prune_arguments, + ) + + +def test_prune_archives_with_date_based_matching_calls_borg_with_date_based_flags(): + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER + flexmock(module.flags).should_receive('make_flags').and_return(()) + flexmock(module.flags).should_receive('make_match_archives_flags').and_return(()) + flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS) + flexmock(module.flags).should_receive('make_flags_from_arguments').and_return( + ( + '--newer', + '1d', + '--newest', + '1y', + '--older', + '1m', + '--oldest', + '1w', + '--match-archives', + None, + ) + ) + flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) + flexmock(module.environment).should_receive('make_environment') + flexmock(module).should_receive('execute_command').with_args( + ( + 'borg', + 'prune', + '--keep-daily', + '1', + '--keep-weekly', + '2', + '--keep-monthly', + '3', + '--newer', + '1d', + '--newest', + '1y', + '--older', + '1m', + '--oldest', + '1w', + '--match-archives', + None, + '--repo', + 'repo', + ), + output_log_level=logging.INFO, + borg_local_path='borg', + extra_environment=None, + ) + + prune_arguments = flexmock( + stats=False, list_archives=False, newer='1d', newest='1y', older='1m', oldest='1w' + ) + module.prune_archives( + dry_run=False, + repository_path='repo', + storage_config={}, + retention_config=flexmock(), + local_borg_version='1.2.3', + global_arguments=flexmock(log_json=False), + prune_arguments=prune_arguments, ) diff --git a/tests/unit/borg/test_rlist.py b/tests/unit/borg/test_rlist.py index e1e04f8b..76bda987 100644 --- a/tests/unit/borg/test_rlist.py +++ b/tests/unit/borg/test_rlist.py @@ -614,3 +614,46 @@ def test_list_repository_with_json_returns_borg_output(): ) == json_output ) + + +def test_make_rlist_command_with_date_based_matching_calls_borg_with_date_based_flags(): + flexmock(module.flags).should_receive('make_flags').and_return(()) + flexmock(module.flags).should_receive('make_match_archives_flags').with_args( + None, None, '1.2.3' + ).and_return(()) + flexmock(module.flags).should_receive('make_flags_from_arguments').and_return( + ('--newer', '1d', '--newest', '1y', '--older', '1m', '--oldest', '1w') + ) + flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) + + command = module.make_rlist_command( + repository_path='repo', + storage_config={}, + local_borg_version='1.2.3', + rlist_arguments=flexmock( + archive=None, + paths=None, + json=False, + prefix=None, + match_archives=None, + newer='1d', + newest='1y', + older='1m', + oldest='1w', + ), + global_arguments=flexmock(log_json=False), + ) + + assert command == ( + 'borg', + 'list', + '--newer', + '1d', + '--newest', + '1y', + '--older', + '1m', + '--oldest', + '1w', + 'repo', + ) diff --git a/tests/unit/borg/test_transfer.py b/tests/unit/borg/test_transfer.py index e85729ea..6cd9530b 100644 --- a/tests/unit/borg/test_transfer.py +++ b/tests/unit/borg/test_transfer.py @@ -430,3 +430,51 @@ def test_transfer_archives_with_source_repository_calls_borg_with_other_repo_fla ), global_arguments=flexmock(log_json=False), ) + + +def test_transfer_archives_with_date_based_matching_calls_borg_with_date_based_flags(): + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.flags).should_receive('make_flags').and_return(()) + flexmock(module.flags).should_receive('make_match_archives_flags').and_return(()) + flexmock(module.flags).should_receive('make_flags_from_arguments').and_return( + ('--newer', '1d', '--newest', '1y', '--older', '1m', '--oldest', '1w') + ) + flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) + flexmock(module.environment).should_receive('make_environment') + flexmock(module).should_receive('execute_command').with_args( + ( + 'borg', + 'transfer', + '--newer', + '1d', + '--newest', + '1y', + '--older', + '1m', + '--oldest', + '1w', + '--repo', + 'repo', + ), + output_log_level=module.borgmatic.logger.ANSWER, + output_file=None, + borg_local_path='borg', + extra_environment=None, + ) + + module.transfer_archives( + dry_run=False, + repository_path='repo', + storage_config={}, + local_borg_version='2.3.4', + global_arguments=flexmock(log_json=False), + transfer_arguments=flexmock( + archive=None, + progress=None, + source_repository='other', + newer='1d', + newest='1y', + older='1m', + oldest='1w', + ), + )