#!/usr/bin/env python3 """Call the credential-isolated Hermes SCM broker over its fixed cluster origin.""" from __future__ import annotations import json import urllib.request from collections.abc import Callable import deadline_http from gitea_api_policy import PolicyError BROKER_ORIGIN = "http://hermes-scm-broker.hermes-scm.svc.cluster.local:9081" MAX_RESPONSE_BYTES = 2 * 1024 * 1024 def _open(request: urllib.request.Request, timeout: int): """Open one broker exchange under a killable absolute deadline. The helper process keeps every request on its fixed in-cluster origin by rejecting redirects before they are followed. """ return deadline_http.open_bounded( request, maximum=MAX_RESPONSE_BYTES, timeout=timeout ) def request( endpoint: str, payload: dict[str, object], *, opener: Callable[..., object] = _open, ) -> bytes: """Send one bounded broker operation without any repository credential.""" if endpoint not in {"/v1/metadata", "/v1/drafts"}: raise PolicyError("SCM broker operation is outside the client allowlist") body = json.dumps(payload, separators=(",", ":")).encode("utf-8") if len(body) > 64 * 1024: raise PolicyError("SCM broker request exceeds the safe size limit") outgoing = urllib.request.Request( BROKER_ORIGIN + endpoint, data=body, method="POST", headers={ "Accept": "application/json", "Content-Type": "application/json", "User-Agent": "hermes-scm-broker-client/1", }, ) with opener(outgoing, timeout=30) as response: # type: ignore[attr-defined] status = getattr(response, "status", None) if status is None and hasattr(response, "getcode"): status = response.getcode() # type: ignore[attr-defined] if status != 200: raise PolicyError("SCM broker returned an unexpected HTTP status") content_type = response.headers.get_content_type() # type: ignore[attr-defined] if content_type != "application/json": raise PolicyError("SCM broker returned an unexpected response type") result = response.read(MAX_RESPONSE_BYTES + 1) # type: ignore[attr-defined] if len(result) > MAX_RESPONSE_BYTES: raise PolicyError("SCM broker response exceeds the safe size limit") json.loads(result) return result def read(path: str, *, opener: Callable[..., object] = _open) -> bytes: """Read explicitly allowed Atlas metadata through the broker.""" return request("/v1/metadata", {"path": path}, opener=opener) def create_draft( repo: str, *, base: str, head: str, head_sha: str, title: str, body: str, opener: Callable[..., object] = _open, ) -> bytes: """Create one verified draft through the broker for human review.""" return request( "/v1/drafts", { "base": base, "body": body, "head": head, "head_sha": head_sha, "repo": repo, "title": title, }, opener=opener, )