Explorar el Código

Support for several more borgmatic/borg info command-line flags (#193).

Dan Helfman hace 6 años
padre
commit
86dbc00cbe

+ 4 - 2
NEWS

@@ -1,6 +1,8 @@
 1.3.11.dev0
- * #193: Pass through several "borg list" flags like --short, --format, --sort-by, --first, --last,
-   etc. via borgmatic list command-line flags.
+ * #193: Pass through several "borg list" and "borg info" flags like --short, --format, --sort-by,
+   --first, --last, etc. via borgmatic command-line flags.
+ * Add borgmatic info --repository and --archive command-line flags to display info for individual
+   repositories or archives.
 
 1.3.10
  * #198: Fix for Borg create error output not showing up at borgmatic verbosity level zero.

+ 28 - 10
borgmatic/borg/info.py

@@ -1,26 +1,44 @@
 import logging
 
+from borgmatic.borg.flags import make_flags, make_flags_from_arguments
 from borgmatic.execute import execute_command
 
 logger = logging.getLogger(__name__)
 
 
 def display_archives_info(
-    repository, storage_config, local_path='borg', remote_path=None, json=False
+    repository, storage_config, info_arguments, local_path='borg', remote_path=None
 ):
     '''
-    Given a local or remote repository path, and a storage config dict, display summary information
-    for Borg archives in the repository or return JSON summary information.
+    Given a local or remote repository path, a storage config dict, and the arguments to the info
+    action, display summary information for Borg archives in the repository or return JSON summary
+    information.
     '''
     lock_wait = storage_config.get('lock_wait', None)
 
     full_command = (
-        (local_path, 'info', repository)
-        + (('--remote-path', remote_path) if remote_path else ())
-        + (('--lock-wait', str(lock_wait)) if lock_wait else ())
-        + (('--info',) if logger.getEffectiveLevel() == logging.INFO and not json else ())
-        + (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) and not json else ())
-        + (('--json',) if json else ())
+        (
+            local_path,
+            'info',
+            '::'.join((repository, info_arguments.archive))
+            if info_arguments.archive
+            else repository,
+        )
+        + (
+            ('--info',)
+            if logger.getEffectiveLevel() == logging.INFO and not info_arguments.json
+            else ()
+        )
+        + (
+            ('--debug', '--show-rc')
+            if logger.isEnabledFor(logging.DEBUG) and not info_arguments.json
+            else ()
+        )
+        + make_flags('remote-path', remote_path)
+        + make_flags('lock-wait', lock_wait)
+        + make_flags_from_arguments(info_arguments, excludes=('repository', 'archive'))
     )
 
-    return execute_command(full_command, output_log_level=None if json else logging.WARNING)
+    return execute_command(
+        full_command, output_log_level=None if info_arguments.json else logging.WARNING
+    )

+ 28 - 3
borgmatic/commands/arguments.py

@@ -224,7 +224,7 @@ def parse_arguments(*unparsed_arguments):
     extract_group = extract_parser.add_argument_group('extract arguments')
     extract_group.add_argument(
         '--repository',
-        help='Path of repository to use, defaults to the configured repository if there is only one',
+        help='Path of repository to extract, defaults to the configured repository if there is only one',
     )
     extract_group.add_argument('--archive', help='Name of archive to operate on', required=True)
     extract_group.add_argument(
@@ -254,9 +254,9 @@ def parse_arguments(*unparsed_arguments):
     list_group = list_parser.add_argument_group('list arguments')
     list_group.add_argument(
         '--repository',
-        help='Path of repository to use, defaults to the configured repository if there is only one',
+        help='Path of repository to list, defaults to the configured repository if there is only one',
     )
-    list_group.add_argument('--archive', help='Name of archive to operate on')
+    list_group.add_argument('--archive', help='Name of archive to list')
     list_group.add_argument(
         '--short', default=False, action='store_true', help='Output only archive or path names'
     )
@@ -301,9 +301,34 @@ def parse_arguments(*unparsed_arguments):
         add_help=False,
     )
     info_group = info_parser.add_argument_group('info arguments')
