Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion scriptworker_client/src/scriptworker_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,5 +181,5 @@ async def _handle_asyncio_loop(async_main, config, task):
try:
await async_main(config, task)
except ClientError as exc:
log.exception("Failed to run async_main")
log.exception(f"Failed to run async_main; exiting {exc.exit_code}")
sys.exit(exc.exit_code)
23 changes: 21 additions & 2 deletions scriptworker_client/src/scriptworker_client/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
Union,
)
from urllib.parse import unquote, urlparse
from scriptworker_client.exceptions import TaskError
from scriptworker_client.exceptions import ClientError, TaskError

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -166,6 +166,13 @@ def get_log_filehandle(log_path=None):


# run_command {{{1
def _get_exception_kwargs(exception, exitcode, copy_exit_codes):
kwargs = {}
if issubclass(exception, ClientError) and exitcode in copy_exit_codes:
kwargs["exit_code"] = exitcode
return kwargs


async def run_command(
cmd,
log_path=None,
Expand All @@ -175,6 +182,9 @@ async def run_command(
env=None,
exception=None,
expected_exit_codes=(0,),
# https://stackoverflow.com/questions/18731791/determining-if-a-python-subprocess-segmentation-faults
# Shell exit codes range from 0 to 255. Therefore 245 == -11, 241 == -15
copy_exit_codes=(245, 241),
output_log_on_exception=False,
):
"""Run a command using ``asyncio.create_subprocess_exec``.
Expand Down Expand Up @@ -205,6 +215,9 @@ async def run_command(
expected_exit_codes (list, optional): the list of exit codes for
a successful run. Only used if ``exception`` is not ``None``.
Defaults to ``(0, )``.
copy_exit_codes (list, optional): the list of exit codes that we
set ``exit_code`` to if ``exception`` is an instance of
``ClientError``. Defaults to ``(245, 241)``.
output_log_on_exception (bool, optional): log the output log if we're
raising an exception.

Expand Down Expand Up @@ -242,8 +255,14 @@ async def run_command(
if output_log_on_exception:
log_filehandle.seek(0)
log_contents = log_filehandle.read()
exc_kwargs = _get_exception_kwargs(exception, exitcode, copy_exit_codes)
raise exception(
"%s in %s exited %s!\n%s", log_cmd, cwd, exitcode, log_contents
"%s in %s exited %s!\n%s",
log_cmd,
cwd,
exitcode,
log_contents,
**exc_kwargs,
)
log.info("%s in %s exited %d", log_cmd, cwd, exitcode)
return exitcode
Expand Down
2 changes: 1 addition & 1 deletion scriptworker_client/tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ async def async_error(*args, **kwargs):
await client._handle_asyncio_loop(async_error, {}, {})

assert excinfo.value.code == 42
m.exception.assert_called_once_with("Failed to run async_main")
m.exception.assert_called_once_with("Failed to run async_main; exiting 42")


def test_init_config_cli(mocker, tmpdir):
Expand Down
35 changes: 23 additions & 12 deletions scriptworker_client/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ def test_get_log_filehandle(path, tmpdir):
False,
),
(
["bash", "-c", ">&2 echo bar && echo foo && exit 1"],
["bash", "-c", ">&2 echo bar && echo foo && exit 3"],
1,
["foo\nbar\n", "bar\nfoo\n"],
TaskError,
Expand All @@ -177,8 +177,8 @@ def test_get_log_filehandle(path, tmpdir):
True,
),
(
["bash", "-c", ">&2 echo bar && echo foo && exit 1"],
1,
["bash", "-c", ">&2 echo bar && echo foo && exit -11"],
245,
["foo\nbar\n", "bar\nfoo\n"],
TaskError,
True,
Expand All @@ -189,7 +189,14 @@ def test_get_log_filehandle(path, tmpdir):
)
@pytest.mark.asyncio
async def test_run_command(
command, status, expected_log, exception, output_log, env, raises, tmpdir
command,
status,
expected_log,
exception,
output_log,
env,
raises,
tmpdir,
):
"""``run_command`` runs the expected command, logs its output, and exits
with its exit status. If ``exception`` is set and we exit non-zero, we
Expand All @@ -201,14 +208,18 @@ async def test_run_command(
log_path = os.path.join(tmpdir, "log")
if raises:
with pytest.raises(exception):
await utils.run_command(
command,
log_path=log_path,
cwd=tmpdir,
env=env,
exception=exception,
output_log_on_exception=output_log,
)
try:
await utils.run_command(
command,
log_path=log_path,
cwd=tmpdir,
env=env,
exception=exception,
output_log_on_exception=output_log,
)
except exception as exc:
assert exc.exit_code == status
raise exc
else:
assert (
await utils.run_command(
Expand Down