Do not treat Borg warnings (exit code 1) as failures (#204).

This commit is contained in:
Dan Helfman 2019-08-03 15:13:54 -07:00
parent a7cc2ea803
commit ccbd0b608b
6 changed files with 41 additions and 6 deletions

1
NEWS
View file

@ -1,4 +1,5 @@
1.3.14 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. * When validating configuration files, require strings instead of allowing any scalar type.
1.3.13 1.3.13

View file

@ -1,7 +1,7 @@
import logging import logging
import subprocess import subprocess
from borgmatic.execute import execute_command from borgmatic.execute import BORG_ERROR_EXIT_CODE, execute_command
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -44,4 +44,8 @@ def initialize_repository(
) )
# Don't use execute_command() here because it doesn't support interactive prompts. # Don't use execute_command() here because it doesn't support interactive prompts.
try:
subprocess.check_call(init_command) subprocess.check_call(init_command)
except subprocess.CalledProcessError as error:
if error.returncode >= BORG_ERROR_EXIT_CODE:
raise

View file

@ -5,6 +5,7 @@ logger = logging.getLogger(__name__)
ERROR_OUTPUT_MAX_LINE_COUNT = 25 ERROR_OUTPUT_MAX_LINE_COUNT = 25
BORG_ERROR_EXIT_CODE = 2
def execute_and_log_output(full_command, output_log_level, shell): 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) logger.log(output_log_level, remaining_output)
exit_code = process.poll() 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 # If an error occurs, include its output in the raised exception so that we don't
# inadvertently hide error output. # inadvertently hide error output.
if len(last_lines) == ERROR_OUTPUT_MAX_LINE_COUNT: if len(last_lines) == ERROR_OUTPUT_MAX_LINE_COUNT:

View file

@ -1,6 +1,6 @@
from setuptools import find_packages, setup from setuptools import find_packages, setup
VERSION = '1.3.13' VERSION = '1.3.14'
setup( setup(

View file

@ -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) 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(): 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: with pytest.raises(subprocess.CalledProcessError) as error:
module.execute_and_log_output(['grep'], output_log_level=logging.INFO, shell=False) 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(): def test_execute_and_log_output_truncates_long_borg_error_output():
flexmock(module).ERROR_OUTPUT_MAX_LINE_COUNT = 0 flexmock(module).ERROR_OUTPUT_MAX_LINE_COUNT = 0
flexmock(module.logger).should_receive('log')
with pytest.raises(subprocess.CalledProcessError) as error: with pytest.raises(subprocess.CalledProcessError) as error:
module.execute_and_log_output(['grep'], output_log_level=logging.INFO, shell=False) 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(): 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): 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)

View file

@ -35,6 +35,25 @@ def test_initialize_repository_calls_borg_with_parameters():
module.initialize_repository(repository='repo', encryption_mode='repokey') 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(): def test_initialize_repository_skips_initialization_when_repository_already_exists():
insert_info_command_found_mock() insert_info_command_found_mock()
flexmock(module.subprocess).should_receive('check_call').never() flexmock(module.subprocess).should_receive('check_call').never()