+    info_group.add_argument(
+        '--repository',
+        help='Path of repository to show info for, defaults to the configured repository if there is only one',
+    )
+    info_group.add_argument('--archive', help='Name of archive to show info for')
     info_group.add_argument(
         '--json', dest='json', default=False, action='store_true', help='Output results as JSON'
     )
+    info_group.add_argument(
+        '-P', '--prefix', help='Only show info for archive names starting with this prefix'
+    )
+    info_group.add_argument(
+        '-a',
+        '--glob-archives',
+        metavar='GLOB',
+        help='Only show info for archive names matching this glob',
+    )
+    info_group.add_argument(
+        '--sort-by', metavar='KEYS', help='Comma-separated list of sorting keys'
+    )
+    info_group.add_argument(
+        '--first',
+        metavar='N',
+        help='Show info for first N archives after other filters are applied',
+    )
+    info_group.add_argument(
+        '--last', metavar='N', help='Show info for first N archives after other filters are applied'
+    )
     info_group.add_argument('-h', '--help', action='help', help='Show this help message and exit')
 
     arguments = parse_subparser_arguments(unparsed_arguments, top_level_parser, subparsers)

+ 11 - 10
borgmatic/commands/borgmatic.py

@@ -178,16 +178,17 @@ def run_actions(
             if json_output:
                 yield json.loads(json_output)
     if 'info' in arguments:
-        logger.info('{}: Displaying summary info for archives'.format(repository))
-        json_output = borg_info.display_archives_info(
-            repository,
-            storage,
-            local_path=local_path,
-            remote_path=remote_path,
-            json=arguments['info'].json,
-        )
-        if json_output:
-            yield json.loads(json_output)
+        if arguments['info'].repository is None or repository == arguments['info'].repository:
+            logger.info('{}: Displaying summary info for archives'.format(repository))
+            json_output = borg_info.display_archives_info(
+                repository,
+                storage,
+                info_arguments=arguments['info'],
+                local_path=local_path,
+                remote_path=remote_path,
+            )
+            if json_output:
+                yield json.loads(json_output)
 
 
 def load_configurations(config_filenames):

+ 3 - 1
tests/unit/borg/test_flags.py

@@ -30,7 +30,9 @@ def test_make_flags_from_arguments_flattens_multiple_arguments():
     )
     arguments = flexmock(foo='bar', baz='quux')
 
-    assert module.make_flags_from_arguments(arguments) == ('foo', 'bar', 'baz', 'quux')
+    assert sorted(module.make_flags_from_arguments(arguments)) == sorted(
+        ('foo', 'bar', 'baz', 'quux')
+    )
 
 
 def test_make_flags_from_arguments_excludes_underscored_argument_names():

+ 60 - 9
tests/unit/borg/test_info.py

@@ -1,5 +1,6 @@
 import logging
 
+import pytest
 from flexmock import flexmock
 
 from borgmatic.borg import info as module
@@ -14,7 +15,9 @@ def test_display_archives_info_calls_borg_with_parameters():
         INFO_COMMAND, output_log_level=logging.WARNING
     )
 
-    module.display_archives_info(repository='repo', storage_config={})
+    module.display_archives_info(
+        repository='repo', storage_config={}, info_arguments=flexmock(archive=None, json=False)
+    )
 
 
 def test_display_archives_info_with_log_info_calls_borg_with_info_parameter():
@@ -22,7 +25,9 @@ def test_display_archives_info_with_log_info_calls_borg_with_info_parameter():
         INFO_COMMAND + ('--info',), output_log_level=logging.WARNING
     )
     insert_logging_mock(logging.INFO)
-    module.display_archives_info(repository='repo', storage_config={})
+    module.display_archives_info(
+        repository='repo', storage_config={}, info_arguments=flexmock(archive=None, json=False)
+    )
 
 
 def test_display_archives_info_with_log_info_and_json_suppresses_most_borg_output():
@@ -31,7 +36,9 @@ def test_display_archives_info_with_log_info_and_json_suppresses_most_borg_outpu
     ).and_return('[]')
 
     insert_logging_mock(logging.INFO)
