diff --git a/src/scriptworker/task.py b/src/scriptworker/task.py index b6ed3c68..06ca108c 100644 --- a/src/scriptworker/task.py +++ b/src/scriptworker/task.py @@ -636,7 +636,7 @@ async def run_task(context, to_cancellable_process): to_cancellable_process (types.Callable): tracks the process so that it can be stopped if the worker is shut down Returns: - int: 1 on failure, 0 on success + int: >= 1 on failure, 0 on success """ env = deepcopy(os.environ) @@ -668,6 +668,8 @@ async def run_task(context, to_cancellable_process): status_line = "exit code: {}".format(exitcode) if exitcode < 0: status_line = "Automation Error: python exited with signal {}".format(exitcode) + # we must return a value > 0 to signal an error + exitcode = 1 log.info(status_line) print(status_line, file=log_filehandle) stopped_due_to_worker_shutdown = context.proc.stopped_due_to_worker_shutdown diff --git a/tests/__init__.py b/tests/__init__.py index e402a4bd..13f22a16 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -28,6 +28,7 @@ } TIMEOUT_SCRIPT = os.path.join(os.path.dirname(__file__), "data", "long_running.py") +KILLED_SCRIPT = os.path.join(os.path.dirname(__file__), "data", "killed.py") AT_LEAST_PY38 = sys.version_info >= (3, 8) diff --git a/tests/data/killed.py b/tests/data/killed.py new file mode 100644 index 00000000..8d4609cf --- /dev/null +++ b/tests/data/killed.py @@ -0,0 +1,6 @@ +#!/usr/bin/python3 + +import os +import signal + +os.kill(0, signal.SIGKILL) diff --git a/tests/test_worker.py b/tests/test_worker.py index 9b9e5e54..0859c5a1 100644 --- a/tests/test_worker.py +++ b/tests/test_worker.py @@ -20,7 +20,7 @@ from scriptworker.exceptions import ScriptWorkerException, WorkerShutdownDuringTask from scriptworker.worker import RunTasks, do_run_task -from . import AT_LEAST_PY38, TIMEOUT_SCRIPT, create_async, create_finished_future, create_slow_async, create_sync, noop_async, noop_sync +from . import AT_LEAST_PY38, KILLED_SCRIPT, TIMEOUT_SCRIPT, create_async, create_finished_future, create_slow_async, create_sync, noop_async, noop_sync # constants helpers and fixtures {{{1 @@ -266,6 +266,27 @@ async def claim_work(*args, **kwargs): assert status == context.config["task_max_timeout_status"] +@pytest.mark.asyncio +async def test_run_tasks_killed(context, successful_queue, mocker): + temp_dir = os.path.join(context.config["work_dir"], "killed") + task = {"foo": "bar", "credentials": {"a": "b"}, "task": {"task_defn": True}} + context.config["task_script"] = (sys.executable, KILLED_SCRIPT, temp_dir) + context.config["task_max_timeout"] = 1 + context.queue = successful_queue + + async def claim_work(*args, **kwargs): + return {"tasks": [task]} + + mocker.patch.object(worker, "claim_work", new=claim_work) + mocker.patch.object(worker, "reclaim_task", new=noop_async) + mocker.patch.object(worker, "generate_cot", new=noop_sync) + mocker.patch.object(worker, "prepare_to_run_task", new=noop_sync) + mocker.patch.object(worker, "upload_artifacts", new=noop_async) + mocker.patch.object(worker, "complete_task", new=noop_async) + status = await worker.run_tasks(context) + assert status == STATUSES["failure"] + + _MOCK_CLAIM_WORK_RETURN = { "tasks": [ {