From b714ffd48b2d77c8ec8ba60196d628f7ac200258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20L=C3=89VEIL?= Date: Sun, 29 Jul 2018 03:17:45 +0000 Subject: [PATCH] add support for `--info --json` (#83) --- AUTHORS | 2 +- borgmatic/borg/info.py | 10 +++++++--- borgmatic/borg/list.py | 4 +++- borgmatic/commands/borgmatic.py | 16 +++++++++++++--- .../tests/integration/commands/test_borgmatic.py | 12 +++++++++++- borgmatic/tests/unit/borg/test_info.py | 13 ++++++++++++- 6 files changed, 47 insertions(+), 10 deletions(-) diff --git a/AUTHORS b/AUTHORS index 2bf82b3..32b105f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -7,5 +7,5 @@ Michele Lazzeri: Custom archive names newtonne: Read encryption password from external file Robin `ypid` Schneider: Support additional options of Borg Scott Squires: Custom archive names -Thomas LÉVEIL: Support for a keep_minutely prune option +Thomas LÉVEIL: Support for a keep_minutely prune option. Support for the --json option Nick Whyte: Support prefix filtering for archive consistency checks diff --git a/borgmatic/borg/info.py b/borgmatic/borg/info.py index c55e365..359da3f 100644 --- a/borgmatic/borg/info.py +++ b/borgmatic/borg/info.py @@ -7,8 +7,9 @@ from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS logger = logging.getLogger(__name__) -def display_archives_info(verbosity, repository, storage_config, local_path='borg', - remote_path=None): +def display_archives_info( + verbosity, repository, storage_config, local_path='borg', remote_path=None, json=False +): ''' Given a verbosity flag, a local or remote repository path, and a storage config dict, display summary information for Borg archives in the repository. @@ -19,6 +20,7 @@ def display_archives_info(verbosity, repository, storage_config, local_path='bor (local_path, 'info', repository) + (('--remote-path', remote_path) if remote_path else ()) + (('--lock-wait', str(lock_wait)) if lock_wait else ()) + + (('--json',) if json else ()) + { VERBOSITY_SOME: ('--info',), VERBOSITY_LOTS: ('--debug',), @@ -26,4 +28,6 @@ def display_archives_info(verbosity, repository, storage_config, local_path='bor ) logger.debug(' '.join(full_command)) - subprocess.check_call(full_command) + + output = subprocess.check_output(full_command) + return output.decode() if output is not None else None diff --git a/borgmatic/borg/list.py b/borgmatic/borg/list.py index 4bc24f7..fb11091 100644 --- a/borgmatic/borg/list.py +++ b/borgmatic/borg/list.py @@ -7,7 +7,9 @@ from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS logger = logging.getLogger(__name__) -def list_archives(verbosity, repository, storage_config, local_path='borg', remote_path=None, json=False): +def list_archives( + verbosity, repository, storage_config, local_path='borg', remote_path=None, json=False +): ''' Given a verbosity flag, a local or remote repository path, and a storage config dict, list Borg archives in the repository. diff --git a/borgmatic/commands/borgmatic.py b/borgmatic/commands/borgmatic.py index 3a22e4d..d3e2d04 100644 --- a/borgmatic/commands/borgmatic.py +++ b/borgmatic/commands/borgmatic.py @@ -97,8 +97,13 @@ def parse_arguments(*arguments): args = parser.parse_args(arguments) - if args.json and not args.list: - raise ValueError('The --json option can only be used with the --list option') + if args.json and not (args.list or args.info): + raise ValueError('The --json option can only be used with the --list or --info options') + + if args.json and args.list and args.info: + raise ValueError( + 'With the --json option, options --list and --info cannot be used together' + ) # If any of the action flags are explicitly requested, leave them as-is. Otherwise, assume # defaults: Mutate the given arguments to enable the default actions. @@ -205,13 +210,18 @@ def _run_commands_on_repository( sys.stdout.write(output) if args.info: logger.info('{}: Displaying summary info for archives'.format(repository)) - borg_info.display_archives_info( + output = borg_info.display_archives_info( args.verbosity, repository, storage, local_path=local_path, remote_path=remote_path, + json=args.json, ) + if args.json: + json_results.append(json.loads(output)) + else: + sys.stdout.write(output) def main(): # pragma: no cover diff --git a/borgmatic/tests/integration/commands/test_borgmatic.py b/borgmatic/tests/integration/commands/test_borgmatic.py index 8cd1f76..bd5f3e7 100644 --- a/borgmatic/tests/integration/commands/test_borgmatic.py +++ b/borgmatic/tests/integration/commands/test_borgmatic.py @@ -90,6 +90,16 @@ def test_parse_arguments_with_invalid_arguments_exits(): module.parse_arguments('--posix-me-harder') -def test_parse_arguments_with_json_flag_but_no_list_flag_raises_value_error(): +def test_parse_arguments_with_json_flag_with_list_or_info_flag_does_not_raise_any_error(): + module.parse_arguments('--list', '--json') + module.parse_arguments('--info', '--json') + + +def test_parse_arguments_with_json_flag_but_no_list_or_info_flag_raises_value_error(): with pytest.raises(ValueError): module.parse_arguments('--json') + + +def test_parse_arguments_with_json_flag_and_both_list_and_info_flag_raises_value_error(): + with pytest.raises(ValueError): + module.parse_arguments('--list', '--info', '--json') diff --git a/borgmatic/tests/unit/borg/test_info.py b/borgmatic/tests/unit/borg/test_info.py index 7a1cbbe..43e881e 100644 --- a/borgmatic/tests/unit/borg/test_info.py +++ b/borgmatic/tests/unit/borg/test_info.py @@ -8,7 +8,7 @@ from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS def insert_subprocess_mock(check_call_command, **kwargs): subprocess = flexmock(module.subprocess) - subprocess.should_receive('check_call').with_args(check_call_command, **kwargs).once() + subprocess.should_receive('check_output').with_args(check_call_command, **kwargs).once() INFO_COMMAND = ('borg', 'info', 'repo') @@ -44,6 +44,17 @@ def test_display_archives_info_with_verbosity_lots_calls_borg_with_debug_paramet ) +def test_display_archives_info_with_json_calls_borg_with_json_parameter(): + insert_subprocess_mock(INFO_COMMAND + ('--json',)) + + module.display_archives_info( + verbosity=None, + repository='repo', + storage_config={}, + json=True, + ) + + def test_display_archives_info_with_local_path_calls_borg_via_local_path(): insert_subprocess_mock(('borg1',) + INFO_COMMAND[1:])