Files

103 lines
4.6 KiB
Python
Raw Permalink Normal View History

"""First deployment / explicit port cutover. Only manages this application's resources."""
import argparse
import json
import os
from pathlib import Path
import shutil
import socket
import subprocess
import sys
ROOT = Path("/adamwen-work-space/xas-test-library")
NAME = "adamwen-script-console"
IMAGE = "adamwen/script-console:20261020-1"
UNIT = "adamwen-script-worker.service"
def validate_runtime(root):
for path in (root / "runtime", *(root / "runtime" / name for name in ("console-data", "library", "runs", "native-ipc"))):
if not path.resolve().is_relative_to(root.resolve()):
raise ValueError("Runtime must remain inside this project")
if any(part.is_symlink() for part in (path, *path.parents)):
raise ValueError("Runtime symlink rejected")
def command(*args, timeout=30):
return subprocess.check_output(args, text=True, timeout=timeout).strip()
def web_arguments(port):
validate_runtime(ROOT)
runtime = ROOT / "runtime"
args = ["docker", "run", "-d", "--name", NAME, "--label", "xas.application=script-console",
"--runtime", "runc", "--network", "host", "--user", "0:0", "--cap-drop", "ALL",
"--security-opt", "no-new-privileges", "--read-only", "--memory", "512m", "--cpus", "1",
"--pids-limit", "96", "--tmpfs", "/tmp:rw,nosuid,nodev,noexec,size=67108864",
"--log-driver", "local", "--log-opt", "max-size=10m", "--log-opt", "max-file=2",
"--env", "XAS_CONSOLE_ROOT=" + str(runtime)]
for name in ("console-data", "library", "runs", "native-ipc"):
path = runtime / name
args += ["--mount", f"type=bind,src={path},dst={path}" + (",readonly" if name in {"runs", "native-ipc"} else "")]
sources = json.loads((ROOT / "catalog/external-sources.json").read_text())["sources"]
for source in sources:
path = Path(source["path"])
if path.is_dir():
args += ["--mount", f"type=bind,src={path},dst={path},readonly"]
if path.name == "scripts":
for folder in ("logs", "results"):
logs = path.parent / folder
if logs.is_dir():
args += ["--mount", f"type=bind,src={logs},dst={logs},readonly"]
return args + [IMAGE, "--port", str(port)]
def prepare():
if os.geteuid() != 0 or ROOT.resolve() != ROOT or not (ROOT / "console/worker.py").is_file():
raise RuntimeError("Run through the documented sudo flow in the confirmed project")
validate_runtime(ROOT)
for name in ("console-data", "library", "runs", "native-ipc"):
path = ROOT / "runtime" / name
if path.is_symlink():
raise RuntimeError("Runtime symlink rejected")
path.mkdir(mode=0o750, exist_ok=True)
runner = command("docker", "image", "inspect", "--format", "{{.Id}}", "adamwen/test-console:20261013-2")
config = {"project": str(ROOT), "runner_image": runner,
"smoke_upstream": "http://100.64.0.55:18100",
"smoke_credential": "/dengyuyun-joy-work-space/workpc-claude-code/20261010-dsv41-trial/configs/vllm.env"}
path = ROOT / "deploy/console-worker.json"
if path.exists() and json.loads(path.read_text()) != config:
raise RuntimeError("Existing worker configuration differs; review before replacing")
path.write_text(json.dumps(config, indent=2))
path.chmod(0o600)
# Existing roots are 0700/root. Container root has no capabilities and read-only mounts.
# Runtime code used by non-root jobs needs read access, without changing original scripts.
for p in (ROOT / "console").rglob("*"):
p.chmod(0o755 if p.is_dir() else 0o644)
(ROOT / "console").chmod(0o755)
def start(port):
existing = command("docker", "ps", "-aq", "--filter", "name=^/" + NAME + "$")
if existing:
raise RuntimeError("Console container already exists; inspect before replacement")
with socket.socket() as probe:
probe.bind(("10.88.118.188", port))
print(command(*web_arguments(port)))
service = Path("/etc/systemd/system") / UNIT
content = (ROOT / "deploy/console-worker.service").read_bytes()
if service.exists() and service.read_bytes() != content:
raise RuntimeError("Existing systemd service differs")
service.write_bytes(content)
command("systemctl", "daemon-reload")
# Start only; no automatic boot scheduling is enabled by this deployment.
command("systemctl", "start", UNIT)
print(command("systemctl", "is-active", UNIT))
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--port", type=int, choices=[14440, 14442], default=14442)
options = parser.parse_args()
prepare()
start(options.port)