-    json_output = module.display_archives_info(repository='repo', storage_config={}, json=True)
+    json_output = module.display_archives_info(
+        repository='repo', storage_config={}, info_arguments=flexmock(archive=None, json=True)
+    )
 
     assert json_output == '[]'
 
@@ -42,7 +49,9 @@ def test_display_archives_info_with_log_debug_calls_borg_with_debug_parameter():
     )
     insert_logging_mock(logging.DEBUG)
 
-    module.display_archives_info(repository='repo', storage_config={})
+    module.display_archives_info(
+        repository='repo', storage_config={}, info_arguments=flexmock(archive=None, json=False)
+    )
 
 
 def test_display_archives_info_with_log_debug_and_json_suppresses_most_borg_output():
@@ -51,7 +60,9 @@ def test_display_archives_info_with_log_debug_and_json_suppresses_most_borg_outp
     ).and_return('[]')
 
     insert_logging_mock(logging.DEBUG)
-    json_output = module.display_archives_info(repository='repo', storage_config={}, json=True)
+    json_output = module.display_archives_info(
+        repository='repo', storage_config={}, info_arguments=flexmock(archive=None, json=True)
+    )
 
     assert json_output == '[]'
 
@@ -61,17 +72,34 @@ def test_display_archives_info_with_json_calls_borg_with_json_parameter():
         INFO_COMMAND + ('--json',), output_log_level=None
     ).and_return('[]')
 
-    json_output = module.display_archives_info(repository='repo', storage_config={}, json=True)
+    json_output = module.display_archives_info(
+        repository='repo', storage_config={}, info_arguments=flexmock(archive=None, json=True)
+    )
 
     assert json_output == '[]'
 
 
+def test_display_archives_info_with_archive_calls_borg_with_archive_parameter():
+    flexmock(module).should_receive('execute_command').with_args(
+        ('borg', 'info', 'repo::archive'), output_log_level=logging.WARNING
+    )
+
+    module.display_archives_info(
+        repository='repo', storage_config={}, info_arguments=flexmock(archive='archive', json=False)
+    )
+
+
 def test_display_archives_info_with_local_path_calls_borg_via_local_path():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg1',) + INFO_COMMAND[1:], output_log_level=logging.WARNING
     )
 
-    module.display_archives_info(repository='repo', storage_config={}, local_path='borg1')
+    module.display_archives_info(
+        repository='repo',
+        storage_config={},
+        info_arguments=flexmock(archive=None, json=False),
+        local_path='borg1',
+    )
 
 
 def test_display_archives_info_with_remote_path_calls_borg_with_remote_path_parameters():
@@ -79,7 +107,12 @@ def test_display_archives_info_with_remote_path_calls_borg_with_remote_path_para
         INFO_COMMAND + ('--remote-path', 'borg1'), output_log_level=logging.WARNING
     )
 
-    module.display_archives_info(repository='repo', storage_config={}, remote_path='borg1')
+    module.display_archives_info(
+        repository='repo',
+        storage_config={},
+        info_arguments=flexmock(archive=None, json=False),
+        remote_path='borg1',
+    )
 
 
 def test_display_archives_info_with_lock_wait_calls_borg_with_lock_wait_parameters():
@@ -88,4 +121,22 @@ def test_display_archives_info_with_lock_wait_calls_borg_with_lock_wait_paramete
         INFO_COMMAND + ('--lock-wait', '5'), output_log_level=logging.WARNING
     )
 
-    module.display_archives_info(repository='repo', storage_config=storage_config)
+    module.display_archives_info(
+        repository='repo',
+        storage_config=storage_config,
+        info_arguments=flexmock(archive=None, json=False),
+    )
+
+
+@pytest.mark.parametrize('argument_name', ('prefix', 'glob_archives', 'sort_by', 'first', 'last'))
+def test_display_archives_info_passes_through_arguments_to_borg(argument_name):
+    flexmock(module).should_receive('execute_command').with_args(
+        INFO_COMMAND + ('--' + argument_name.replace('_', '-'), 'value'),
+        output_log_level=logging.WARNING,
+    )
+
+    module.display_archives_info(
+        repository='repo',
+        storage_config={},
+        info_arguments=flexmock(archive=None, json=False, **{argument_name: 'value'}),
+    )