Fix restore and get end-to-end tests passing (#1105).

This commit is contained in:
Dan Helfman
2025-09-30 21:56:04 -07:00
parent 5dca281439
commit 35feeb4615
6 changed files with 46 additions and 66 deletions
+9
View File
@@ -5,6 +5,7 @@ import pathlib
import shutil
import tempfile
import borgmatic.actions.pattern
import borgmatic.borg.extract
import borgmatic.borg.list
import borgmatic.borg.mount
@@ -536,13 +537,20 @@ def run_restore(
return
logger.info(f'Restoring data sources from archive {restore_arguments.archive}')
working_directory = borgmatic.config.paths.get_working_directory(config)
with borgmatic.config.paths.Runtime_directory(config) as borgmatic_runtime_directory:
patterns = borgmatic.actions.pattern.process_patterns(
borgmatic.actions.pattern.collect_patterns(config),
config,
working_directory,
)
borgmatic.hooks.dispatch.call_hooks_even_if_unconfigured(
'remove_data_source_dumps',
config,
borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE,
borgmatic_runtime_directory,
patterns,
global_arguments.dry_run,
)
@@ -625,6 +633,7 @@ def run_restore(
config,
borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE,
borgmatic_runtime_directory,
patterns,
global_arguments.dry_run,
)
+13 -22
View File
@@ -12,9 +12,8 @@ def parse_arguments(*unparsed_arguments):
subvolume_parser = action_parsers.add_parser('subvolume')
subvolume_subparser = subvolume_parser.add_subparsers(dest='subaction')
list_parser = subvolume_subparser.add_parser('list')
list_parser.add_argument('-s', dest='snapshots_only', action='store_true')
list_parser.add_argument('subvolume_path')
show_parser = subvolume_subparser.add_parser('show')
show_parser.add_argument('subvolume_path')
snapshot_parser = subvolume_subparser.add_parser('snapshot')
snapshot_parser.add_argument('-r', dest='read_only', action='store_true')
@@ -24,6 +23,9 @@ def parse_arguments(*unparsed_arguments):
delete_parser = subvolume_subparser.add_parser('delete')
delete_parser.add_argument('snapshot_path')
ensure_deleted_parser = subvolume_subparser.add_parser('ensure_deleted')
ensure_deleted_parser.add_argument('snapshot_path')
property_parser = action_parsers.add_parser('property')
property_subparser = property_parser.add_subparsers(dest='subaction')
get_parser = property_subparser.add_parser('get')
@@ -34,13 +36,6 @@ def parse_arguments(*unparsed_arguments):
return (global_parser, global_parser.parse_args(unparsed_arguments))
BUILTIN_SUBVOLUME_LIST_LINES = (
'261 gen 29 top level 5 path sub',
'262 gen 29 top level 5 path other',
)
SUBVOLUME_LIST_LINE_PREFIX = '263 gen 29 top level 5 path '
def load_snapshots():
try:
return json.load(open('/tmp/fake_btrfs.json'))
@@ -52,18 +47,12 @@ def save_snapshots(snapshot_paths):
json.dump(snapshot_paths, open('/tmp/fake_btrfs.json', 'w'))
def print_subvolume_list(arguments, snapshot_paths):
def print_subvolume_show(arguments):
assert arguments.subvolume_path == '/e2e/mnt/subvolume'
if not arguments.snapshots_only:
for line in BUILTIN_SUBVOLUME_LIST_LINES:
print(line)
for snapshot_path in snapshot_paths:
print(
SUBVOLUME_LIST_LINE_PREFIX
+ snapshot_path[snapshot_path.index('.borgmatic-snapshot-') :],
)
# borgmatic doesn't currently parse the output of "btrfs subvolume show"—it's just checking the
# exit code—so what we print in response doesn't matter in this test.
print('Totally legit btrfs subvolume!')
def main():
@@ -74,8 +63,8 @@ def main():
global_parser.print_help()
sys.exit(1)
if arguments.subaction == 'list':
print_subvolume_list(arguments, snapshot_paths)
if arguments.subaction == 'show':
print_subvolume_show(arguments)
elif arguments.subaction == 'snapshot':
snapshot_paths.append(arguments.snapshot_path)
save_snapshots(snapshot_paths)
@@ -95,6 +84,8 @@ def main():
if snapshot_path.endswith('/' + arguments.snapshot_path)
]
save_snapshots(snapshot_paths)
elif arguments.subaction == 'ensure_deleted':
assert arguments.snapshot_path not in snapshot_paths
elif arguments.action == 'property' and arguments.subaction == 'get':
print(f'{arguments.property_name}=false')
-42
View File
@@ -1,42 +0,0 @@
import argparse
import sys
def parse_arguments(*unparsed_arguments):
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('-t', dest='type')
parser.add_argument('--json', action='store_true')
parser.add_argument('--list', action='store_true')
return parser.parse_args(unparsed_arguments)
BUILTIN_FILESYSTEM_MOUNT_OUTPUT = '''{
"filesystems": [
{
"target": "/e2e/mnt/subvolume",
"source": "/dev/loop0",
"fstype": "btrfs",
"options": "rw,relatime,ssd,space_cache=v2,subvolid=5,subvol=/"
}
]
}
'''
def print_filesystem_mounts():
print(BUILTIN_FILESYSTEM_MOUNT_OUTPUT)
def main():
arguments = parse_arguments(*sys.argv[1:])
assert arguments.type == 'btrfs'
assert arguments.json
assert arguments.list
print_filesystem_mounts()
if __name__ == '__main__':
main()
@@ -24,7 +24,6 @@ def generate_configuration(config_path, repository_path):
+ 'encryption_passphrase: "test"\n'
+ 'btrfs:\n'
+ ' btrfs_command: python3 /app/tests/end-to-end/commands/fake_btrfs.py\n'
+ ' findmnt_command: python3 /app/tests/end-to-end/commands/fake_findmnt.py\n'
)
config_file = open(config_path, 'w')
config_file.write(config)
@@ -55,7 +54,7 @@ def test_btrfs_create_and_list():
# Assert that the snapshot has been deleted.
assert not subprocess.check_output(
'python3 /app/tests/end-to-end/commands/fake_btrfs.py subvolume list -s /e2e/mnt/subvolume'.split(
'python3 /app/tests/end-to-end/commands/fake_btrfs.py subvolume ensure_deleted /e2e/mnt/subvolume'.split(
' ',
),
)
+5
View File
@@ -18,6 +18,7 @@ def test_run_create_executes_and_calls_hooks_for_configured_repository():
flexmock(module.borgmatic.hooks.dispatch).should_receive(
'call_hooks_even_if_unconfigured',
).and_return({})
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(())
flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return([])
flexmock(os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
@@ -60,6 +61,7 @@ def test_run_create_runs_with_selected_repository():
flexmock(module.borgmatic.hooks.dispatch).should_receive(
'call_hooks_even_if_unconfigured',
).and_return({})
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(())
flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return([])
flexmock(os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
@@ -207,6 +209,7 @@ def test_run_create_produces_json():
flexmock(module.borgmatic.hooks.dispatch).should_receive(
'call_hooks_even_if_unconfigured',
).and_return({})
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(())
flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return([])
flexmock(os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
@@ -252,6 +255,7 @@ def test_run_create_with_active_dumps_roundtrips_via_checkpoint_archive():
flexmock(module.borgmatic.hooks.dispatch).should_receive(
'call_hooks_even_if_unconfigured',
).and_return({})
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(())
flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return([])
flexmock(os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
@@ -335,6 +339,7 @@ def test_run_create_with_active_dumps_json_updates_archive_info():
flexmock(module.borgmatic.hooks.dispatch).should_receive(
'call_hooks_even_if_unconfigured',
).and_return({})
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(())
flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return([])
flexmock(os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
+18
View File
@@ -1176,6 +1176,9 @@ def test_run_restore_restores_each_data_source():
flexmock(module.borgmatic.config.paths).should_receive(
'make_runtime_directory_glob',
).replace_with(lambda path: path)
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(())
flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return([])
flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured')
flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return(
flexmock(),
@@ -1245,6 +1248,9 @@ def test_run_restore_bails_for_non_matching_repository():
flexmock(module.borgmatic.config.paths).should_receive(
'make_runtime_directory_glob',
).replace_with(lambda path: path)
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(())
flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return([])
flexmock(module.borgmatic.hooks.dispatch).should_receive(
'call_hooks_even_if_unconfigured',
).never()
@@ -1274,6 +1280,9 @@ def test_run_restore_restores_data_source_by_falling_back_to_all_name():
flexmock(module.borgmatic.config.paths).should_receive(
'make_runtime_directory_glob',
).replace_with(lambda path: path)
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(())
flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return([])
flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured')
flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return(
flexmock(),
@@ -1334,6 +1343,9 @@ def test_run_restore_restores_data_source_configured_with_all_name():
flexmock(module.borgmatic.config.paths).should_receive(
'make_runtime_directory_glob',
).replace_with(lambda path: path)
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(())
flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return([])
flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured')
flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return(
flexmock(),
@@ -1416,6 +1428,9 @@ def test_run_restore_skips_missing_data_source():
flexmock(module.borgmatic.config.paths).should_receive(
'make_runtime_directory_glob',
).replace_with(lambda path: path)
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(())
flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return([])
flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured')
flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return(
flexmock(),
@@ -1498,6 +1513,9 @@ def test_run_restore_restores_data_sources_from_different_hooks():
flexmock(module.borgmatic.config.paths).should_receive(
'make_runtime_directory_glob',
).replace_with(lambda path: path)
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(())
flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return([])
flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured')
flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return(
flexmock(),