From ccbd0b608b5dc3293b17a3c69a7f7c8f3e5bf339 Mon Sep 17 00:00:00 2001 From: Dan Helfman Date: Sat, 3 Aug 2019 15:13:54 -0700 Subject: [PATCH] Do not treat Borg warnings (exit code 1) as failures (#204). --- NEWS | 1 + borgmatic/borg/init.py | 8 ++++++-- borgmatic/execute.py | 3 ++- setup.py | 2 +- tests/integration/test_execute.py | 14 ++++++++++++-- tests/unit/borg/test_init.py | 19 +++++++++++++++++++ 6 files changed, 41 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 816e8df..8a6caf2 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,5 @@ 1.3.14 + * #204: Do not treat Borg warnings (exit code 1) as failures. * When validating configuration files, require strings instead of allowing any scalar type. 1.3.13 diff --git a/borgmatic/borg/init.py b/borgmatic/borg/init.py index f3f9e8a..8dc392f 100644 --- a/borgmatic/borg/init.py +++ b/borgmatic/borg/init.py @@ -1,7 +1,7 @@ import logging import subprocess -from borgmatic.execute import execute_command +from borgmatic.execute import BORG_ERROR_EXIT_CODE, execute_command logger = logging.getLogger(__name__) @@ -44,4 +44,8 @@ def initialize_repository( ) # Don't use execute_command() here because it doesn't support interactive prompts. - subprocess.check_call(init_command) + try: + subprocess.check_call(init_command) + except subprocess.CalledProcessError as error: + if error.returncode >= BORG_ERROR_EXIT_CODE: + raise diff --git a/borgmatic/execute.py b/borgmatic/execute.py index 261e0e4..efa0385 100644 --- a/borgmatic/execute.py +++ b/borgmatic/execute.py @@ -5,6 +5,7 @@ logger = logging.getLogger(__name__) ERROR_OUTPUT_MAX_LINE_COUNT = 25 +BORG_ERROR_EXIT_CODE = 2 def execute_and_log_output(full_command, output_log_level, shell): @@ -31,7 +32,7 @@ def execute_and_log_output(full_command, output_log_level, shell): logger.log(output_log_level, remaining_output) exit_code = process.poll() - if exit_code != 0: + if exit_code >= BORG_ERROR_EXIT_CODE: # If an error occurs, include its output in the raised exception so that we don't # inadvertently hide error output. if len(last_lines) == ERROR_OUTPUT_MAX_LINE_COUNT: diff --git a/setup.py b/setup.py index 19cd184..4d7911d 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import find_packages, setup -VERSION = '1.3.13' +VERSION = '1.3.14' setup( diff --git a/tests/integration/test_execute.py b/tests/integration/test_execute.py index 5807fcd..c9bd9a4 100644 --- a/tests/integration/test_execute.py +++ b/tests/integration/test_execute.py @@ -15,7 +15,16 @@ def test_execute_and_log_output_logs_each_line_separately(): module.execute_and_log_output(['echo', 'there'], output_log_level=logging.INFO, shell=False) +def test_execute_and_log_output_with_borg_warning_does_not_raise(): + flexmock(module.logger).should_receive('log') + + # Borg's exit code 1 is a warning, not an error. + module.execute_and_log_output(['false'], output_log_level=logging.INFO, shell=False) + + def test_execute_and_log_output_includes_borg_error_output_in_exception(): + flexmock(module.logger).should_receive('log') + with pytest.raises(subprocess.CalledProcessError) as error: module.execute_and_log_output(['grep'], output_log_level=logging.INFO, shell=False) @@ -25,6 +34,7 @@ def test_execute_and_log_output_includes_borg_error_output_in_exception(): def test_execute_and_log_output_truncates_long_borg_error_output(): flexmock(module).ERROR_OUTPUT_MAX_LINE_COUNT = 0 + flexmock(module.logger).should_receive('log') with pytest.raises(subprocess.CalledProcessError) as error: module.execute_and_log_output(['grep'], output_log_level=logging.INFO, shell=False) @@ -40,7 +50,7 @@ def test_execute_and_log_output_with_no_output_logs_nothing(): def test_execute_and_log_output_with_error_exit_status_raises(): - flexmock(module.logger).should_receive('log').never() + flexmock(module.logger).should_receive('log') with pytest.raises(subprocess.CalledProcessError): - module.execute_and_log_output(['false'], output_log_level=logging.INFO, shell=False) + module.execute_and_log_output(['grep'], output_log_level=logging.INFO, shell=False) diff --git a/tests/unit/borg/test_init.py b/tests/unit/borg/test_init.py index 19b26c2..4e789f4 100644 --- a/tests/unit/borg/test_init.py +++ b/tests/unit/borg/test_init.py @@ -35,6 +35,25 @@ def test_initialize_repository_calls_borg_with_parameters(): module.initialize_repository(repository='repo', encryption_mode='repokey') +def test_initialize_repository_does_not_raise_for_borg_init_warning(): + insert_info_command_not_found_mock() + flexmock(module.subprocess).should_receive('check_call').and_raise( + module.subprocess.CalledProcessError(1, 'borg init') + ) + + module.initialize_repository(repository='repo', encryption_mode='repokey') + + +def test_initialize_repository_raises_for_borg_init_error(): + insert_info_command_not_found_mock() + flexmock(module.subprocess).should_receive('check_call').and_raise( + module.subprocess.CalledProcessError(2, 'borg init') + ) + + with pytest.raises(subprocess.CalledProcessError): + module.initialize_repository(repository='repo', encryption_mode='repokey') + + def test_initialize_repository_skips_initialization_when_repository_already_exists(): insert_info_command_found_mock() flexmock(module.subprocess).should_receive('check_call').never()