(
requirements_paths: list[str],
job_dir: str,
python_action: PythonAction,
)
| 420 | |
| 421 | |
| 422 | async def _jailed_python_execution( |
| 423 | requirements_paths: list[str], |
| 424 | job_dir: str, |
| 425 | python_action: PythonAction, |
| 426 | ) -> None: |
| 427 | execution_start = time.monotonic_ns() |
| 428 | |
| 429 | logger.info("Executing Python action with nsjail") |
| 430 | x = ":".join(requirements_paths) |
| 431 | logger.info(f"Requirements paths: {x}") |
| 432 | |
| 433 | requirements_mounts = "\n".join( |
| 434 | [ |
| 435 | MOUNT_TEMPLATE.format(REQUIREMENT_PATH=requirement_path) |
| 436 | for requirement_path in requirements_paths |
| 437 | ] |
| 438 | ) |
| 439 | |
| 440 | async with aiofiles.open( |
| 441 | os.path.join("admyral", "workers", "nsjail", "template.python_action.cfg") |
| 442 | ) as f: |
| 443 | nsjail_config_template = await f.read() |
| 444 | |
| 445 | nsjail_config = nsjail_config_template.format( |
| 446 | EXECUTOR_PATH=os.path.join(job_dir, "python_action_executor.py"), |
| 447 | ACTION_PATH=os.path.join(job_dir, "action.py"), |
| 448 | PYTHON_DEPENDENCIES=":".join([ADMYRAL_PYTHON_PATH] + requirements_paths), |
| 449 | PYTHON_DEPENDENCIES_MOUNT=requirements_mounts, |
| 450 | JOB_DIR=job_dir, |
| 451 | PATH=os.environ.get("PATH", ""), |
| 452 | ) |
| 453 | |
| 454 | nsjail_config_path = os.path.join(job_dir, "action.cfg") |
| 455 | async with aiofiles.open(nsjail_config_path, "w") as f: |
| 456 | await f.write(nsjail_config) |
| 457 | |
| 458 | # load secrets and inject as environment variables |
| 459 | env = await _load_secrets(python_action) |
| 460 | |
| 461 | cmd = [ |
| 462 | "nsjail", |
| 463 | "--config", |
| 464 | nsjail_config_path, |
| 465 | "--", |
| 466 | ADMYRAL_PYTHON_PATH, |
| 467 | "-u", # unbuffered binary stdout and stderr |
| 468 | "python_action_executor.py", |
| 469 | ] |
| 470 | # inject secrets by appending them as command line arguments |
| 471 | cmd_with_secrets = cmd + list( |
| 472 | itertools.chain.from_iterable( |
| 473 | [["-s", f"'{len(key)}|{key}{value}'"] for key, value in env.items()] |
| 474 | ) |
| 475 | ) |
| 476 | |
| 477 | async def _append_logs(logs: list[str]) -> None: |
| 478 | logger.info( |
| 479 | f"workflow_id={ctx.get().workflow_id} run_id={ctx.get().run_id} step_id={ctx.get().step_id} [_jailed_python_execution]: {''.join(logs)}" |
no test coverage detected