Browse Source

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

Dan Helfman 1 month ago
parent
commit
35feeb4615

+ 9 - 0
borgmatic/actions/restore.py

@@ -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
tests/end-to-end/commands/fake_btrfs.py

@@ -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')
 

+ 0 - 42
tests/end-to-end/commands/fake_findmnt.py

@@ -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()

+ 1 - 2
tests/end-to-end/hooks/data_source/test_btrfs.py

@@ -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 - 0
tests/unit/actions/test_create.py

@@ -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 - 0
tests/unit/actions/test_restore.py

@@ -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(),