feat: initialize XAS test console with native execution

This commit is contained in:
AdamWen95
2026-10-14 09:38:58 +08:00
commit 438844b01a
83 changed files with 9334 additions and 0 deletions
+175
View File
@@ -0,0 +1,175 @@
#!/usr/bin/env python3
"""Run only the bundled synthetic acceptance task, with bounded lifetime."""
import argparse
import ctypes
from datetime import datetime, timezone
import hashlib
import json
import math
import os
from pathlib import Path
import re
import signal
import subprocess
import sys
import time
MODES = ("success", "failure", "invalid-report", "wait")
def now():
return datetime.now(timezone.utc).isoformat()
def write_json(path, value):
temporary = path.with_suffix(".tmp")
with temporary.open("x", encoding="utf-8") as stream:
json.dump(value, stream, ensure_ascii=False, indent=2)
stream.write("\n")
stream.flush()
os.fsync(stream.fileno())
temporary.replace(path)
def subreaper():
# Adopt the fixture's child so cancellation can reap it even in containers.
if sys.platform == "linux":
libc = ctypes.CDLL(None, use_errno=True)
if libc.prctl(36, 1, 0, 0, 0) != 0:
raise OSError(ctypes.get_errno(), "cannot enable child subreaper")
def cleanup(proc):
if os.name == "posix":
for sig in (signal.SIGTERM, signal.SIGKILL):
try:
os.killpg(proc.pid, sig)
except ProcessLookupError:
break
if sig == signal.SIGTERM:
time.sleep(.15)
elif proc.poll() is None:
proc.terminate()
try:
proc.wait(timeout=2)
except subprocess.TimeoutExpired:
proc.kill()
proc.wait(timeout=2)
if sys.platform == "linux":
deadline = time.monotonic() + 2
while True:
try:
pid, _ = os.waitpid(-1, os.WNOHANG)
except ChildProcessError:
break
if pid == 0:
if time.monotonic() >= deadline:
raise RuntimeError("child cleanup could not be confirmed")
time.sleep(.02)
def read_result(folder, run_id):
path = folder / "result.json"
if path.is_symlink() or path.stat().st_size > 65536:
raise ValueError("invalid result file")
result = json.loads(path.read_text(encoding="utf-8"))
if (not isinstance(result, dict) or result.get("run_id") != run_id
or result.get("synthetic") is not True
or type(result.get("checks_passed")) is not int
or result["checks_passed"] != 3):
raise ValueError("result does not satisfy the acceptance contract")
return result
def arguments():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--mode", choices=MODES, default=os.getenv("RD_OPTION_MODE", "success"))
parser.add_argument("--run-id", default=os.getenv("RD_JOB_EXECID"))
parser.add_argument("--timeout", type=float, default=os.getenv("RD_OPTION_TIMEOUT", "8"))
parser.add_argument("--results-root", type=Path,
default=Path(os.getenv("XAS_RESULTS_ROOT", "/home/rundeck/test-results")))
args = parser.parse_args()
if args.mode not in MODES:
parser.error("unregistered mode")
if not args.run_id or not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_-]{0,63}", args.run_id):
parser.error("run-id must be 1-64 letters, digits, underscores or hyphens")
if not math.isfinite(args.timeout) or not 2 <= args.timeout <= 30:
parser.error("timeout must be between 2 and 30 seconds")
if os.name != "posix" and args.mode == "wait":
parser.error("wait/cancellation acceptance requires Linux")
return args
def main():
args = arguments()
os.umask(0o077)
args.results_root.mkdir(parents=True, exist_ok=True)
folder = args.results_root.resolve() / args.run_id
try:
folder.mkdir()
except FileExistsError:
print("Run ID already exists; history was preserved", file=sys.stderr)
return 2
source = Path(__file__).resolve().parent
summary = {"schema_version": 1, "run_id": args.run_id, "synthetic": True,
"mode": args.mode, "timeout_seconds": args.timeout,
"started_at": now(), "status": "running", "exit_code": None,
"source_sha256": {name: hashlib.sha256((source / name).read_bytes()).hexdigest()
for name in ("run_smoke.py", "smoke_task.py")}}
write_json(folder / "request.json", summary)
cancelled = False
def on_signal(signum, frame):
nonlocal cancelled
cancelled = True
signal.signal(signal.SIGTERM, on_signal)
signal.signal(signal.SIGINT, on_signal)
proc = None
rc = 1
try:
subreaper()
proc = subprocess.Popen([sys.executable, "-u", str(source / "smoke_task.py"),
args.mode, args.run_id, str(folder)],
start_new_session=(os.name == "posix"))
deadline = time.monotonic() + args.timeout
while proc.poll() is None and not cancelled and time.monotonic() < deadline:
time.sleep(.04)
summary["exit_code"] = proc.poll()
if cancelled:
summary["status"], rc = "cancelled", 130
elif proc.poll() is None:
summary["status"], rc = "timed_out", 124
elif proc.returncode != 0:
summary["status"] = "failed"
else:
try:
summary["result"] = read_result(folder, args.run_id)
summary["status"], rc = "succeeded", 0
except (OSError, ValueError, TypeError):
summary["status"] = "invalid_result"
except (OSError, RuntimeError) as exc:
summary["status"] = "runner_error"
summary["error_type"] = type(exc).__name__
finally:
if proc is not None:
try:
cleanup(proc)
summary["cleanup_confirmed"] = True
except (OSError, RuntimeError, subprocess.TimeoutExpired) as exc:
summary["status"], rc = "cleanup_failed", 1
summary["cleanup_confirmed"] = False
summary["error_type"] = type(exc).__name__
summary["worker_pid"] = proc.pid
summary["final_exit_code"] = proc.returncode
child_file = folder / "child.pid"
if child_file.exists():
summary["child_pid"] = int(child_file.read_text(encoding="utf-8"))
summary["finished_at"] = now()
write_json(folder / "summary.json", summary)
print("XAS_ACCEPTANCE_RESULT " + json.dumps(summary, ensure_ascii=False), flush=True)
return rc
if __name__ == "__main__":
sys.exit(main())