mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 19:59:26 +00:00
plane(1.1): PlaneProfile -- one TOML per cluster, env-overridable, no secrets
ops/k8s/profiles/kind.toml and k3s.toml hold the values that differ per cluster (exec, kube_context, registry, images, router_url, ax_server, memory, atespace, snapshots_bucket, image_pull_secret, pools); kind-images.json carries the digest pin ops/ax/up.sh wrote. load_profile() reads OUSAST_PLANE_PROFILE (name or path, default kind), applies OUSAST_<FIELD> and OUSAST_POOL_<NAME>_<FIELD>, and refuses an unknown key, a credential-shaped key or value, an exec outside local|remote and an images file without digest pins. `python -m openultrasast.plane.profile --print FIELD` serves shell scripts. The module-audit manifest is regenerated for the new module (counts 147/5, no orphan). Gates: pytest full suite 2002 passed, 1 failed (the module-audit manifest, fixed by regenerating it; tests/test_module_audit.py re-run exit 0), ruff check 0, ruff format 0, mypy 0. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
6bd44caa50
commit
8eac1b2c69
@@ -0,0 +1,266 @@
|
||||
"""The plane's configuration surface: one ``PlaneProfile`` per cluster (plane-on-kubernetes design section 1).
|
||||
|
||||
A profile is a TOML file under ``ops/k8s/profiles/`` (``kind.toml`` for the laptop's kind cluster, ``k3s.toml`` for
|
||||
the production cluster) holding every value that differs between clusters: the execution mode, the kube context,
|
||||
the registry and the digest-pinned images file, the router and ax-server addresses (empty: tunnel through the
|
||||
Kubernetes API), the memory store, the atespace, ax's snapshot bucket, the worker pools and the image pull secret.
|
||||
:func:`load_profile` reads the file ``OUSAST_PLANE_PROFILE`` names (a profile name or a path; default ``kind``) and
|
||||
then applies the environment: every scalar field is overridable by ``OUSAST_<FIELD>`` (``OUSAST_KUBE_CONTEXT``,
|
||||
``OUSAST_REGISTRY``, ``OUSAST_MEMORY``, ``OUSAST_ROUTER_URL``, ...), a pool by ``OUSAST_POOL_<NAME>_<FIELD>``.
|
||||
|
||||
A profile holds no secret: a key whose name ends in ``key``, ``secret``, ``token`` or ``password`` is refused, and so
|
||||
is any unknown key, an ``exec`` outside ``local``/``remote``, and an images file whose references are not pinned by
|
||||
digest (Agent Substrate rejects tags). Provider and store credentials stay in the environment or ``.env``.
|
||||
|
||||
``python -m openultrasast.plane.profile --print FIELD`` prints one value for shell scripts (``ops/ax/up.sh``):
|
||||
``registry``, ``kube_context``, ``images.runner``, ``pools.default.replicas``, ...
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import tomllib
|
||||
from collections.abc import Mapping
|
||||
from dataclasses import dataclass, field, fields
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
__all__ = ["DEFAULT_PROFILE", "PROFILES_DIR", "PlaneProfile", "Pool", "ProfileError", "load_images", "load_profile", "profiles_dir"]
|
||||
|
||||
DEFAULT_PROFILE = "kind"
|
||||
PROFILES_DIR = Path("ops/k8s/profiles")
|
||||
EXEC_MODES = ("local", "remote")
|
||||
_DIGEST = re.compile(r"\S+@sha256:[0-9a-f]{64}")
|
||||
_SECRET_KEY = re.compile(r"(key|secret|token|password)$", re.IGNORECASE)
|
||||
_SECRET_VALUE = re.compile(r"(sk-[A-Za-z0-9]{8,}|AKIA[0-9A-Z]{12,}|[A-Za-z0-9+/]{40,}={0,2}$)")
|
||||
_ENV_PREFIX = "OUSAST_"
|
||||
|
||||
|
||||
class ProfileError(ValueError):
|
||||
"""A profile that cannot be used; the message names the file and the field."""
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Pool:
|
||||
"""One WorkerPool's size: how many warm workers, and each worker's CPU and memory limit."""
|
||||
|
||||
replicas: int
|
||||
cpu: str
|
||||
memory: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PlaneProfile:
|
||||
"""The values of one cluster. ``images`` is resolved against the profile file's directory."""
|
||||
|
||||
name: str
|
||||
path: Path
|
||||
exec: str = "local"
|
||||
kube_context: str = ""
|
||||
registry: str = ""
|
||||
images: Path = Path("images.json")
|
||||
router_url: str = ""
|
||||
ax_server: str = ""
|
||||
memory: str = ""
|
||||
atespace: str = "default"
|
||||
snapshots_bucket: str = ""
|
||||
image_pull_secret: str = ""
|
||||
pools: Mapping[str, Pool] = field(default_factory=dict)
|
||||
|
||||
def load_images(self) -> dict[str, str]:
|
||||
"""The digest-pinned image references of ``images`` (``{"runner": ..., "engine": ...}``)."""
|
||||
return load_images(self.images)
|
||||
|
||||
def addresses(self) -> dict[str, str]:
|
||||
"""Every configured address, for ``ousast plane doctor`` (Req 1.2); an empty router or ax-server address
|
||||
means the Kubernetes API tunnel."""
|
||||
return {
|
||||
"exec": self.exec,
|
||||
"kube_context": self.kube_context,
|
||||
"registry": self.registry,
|
||||
"images": str(self.images),
|
||||
"router_url": self.router_url or "(tunnel through the kube context)",
|
||||
"ax_server": self.ax_server or "(tunnel through the kube context)",
|
||||
"memory": self.memory,
|
||||
"atespace": self.atespace,
|
||||
"snapshots_bucket": self.snapshots_bucket,
|
||||
"image_pull_secret": self.image_pull_secret or "(none)",
|
||||
}
|
||||
|
||||
|
||||
_SCALARS = {f.name: str(f.type) for f in fields(PlaneProfile) if f.name not in ("name", "path", "pools")}
|
||||
_POOL_FIELDS = {f.name: str(f.type) for f in fields(Pool)}
|
||||
|
||||
|
||||
def profiles_dir() -> Path:
|
||||
"""``ops/k8s/profiles`` of this checkout (``OUSAST_PLANE_PROFILES`` overrides; an installed package falls back
|
||||
to the working directory)."""
|
||||
override = os.environ.get("OUSAST_PLANE_PROFILES")
|
||||
if override:
|
||||
return Path(override)
|
||||
checkout = Path(__file__).resolve().parents[3] / PROFILES_DIR
|
||||
return checkout if checkout.is_dir() else PROFILES_DIR
|
||||
|
||||
|
||||
def load_images(path: Path) -> dict[str, str]:
|
||||
"""``{"runner": "<registry>/<name>@sha256:<64 hex>", ...}`` from a JSON images file; every value must be
|
||||
digest-pinned. A one-line file holding a single reference (``ops/ax/up.sh`` before this profile) reads as
|
||||
``{"runner": <line>}``."""
|
||||
try:
|
||||
text = path.read_text(encoding="utf-8").strip()
|
||||
except OSError as exc:
|
||||
raise ProfileError(f"images file {path}: {exc.strerror or exc}") from None
|
||||
try:
|
||||
loaded: Any = json.loads(text) if text.startswith("{") else {"runner": text}
|
||||
except ValueError as exc:
|
||||
raise ProfileError(f"images file {path}: not JSON ({exc})") from None
|
||||
if not isinstance(loaded, dict) or not loaded or not all(isinstance(k, str) and isinstance(v, str) for k, v in loaded.items()):
|
||||
raise ProfileError(f"images file {path}: expected an object of image names to references")
|
||||
for name, reference in loaded.items():
|
||||
if not _DIGEST.fullmatch(reference):
|
||||
raise ProfileError(f"images file {path}: {name} is not digest-pinned ({reference!r}); Agent Substrate rejects tags")
|
||||
return dict(loaded)
|
||||
|
||||
|
||||
def _reject_secret(where: str, key: str, value: object, declared: bool = False) -> None:
|
||||
"""A key that is not a declared field and is named like a credential, or any value shaped like one, is refused.
|
||||
``image_pull_secret`` is declared: its value is the *name* of a Kubernetes Secret, never its content."""
|
||||
if not declared and _SECRET_KEY.search(key):
|
||||
raise ProfileError(f"{where}: {key} looks like a credential; profiles hold no secret (use the environment or .env)")
|
||||
if isinstance(value, str) and _SECRET_VALUE.search(value):
|
||||
raise ProfileError(f"{where}: the value of {key} looks like a credential; profiles hold no secret")
|
||||
|
||||
|
||||
def _coerce(where: str, key: str, value: object, kind: str) -> Any:
|
||||
if kind == "int":
|
||||
if isinstance(value, bool) or not isinstance(value, int):
|
||||
raise ProfileError(f"{where}: {key} must be an integer, got {value!r}")
|
||||
return value
|
||||
if kind == "Path":
|
||||
if not isinstance(value, str) or not value:
|
||||
raise ProfileError(f"{where}: {key} must be a file name")
|
||||
return Path(value)
|
||||
if not isinstance(value, str):
|
||||
raise ProfileError(f"{where}: {key} must be a string, got {value!r}")
|
||||
return value
|
||||
|
||||
|
||||
def _pools(where: str, raw: object) -> dict[str, Pool]:
|
||||
if not isinstance(raw, dict):
|
||||
raise ProfileError(f"{where}: pools must be a table of pool names")
|
||||
out: dict[str, Pool] = {}
|
||||
for name, spec in raw.items():
|
||||
if not isinstance(spec, dict):
|
||||
raise ProfileError(f"{where}: pools.{name} must be a table")
|
||||
unknown = sorted(set(spec) - set(_POOL_FIELDS))
|
||||
if unknown:
|
||||
raise ProfileError(f"{where}: pools.{name} has unknown keys: {', '.join(unknown)}")
|
||||
missing = sorted(set(_POOL_FIELDS) - set(spec))
|
||||
if missing:
|
||||
raise ProfileError(f"{where}: pools.{name} lacks {', '.join(missing)}")
|
||||
for key, value in spec.items():
|
||||
_reject_secret(where, f"pools.{name}.{key}", value, declared=True)
|
||||
out[str(name)] = Pool(**{k: _coerce(where, f"pools.{name}.{k}", spec[k], _POOL_FIELDS[k]) for k in _POOL_FIELDS})
|
||||
return out
|
||||
|
||||
|
||||
def _env_overrides(environ: Mapping[str, str], where: str, values: dict[str, Any], pools: dict[str, Pool]) -> None:
|
||||
for name, kind in _SCALARS.items():
|
||||
raw = environ.get(_ENV_PREFIX + name.upper())
|
||||
if raw is not None and raw != "":
|
||||
value: Any = raw
|
||||
if kind == "int":
|
||||
try:
|
||||
value = int(raw)
|
||||
except ValueError:
|
||||
raise ProfileError(f"{where}: {_ENV_PREFIX}{name.upper()} must be an integer, got {raw!r}") from None
|
||||
values[name] = _coerce(where, _ENV_PREFIX + name.upper(), value, kind)
|
||||
pattern = re.compile(rf"^{_ENV_PREFIX}POOL_([A-Z0-9]+)_(REPLICAS|CPU|MEMORY)$")
|
||||
for variable, raw in environ.items():
|
||||
found = pattern.match(variable)
|
||||
if not found or raw == "":
|
||||
continue
|
||||
pool_name, key = found.group(1).lower(), found.group(2).lower()
|
||||
current = pools.get(pool_name)
|
||||
if current is None:
|
||||
raise ProfileError(f"{where}: {variable} names a pool the profile does not declare ({pool_name})")
|
||||
value = int(raw) if key == "replicas" and raw.isdigit() else raw
|
||||
pools[pool_name] = Pool(**{**vars(current), key: _coerce(where, variable, value, _POOL_FIELDS[key])})
|
||||
|
||||
|
||||
def load_profile(name: str | None = None, environ: Mapping[str, str] | None = None) -> PlaneProfile:
|
||||
"""The profile ``name`` (a name under :func:`profiles_dir` or a path to a ``.toml``; default
|
||||
``OUSAST_PLANE_PROFILE``, else ``kind``), with the environment's overrides applied; :class:`ProfileError`
|
||||
names what is wrong."""
|
||||
env = os.environ if environ is None else environ
|
||||
chosen = name or env.get("OUSAST_PLANE_PROFILE") or DEFAULT_PROFILE
|
||||
path = Path(chosen) if chosen.endswith(".toml") or "/" in chosen else profiles_dir() / f"{chosen}.toml"
|
||||
where = str(path)
|
||||
try:
|
||||
raw = tomllib.loads(path.read_text(encoding="utf-8"))
|
||||
except OSError as exc:
|
||||
raise ProfileError(f"profile {where}: {exc.strerror or exc}") from None
|
||||
except tomllib.TOMLDecodeError as exc:
|
||||
raise ProfileError(f"profile {where}: {exc}") from None
|
||||
for key, value in raw.items(): # a credential-shaped key is named as such before it is merely unknown
|
||||
_reject_secret(where, key, value, declared=key in _SCALARS)
|
||||
unknown = sorted(set(raw) - set(_SCALARS) - {"pools"})
|
||||
if unknown:
|
||||
raise ProfileError(f"{where}: unknown keys: {', '.join(unknown)}")
|
||||
values: dict[str, Any] = {}
|
||||
for key, value in raw.items():
|
||||
if key != "pools":
|
||||
values[key] = _coerce(where, key, value, _SCALARS[key])
|
||||
pools = _pools(where, raw.get("pools", {}))
|
||||
_env_overrides(env, where, values, pools)
|
||||
if values.get("exec", "local") not in EXEC_MODES:
|
||||
raise ProfileError(f"{where}: exec must be one of {', '.join(EXEC_MODES)}, got {values.get('exec')!r}")
|
||||
images = Path(values.get("images", "images.json"))
|
||||
if not images.is_absolute():
|
||||
images = path.resolve().parent / images
|
||||
values["images"] = images
|
||||
if images.is_file():
|
||||
load_images(images) # a committed file must be pinned; a file the release has not published yet is doctor's finding
|
||||
return PlaneProfile(name=path.stem, path=path, pools=pools, **values)
|
||||
|
||||
|
||||
def _lookup(profile: PlaneProfile, dotted: str) -> str:
|
||||
head, _, rest = dotted.partition(".")
|
||||
if head == "images":
|
||||
images = profile.load_images()
|
||||
if rest not in images:
|
||||
raise ProfileError(f"{profile.images}: no image named {rest!r} (have {', '.join(sorted(images))})")
|
||||
return images[rest]
|
||||
if head == "pools":
|
||||
pool_name, _, key = rest.partition(".")
|
||||
if pool_name not in profile.pools or key not in _POOL_FIELDS:
|
||||
raise ProfileError(f"{profile.path}: no such pool field {dotted!r}")
|
||||
return str(getattr(profile.pools[pool_name], key))
|
||||
if head not in _SCALARS or rest:
|
||||
raise ProfileError(f"{profile.path}: no such field {dotted!r}")
|
||||
return str(getattr(profile, head))
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
"""``python -m openultrasast.plane.profile [--profile NAME] --print FIELD [FIELD...]``: one value per line."""
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(prog="openultrasast.plane.profile", description=main.__doc__)
|
||||
parser.add_argument("--profile", help="profile name or path (default OUSAST_PLANE_PROFILE, else kind)")
|
||||
parser.add_argument("--print", dest="fields", nargs="+", required=True, metavar="FIELD")
|
||||
args = parser.parse_args(argv)
|
||||
try:
|
||||
profile = load_profile(args.profile)
|
||||
for dotted in args.fields:
|
||||
print(_lookup(profile, dotted))
|
||||
except ProfileError as exc:
|
||||
print(f"profile: {exc}", file=sys.stderr)
|
||||
return 2
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user