When using the "numeric_owner" option with the "extract" action, tailor the flags passed to Borg depending on the Borg version (#394).

This commit is contained in:
Dan Helfman 2022-02-10 10:09:18 -08:00
parent 7e7209322a
commit 948c86f62c
4 changed files with 53 additions and 7 deletions

View file

@ -2,6 +2,7 @@ import logging
import os
import subprocess
from borgmatic.borg import feature
from borgmatic.execute import DO_NOT_CAPTURE, execute_command
logger = logging.getLogger(__name__)
@ -61,6 +62,7 @@ def extract_archive(
paths,
location_config,
storage_config,
local_borg_version,
local_path='borg',
remote_path=None,
destination_path=None,
@ -70,9 +72,9 @@ def extract_archive(
):
'''
Given a dry-run flag, a local or remote repository path, an archive name, zero or more paths to
restore from the archive, location/storage configuration dicts, optional local and remote Borg
paths, and an optional destination path to extract to, extract the archive into the current
directory.
restore from the archive, the local Borg version string, location/storage configuration dicts,
optional local and remote Borg paths, and an optional destination path to extract to, extract
the archive into the current directory.
If extract to stdout is True, then start the extraction streaming to stdout, and return that
extract process as an instance of subprocess.Popen.
@ -83,10 +85,15 @@ def extract_archive(
if progress and extract_to_stdout:
raise ValueError('progress and extract_to_stdout cannot both be set')
if feature.available(feature.Feature.NUMERIC_IDS, local_borg_version):
numeric_ids_flags = ('--numeric-ids',) if location_config.get('numeric_owner') else ()
else:
numeric_ids_flags = ('--numeric-owner',) if location_config.get('numeric_owner') else ()
full_command = (
(local_path, 'extract')
+ (('--remote-path', remote_path) if remote_path else ())
+ (('--numeric-owner',) if location_config.get('numeric_owner') else ())
+ numeric_ids_flags
+ (('--umask', str(umask)) if umask else ())
+ (('--lock-wait', str(lock_wait)) if lock_wait else ())
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())

View file

@ -425,6 +425,7 @@ def run_actions(
arguments['extract'].paths,
location,
storage,
local_borg_version,
local_path=local_path,
remote_path=remote_path,
destination_path=arguments['extract'].destination,
@ -533,6 +534,7 @@ def run_actions(
paths=dump.convert_glob_patterns_to_borg_patterns([dump_pattern]),
location_config=location,
storage_config=storage,
local_borg_version=local_borg_version,
local_path=local_path,
remote_path=remote_path,
destination_path='/',

View file

@ -715,7 +715,7 @@ def test_create_archive_with_numeric_owner_calls_borg_with_numeric_ids_parameter
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,
('borg', 'create', option_flag) + ARCHIVE_WITH_PATHS,
output_log_level=logging.INFO,
output_file=None,
borg_local_path='borg',

View file

@ -25,12 +25,14 @@ def test_extract_last_archive_dry_run_calls_borg_with_last_archive():
('borg', 'list', '--short', 'repo'), result='archive1\narchive2\n'
)
insert_execute_command_mock(('borg', 'extract', '--dry-run', 'repo::archive2'))
flexmock(module.feature).should_receive('available').and_return(True)
module.extract_last_archive_dry_run(repository='repo', lock_wait=None)
def test_extract_last_archive_dry_run_without_any_archives_should_not_raise():
insert_execute_command_output_mock(('borg', 'list', '--short', 'repo'), result='\n')
flexmock(module.feature).should_receive('available').and_return(True)
module.extract_last_archive_dry_run(repository='repo', lock_wait=None)
@ -41,6 +43,7 @@ def test_extract_last_archive_dry_run_with_log_info_calls_borg_with_info_paramet
)
insert_execute_command_mock(('borg', 'extract', '--dry-run', '--info', 'repo::archive2'))
insert_logging_mock(logging.INFO)
flexmock(module.feature).should_receive('available').and_return(True)
module.extract_last_archive_dry_run(repository='repo', lock_wait=None)
@ -53,6 +56,7 @@ def test_extract_last_archive_dry_run_with_log_debug_calls_borg_with_debug_param
('borg', 'extract', '--dry-run', '--debug', '--show-rc', '--list', 'repo::archive2')
)
insert_logging_mock(logging.DEBUG)
flexmock(module.feature).should_receive('available').and_return(True)
module.extract_last_archive_dry_run(repository='repo', lock_wait=None)
@ -62,6 +66,7 @@ def test_extract_last_archive_dry_run_calls_borg_via_local_path():
('borg1', 'list', '--short', 'repo'), result='archive1\narchive2\n'
)
insert_execute_command_mock(('borg1', 'extract', '--dry-run', 'repo::archive2'))
flexmock(module.feature).should_receive('available').and_return(True)
module.extract_last_archive_dry_run(repository='repo', lock_wait=None, local_path='borg1')
@ -73,6 +78,7 @@ def test_extract_last_archive_dry_run_calls_borg_with_remote_path_parameters():
insert_execute_command_mock(
('borg', 'extract', '--dry-run', '--remote-path', 'borg1', 'repo::archive2')
)
flexmock(module.feature).should_receive('available').and_return(True)
module.extract_last_archive_dry_run(repository='repo', lock_wait=None, remote_path='borg1')
@ -84,6 +90,7 @@ def test_extract_last_archive_dry_run_calls_borg_with_lock_wait_parameters():
insert_execute_command_mock(
('borg', 'extract', '--dry-run', '--lock-wait', '5', 'repo::archive2')
)
flexmock(module.feature).should_receive('available').and_return(True)
module.extract_last_archive_dry_run(repository='repo', lock_wait=5)
@ -91,6 +98,7 @@ def test_extract_last_archive_dry_run_calls_borg_with_lock_wait_parameters():
def test_extract_archive_calls_borg_with_path_parameters():
flexmock(module.os.path).should_receive('abspath').and_return('repo')
insert_execute_command_mock(('borg', 'extract', 'repo::archive', 'path1', 'path2'))
flexmock(module.feature).should_receive('available').and_return(True)
module.extract_archive(
dry_run=False,
@ -99,12 +107,14 @@ def test_extract_archive_calls_borg_with_path_parameters():
paths=['path1', 'path2'],
location_config={},
storage_config={},
local_borg_version='1.2.3',
)
def test_extract_archive_calls_borg_with_remote_path_parameters():
flexmock(module.os.path).should_receive('abspath').and_return('repo')
insert_execute_command_mock(('borg', 'extract', '--remote-path', 'borg1', 'repo::archive'))
flexmock(module.feature).should_receive('available').and_return(True)
module.extract_archive(
dry_run=False,
@ -113,13 +123,18 @@ def test_extract_archive_calls_borg_with_remote_path_parameters():
paths=None,
location_config={},
storage_config={},
local_borg_version='1.2.3',
remote_path='borg1',
)
def test_extract_archive_calls_borg_with_numeric_owner_parameter():
@pytest.mark.parametrize(
'feature_available,option_flag', ((True, '--numeric-ids'), (False, '--numeric-owner'),),
)
def test_extract_archive_calls_borg_with_numeric_ids_parameter(feature_available, option_flag):
flexmock(module.os.path).should_receive('abspath').and_return('repo')
insert_execute_command_mock(('borg', 'extract', '--numeric-owner', 'repo::archive'))
insert_execute_command_mock(('borg', 'extract', option_flag, 'repo::archive'))
flexmock(module.feature).should_receive('available').and_return(feature_available)
module.extract_archive(
dry_run=False,
@ -128,12 +143,14 @@ def test_extract_archive_calls_borg_with_numeric_owner_parameter():
paths=None,
location_config={'numeric_owner': True},
storage_config={},
local_borg_version='1.2.3',
)
def test_extract_archive_calls_borg_with_umask_parameters():
flexmock(module.os.path).should_receive('abspath').and_return('repo')
insert_execute_command_mock(('borg', 'extract', '--umask', '0770', 'repo::archive'))
flexmock(module.feature).should_receive('available').and_return(True)
module.extract_archive(
dry_run=False,
@ -142,12 +159,14 @@ def test_extract_archive_calls_borg_with_umask_parameters():
paths=None,
location_config={},
storage_config={'umask': '0770'},
local_borg_version='1.2.3',
)
def test_extract_archive_calls_borg_with_lock_wait_parameters():
flexmock(module.os.path).should_receive('abspath').and_return('repo')
insert_execute_command_mock(('borg', 'extract', '--lock-wait', '5', 'repo::archive'))
flexmock(module.feature).should_receive('available').and_return(True)
module.extract_archive(
dry_run=False,
@ -156,6 +175,7 @@ def test_extract_archive_calls_borg_with_lock_wait_parameters():
paths=None,
location_config={},
storage_config={'lock_wait': '5'},
local_borg_version='1.2.3',
)
@ -163,6 +183,7 @@ def test_extract_archive_with_log_info_calls_borg_with_info_parameter():
flexmock(module.os.path).should_receive('abspath').and_return('repo')
insert_execute_command_mock(('borg', 'extract', '--info', 'repo::archive'))
insert_logging_mock(logging.INFO)
flexmock(module.feature).should_receive('available').and_return(True)
module.extract_archive(
dry_run=False,
@ -171,6 +192,7 @@ def test_extract_archive_with_log_info_calls_borg_with_info_parameter():
paths=None,
location_config={},
storage_config={},
local_borg_version='1.2.3',
)
@ -180,6 +202,7 @@ def test_extract_archive_with_log_debug_calls_borg_with_debug_parameters():
('borg', 'extract', '--debug', '--list', '--show-rc', 'repo::archive')
)
insert_logging_mock(logging.DEBUG)
flexmock(module.feature).should_receive('available').and_return(True)
module.extract_archive(
dry_run=False,
@ -188,12 +211,14 @@ def test_extract_archive_with_log_debug_calls_borg_with_debug_parameters():
paths=None,
location_config={},
storage_config={},
local_borg_version='1.2.3',
)
def test_extract_archive_calls_borg_with_dry_run_parameter():
flexmock(module.os.path).should_receive('abspath').and_return('repo')
insert_execute_command_mock(('borg', 'extract', '--dry-run', 'repo::archive'))
flexmock(module.feature).should_receive('available').and_return(True)
module.extract_archive(
dry_run=True,
@ -202,12 +227,14 @@ def test_extract_archive_calls_borg_with_dry_run_parameter():
paths=None,
location_config={},
storage_config={},
local_borg_version='1.2.3',
)
def test_extract_archive_calls_borg_with_destination_path():
flexmock(module.os.path).should_receive('abspath').and_return('repo')
insert_execute_command_mock(('borg', 'extract', 'repo::archive'), working_directory='/dest')
flexmock(module.feature).should_receive('available').and_return(True)
module.extract_archive(
dry_run=False,
@ -216,6 +243,7 @@ def test_extract_archive_calls_borg_with_destination_path():
paths=None,
location_config={},
storage_config={},
local_borg_version='1.2.3',
destination_path='/dest',
)
@ -223,6 +251,7 @@ def test_extract_archive_calls_borg_with_destination_path():
def test_extract_archive_calls_borg_with_strip_components():
flexmock(module.os.path).should_receive('abspath').and_return('repo')
insert_execute_command_mock(('borg', 'extract', '--strip-components', '5', 'repo::archive'))
flexmock(module.feature).should_receive('available').and_return(True)
module.extract_archive(
dry_run=False,
@ -231,6 +260,7 @@ def test_extract_archive_calls_borg_with_strip_components():
paths=None,
location_config={},
storage_config={},
local_borg_version='1.2.3',
strip_components=5,
)
@ -242,6 +272,7 @@ def test_extract_archive_calls_borg_with_progress_parameter():
output_file=module.DO_NOT_CAPTURE,
working_directory=None,
).once()
flexmock(module.feature).should_receive('available').and_return(True)
module.extract_archive(
dry_run=False,
@ -250,6 +281,7 @@ def test_extract_archive_calls_borg_with_progress_parameter():
paths=None,
location_config={},
storage_config={},
local_borg_version='1.2.3',
progress=True,
)
@ -265,6 +297,7 @@ def test_extract_archive_with_progress_and_extract_to_stdout_raises():
paths=None,
location_config={},
storage_config={},
local_borg_version='1.2.3',
progress=True,
extract_to_stdout=True,
)
@ -279,6 +312,7 @@ def test_extract_archive_calls_borg_with_stdout_parameter_and_returns_process():
working_directory=None,
run_to_completion=False,
).and_return(process).once()
flexmock(module.feature).should_receive('available').and_return(True)
assert (
module.extract_archive(
@ -288,6 +322,7 @@ def test_extract_archive_calls_borg_with_stdout_parameter_and_returns_process():
paths=None,
location_config={},
storage_config={},
local_borg_version='1.2.3',
extract_to_stdout=True,
)
== process
@ -299,6 +334,7 @@ def test_extract_archive_skips_abspath_for_remote_repository():
flexmock(module).should_receive('execute_command').with_args(
('borg', 'extract', 'server:repo::archive'), working_directory=None
).once()
flexmock(module.feature).should_receive('available').and_return(True)
module.extract_archive(
dry_run=False,
@ -307,4 +343,5 @@ def test_extract_archive_skips_abspath_for_remote_repository():
paths=None,
location_config={},
storage_config={},
local_borg_version='1.2.3',
)