When using the "bsd_flags" option, tailor the flags passed to Borg depending on the Borg version (#394).

This commit is contained in:
Dan Helfman 2022-02-09 21:11:00 -08:00
parent 4b5af2770d
commit 6bf6ac310b
6 changed files with 65 additions and 16 deletions

3
NEWS
View file

@ -2,7 +2,8 @@
* #394: Compact repository segments and free space with new "borgmatic compact" action. Borg 1.2+ * #394: Compact repository segments and free space with new "borgmatic compact" action. Borg 1.2+
only. Also run "compact" by default when no actions are specified, as "prune" in Borg 1.2 no only. Also run "compact" by default when no actions are specified, as "prune" in Borg 1.2 no
longer frees up space unless "compact" is run. longer frees up space unless "compact" is run.
* #394: When the "atime" option is used, tailor the flags passed to Borg depending on version. * #394: When using the "atime" or "bsd_flags" options, tailor the flags passed to Borg depending on
the Borg version.
* #480, #482: Fix traceback when a YAML validation error occurs. * #480, #482: Fix traceback when a YAML validation error occurs.
1.5.22 1.5.22

View file

@ -227,12 +227,16 @@ def create_archive(
archive_name_format = storage_config.get('archive_name_format', DEFAULT_ARCHIVE_NAME_FORMAT) archive_name_format = storage_config.get('archive_name_format', DEFAULT_ARCHIVE_NAME_FORMAT)
extra_borg_options = storage_config.get('extra_borg_options', {}).get('create', '') extra_borg_options = storage_config.get('extra_borg_options', {}).get('create', '')
atime_feature_available = feature.available(feature.Feature.ATIME, local_borg_version) if feature.available(feature.Feature.ATIME, local_borg_version):
if atime_feature_available:
atime_flags = ('--atime',) if location_config.get('atime') is True else () atime_flags = ('--atime',) if location_config.get('atime') is True else ()
else: else:
atime_flags = ('--noatime',) if location_config.get('atime') is False else () atime_flags = ('--noatime',) if location_config.get('atime') is False else ()
if feature.available(feature.Feature.NOFLAGS, local_borg_version):
noflags_flags = ('--noflags',) if location_config.get('bsd_flags') is False else ()
else:
noflags_flags = ('--nobsdflags',) if location_config.get('bsd_flags') is False else ()
full_command = ( full_command = (
tuple(local_path.split(' ')) tuple(local_path.split(' '))
+ ('create',) + ('create',)
@ -252,7 +256,7 @@ def create_archive(
+ (('--noctime',) if location_config.get('ctime') is False else ()) + (('--noctime',) if location_config.get('ctime') is False else ())
+ (('--nobirthtime',) if location_config.get('birthtime') is False else ()) + (('--nobirthtime',) if location_config.get('birthtime') is False else ())
+ (('--read-special',) if (location_config.get('read_special') or stream_processes) else ()) + (('--read-special',) if (location_config.get('read_special') or stream_processes) else ())
+ (('--nobsdflags',) if location_config.get('bsd_flags') is False else ()) + noflags_flags
+ (('--files-cache', files_cache) if files_cache else ()) + (('--files-cache', files_cache) if files_cache else ())
+ (('--remote-path', remote_path) if remote_path else ()) + (('--remote-path', remote_path) if remote_path else ())
+ (('--umask', str(umask)) if umask else ()) + (('--umask', str(umask)) if umask else ())

View file

@ -6,11 +6,13 @@ from pkg_resources import parse_version
class Feature(Enum): class Feature(Enum):
COMPACT = 1 COMPACT = 1
ATIME = 2 ATIME = 2
NOFLAGS = 3
FEATURE_TO_MINIMUM_BORG_VERSION = { FEATURE_TO_MINIMUM_BORG_VERSION = {
Feature.COMPACT: parse_version('1.2.0a2'), Feature.COMPACT: parse_version('1.2.0a2'), # borg compact
Feature.ATIME: parse_version('1.2.0a7'), Feature.ATIME: parse_version('1.2.0a7'), # borg create --atime
Feature.NOFLAGS: parse_version('1.2.0a8'), # borg create --noflags
} }

View file

@ -111,10 +111,10 @@ properties:
type: string type: string
description: | description: |
Any paths matching these patterns are included/excluded from Any paths matching these patterns are included/excluded from
backups. Globs are expanded. (Tildes are not.) Note that backups. Globs are expanded. (Tildes are not.) See the
Borg considers this option experimental. See the output of output of "borg help patterns" for more details. Quote any
"borg help patterns" for more details. Quote any value if it value if it contains leading punctuation, so it parses
contains leading punctuation, so it parses correctly. correctly.
example: example:
- 'R /' - 'R /'
- '- /home/*/.cache' - '- /home/*/.cache'

View file

@ -38,7 +38,7 @@ for sub_command in prune create check list info; do
| grep -v '^--json$' \ | grep -v '^--json$' \
| grep -v '^--keep-last$' \ | grep -v '^--keep-last$' \
| grep -v '^--list$' \ | grep -v '^--list$' \
| grep -v '^--nobsdflags$' \ | grep -v '^--bsdflags$' \
| grep -v '^--pattern$' \ | grep -v '^--pattern$' \
| grep -v '^--progress$' \ | grep -v '^--progress$' \
| grep -v '^--stats$' \ | grep -v '^--stats$' \

View file

@ -768,11 +768,9 @@ def test_create_archive_with_read_special_calls_borg_with_read_special_parameter
('ctime', False), ('ctime', False),
('birthtime', True), ('birthtime', True),
('birthtime', False), ('birthtime', False),
('bsd_flags', True),
('bsd_flags', False),
), ),
) )
def test_create_archive_with_option_calls_borg_without_corresponding_parameter( def test_create_archive_with_basic_option_calls_borg_with_corresponding_parameter(
option_name, option_value option_name, option_value
): ):
option_flag = '--no' + option_name.replace('_', '') if option_value is False else None option_flag = '--no' + option_name.replace('_', '') if option_value is False else None
@ -815,7 +813,7 @@ def test_create_archive_with_option_calls_borg_without_corresponding_parameter(
(False, False, '--noatime'), (False, False, '--noatime'),
), ),
) )
def test_create_archive_with_atime_option_calls_borg_without_corresponding_parameter( def test_create_archive_with_atime_option_calls_borg_with_corresponding_parameter(
option_value, feature_available, option_flag option_value, feature_available, option_flag
): ):
flexmock(module).should_receive('borgmatic_source_directories').and_return([]) flexmock(module).should_receive('borgmatic_source_directories').and_return([])
@ -824,7 +822,8 @@ def test_create_archive_with_atime_option_calls_borg_without_corresponding_param
flexmock(module).should_receive('_expand_directories').and_return(()) flexmock(module).should_receive('_expand_directories').and_return(())
flexmock(module).should_receive('_expand_home_directories').and_return(()) flexmock(module).should_receive('_expand_home_directories').and_return(())
flexmock(module).should_receive('_write_pattern_file').and_return(None) flexmock(module).should_receive('_write_pattern_file').and_return(None)
flexmock(module.feature).should_receive('available').and_return(feature_available) flexmock(module.feature).should_receive('available').with_args(module.feature.Feature.ATIME, '1.2.3').and_return(feature_available)
flexmock(module.feature).should_receive('available').with_args(module.feature.Feature.NOFLAGS, '1.2.3').and_return(True)
flexmock(module).should_receive('_make_pattern_flags').and_return(()) flexmock(module).should_receive('_make_pattern_flags').and_return(())
flexmock(module).should_receive('_make_exclude_flags').and_return(()) flexmock(module).should_receive('_make_exclude_flags').and_return(())
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
@ -848,6 +847,49 @@ def test_create_archive_with_atime_option_calls_borg_without_corresponding_param
) )
@pytest.mark.parametrize(
'option_value,feature_available,option_flag',
(
(True, True, None),
(True, False, None),
(False, True, '--noflags'),
(False, False, '--nobsdflags'),
),
)
def test_create_archive_with_bsd_flags_option_calls_borg_with_corresponding_parameter(
option_value, feature_available, option_flag
):
flexmock(module).should_receive('borgmatic_source_directories').and_return([])
flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
flexmock(module).should_receive('map_directories_to_devices').and_return({})
flexmock(module).should_receive('_expand_directories').and_return(())
flexmock(module).should_receive('_expand_home_directories').and_return(())
flexmock(module).should_receive('_write_pattern_file').and_return(None)
flexmock(module.feature).should_receive('available').with_args(module.feature.Feature.ATIME, '1.2.3').and_return(True)
flexmock(module.feature).should_receive('available').with_args(module.feature.Feature.NOFLAGS, '1.2.3').and_return(feature_available)
flexmock(module).should_receive('_make_pattern_flags').and_return(())
flexmock(module).should_receive('_make_exclude_flags').and_return(())
flexmock(module).should_receive('execute_command').with_args(
('borg', 'create') + ((option_flag,) if option_flag else ()) + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO,
output_file=None,
borg_local_path='borg',
)
module.create_archive(
dry_run=False,
repository='repo',
location_config={
'source_directories': ['foo', 'bar'],
'repositories': ['repo'],
'bsd_flags': option_value,
'exclude_patterns': None,
},
storage_config={},
local_borg_version='1.2.3',
)
def test_create_archive_with_files_cache_calls_borg_with_files_cache_parameters(): def test_create_archive_with_files_cache_calls_borg_with_files_cache_parameters():
flexmock(module).should_receive('borgmatic_source_directories').and_return([]) flexmock(module).should_receive('borgmatic_source_directories').and_return([])
flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar')) flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))