Browse Source

Ignore archive filter parameters passed to list action when --archive is given (#557).

Dan Helfman 2 years ago
parent
commit
b40e9b7da2
4 changed files with 172 additions and 42 deletions
  1. 3 4
      NEWS
  2. 19 6
      borgmatic/borg/list.py
  3. 3 0
      docs/how-to/set-up-backups.md
  4. 147 32
      tests/unit/borg/test_list.py

+ 3 - 4
NEWS

@@ -3,10 +3,9 @@
    like "rcreate" (replaces "init"), "rlist" (list archives in repository), and "rinfo" (show
    repository info). For the most part, borgmatic tries to smooth over differences between Borg 1
    and 2 to make your upgrade process easier. However, there are still a few cases where Borg made
-   breaking changes, such as moving flags from "borg list" to "borg rlist". See the Borg 2.0
-   changelog for more information (https://www.borgbackup.org/releases/borg-2.0.html). If you
-   install Borg 2, you'll need to manually "borg transfer" or "borgmatic transfer" your existing
-   Borg 1 repositories before use.
+   breaking changes. See the Borg 2.0 changelog for more information
+   (https://www.borgbackup.org/releases/borg-2.0.html). If you install Borg 2, you'll need to
+   manually "borg transfer" or "borgmatic transfer" your existing Borg 1 repositories before use.
  * #557: Rename several configuration options to match Borg 2: "remote_rate_limit" is now
    "upload_rate_limit", "numeric_owner" is "numeric_ids", and "bsd_flags" is "flags". borgmatic
    still works with the old options.

+ 19 - 6
borgmatic/borg/list.py

@@ -9,7 +9,14 @@ from borgmatic.execute import execute_command
 logger = logging.getLogger(__name__)
 
 
-MAKE_FLAGS_EXCLUDES = ('repository', 'archive', 'successful', 'paths', 'find_paths')
+ARCHIVE_FILTER_FLAGS_MOVED_TO_RLIST = ('prefix', 'glob_archives', 'sort_by', 'first', 'last')
+MAKE_FLAGS_EXCLUDES = (
+    'repository',
+    'archive',
+    'successful',
+    'paths',
+    'find_paths',
+) + ARCHIVE_FILTER_FLAGS_MOVED_TO_RLIST
 
 
 def make_list_command(
@@ -113,11 +120,11 @@ def list_archive(
             repository, storage_config, local_borg_version, rlist_arguments, local_path, remote_path
         )
 
-    if feature.available(feature.Feature.RLIST, local_borg_version):
-        for flag_name in ('prefix', 'glob-archives', 'sort-by', 'first', 'last'):
-            if getattr(list_arguments, flag_name.replace('-', '_'), None):
-                raise ValueError(
-                    f'The --{flag_name} flag on the list action is not supported when using the --archive/--find flags and Borg 2.x+.'
+    if list_arguments.archive:
+        for name in ARCHIVE_FILTER_FLAGS_MOVED_TO_RLIST:
+            if getattr(list_arguments, name, None):
+                logger.warning(
+                    f"The --{name.replace('_', '-')} flag on the list action is ignored when using the --archive flag."
                 )
 
     if list_arguments.json:
@@ -169,6 +176,12 @@ def list_archive(
 
         archive_arguments = copy.copy(list_arguments)
         archive_arguments.archive = archive
+
+        # This list call is to show the files in a single archive, not list multiple archives. So
+        # blank out any archive filtering flags. They'll break anyway in Borg 2.
+        for name in ARCHIVE_FILTER_FLAGS_MOVED_TO_RLIST:
+            setattr(archive_arguments, name, None)
+
         main_command = make_list_command(
             repository,
             storage_config,

+ 3 - 0
docs/how-to/set-up-backups.md

@@ -204,6 +204,9 @@ Or, with Borg 2.x:
 sudo borgmatic rcreate --encryption repokey-aes-ocb
 ```
 
+(Note that `repokey-chacha20-poly1305` may be faster than `repokey-aes-ocb` on
+certain platforms like ARM64.)
+
 This uses the borgmatic configuration file you created above to determine
 which local or remote repository to create, and encrypts it with the
 encryption passphrase specified there if one is provided. Read about [Borg

+ 147 - 32
tests/unit/borg/test_list.py

@@ -254,7 +254,17 @@ def test_make_find_paths_adds_globs_to_path_fragments():
 
 
 def test_list_archive_calls_borg_with_parameters():
-    list_arguments = argparse.Namespace(archive='archive', paths=None, json=False, find_paths=None)
+    list_arguments = argparse.Namespace(
+        archive='archive',
+        paths=None,
+        json=False,
+        find_paths=None,
+        prefix=None,
+        glob_archives=None,
+        sort_by=None,
+        first=None,
+        last=None,
+    )
 
     flexmock(module.feature).should_receive('available').and_return(False)
     flexmock(module).should_receive('make_list_command').with_args(
@@ -297,7 +307,17 @@ def test_list_archive_with_archive_and_json_errors():
 
 
 def test_list_archive_calls_borg_with_local_path():
-    list_arguments = argparse.Namespace(archive='archive', paths=None, json=False, find_paths=None)
+    list_arguments = argparse.Namespace(
+        archive='archive',
+        paths=None,
+        json=False,
+        find_paths=None,
+        prefix=None,
+        glob_archives=None,
+        sort_by=None,
+        first=None,
+        last=None,
+    )
 
     flexmock(module.feature).should_receive('available').and_return(False)
     flexmock(module).should_receive('make_list_command').with_args(
@@ -346,9 +366,7 @@ def test_list_archive_calls_borg_multiple_times_with_find_paths():
         output_log_level=None,
         borg_local_path='borg',
         extra_environment=None,
-    ).and_return(
-        'archive1   Sun, 2022-05-29 15:27:04 [abc]\narchive2   Mon, 2022-05-30 19:47:15 [xyz]'
-    ).once()
+    ).and_return('archive1\narchive2').once()
     flexmock(module).should_receive('make_list_command').and_return(
         ('borg', 'list', 'repo::archive1')
     ).and_return(('borg', 'list', 'repo::archive2'))
@@ -376,7 +394,17 @@ def test_list_archive_calls_borg_multiple_times_with_find_paths():
 
 
 def test_list_archive_calls_borg_with_archive():
-    list_arguments = argparse.Namespace(archive='archive', paths=None, json=False, find_paths=None)
+    list_arguments = argparse.Namespace(
+        archive='archive',
+        paths=None,
+        json=False,
+        find_paths=None,
+        prefix=None,
+        glob_archives=None,
+        sort_by=None,
+        first=None,
+        last=None,
+    )
 
     flexmock(module.feature).should_receive('available').and_return(False)
     flexmock(module).should_receive('make_list_command').with_args(
@@ -461,51 +489,129 @@ 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',),
 )
-def test_list_archive_with_archive_disallows_archive_filter_flag_if_rlist_feature_available(
-    archive_filter_flag,
-):
-    list_arguments = argparse.Namespace(
-        archive='archive', paths=None, json=False, find_paths=None, **{archive_filter_flag: 'foo'}
-    )
+def test_list_archive_with_archive_ignores_archive_filter_flag(archive_filter_flag,):
+    default_filter_flags = {
+        'prefix': None,
+        'glob_archives': None,
+        'sort_by': None,
+        'first': None,
+        'last': None,
+    }
+    altered_filter_flags = {**default_filter_flags, **{archive_filter_flag: 'foo'}}
 
     flexmock(module.feature).should_receive('available').with_args(
         module.feature.Feature.RLIST, '1.2.3'
-    ).and_return(True)
+    ).and_return(False)
+    flexmock(module).should_receive('make_list_command').with_args(
+        repository='repo',
+        storage_config={},
+        local_borg_version='1.2.3',
+        list_arguments=argparse.Namespace(
+            archive='archive', paths=None, json=False, find_paths=None, **default_filter_flags
+        ),
+        local_path='borg',
+        remote_path=None,
+    ).and_return(('borg', 'list', 'repo::archive'))
+    flexmock(module).should_receive('make_find_paths').and_return(())
+    flexmock(module.environment).should_receive('make_environment')
+    flexmock(module).should_receive('execute_command').with_args(
+        ('borg', 'list', 'repo::archive'),
+        output_log_level=logging.WARNING,
+        borg_local_path='borg',
+        extra_environment=None,
+    ).once()
 
-    with pytest.raises(ValueError):
-        module.list_archive(
-            repository='repo',
-            storage_config={},
-            local_borg_version='1.2.3',
-            list_arguments=list_arguments,
-        )
+    module.list_archive(
+        repository='repo',
+        storage_config={},
+        local_borg_version='1.2.3',
+        list_arguments=argparse.Namespace(
+            archive='archive', paths=None, json=False, find_paths=None, **altered_filter_flags
+        ),
+    )
 
 
 @pytest.mark.parametrize(
     'archive_filter_flag', ('prefix', 'glob_archives', 'sort_by', 'first', 'last',),
 )
-def test_list_archive_with_archive_allows_archive_filter_flag_if_rlist_feature_unavailable(
+def test_list_archive_with_find_paths_allows_archive_filter_flag_but_only_passes_it_to_rlist(
     archive_filter_flag,
 ):
-    list_arguments = argparse.Namespace(
-        archive='archive', paths=None, json=False, find_paths=None, **{archive_filter_flag: 'foo'}
-    )
+    default_filter_flags = {
+        'prefix': None,
+        'glob_archives': None,
+        'sort_by': None,
+        'first': None,
+        'last': None,
+    }
+    altered_filter_flags = {**default_filter_flags, **{archive_filter_flag: 'foo'}}
+    glob_paths = ('**/*foo.txt*/**',)
+    flexmock(module.feature).should_receive('available').and_return(True)
+
+    flexmock(module.rlist).should_receive('make_rlist_command').with_args(
+        repository='repo',
+        storage_config={},
+        local_borg_version='1.2.3',
+        rlist_arguments=argparse.Namespace(
+            repository='repo', short=True, format=None, json=None, **altered_filter_flags
+        ),
+        local_path='borg',
+        remote_path=None,
+    ).and_return(('borg', 'rlist', '--repo', 'repo'))
+
+    flexmock(module).should_receive('execute_command').with_args(
+        ('borg', 'rlist', '--repo', 'repo'),
+        output_log_level=None,
+        borg_local_path='borg',
+        extra_environment=None,
+    ).and_return('archive1\narchive2').once()
 
-    flexmock(module.feature).should_receive('available').with_args(
-        module.feature.Feature.RLIST, '1.2.3'
-    ).and_return(False)
     flexmock(module).should_receive('make_list_command').with_args(
         repository='repo',
         storage_config={},
         local_borg_version='1.2.3',
-        list_arguments=list_arguments,
+        list_arguments=argparse.Namespace(
+            repository='repo',
+            archive='archive1',
+            paths=None,
+            short=True,
+            format=None,
+            json=None,
+            find_paths=['foo.txt'],
+            **default_filter_flags,
+        ),
         local_path='borg',
         remote_path=None,
-    ).and_return(('borg', 'list', 'repo::archive'))
-    flexmock(module).should_receive('make_find_paths').and_return(())
+    ).and_return(('borg', 'list', '--repo', 'repo', 'archive1'))
+
+    flexmock(module).should_receive('make_list_command').with_args(
+        repository='repo',
+        storage_config={},
+        local_borg_version='1.2.3',
+        list_arguments=argparse.Namespace(
+            repository='repo',
+            archive='archive2',
+            paths=None,
+            short=True,
+            format=None,
+            json=None,
+            find_paths=['foo.txt'],
+            **default_filter_flags,
+        ),
+        local_path='borg',
+        remote_path=None,
+    ).and_return(('borg', 'list', '--repo', 'repo', 'archive2'))
+
+    flexmock(module).should_receive('make_find_paths').and_return(glob_paths)
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
-        ('borg', 'list', 'repo::archive'),
+        ('borg', 'list', '--repo', 'repo', 'archive1') + glob_paths,
+        output_log_level=logging.WARNING,
+        borg_local_path='borg',
+        extra_environment=None,
+    ).once()
+    flexmock(module).should_receive('execute_command').with_args(
+        ('borg', 'list', '--repo', 'repo', 'archive2') + glob_paths,
         output_log_level=logging.WARNING,
         borg_local_path='borg',
         extra_environment=None,
@@ -515,5 +621,14 @@ def test_list_archive_with_archive_allows_archive_filter_flag_if_rlist_feature_u
         repository='repo',
         storage_config={},
         local_borg_version='1.2.3',
-        list_arguments=list_arguments,
+        list_arguments=argparse.Namespace(
+            repository='repo',
+            archive=None,
+            paths=None,
+            short=True,
+            format=None,
+            json=None,
+            find_paths=['foo.txt'],
+            **altered_filter_flags,
+        ),
     )