async def run_task(context, to_cancellable_process): """Run the task, sending stdout+stderr to files. https://github.com/python/asyncio/blob/master/examples/subprocess_shell.py Args: context (scriptworker.context.Context): the scriptworker context. 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 """ env = deepcopy(os.environ) env["TASK_ID"] = context.task_id or "None" kwargs = { "stdout": PIPE, "stderr": PIPE, "stdin": None, "close_fds": True, "preexec_fn": lambda: os.setsid(), "env": env } # pragma: no branch subprocess = await asyncio.create_subprocess_exec( *context.config["task_script"], **kwargs) context.proc = await to_cancellable_process(TaskProcess(subprocess)) timeout = context.config["task_max_timeout"] with get_log_filehandle(context) as log_filehandle: stderr_future = asyncio.ensure_future( pipe_to_log(context.proc.process.stderr, filehandles=[log_filehandle])) stdout_future = asyncio.ensure_future( pipe_to_log(context.proc.process.stdout, filehandles=[log_filehandle])) try: _, pending = await asyncio.wait([stderr_future, stdout_future], timeout=timeout) if pending: message = "Exceeded task_max_timeout of {} seconds".format( timeout) log.warning(message) await context.proc.stop() raise ScriptWorkerTaskException( message, exit_code=context.config["task_max_timeout_status"]) finally: # in the case of a timeout, this will be -15. # this code is in the finally: block so we still get the final # log lines. exitcode = await context.proc.process.wait() # make sure we haven't lost any of the logs await asyncio.wait([stdout_future, stderr_future]) # add an exit code line at the end of the log status_line = "exit code: {}".format(exitcode) if exitcode < 0: status_line = "Automation Error: python exited with signal {}".format( exitcode) log.info(status_line) print(status_line, file=log_filehandle) stopped_due_to_worker_shutdown = context.proc.stopped_due_to_worker_shutdown context.proc = None if stopped_due_to_worker_shutdown: raise WorkerShutdownDuringTask return 1 if exitcode != 0 else 0
async def test_stop_handle_process_lookup_error(): process = MagicMock() process.terminate.side_effect = ProcessLookupError task_process = TaskProcess(process) await task_process.stop()
async def test_set_killed_due_to_worker_shutdown(): task_process = TaskProcess(MagicMock()) assert task_process.stopped_due_to_worker_shutdown is False await task_process.worker_shutdown_stop() assert task_process.stopped_due_to_worker_shutdown is True