use exit_code_indicates_error and modify it to accept a command

This commit is contained in:
Divyansh Singh 2023-03-22 16:23:53 +05:30
parent 09183464cd
commit bd235f0426
4 changed files with 38 additions and 42 deletions

View file

@ -291,7 +291,7 @@ def collect_special_file_paths(
capture_stderr=True, capture_stderr=True,
working_directory=working_directory, working_directory=working_directory,
extra_environment=borg_environment, extra_environment=borg_environment,
raise_on_exit_code_one=False, treat_exit_code_warning_as_error=False,
) )
paths = tuple( paths = tuple(

View file

@ -11,7 +11,7 @@ ERROR_OUTPUT_MAX_LINE_COUNT = 25
BORG_ERROR_EXIT_CODE = 2 BORG_ERROR_EXIT_CODE = 2
def exit_code_indicates_error(process, exit_code, borg_local_path=None): def exit_code_indicates_error(command, exit_code, borg_local_path=None):
''' '''
Return True if the given exit code from running a command corresponds to an error. If a Borg Return True if the given exit code from running a command corresponds to an error. If a Borg
local path is given and matches the process' command, then treat exit code 1 as a warning local path is given and matches the process' command, then treat exit code 1 as a warning
@ -20,8 +20,6 @@ def exit_code_indicates_error(process, exit_code, borg_local_path=None):
if exit_code is None: if exit_code is None:
return False return False
command = process.args.split(' ') if isinstance(process.args, str) else process.args
if borg_local_path and command[0] == borg_local_path: if borg_local_path and command[0] == borg_local_path:
return bool(exit_code < 0 or exit_code >= BORG_ERROR_EXIT_CODE) return bool(exit_code < 0 or exit_code >= BORG_ERROR_EXIT_CODE)
@ -121,8 +119,9 @@ def log_outputs(processes, exclude_stdouts, output_log_level, borg_local_path):
if exit_code is None: if exit_code is None:
still_running = True still_running = True
command = process.args.split(' ') if isinstance(process.args, str) else process.args
# If any process errors, then raise accordingly. # If any process errors, then raise accordingly.
if exit_code_indicates_error(process, exit_code, borg_local_path): if exit_code_indicates_error(command, exit_code, borg_local_path):
# 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.
output_buffer = output_buffer_for_process(process, exclude_stdouts) output_buffer = output_buffer_for_process(process, exclude_stdouts)
@ -213,12 +212,7 @@ def execute_command(
def execute_command_and_capture_output( def execute_command_and_capture_output(
full_command, full_command, capture_stderr=False, shell=False, extra_environment=None, working_directory=None,
capture_stderr=False,
shell=False,
extra_environment=None,
working_directory=None,
raise_on_exit_code_one=True,
): ):
''' '''
Execute the given command (a sequence of command/argument strings), capturing and returning its Execute the given command (a sequence of command/argument strings), capturing and returning its
@ -244,10 +238,10 @@ def execute_command_and_capture_output(
cwd=working_directory, cwd=working_directory,
) )
logger.warning('Command output: {}'.format(output)) logger.warning('Command output: {}'.format(output))
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as error:
if raise_on_exit_code_one or e.returncode != 1: if exit_code_indicates_error(error.returncode):
raise raise
output = e.output output = error.output
logger.warning('Command output: {}'.format(output)) logger.warning('Command output: {}'.format(output))
return output.decode() if output is not None else None return output.decode() if output is not None else None

View file

@ -138,10 +138,10 @@ def test_log_outputs_kills_other_processes_when_one_errors():
process = subprocess.Popen(['grep'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) process = subprocess.Popen(['grep'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
flexmock(module).should_receive('exit_code_indicates_error').with_args( flexmock(module).should_receive('exit_code_indicates_error').with_args(
process, None, 'borg' ['grep'], None, 'borg'
).and_return(False) ).and_return(False)
flexmock(module).should_receive('exit_code_indicates_error').with_args( flexmock(module).should_receive('exit_code_indicates_error').with_args(
process, 2, 'borg' ['grep'], 2, 'borg'
).and_return(True) ).and_return(True)
other_process = subprocess.Popen( other_process = subprocess.Popen(
['sleep', '2'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT ['sleep', '2'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT
@ -245,10 +245,10 @@ def test_log_outputs_truncates_long_error_output():
process = subprocess.Popen(['grep'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) process = subprocess.Popen(['grep'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
flexmock(module).should_receive('exit_code_indicates_error').with_args( flexmock(module).should_receive('exit_code_indicates_error').with_args(
process, None, 'borg' ['grep'], None, 'borg'
).and_return(False) ).and_return(False)
flexmock(module).should_receive('exit_code_indicates_error').with_args( flexmock(module).should_receive('exit_code_indicates_error').with_args(
process, 2, 'borg' ['grep'], 2, 'borg'
).and_return(True) ).and_return(True)
flexmock(module).should_receive('output_buffer_for_process').and_return(process.stdout) flexmock(module).should_receive('output_buffer_for_process').and_return(process.stdout)

View file

@ -7,32 +7,32 @@ from borgmatic import execute as module
@pytest.mark.parametrize( @pytest.mark.parametrize(
'process,exit_code,borg_local_path,expected_result', 'command,exit_code,borg_local_path,expected_result',
( (
(flexmock(args=['grep']), 2, None, True), (['grep'], 2, None, True),
(flexmock(args=['grep']), 2, 'borg', True), (['grep'], 2, 'borg', True),
(flexmock(args=['borg']), 2, 'borg', True), (['borg'], 2, 'borg', True),
(flexmock(args=['borg1']), 2, 'borg1', True), (['borg1'], 2, 'borg1', True),
(flexmock(args=['grep']), 1, None, True), (['grep'], 1, None, True),
(flexmock(args=['grep']), 1, 'borg', True), (['grep'], 1, 'borg', True),
(flexmock(args=['borg']), 1, 'borg', False), (['borg'], 1, 'borg', False),
(flexmock(args=['borg1']), 1, 'borg1', False), (['borg1'], 1, 'borg1', False),
(flexmock(args=['grep']), 0, None, False), (['grep'], 0, None, False),
(flexmock(args=['grep']), 0, 'borg', False), (['grep'], 0, 'borg', False),
(flexmock(args=['borg']), 0, 'borg', False), (['borg'], 0, 'borg', False),
(flexmock(args=['borg1']), 0, 'borg1', False), (['borg1'], 0, 'borg1', False),
# -9 exit code occurs when child process get SIGKILLed. # -9 exit code occurs when child process get SIGKILLed.
(flexmock(args=['grep']), -9, None, True), (['grep'], -9, None, True),
(flexmock(args=['grep']), -9, 'borg', True), (['grep'], -9, 'borg', True),
(flexmock(args=['borg']), -9, 'borg', True), (['borg'], -9, 'borg', True),
(flexmock(args=['borg1']), -9, 'borg1', True), (['borg1'], -9, 'borg1', True),
(flexmock(args=['borg']), None, None, False), (['borg'], None, None, False),
), ),
) )
def test_exit_code_indicates_error_respects_exit_code_and_borg_local_path( def test_exit_code_indicates_error_respects_exit_code_and_borg_local_path(
process, exit_code, borg_local_path, expected_result command, exit_code, borg_local_path, expected_result
): ):
assert module.exit_code_indicates_error(process, exit_code, borg_local_path) is expected_result assert module.exit_code_indicates_error(command, exit_code, borg_local_path) is expected_result
def test_command_for_process_converts_sequence_command_to_string(): def test_command_for_process_converts_sequence_command_to_string():
@ -239,7 +239,7 @@ def test_execute_command_and_capture_output_with_capture_stderr_returns_stderr()
assert output == expected_output assert output == expected_output
def test_execute_command_and_capture_output_returns_output_with_raise_on_exit_code_one_false(): def test_execute_command_and_capture_output_returns_output_when_error_code_is_one():
full_command = ['foo', 'bar'] full_command = ['foo', 'bar']
expected_output = '[]' expected_output = '[]'
err_output = b'[]' err_output = b'[]'
@ -247,22 +247,24 @@ def test_execute_command_and_capture_output_returns_output_with_raise_on_exit_co
flexmock(module.subprocess).should_receive('check_output').with_args( flexmock(module.subprocess).should_receive('check_output').with_args(
full_command, stderr=None, shell=False, env=None, cwd=None full_command, stderr=None, shell=False, env=None, cwd=None
).and_raise(subprocess.CalledProcessError(1, full_command, err_output)).once() ).and_raise(subprocess.CalledProcessError(1, full_command, err_output)).once()
flexmock(module).should_receive('exit_code_indicates_error').and_return(False).once()
output = module.execute_command_and_capture_output(full_command, raise_on_exit_code_one=False) output = module.execute_command_and_capture_output(full_command)
assert output == expected_output assert output == expected_output
def test_execute_command_and_capture_output_returns_output_with_raise_on_exit_code_one_false_and_exit_code_not_one(): def test_execute_command_and_capture_output_returns_output_when_error_code_not_one():
full_command = ['foo', 'bar'] full_command = ['foo', 'bar']
expected_output = '[]' expected_output = '[]'
flexmock(module.os, environ={'a': 'b'}) flexmock(module.os, environ={'a': 'b'})
flexmock(module.subprocess).should_receive('check_output').with_args( flexmock(module.subprocess).should_receive('check_output').with_args(
full_command, stderr=None, shell=False, env=None, cwd=None full_command, stderr=None, shell=False, env=None, cwd=None
).and_raise(subprocess.CalledProcessError(2, full_command, expected_output)).once() ).and_raise(subprocess.CalledProcessError(2, full_command, expected_output)).once()
flexmock(module).should_receive('exit_code_indicates_error').and_return(True).once()
with pytest.raises(subprocess.CalledProcessError): with pytest.raises(subprocess.CalledProcessError):
module.execute_command_and_capture_output(full_command, raise_on_exit_code_one=False) module.execute_command_and_capture_output(full_command)
def test_execute_command_and_capture_output_returns_output_with_shell(): def test_execute_command_and_capture_output_returns_output_with_shell():