packages feed

hurl-workbench-cli-0.1.0.0: examples/multi-service/rei/launch.py

#!/usr/bin/env python3
"""Adapters for real Rei executables; database allocation belongs to the caller."""
import http.client
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import json
import os
from pathlib import Path
import pty
import select
import signal
import subprocess
import sys
import time

assert not any(name in os.environ for name in ["REI_PG_CONNECTION_STRING", "MORI_API_URL", "PGHOST", "PGDATABASE", "PGPASSFILE"]), "managed command inherited ambient database configuration"
root = Path(os.environ["EVIDENCE"])
variant = os.environ["VARIANT"]
role = sys.argv[1]


def event(name, **fields):
    data = (json.dumps(dict(event=name, at=time.time_ns(), **fields)) + "\n").encode()
    fd = os.open(root / "events.jsonl", os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o600)
    try:
        os.write(fd, data)
    finally:
        os.close(fd)


def pid(value):
    path = root / "pids.json"
    values = json.loads(path.read_text()) if path.exists() else []
    path.write_text(json.dumps(values + [value]))
    path.chmod(0o600)


def environment():
    return {"PATH": os.environ["PATH"], "HOME": str(root / "home"),
            "REI_CONFIG": str(root / "rei.yaml"), "REI_PG_CONNECTION_STRING": os.environ["DB"],
            "REI_API_PORT": os.environ["API_PORT"], "REI_API_TOKENS": os.environ["TOKEN"] + "=human",
            "MORI_API_URL": "http://127.0.0.1:" + os.environ["STUB_PORT"], "OTEL_SDK_DISABLED": "true"}


if role == "setup":
    (root / "home").mkdir(mode=0o700)
    (root / "workspace").mkdir(mode=0o700)
    (root / "rei.yaml").write_text("workspace:\n  path: " + str(root / "workspace") + "\n")
    (root / "rei.yaml").chmod(0o600)
    event("setup")
    raise SystemExit(0)
if role == "teardown":
    for value in json.loads((root / "pids.json").read_text()) if (root / "pids.json").exists() else []:
        try:
            os.kill(value, 0)
        except ProcessLookupError:
            continue
        raise RuntimeError("owned process survived cleanup")
    event("consumers-stopped")
    raise SystemExit(0)
if role == "probe-worker":
    raise SystemExit(0 if variant != "readiness-failure" and (root / "worker-ready").exists() else 1)

pid(os.getpid())
event("start", service=role)
if role == "stub":
    class Proxy(BaseHTTPRequestHandler):
        def log_message(self, *_):
            pass

        def request(self):
            if self.path == "/health":
                self.send_response(200)
                self.end_headers()
                self.wfile.write(b"ok")
                return
            if self.path == "/after" and variant == "unexpected-exit":
                time.sleep(2)
            if self.path == "/after" and variant == "cancellation":
                event("cancel-ready")
                time.sleep(60)
                return
            if self.path == "/after":
                self.path = "/api/v2/notes?tag=workbench-" + variant + "&first=10"
            body = self.rfile.read(int(self.headers.get("Content-Length", "0")))
            connection = http.client.HTTPConnection("127.0.0.1", int(os.environ["API_PORT"]), timeout=15)
            headers = {key: value for key, value in self.headers.items() if key.lower() not in ("host", "content-length")}
            connection.request(self.command, self.path, body=body, headers=headers)
            response = connection.getresponse()
            data = response.read()
            if self.command == "POST" and response.status == 201:
                event("mutation")
            if self.command == "GET" and response.status == 200:
                parsed = json.loads(data)
                if parsed.get("edges"):
                    event("observation", owner="rei-note-query-projection")
                    if variant == "unexpected-exit":
                        (root / "worker-exit").touch()
            self.send_response(response.status)
            self.send_header("Content-Type", "application/json")
            self.send_header("Content-Length", str(len(data)))
            self.end_headers()
            self.wfile.write(data)
            connection.close()

        do_GET = request
        do_POST = request

    server = ThreadingHTTPServer(("127.0.0.1", int(os.environ["STUB_PORT"])), Proxy)
    server.daemon_threads = True
    signal.signal(signal.SIGTERM, lambda *_: sys.exit(0))
    try:
        server.serve_forever(poll_interval=0.02)
    finally:
        server.server_close()
        event("stop", service=role)
    raise SystemExit(0)

argv = [os.environ["API"]] if role == "api" else [os.environ["REI"], "worker", "query-projections"]
master = slave = None
with (root / (role + ".log")).open("wb") as output:
    (root / (role + ".log")).chmod(0o600)
    if role == "worker":
        master, slave = pty.openpty()
    child = subprocess.Popen(argv, env=environment(), stdin=subprocess.DEVNULL,
                             stdout=slave if slave is not None else output, stderr=subprocess.STDOUT)
    pid(child.pid)
    if slave is not None:
        os.close(slave)
    stopping = False

    def stop(*_):
        global stopping
        stopping = True
        if child.poll() is None:
            child.terminate()

    signal.signal(signal.SIGTERM, stop)
    try:
        buffer = b""
        death_at = None
        while child.poll() is None:
            if master is not None and select.select([master], [], [], 0.02)[0]:
                try:
                    data = os.read(master, 65536)
                except OSError:
                    data = b""
                output.write(data)
                output.flush()
                buffer = (buffer + data)[-65536:]
                if b"Starting Note, assignment and definition query projection workers." in buffer:
                    (root / "worker-ready").touch()
            else:
                time.sleep(0.02)
            if role == "worker" and (root / "worker-exit").exists():
                death_at = death_at or time.monotonic() + 0.15
                if time.monotonic() >= death_at:
                    child.terminate()
                    child.wait(timeout=4)
                    raise SystemExit(9)
        raise SystemExit(0 if stopping else child.returncode)
    finally:
        if child.poll() is None:
            child.terminate()
        child.wait(timeout=4)
        if master is not None:
            os.close(master)
        if role == "worker":
            (root / "worker-ready").unlink(missing_ok=True)
        event("stop", service=role)