Add support for Borg 2's "--match-archives" flag (replaces "--glob-archives") (#591).

This commit is contained in:
Dan Helfman 2022-10-03 22:50:37 -07:00
parent ae036aebd7
commit 2774c2e4c0
20 changed files with 251 additions and 190 deletions

2
NEWS
View File

@ -3,6 +3,8 @@
files for a "create" action to prevent Borg from hanging.
* #587: Warn when ignoring a configured "read_special" value of false, as true is needed when
database hooks are enabled.
* #591: Add support for Borg 2's "--match-archives" flag (replaces "--glob-archives").
* Fix for "borgmatic --archive latest" not finding the latest archive when a verbosity is set.
1.7.2
* #577: Fix regression in which "borgmatic info --archive ..." showed repository info instead of

View File

@ -5,7 +5,7 @@ import logging
import os
import pathlib
from borgmatic.borg import environment, extract, flags, rinfo, state
from borgmatic.borg import environment, extract, feature, flags, rinfo, state
from borgmatic.execute import DO_NOT_CAPTURE, execute_command
DEFAULT_CHECKS = (
@ -146,9 +146,10 @@ def filter_checks_on_frequency(
return tuple(filtered_checks)
def make_check_flags(checks, check_last=None, prefix=None):
def make_check_flags(local_borg_version, checks, check_last=None, prefix=None):
'''
Given a parsed sequence of checks, transform it into tuple of command-line flags.
Given the local Borg version and a parsed sequence of checks, transform the checks into tuple of
command-line flags.
For example, given parsed checks of:
@ -163,14 +164,17 @@ def make_check_flags(checks, check_last=None, prefix=None):
Additionally, if a check_last value is given and "archives" is in checks, then include a
"--last" flag. And if a prefix value is given and "archives" is in checks, then include a
"--glob-archives" flag.
"--match-archives" flag.
'''
if 'archives' in checks:
last_flags = ('--last', str(check_last)) if check_last else ()
glob_archives_flags = ('--glob-archives', f'{prefix}*') if prefix else ()
if feature.available(feature.Feature.MATCH_ARCHIVES, local_borg_version):
match_archives_flags = ('--match-archives', f'sh:{prefix}*') if prefix else ()
else:
match_archives_flags = ('--glob-archives', f'{prefix}*') if prefix else ()
else:
last_flags = ()
glob_archives_flags = ()
match_archives_flags = ()
if check_last:
logger.info('Ignoring check_last option, as "archives" is not in consistency checks')
if prefix:
@ -184,7 +188,7 @@ def make_check_flags(checks, check_last=None, prefix=None):
else:
data_flags = ()
common_flags = last_flags + glob_archives_flags + data_flags
common_flags = last_flags + match_archives_flags + data_flags
if {'repository', 'archives'}.issubset(set(checks)):
return common_flags
@ -298,7 +302,7 @@ def check_archives(
full_command = (
(local_path, 'check')
+ (('--repair',) if repair else ())
+ make_check_flags(checks, check_last, prefix)
+ make_check_flags(local_borg_version, checks, check_last, prefix)
+ (('--remote-path', remote_path) if remote_path else ())
+ (('--lock-wait', str(lock_wait)) if lock_wait else ())
+ verbosity_flags

View File

@ -13,6 +13,7 @@ class Feature(Enum):
RCREATE = 7
RLIST = 8
RINFO = 9
MATCH_ARCHIVES = 10
FEATURE_TO_MINIMUM_BORG_VERSION = {
@ -25,6 +26,7 @@ FEATURE_TO_MINIMUM_BORG_VERSION = {
Feature.RCREATE: parse_version('2.0.0a2'), # borg rcreate
Feature.RLIST: parse_version('2.0.0a2'), # borg rlist
Feature.RINFO: parse_version('2.0.0a2'), # borg rinfo
Feature.MATCH_ARCHIVES: parse_version('2.0.0b3'), # borg --match-archives
}

View File

@ -1,6 +1,6 @@
import logging
from borgmatic.borg import environment, flags
from borgmatic.borg import environment, feature, flags
from borgmatic.execute import execute_command
logger = logging.getLogger(__name__)
@ -36,7 +36,11 @@ def display_archives_info(
+ flags.make_flags('remote-path', remote_path)
+ flags.make_flags('lock-wait', lock_wait)
+ (
flags.make_flags('glob-archives', f'{info_arguments.prefix}*')
(
flags.make_flags('match-archives', f'sh:{info_arguments.prefix}*')
if feature.available(feature.Feature.MATCH_ARCHIVES, local_borg_version)
else flags.make_flags('glob-archives', f'{info_arguments.prefix}*')
)
if info_arguments.prefix
else ()
)
@ -44,7 +48,11 @@ def display_archives_info(
info_arguments, excludes=('repository', 'archive', 'prefix')
)
+ flags.make_repository_flags(repository, local_borg_version)
+ flags.make_flags('glob-archives', info_arguments.archive)
+ (
flags.make_flags('match-archives', info_arguments.archive)
if feature.available(feature.Feature.MATCH_ARCHIVES, local_borg_version)
else flags.make_flags('glob-archives', info_arguments.archive)
)
)
return execute_command(

View File

@ -9,7 +9,7 @@ from borgmatic.execute import execute_command
logger = logging.getLogger(__name__)
ARCHIVE_FILTER_FLAGS_MOVED_TO_RLIST = ('prefix', 'glob_archives', 'sort_by', 'first', 'last')
ARCHIVE_FILTER_FLAGS_MOVED_TO_RLIST = ('prefix', 'match_archives', 'sort_by', 'first', 'last')
MAKE_FLAGS_EXCLUDES = (
'repository',
'archive',
@ -111,7 +111,7 @@ def list_archive(
format=list_arguments.format,
json=list_arguments.json,
prefix=list_arguments.prefix,
glob_archives=list_arguments.glob_archives,
match_archives=list_arguments.match_archives,
sort_by=list_arguments.sort_by,
first=list_arguments.first,
last=list_arguments.last,
@ -143,7 +143,7 @@ def list_archive(
format=None,
json=None,
prefix=list_arguments.prefix,
glob_archives=list_arguments.glob_archives,
match_archives=list_arguments.match_archives,
sort_by=list_arguments.sort_by,
first=list_arguments.first,
last=list_arguments.last,

View File

@ -39,7 +39,11 @@ def mount_archive(
+ (
(
flags.make_repository_flags(repository, local_borg_version)
+ ('--glob-archives', archive)
+ (
('--match-archives', archive)
if feature.available(feature.Feature.MATCH_ARCHIVES, local_borg_version)
else ('--glob-archives', archive)
)
)
if feature.available(feature.Feature.SEPARATE_REPOSITORY_ARCHIVE, local_borg_version)
else (

View File

@ -1,12 +1,12 @@
import logging
from borgmatic.borg import environment, flags
from borgmatic.borg import environment, feature, flags
from borgmatic.execute import execute_command
logger = logging.getLogger(__name__)
def make_prune_flags(retention_config):
def make_prune_flags(retention_config, local_borg_version):
'''
Given a retention config dict mapping from option name to value, tranform it into an iterable of
command-line name-value flag pairs.
@ -24,8 +24,12 @@ def make_prune_flags(retention_config):
'''
config = retention_config.copy()
prefix = config.pop('prefix', '{hostname}-')
if prefix:
config['glob_archives'] = f'{prefix}*'
if feature.available(feature.Feature.MATCH_ARCHIVES, local_borg_version):
config['match_archives'] = f'sh:{prefix}*'
else:
config['glob_archives'] = f'{prefix}*'
return (
('--' + option_name.replace('_', '-'), str(value)) for option_name, value in config.items()
@ -54,7 +58,11 @@ def prune_archives(
full_command = (
(local_path, 'prune')
+ tuple(element for pair in make_prune_flags(retention_config) for element in pair)
+ tuple(
element
for pair in make_prune_flags(retention_config, local_borg_version)
for element in pair
)
+ (('--remote-path', remote_path) if remote_path else ())
+ (('--umask', str(umask)) if umask else ())
+ (('--lock-wait', str(lock_wait)) if lock_wait else ())

View File

@ -26,8 +26,6 @@ def resolve_archive_name(
local_path,
'rlist' if feature.available(feature.Feature.RLIST, local_borg_version) else 'list',
)
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
+ flags.make_flags('remote-path', remote_path)
+ flags.make_flags('lock-wait', lock_wait)
+ flags.make_flags('last', 1)
@ -87,7 +85,11 @@ def make_rlist_command(
+ flags.make_flags('remote-path', remote_path)
+ flags.make_flags('lock-wait', lock_wait)
+ (
flags.make_flags('glob-archives', f'{rlist_arguments.prefix}*')
(
flags.make_flags('match-archives', f'sh:{rlist_arguments.prefix}*')
if feature.available(feature.Feature.MATCH_ARCHIVES, local_borg_version)
else flags.make_flags('glob-archives', f'{rlist_arguments.prefix}*')
)
if rlist_arguments.prefix
else ()
)

View File

@ -25,12 +25,14 @@ def transfer_archives(
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
+ flags.make_flags('remote-path', remote_path)
+ flags.make_flags('lock-wait', storage_config.get('lock_wait', None))
+ flags.make_flags(
'glob-archives', transfer_arguments.glob_archives or transfer_arguments.archive
+ (
flags.make_flags(
'match-archives', transfer_arguments.match_archives or transfer_arguments.archive
)
)
+ flags.make_flags_from_arguments(
transfer_arguments,
excludes=('repository', 'source_repository', 'archive', 'glob_archives'),
excludes=('repository', 'source_repository', 'archive', 'match_archives'),
)
+ flags.make_repository_flags(repository, local_borg_version)
+ flags.make_flags('other-repo', transfer_arguments.source_repository)

View File

@ -293,9 +293,10 @@ def make_parsers():
)
transfer_group.add_argument(
'-a',
'--match-archives',
'--glob-archives',
metavar='GLOB',
help='Only transfer archives with names matching this glob',
metavar='PATTERN',
help='Only transfer archives with names matching this pattern',
)
transfer_group.add_argument(
'--sort-by', metavar='KEYS', help='Comma-separated list of sorting keys'
@ -627,7 +628,11 @@ def make_parsers():
'-P', '--prefix', help='Only list archive names starting with this prefix'
)
rlist_group.add_argument(
'-a', '--glob-archives', metavar='GLOB', help='Only list archive names matching this glob'
'-a',
'--match-archives',
'--glob-archives',
metavar='PATTERN',
help='Only list archive names matching this pattern',
)
rlist_group.add_argument(
'--sort-by', metavar='KEYS', help='Comma-separated list of sorting keys'
@ -678,7 +683,11 @@ def make_parsers():
'-P', '--prefix', help='Only list archive names starting with this prefix'
)
list_group.add_argument(
'-a', '--glob-archives', metavar='GLOB', help='Only list archive names matching this glob'
'-a',
'--match-archives',
'--glob-archives',
metavar='PATTERN',
help='Only list archive names matching this pattern',
)
list_group.add_argument(
'--successful',
@ -747,9 +756,10 @@ def make_parsers():
)
info_group.add_argument(
'-a',
'--match-archives',
'--glob-archives',
metavar='GLOB',
help='Only show info for archive names matching this glob',
metavar='PATTERN',
help='Only show info for archive names matching this pattern',
)
info_group.add_argument(
'--sort-by', metavar='KEYS', help='Comma-separated list of sorting keys'
@ -816,7 +826,7 @@ def parse_arguments(*unparsed_arguments):
if (
'transfer' in arguments
and arguments['transfer'].archive
and arguments['transfer'].glob_archives
and arguments['transfer'].match_archives
):
raise ValueError(
'With the transfer action, only one of --archive and --glob-archives flags can be used.'
@ -824,11 +834,11 @@ def parse_arguments(*unparsed_arguments):
if 'info' in arguments and (
(arguments['info'].archive and arguments['info'].prefix)
or (arguments['info'].archive and arguments['info'].glob_archives)
or (arguments['info'].prefix and arguments['info'].glob_archives)
or (arguments['info'].archive and arguments['info'].match_archives)
or (arguments['info'].prefix and arguments['info'].match_archives)
):
raise ValueError(
'With the info action, only one of --archive, --prefix, or --glob-archives flags can be used.'
'With the info action, only one of --archive, --prefix, or --match-archives flags can be used.'
)
return arguments

View File

@ -84,7 +84,7 @@ be a [Borg
pattern](https://borgbackup.readthedocs.io/en/stable/usage/help.html#borg-patterns).
To limit the archives searched, use the standard `list` parameters for
filtering archives such as `--last`, `--archive`, `--glob-archives`, etc. For
filtering archives such as `--last`, `--archive`, `--match-archives`, etc. For
example, to search only the last five archives:
```bash

View File

@ -53,6 +53,7 @@ for sub_command in prune create check list info; do
| grep -v '^--first' \
| grep -v '^--format' \
| grep -v '^--glob-archives' \
| grep -v '^--match-archives' \
| grep -v '^--last' \
| grep -v '^--format' \
| grep -v '^--patterns-from' \

View File

@ -450,7 +450,7 @@ def test_parse_arguments_disallows_json_with_both_rinfo_and_info():
module.parse_arguments('rinfo', 'info', '--json')
def test_parse_arguments_disallows_transfer_with_both_archive_and_glob_archives():
def test_parse_arguments_disallows_transfer_with_both_archive_and_match_archives():
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
with pytest.raises(ValueError):
@ -460,16 +460,16 @@ def test_parse_arguments_disallows_transfer_with_both_archive_and_glob_archives(
'source.borg',
'--archive',
'foo',
'--glob-archives',
'*bar',
'--match-archives',
'sh:*bar',
)
def test_parse_arguments_disallows_info_with_both_archive_and_glob_archives():
def test_parse_arguments_disallows_info_with_both_archive_and_match_archives():
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
with pytest.raises(ValueError):
module.parse_arguments('info', '--archive', 'foo', '--glob-archives', '*bar')
module.parse_arguments('info', '--archive', 'foo', '--match-archives', 'sh:*bar')
def test_parse_arguments_disallows_info_with_both_archive_and_prefix():
@ -479,11 +479,11 @@ def test_parse_arguments_disallows_info_with_both_archive_and_prefix():
module.parse_arguments('info', '--archive', 'foo', '--prefix', 'bar')
def test_parse_arguments_disallows_info_with_both_prefix_and_glob_archives():
def test_parse_arguments_disallows_info_with_both_prefix_and_match_archives():
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
with pytest.raises(ValueError):
module.parse_arguments('info', '--prefix', 'foo', '--glob-archives', '*bar')
module.parse_arguments('info', '--prefix', 'foo', '--match-archives', 'sh:*bar')
def test_parse_arguments_check_only_extract_does_not_raise_extract_subparser_error():

View File

@ -188,95 +188,137 @@ def test_filter_checks_on_frequency_restains_check_with_unelapsed_frequency_and_
def test_make_check_flags_with_repository_check_returns_flag():
flags = module.make_check_flags(('repository',))
flexmock(module.feature).should_receive('available').and_return(True)
flags = module.make_check_flags('1.2.3', ('repository',))
assert flags == ('--repository-only',)
def test_make_check_flags_with_archives_check_returns_flag():
flags = module.make_check_flags(('archives',))
flexmock(module.feature).should_receive('available').and_return(True)
flags = module.make_check_flags('1.2.3', ('archives',))
assert flags == ('--archives-only',)
def test_make_check_flags_with_data_check_returns_flag_and_implies_archives():
flags = module.make_check_flags(('data',))
flexmock(module.feature).should_receive('available').and_return(True)
flags = module.make_check_flags('1.2.3', ('data',))
assert flags == ('--archives-only', '--verify-data',)
def test_make_check_flags_with_extract_omits_extract_flag():
flags = module.make_check_flags(('extract',))
flexmock(module.feature).should_receive('available').and_return(True)
flags = module.make_check_flags('1.2.3', ('extract',))
assert flags == ()
def test_make_check_flags_with_repository_and_data_checks_does_not_return_repository_only():
flags = module.make_check_flags(('repository', 'data',))
flexmock(module.feature).should_receive('available').and_return(True)
flags = module.make_check_flags('1.2.3', ('repository', 'data',))
assert flags == ('--verify-data',)
def test_make_check_flags_with_default_checks_and_default_prefix_returns_default_flags():
flags = module.make_check_flags(('repository', 'archives'), prefix=module.DEFAULT_PREFIX)
flexmock(module.feature).should_receive('available').and_return(True)
assert flags == ('--glob-archives', f'{module.DEFAULT_PREFIX}*')
flags = module.make_check_flags(
'1.2.3', ('repository', 'archives'), prefix=module.DEFAULT_PREFIX
)
assert flags == ('--match-archives', f'sh:{module.DEFAULT_PREFIX}*')
def test_make_check_flags_with_all_checks_and_default_prefix_returns_default_flags():
flexmock(module.feature).should_receive('available').and_return(True)
flags = module.make_check_flags(
('repository', 'archives', 'extract'), prefix=module.DEFAULT_PREFIX
'1.2.3', ('repository', 'archives', 'extract'), prefix=module.DEFAULT_PREFIX
)
assert flags == ('--match-archives', f'sh:{module.DEFAULT_PREFIX}*')
def test_make_check_flags_with_all_checks_and_default_prefix_without_borg_features_returns_glob_archives_flags():
flexmock(module.feature).should_receive('available').and_return(False)
flags = module.make_check_flags(
'1.2.3', ('repository', 'archives', 'extract'), prefix=module.DEFAULT_PREFIX
)
assert flags == ('--glob-archives', f'{module.DEFAULT_PREFIX}*')
def test_make_check_flags_with_archives_check_and_last_includes_last_flag():
flags = module.make_check_flags(('archives',), check_last=3)
flexmock(module.feature).should_receive('available').and_return(True)
flags = module.make_check_flags('1.2.3', ('archives',), check_last=3)
assert flags == ('--archives-only', '--last', '3')
def test_make_check_flags_with_repository_check_and_last_omits_last_flag():
flags = module.make_check_flags(('repository',), check_last=3)
flexmock(module.feature).should_receive('available').and_return(True)
flags = module.make_check_flags('1.2.3', ('repository',), check_last=3)
assert flags == ('--repository-only',)
def test_make_check_flags_with_default_checks_and_last_includes_last_flag():
flags = module.make_check_flags(('repository', 'archives'), check_last=3)
flexmock(module.feature).should_receive('available').and_return(True)
flags = module.make_check_flags('1.2.3', ('repository', 'archives'), check_last=3)
assert flags == ('--last', '3')
def test_make_check_flags_with_archives_check_and_prefix_includes_glob_archives_flag():
flags = module.make_check_flags(('archives',), prefix='foo-')
def test_make_check_flags_with_archives_check_and_prefix_includes_match_archives_flag():
flexmock(module.feature).should_receive('available').and_return(True)
assert flags == ('--archives-only', '--glob-archives', 'foo-*')
flags = module.make_check_flags('1.2.3', ('archives',), prefix='foo-')
assert flags == ('--archives-only', '--match-archives', 'sh:foo-*')
def test_make_check_flags_with_archives_check_and_empty_prefix_omits_glob_archives_flag():
flags = module.make_check_flags(('archives',), prefix='')
def test_make_check_flags_with_archives_check_and_empty_prefix_omits_match_archives_flag():
flexmock(module.feature).should_receive('available').and_return(True)
flags = module.make_check_flags('1.2.3', ('archives',), prefix='')
assert flags == ('--archives-only',)
def test_make_check_flags_with_archives_check_and_none_prefix_omits_glob_archives_flag():
flags = module.make_check_flags(('archives',), prefix=None)
def test_make_check_flags_with_archives_check_and_none_prefix_omits_match_archives_flag():
flexmock(module.feature).should_receive('available').and_return(True)
flags = module.make_check_flags('1.2.3', ('archives',), prefix=None)
assert flags == ('--archives-only',)
def test_make_check_flags_with_repository_check_and_prefix_omits_glob_archives_flag():
flags = module.make_check_flags(('repository',), prefix='foo-')
def test_make_check_flags_with_repository_check_and_prefix_omits_match_archives_flag():
flexmock(module.feature).should_receive('available').and_return(True)
flags = module.make_check_flags('1.2.3', ('repository',), prefix='foo-')
assert flags == ('--repository-only',)
def test_make_check_flags_with_default_checks_and_prefix_includes_glob_archives_flag():
flags = module.make_check_flags(('repository', 'archives'), prefix='foo-')
def test_make_check_flags_with_default_checks_and_prefix_includes_match_archives_flag():
flexmock(module.feature).should_receive('available').and_return(True)
assert flags == ('--glob-archives', 'foo-*')
flags = module.make_check_flags('1.2.3', ('repository', 'archives'), prefix='foo-')
assert flags == ('--match-archives', 'sh:foo-*')
def test_read_check_time_does_not_raise():
@ -369,7 +411,7 @@ def test_check_archives_calls_borg_with_parameters(checks):
'{"repository": {"id": "repo"}}'
)
flexmock(module).should_receive('make_check_flags').with_args(
checks, check_last, module.DEFAULT_PREFIX
'1.2.3', checks, check_last, module.DEFAULT_PREFIX
).and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(('borg', 'check', 'repo'))
@ -523,7 +565,7 @@ def test_check_archives_with_local_path_calls_borg_via_local_path():
'{"repository": {"id": "repo"}}'
)
flexmock(module).should_receive('make_check_flags').with_args(
checks, check_last, module.DEFAULT_PREFIX
'1.2.3', checks, check_last, module.DEFAULT_PREFIX
).and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(('borg1', 'check', 'repo'))
@ -550,7 +592,7 @@ def test_check_archives_with_remote_path_calls_borg_with_remote_path_parameters(
'{"repository": {"id": "repo"}}'
)
flexmock(module).should_receive('make_check_flags').with_args(
checks, check_last, module.DEFAULT_PREFIX
'1.2.3', checks, check_last, module.DEFAULT_PREFIX
).and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(('borg', 'check', '--remote-path', 'borg1', 'repo'))
@ -577,7 +619,7 @@ def test_check_archives_with_lock_wait_calls_borg_with_lock_wait_parameters():
'{"repository": {"id": "repo"}}'
)
flexmock(module).should_receive('make_check_flags').with_args(
checks, check_last, module.DEFAULT_PREFIX
'1.2.3', checks, check_last, module.DEFAULT_PREFIX
).and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(('borg', 'check', '--lock-wait', '5', 'repo'))
@ -604,7 +646,7 @@ def test_check_archives_with_retention_prefix():
'{"repository": {"id": "repo"}}'
)
flexmock(module).should_receive('make_check_flags').with_args(
checks, check_last, prefix
'1.2.3', checks, check_last, prefix
).and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(('borg', 'check', 'repo'))

View File

@ -137,16 +137,16 @@ def test_display_archives_info_with_json_calls_borg_with_json_parameter():
assert json_output == '[]'
def test_display_archives_info_with_archive_calls_borg_with_glob_archives_parameter():
def test_display_archives_info_with_archive_calls_borg_with_match_archives_parameter():
flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_flags').with_args(
'glob-archives', 'archive'
).and_return(('--glob-archives', 'archive'))
'match-archives', 'archive'
).and_return(('--match-archives', 'archive'))
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
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', '--repo', 'repo', '--glob-archives', 'archive'),
('borg', 'info', '--repo', 'repo', '--match-archives', 'archive'),
output_log_level=logging.WARNING,
borg_local_path='borg',
extra_environment=None,
@ -229,16 +229,16 @@ def test_display_archives_info_with_lock_wait_calls_borg_with_lock_wait_paramete
)
def test_display_archives_info_with_prefix_calls_borg_with_glob_archives_parameters():
def test_display_archives_info_with_prefix_calls_borg_with_match_archives_parameters():
flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_flags').with_args(
'glob-archives', 'foo*'
).and_return(('--glob-archives', 'foo*'))
'match-archives', 'sh:foo*'
).and_return(('--match-archives', 'sh:foo*'))
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
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', '--glob-archives', 'foo*', '--repo', 'repo'),
('borg', 'info', '--match-archives', 'sh:foo*', '--repo', 'repo'),
output_log_level=logging.WARNING,
borg_local_path='borg',
extra_environment=None,
@ -252,7 +252,7 @@ def test_display_archives_info_with_prefix_calls_borg_with_glob_archives_paramet
)
@pytest.mark.parametrize('argument_name', ('glob_archives', 'sort_by', 'first', 'last'))
@pytest.mark.parametrize('argument_name', ('match_archives', 'sort_by', 'first', 'last'))
def test_display_archives_info_passes_through_arguments_to_borg(argument_name):
flag_name = f"--{argument_name.replace('_', ' ')}"
flexmock(module.flags).should_receive('make_flags').and_return(())

View File

@ -192,7 +192,7 @@ def test_make_list_command_includes_short():
'argument_name',
(
'prefix',
'glob_archives',
'match_archives',
'sort_by',
'first',
'last',
@ -260,7 +260,7 @@ def test_list_archive_calls_borg_with_parameters():
json=False,
find_paths=None,
prefix=None,
glob_archives=None,
match_archives=None,
sort_by=None,
first=None,
last=None,
@ -313,7 +313,7 @@ def test_list_archive_calls_borg_with_local_path():
json=False,
find_paths=None,
prefix=None,
glob_archives=None,
match_archives=None,
sort_by=None,
first=None,
last=None,
@ -353,7 +353,7 @@ def test_list_archive_calls_borg_multiple_times_with_find_paths():
json=False,
find_paths=['foo.txt'],
prefix=None,
glob_archives=None,
match_archives=None,
sort_by=None,
first=None,
last=None,
@ -400,7 +400,7 @@ def test_list_archive_calls_borg_with_archive():
json=False,
find_paths=None,
prefix=None,
glob_archives=None,
match_archives=None,
sort_by=None,
first=None,
last=None,
@ -439,7 +439,7 @@ def test_list_archive_without_archive_delegates_to_list_repository():
format=None,
json=None,
prefix=None,
glob_archives=None,
match_archives=None,
sort_by=None,
first=None,
last=None,
@ -466,7 +466,7 @@ def test_list_archive_with_borg_features_without_archive_delegates_to_list_repos
format=None,
json=None,
prefix=None,
glob_archives=None,
match_archives=None,
sort_by=None,
first=None,
last=None,
@ -487,12 +487,12 @@ def test_list_archive_with_borg_features_without_archive_delegates_to_list_repos
@pytest.mark.parametrize(
'archive_filter_flag', ('prefix', 'glob_archives', 'sort_by', 'first', 'last',),
'archive_filter_flag', ('prefix', 'match_archives', 'sort_by', 'first', 'last',),
)
def test_list_archive_with_archive_ignores_archive_filter_flag(archive_filter_flag,):
default_filter_flags = {
'prefix': None,
'glob_archives': None,
'match_archives': None,
'sort_by': None,
'first': None,
'last': None,
@ -532,14 +532,14 @@ def test_list_archive_with_archive_ignores_archive_filter_flag(archive_filter_fl
@pytest.mark.parametrize(
'archive_filter_flag', ('prefix', 'glob_archives', 'sort_by', 'first', 'last',),
'archive_filter_flag', ('prefix', 'match_archives', 'sort_by', 'first', 'last',),
)
def test_list_archive_with_find_paths_allows_archive_filter_flag_but_only_passes_it_to_rlist(
archive_filter_flag,
):
default_filter_flags = {
'prefix': None,
'glob_archives': None,
'match_archives': None,
'sort_by': None,
'first': None,
'last': None,

View File

@ -31,11 +31,11 @@ def test_mount_archive_calls_borg_with_required_flags():
)
def test_mount_archive_with_borg_features_calls_borg_with_repository_and_glob_archives_flags():
def test_mount_archive_with_borg_features_calls_borg_with_repository_and_match_archives_flags():
flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo',))
insert_execute_command_mock(
('borg', 'mount', '--repo', 'repo', '--glob-archives', 'archive', '/mnt')
('borg', 'mount', '--repo', 'repo', '--match-archives', 'archive', '/mnt')
)
module.mount_archive(

View File

@ -23,16 +23,29 @@ BASE_PRUNE_FLAGS = (('--keep-daily', '1'), ('--keep-weekly', '2'), ('--keep-mont
def test_make_prune_flags_returns_flags_from_config_plus_default_prefix_glob():
retention_config = OrderedDict((('keep_daily', 1), ('keep_weekly', 2), ('keep_monthly', 3)))
flexmock(module.feature).should_receive('available').and_return(True)
result = module.make_prune_flags(retention_config)
result = module.make_prune_flags(retention_config, local_borg_version='1.2.3')
assert tuple(result) == BASE_PRUNE_FLAGS + (('--glob-archives', '{hostname}-*'),)
assert tuple(result) == BASE_PRUNE_FLAGS + (('--match-archives', 'sh:{hostname}-*'),)
def test_make_prune_flags_accepts_prefix_with_placeholders():
retention_config = OrderedDict((('keep_daily', 1), ('prefix', 'Documents_{hostname}-{now}')))
flexmock(module.feature).should_receive('available').and_return(True)
result = module.make_prune_flags(retention_config)
result = module.make_prune_flags(retention_config, local_borg_version='1.2.3')
expected = (('--keep-daily', '1'), ('--match-archives', 'sh:Documents_{hostname}-{now}*'))
assert tuple(result) == expected
def test_make_prune_flags_with_prefix_without_borg_features_uses_glob_archives():
retention_config = OrderedDict((('keep_daily', 1), ('prefix', 'Documents_{hostname}-{now}')))
flexmock(module.feature).should_receive('available').and_return(False)
result = module.make_prune_flags(retention_config, local_borg_version='1.2.3')
expected = (('--keep-daily', '1'), ('--glob-archives', 'Documents_{hostname}-{now}*'))
@ -41,8 +54,9 @@ def test_make_prune_flags_accepts_prefix_with_placeholders():
def test_make_prune_flags_treats_empty_prefix_as_no_prefix():
retention_config = OrderedDict((('keep_daily', 1), ('prefix', '')))
flexmock(module.feature).should_receive('available').and_return(True)
result = module.make_prune_flags(retention_config)
result = module.make_prune_flags(retention_config, local_borg_version='1.2.3')
expected = (('--keep-daily', '1'),)
@ -51,8 +65,9 @@ def test_make_prune_flags_treats_empty_prefix_as_no_prefix():
def test_make_prune_flags_treats_none_prefix_as_no_prefix():
retention_config = OrderedDict((('keep_daily', 1), ('prefix', None)))
flexmock(module.feature).should_receive('available').and_return(True)
result = module.make_prune_flags(retention_config)
result = module.make_prune_flags(retention_config, local_borg_version='1.2.3')
expected = (('--keep-daily', '1'),)
@ -63,10 +78,7 @@ PRUNE_COMMAND = ('borg', 'prune', '--keep-daily', '1', '--keep-weekly', '2', '--
def test_prune_archives_calls_borg_with_parameters():
retention_config = flexmock()
flexmock(module).should_receive('make_prune_flags').with_args(retention_config).and_return(
BASE_PRUNE_FLAGS
)
flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(PRUNE_COMMAND + ('repo',), logging.INFO)
@ -74,16 +86,13 @@ def test_prune_archives_calls_borg_with_parameters():
dry_run=False,
repository='repo',
storage_config={},
retention_config=retention_config,
retention_config=flexmock(),
local_borg_version='1.2.3',
)
def test_prune_archives_with_log_info_calls_borg_with_info_parameter():
retention_config = flexmock()
flexmock(module).should_receive('make_prune_flags').with_args(retention_config).and_return(
BASE_PRUNE_FLAGS
)
flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(PRUNE_COMMAND + ('--info', 'repo'), logging.INFO)
insert_logging_mock(logging.INFO)
@ -92,16 +101,13 @@ def test_prune_archives_with_log_info_calls_borg_with_info_parameter():
repository='repo',
storage_config={},
dry_run=False,
retention_config=retention_config,
retention_config=flexmock(),
local_borg_version='1.2.3',
)
def test_prune_archives_with_log_debug_calls_borg_with_debug_parameter():
retention_config = flexmock()
flexmock(module).should_receive('make_prune_flags').with_args(retention_config).and_return(
BASE_PRUNE_FLAGS
)
flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(PRUNE_COMMAND + ('--debug', '--show-rc', 'repo'), logging.INFO)
insert_logging_mock(logging.DEBUG)
@ -110,16 +116,13 @@ def test_prune_archives_with_log_debug_calls_borg_with_debug_parameter():
repository='repo',
storage_config={},
dry_run=False,
retention_config=retention_config,
retention_config=flexmock(),
local_borg_version='1.2.3',
)
def test_prune_archives_with_dry_run_calls_borg_with_dry_run_parameter():
retention_config = flexmock()
flexmock(module).should_receive('make_prune_flags').with_args(retention_config).and_return(
BASE_PRUNE_FLAGS
)
flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(PRUNE_COMMAND + ('--dry-run', 'repo'), logging.INFO)
@ -127,16 +130,13 @@ def test_prune_archives_with_dry_run_calls_borg_with_dry_run_parameter():
repository='repo',
storage_config={},
dry_run=True,
retention_config=retention_config,
retention_config=flexmock(),
local_borg_version='1.2.3',
)
def test_prune_archives_with_local_path_calls_borg_via_local_path():
retention_config = flexmock()
flexmock(module).should_receive('make_prune_flags').with_args(retention_config).and_return(
BASE_PRUNE_FLAGS
)
flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(('borg1',) + PRUNE_COMMAND[1:] + ('repo',), logging.INFO)
@ -144,17 +144,14 @@ def test_prune_archives_with_local_path_calls_borg_via_local_path():
dry_run=False,
repository='repo',
storage_config={},
retention_config=retention_config,
retention_config=flexmock(),
local_borg_version='1.2.3',
local_path='borg1',
)
def test_prune_archives_with_remote_path_calls_borg_with_remote_path_parameters():
retention_config = flexmock()
flexmock(module).should_receive('make_prune_flags').with_args(retention_config).and_return(
BASE_PRUNE_FLAGS
)
flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(PRUNE_COMMAND + ('--remote-path', 'borg1', 'repo'), logging.INFO)
@ -162,17 +159,14 @@ def test_prune_archives_with_remote_path_calls_borg_with_remote_path_parameters(
dry_run=False,
repository='repo',
storage_config={},
retention_config=retention_config,
retention_config=flexmock(),
local_borg_version='1.2.3',
remote_path='borg1',
)
def test_prune_archives_with_stats_calls_borg_with_stats_parameter_and_warning_output_log_level():
retention_config = flexmock()
flexmock(module).should_receive('make_prune_flags').with_args(retention_config).and_return(
BASE_PRUNE_FLAGS
)
flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(PRUNE_COMMAND + ('--stats', 'repo'), logging.WARNING)
@ -180,17 +174,14 @@ def test_prune_archives_with_stats_calls_borg_with_stats_parameter_and_warning_o
dry_run=False,
repository='repo',
storage_config={},
retention_config=retention_config,
retention_config=flexmock(),
local_borg_version='1.2.3',
stats=True,
)
def test_prune_archives_with_stats_and_log_info_calls_borg_with_stats_parameter_and_info_output_log_level():
retention_config = flexmock()
flexmock(module).should_receive('make_prune_flags').with_args(retention_config).and_return(
BASE_PRUNE_FLAGS
)
flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_logging_mock(logging.INFO)
insert_execute_command_mock(PRUNE_COMMAND + ('--stats', '--info', 'repo'), logging.INFO)
@ -199,17 +190,14 @@ def test_prune_archives_with_stats_and_log_info_calls_borg_with_stats_parameter_
dry_run=False,
repository='repo',
storage_config={},
retention_config=retention_config,
retention_config=flexmock(),
local_borg_version='1.2.3',
stats=True,
)
def test_prune_archives_with_files_calls_borg_with_list_parameter_and_warning_output_log_level():
retention_config = flexmock()
flexmock(module).should_receive('make_prune_flags').with_args(retention_config).and_return(
BASE_PRUNE_FLAGS
)
flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(PRUNE_COMMAND + ('--list', 'repo'), logging.WARNING)
@ -217,17 +205,14 @@ def test_prune_archives_with_files_calls_borg_with_list_parameter_and_warning_ou
dry_run=False,
repository='repo',
storage_config={},
retention_config=retention_config,
retention_config=flexmock(),
local_borg_version='1.2.3',
list_archives=True,
)
def test_prune_archives_with_files_and_log_info_calls_borg_with_list_parameter_and_info_output_log_level():
retention_config = flexmock()
flexmock(module).should_receive('make_prune_flags').with_args(retention_config).and_return(
BASE_PRUNE_FLAGS
)
flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_logging_mock(logging.INFO)
insert_execute_command_mock(PRUNE_COMMAND + ('--info', '--list', 'repo'), logging.INFO)
@ -236,7 +221,7 @@ def test_prune_archives_with_files_and_log_info_calls_borg_with_list_parameter_a
dry_run=False,
repository='repo',
storage_config={},
retention_config=retention_config,
retention_config=flexmock(),
local_borg_version='1.2.3',
list_archives=True,
)
@ -244,10 +229,7 @@ def test_prune_archives_with_files_and_log_info_calls_borg_with_list_parameter_a
def test_prune_archives_with_umask_calls_borg_with_umask_parameters():
storage_config = {'umask': '077'}
retention_config = flexmock()
flexmock(module).should_receive('make_prune_flags').with_args(retention_config).and_return(
BASE_PRUNE_FLAGS
)
flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(PRUNE_COMMAND + ('--umask', '077', 'repo'), logging.INFO)
@ -255,17 +237,14 @@ def test_prune_archives_with_umask_calls_borg_with_umask_parameters():
dry_run=False,
repository='repo',
storage_config=storage_config,
retention_config=retention_config,
retention_config=flexmock(),
local_borg_version='1.2.3',
)
def test_prune_archives_with_lock_wait_calls_borg_with_lock_wait_parameters():
storage_config = {'lock_wait': 5}
retention_config = flexmock()
flexmock(module).should_receive('make_prune_flags').with_args(retention_config).and_return(
BASE_PRUNE_FLAGS
)
flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(PRUNE_COMMAND + ('--lock-wait', '5', 'repo'), logging.INFO)
@ -273,16 +252,13 @@ def test_prune_archives_with_lock_wait_calls_borg_with_lock_wait_parameters():
dry_run=False,
repository='repo',
storage_config=storage_config,
retention_config=retention_config,
retention_config=flexmock(),
local_borg_version='1.2.3',
)
def test_prune_archives_with_extra_borg_options_calls_borg_with_extra_options():
retention_config = flexmock()
flexmock(module).should_receive('make_prune_flags').with_args(retention_config).and_return(
BASE_PRUNE_FLAGS
)
flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(PRUNE_COMMAND + ('--extra', '--options', 'repo'), logging.INFO)
@ -290,6 +266,6 @@ def test_prune_archives_with_extra_borg_options_calls_borg_with_extra_options():
dry_run=False,
repository='repo',
storage_config={'extra_borg_options': {'prune': '--extra --options'}},
retention_config=retention_config,
retention_config=flexmock(),
local_borg_version='1.2.3',
)

View File

@ -41,11 +41,11 @@ def test_resolve_archive_name_calls_borg_with_parameters():
)
def test_resolve_archive_name_with_log_info_calls_borg_with_info_parameter():
def test_resolve_archive_name_with_log_info_calls_borg_without_info_parameter():
expected_archive = 'archive-name'
flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').with_args(
('borg', 'list', '--info') + BORG_LIST_LATEST_ARGUMENTS,
('borg', 'list') + BORG_LIST_LATEST_ARGUMENTS,
output_log_level=None,
borg_local_path='borg',
extra_environment=None,
@ -58,11 +58,11 @@ def test_resolve_archive_name_with_log_info_calls_borg_with_info_parameter():
)
def test_resolve_archive_name_with_log_debug_calls_borg_with_debug_parameter():
def test_resolve_archive_name_with_log_debug_calls_borg_without_debug_parameter():
expected_archive = 'archive-name'
flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').with_args(
('borg', 'list', '--debug', '--show-rc') + BORG_LIST_LATEST_ARGUMENTS,
('borg', 'list') + BORG_LIST_LATEST_ARGUMENTS,
output_log_level=None,
borg_local_path='borg',
extra_environment=None,
@ -273,9 +273,9 @@ def test_make_rlist_command_includes_remote_path():
assert command == ('borg', 'list', '--remote-path', 'borg2', 'repo')
def test_make_rlist_command_transforms_prefix_into_glob_archives():
def test_make_rlist_command_transforms_prefix_into_match_archives():
flexmock(module.flags).should_receive('make_flags').and_return(()).and_return(()).and_return(
('--glob-archives', 'foo*')
('--match-archives', 'sh:foo*')
)
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
@ -287,7 +287,7 @@ def test_make_rlist_command_transforms_prefix_into_glob_archives():
rlist_arguments=flexmock(archive=None, paths=None, json=False, prefix='foo'),
)
assert command == ('borg', 'list', '--glob-archives', 'foo*', 'repo')
assert command == ('borg', 'list', '--match-archives', 'sh:foo*', 'repo')
def test_make_rlist_command_includes_short():
@ -308,7 +308,7 @@ def test_make_rlist_command_includes_short():
@pytest.mark.parametrize(
'argument_name',