plane(1.2): literals out of doctor, egress, router, generate -- the profile is handed in

doctor(profile) checks the profile's kube context, the two namespaces behind a
reachable context only, the images file's runner reference in the profile's
registry (a local registry over its v2 API, a remote one via docker manifest
inspect) and opens the memory store; its first line reports every configured
address (Req 1.2). Egress(executable, context), open_router(context, url=...),
receiver_cluster_ip(context) and Receiver(..., context) take the profile's
context and build none; generate.increment/render and harvest take the profile's
atespace. `ousast plane doctor|run|workspaces|harvest --profile`. The test fakes
(kubectl-ate, kubectl, ax) record the context they received and the tests assert
it is the profile's. grep "kind-" src/openultrasast/plane: no match.

Gates: pytest 2006 passed, 42 skipped, 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:
Marius Ciepluch
2026-10-02 20:26:46 +02:00
co-authored by Claude Fable 5.1
parent 8eac1b2c69
commit 77a03bad61
14 changed files with 331 additions and 104 deletions
+26 -2
View File
@@ -65,11 +65,12 @@ from pathlib import Path
HERE = Path(__file__).resolve().parent
argv = sys.argv[1:]
context = ""
if argv[:1] == ["--context"]:
argv = argv[2:]
context, argv = argv[1], argv[2:]
verb, actor, space = argv[0], argv[2], argv[argv.index("-a") + 1]
with (HERE / "ax.log").open("a") as log:
log.write(f"{time.monotonic():.4f} egress {verb} {actor}\n")
log.write(f"{time.monotonic():.4f} egress {verb} {actor} {context}\n")
record = HERE / "applied" / f"{actor}.json"
alive = record.exists() and not json.loads(record.read_text()).get("gone")
store = HERE / "policies" / f"{space}_{actor}.json"
@@ -103,3 +104,26 @@ def kubectl_ate(tmp_path, monkeypatch: pytest.MonkeyPatch):
monkeypatch.setenv("OUSAST_KUBECTL_ATE", str(path))
monkeypatch.setenv("OUSAST_EGRESS_SETTLE", "0")
return path
PROFILE_CONTEXT = "kind-test" # the fakes must receive this, never a context a module built (plane-on-kubernetes 1.2)
PROFILE_TOML = (
'exec = "local"\nkube_context = "{context}"\nregistry = "registry.test:5000"\nimages = "images.json"\n'
'memory = "file://{memory}"\natespace = "default"\n[pools.default]\nreplicas = 1\ncpu = "1"\nmemory = "1Gi"\n'
)
@pytest.fixture
def plane_profile(tmp_path, monkeypatch: pytest.MonkeyPatch):
"""A throwaway PlaneProfile under ``tmp_path`` (context ``kind-test``, a file:// memory store, a digest-pinned
images file), selected through ``OUSAST_PLANE_PROFILE`` so every plane module loads it."""
import json
root = tmp_path / "profile"
root.mkdir()
(root / "test.toml").write_text(PROFILE_TOML.format(context=PROFILE_CONTEXT, memory=tmp_path / "memory"), encoding="utf-8")
(root / "images.json").write_text(json.dumps({"runner": "registry.test:5000/ousast-runner@sha256:" + "ab" * 32}), encoding="utf-8")
monkeypatch.setenv("OUSAST_PLANE_PROFILE", str(root / "test.toml"))
for key in ("OUSAST_KUBE_CONTEXT", "OUSAST_MEMORY", "OUSAST_REGISTRY", "OUSAST_IMAGES", "OUSAST_EXEC"):
monkeypatch.delenv(key, raising=False)
return root / "test.toml"
+8 -4
View File
@@ -134,10 +134,11 @@ def test_apply_creates_reads_back_and_delete_confirms_the_cascade(tmp_path: Path
actor(tmp_path, "t1", gone=True) # ax delete task: the actor, and with it the policy, is gone
assert cli.delete("t1", "default") is None
assert calls(tmp_path) == ["get t1", "create t1", "get t1", "get t1", "get t1", "get t1"]
assert {line.split()[-1] for line in (tmp_path / "ax.log").read_text().splitlines()} == {"kind-test"}, "the given context"
def test_apply_replaces_an_existing_policy(tmp_path: Path, kubectl_ate: Path) -> None:
cli = egress.Egress(str(kubectl_ate))
cli = egress.Egress(str(kubectl_ate), context="kind-test")
actor(tmp_path, "t2")
cli.apply("t2", "default", {"rules": []})
cli.apply("t2", "default", POLICY)
@@ -146,7 +147,7 @@ def test_apply_replaces_an_existing_policy(tmp_path: Path, kubectl_ate: Path) ->
def test_apply_waits_for_the_actor_then_gives_up(tmp_path: Path, kubectl_ate: Path) -> None:
cli = egress.Egress(str(kubectl_ate))
cli = egress.Egress(str(kubectl_ate), context="kind-test")
with pytest.raises(RuntimeError, match="not found"):
cli.apply("missing", "default", POLICY, timeout=0.2)
timer = threading.Timer(0.3, actor, args=(tmp_path, "late"))
@@ -157,10 +158,13 @@ def test_apply_waits_for_the_actor_then_gives_up(tmp_path: Path, kubectl_ate: Pa
def test_receiver_address_modes(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("OUSAST_ARTIFACT_HOST", "127.0.0.1")
assert egress.receiver_address(4242) == ("http://127.0.0.1:4242/", None)
assert egress.receiver_address(4242, None) == ("http://127.0.0.1:4242/", None)
monkeypatch.delenv("OUSAST_ARTIFACT_HOST")
monkeypatch.setenv("OUSAST_ARTIFACT_DIAL", "172.19.0.1:80")
assert egress.receiver_address(18090) == (f"http://{egress.RECEIVER_HOST}/", "172.19.0.1:80")
assert egress.receiver_address(18090, "kind-test") == (f"http://{egress.RECEIVER_HOST}/", "172.19.0.1:80")
monkeypatch.delenv("OUSAST_ARTIFACT_DIAL")
with pytest.raises(RuntimeError, match="kube context"):
egress.receiver_address(18090, None) # no context to look the Service up in: never a built one
# --- the runner dials the gateway address with the receiver's name as Host ----------------------------------------
+22
View File
@@ -290,3 +290,25 @@ def test_case_record_carries_sites_in_set_and_cost_and_the_image_is_repinned(tmp
(tmp_path / "runner-image").write_text("localhost:5001/ousast-runner:dev\n")
with pytest.raises(ValueError, match="digest-pinned"):
repin_templates(paths["plane"], tmp_path / "runner-image")
def test_workspaces_go_to_the_profiles_atespace(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
"""plane-on-kubernetes 1.2: ``generate.ATESPACE`` is a default; ``increment`` takes the profile's atespace."""
paths = _fixture(tmp_path)
(tmp_path / "p.toml").write_text('exec = "local"\nkube_context = "c"\nregistry = "r"\nimages = "i.json"\natespace = "team-a"\n')
monkeypatch.setenv("OUSAST_PLANE_PROFILE", str(tmp_path / "p.toml"))
increment(paths["population"], paths["set"], paths["candidates"], paths["scans"], plane=paths["plane"], run_name="sp", command="t")
manifests = load_manifests(sorted((paths["plane"] / "workspaces").glob("*.yaml")))
assert manifests.workspaces and {w.metadata.atespace for w in manifests.workspaces.values()} == {"team-a"}
increment(
paths["population"],
paths["set"],
paths["candidates"],
paths["scans"],
plane=paths["plane"],
run_name="sp",
command="t",
atespace="z",
)
manifests = load_manifests(sorted((paths["plane"] / "workspaces").glob("*.yaml")))
assert {w.metadata.atespace for w in manifests.workspaces.values()} == {"z"}, "an explicit atespace wins over the profile"
+47 -11
View File
@@ -33,6 +33,7 @@ from openultrasast.plane import reconciler
from openultrasast.plane.workspaces import workspaces
RECONCILER = Path("src/openultrasast/plane/reconciler.py")
PROFILE_CONTEXT = "kind-test" # conftest's plane_profile fixture: the context every fake must be handed
PIN = "0123456789abcdef0123456789abcdef01234567"
SECRET = "sk-reconciler-test-7a2b-only-in-the-start-request"
@@ -355,7 +356,7 @@ class _RouterHandler(BaseHTTPRequestHandler):
@pytest.fixture
def fake(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, kubectl_ate: Path) -> Iterator[Fake]:
def fake(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, kubectl_ate: Path, plane_profile: Path) -> Iterator[Fake]:
monkeypatch.setenv("OUSAST_RESULTS", str(tmp_path / "results"))
monkeypatch.setenv("OUSAST_ARTIFACT_HOST", "127.0.0.1")
monkeypatch.setenv("OUSAST_POLL_SECONDS", "0.02")
@@ -631,7 +632,8 @@ def tar_of(files: dict[str, bytes]) -> bytes:
return buf.getvalue()
def test_receiver_rejects_traversal_unknown_tasks_and_wrong_types(tmp_path: Path) -> None:
def test_receiver_rejects_traversal_unknown_tasks_and_wrong_types(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("OUSAST_ARTIFACT_HOST", "127.0.0.1") # the direct test address: no Service lookup, no context needed
receiver = reconciler.Receiver("r", tmp_path / "r", 0)
receiver.running.add("t")
headers = {"X-Ousast-Run": "r", "X-Ousast-Task": "t", "Content-Type": "application/x-tar"}
@@ -719,16 +721,31 @@ def test_status_of_an_unknown_run_says_so(fake: Fake) -> None:
# --- doctor, workspaces, size ---------------------------------------------------------------------------------------
def test_doctor_names_the_bring_up_script_when_nothing_is_reachable(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
monkeypatch.setenv("PATH", str(tmp_path)) # no kubectl, no curl
def test_doctor_checks_the_profiles_cluster_and_reports_every_address(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, plane_profile: Path
) -> None:
"""Req 1.2: the configured context (never kind by name), the registry's runner reference, the memory store; the
namespaces are not checked behind an unreachable context, so only that line fails for it."""
monkeypatch.setenv("PATH", str(tmp_path)) # no kubectl, no docker
from openultrasast.plane import doctor as doctor_module
monkeypatch.setattr(doctor_module, "_registry_has", lambda image: (False, f"registry.test:5000 unreachable for {image}"))
checks = reconciler.doctor()
assert [name for name, _, _ in checks] == [
"kind cluster",
"agent substrate (ate-system)",
"ax controller (ax-system)",
"runner image in kind registry",
names = [name for name, _, _ in checks]
assert names == [
"profile test",
"cluster kind-test",
"runner image in registry.test:5000",
f"memory store file://{tmp_path / 'memory'}",
]
assert all(not ok and "ops/ax/up.sh" in text for _, ok, text in checks)
by_name = {name: (ok, text) for name, ok, text in checks}
assert by_name["profile test"][0] and "kube_context=kind-test" in by_name["profile test"][1]
for address in ("registry=registry.test:5000", "exec=local", "atespace=default", "router_url=(tunnel"):
assert address in by_name["profile test"][1], address
assert not by_name["cluster kind-test"][0] and "kind-test unreachable" in by_name["cluster kind-test"][1]
assert not by_name["runner image in registry.test:5000"][0]
assert by_name[f"memory store file://{tmp_path / 'memory'}"][0], "a file store opens; an S3 store is verified"
assert not any("kind-" + "ousast" in text for _, _, text in checks), "nothing names the kind cluster"
def test_workspaces_one_manifest_per_case_pin(tmp_path: Path) -> None:
@@ -755,6 +772,23 @@ def test_workspaces_one_manifest_per_case_pin(tmp_path: Path) -> None:
assert loaded["demo-sqli-benign-tip"].pins == {"repo": "d" * 40}
def test_cli_doctor_takes_a_profile_and_fails_on_its_unreachable_context(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path, plane_profile: Path, capsys: pytest.CaptureFixture[str]
) -> None:
"""plane-on-kubernetes 1.2: ``ousast plane doctor --profile`` drives the checks from that file, nothing built."""
from openultrasast.cli import main
from openultrasast.plane import doctor as doctor_module
monkeypatch.setenv("PATH", str(tmp_path))
monkeypatch.setattr(doctor_module, "_registry_has", lambda image: (True, "present"))
monkeypatch.delenv("OUSAST_PLANE_PROFILE")
assert main(["plane", "doctor", "--profile", str(plane_profile)]) == 1
out = capsys.readouterr().out
assert "[FAIL] cluster kind-test:" in out and "[ok] profile test:" in out and "[ok] runner image in registry.test:5000" in out
assert main(["plane", "doctor", "--profile", str(tmp_path / "missing.toml")]) == 2
assert "missing.toml" in capsys.readouterr().err
def test_rendered_documents_carry_only_ax_fields(tmp_path: Path) -> None:
"""Req 1.1/1.4: whatever ``ax apply`` receives names only fields ax documents; annotations never leave."""
(tmp_path / "repo-facts").mkdir()
@@ -829,7 +863,7 @@ def test_doctor_treats_an_empty_namespace_as_not_ready(monkeypatch: pytest.Monke
from openultrasast.plane import doctor
monkeypatch.setattr(doctor, "_sh", lambda *a: (True, "No resources found in ax-system namespace."))
ok, text = doctor._pods_ready("ax-system", "kind-ousast")
ok, text = doctor._pods_ready("ax-system", "kind-test")
assert not ok and "no pods" in text
monkeypatch.setattr(doctor, "_sh", lambda *a: (True, "a-1 1/1 Running 0 1m\nb-2 0/1 Completed 0 1m"))
assert doctor._pods_ready("ns", "ctx") == (True, "2 pods ready")
@@ -854,6 +888,8 @@ def test_egress_policy_between_apply_and_resume_and_checked_after_delete(fake: F
order.append(first(prefix, order[-1] + 1))
assert order == sorted(order)
assert not (tmp_path / "policies" / "default_eg-verify.json").exists(), "the policy went with its actor"
contexts = {line.split()[-1] for line in lines if line.startswith("egress ")}
assert contexts == {PROFILE_CONTEXT}, "kubectl-ate received the profile's context, never a built one (1.2)"
written = json.loads((tmp_path / "policies" / "default_eg-verify.written.json").read_text())
tls = {"hostnames": ["api.deepseek.com", "example.invalid"], "ports": {"numbers": [443]}}
assert written["rules"] == [{"tlsPassthrough": tls}], "the Model host and the Git host, nothing else"
+17 -9
View File
@@ -93,17 +93,17 @@ def alive(pid: int) -> bool:
@pytest.fixture(autouse=True)
def _clean_env(monkeypatch: pytest.MonkeyPatch) -> None:
for key in ("OUSAST_ROUTER_URL", "OUSAST_START_TIMEOUT", "KIND_CLUSTER_NAME", "FAKE_KUBECTL_MODE"):
for key in ("OUSAST_ROUTER_URL", "OUSAST_START_TIMEOUT", "FAKE_KUBECTL_MODE"):
monkeypatch.delenv(key, raising=False)
def test_port_forward_port_is_parsed_and_the_child_is_terminated_on_exit(tmp_path: Path, fake_router: FakeRouter) -> None:
port = int(fake_router.server_address[1])
kubectl = write_kubectl(tmp_path, port)
with open_router(kubectl=str(kubectl)) as router:
with open_router("kind-test", kubectl=str(kubectl)) as router: # the profile's context, as the reconciler passes it
assert router.url == f"http://127.0.0.1:{port}"
seen = json.loads((tmp_path / "kubectl.json").read_text())
assert seen["argv"] == ["--context", "kind-ousast", "-n", "ate-system", "port-forward", "svc/atenet-router", ":80"]
assert seen["argv"] == ["--context", "kind-test", "-n", "ate-system", "port-forward", "svc/atenet-router", ":80"]
assert alive(seen["pid"])
router.start_task("default", "run-t", "run", {})
deadline = time.monotonic() + 5
@@ -123,15 +123,23 @@ def test_a_failing_port_forward_raises_with_kubectls_output(tmp_path: Path, monk
def test_router_url_override_starts_no_port_forward(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, fake_router: FakeRouter) -> None:
monkeypatch.setenv("OUSAST_ROUTER_URL", fake_router.url)
with open_router(kubectl=str(tmp_path / "no-such-kubectl")) as router:
with open_router("kind-test", kubectl=str(tmp_path / "no-such-kubectl")) as router:
assert router.url == fake_router.url
assert not (tmp_path / "kubectl.json").exists()
monkeypatch.delenv("OUSAST_ROUTER_URL")
with open_router("kind-test", url=fake_router.url, kubectl=str(tmp_path / "no-such-kubectl")) as router:
assert router.url == fake_router.url, "the profile's router_url is the same override"
def test_open_router_refuses_to_build_a_context(tmp_path: Path) -> None:
with pytest.raises(StartError, match="kube context"), open_router("", kubectl=str(tmp_path / "no-such-kubectl")):
pass
@pytest.mark.parametrize("fake_router", [[(503, "no actor yet"), (502, "bad gateway"), (202, "started")]], indirect=True)
def test_start_retries_503_and_502_then_succeeds(monkeypatch: pytest.MonkeyPatch, fake_router: FakeRouter) -> None:
monkeypatch.setenv("OUSAST_ROUTER_URL", fake_router.url)
with open_router() as router:
with open_router("kind-test") as router:
router.start_task("default", "chain-verify", "chain", {"DEEPSEEK_API_KEY": SECRET})
assert len(fake_router.requests) == 3
path, headers, body = fake_router.requests[-1]
@@ -142,7 +150,7 @@ def test_start_retries_503_and_502_then_succeeds(monkeypatch: pytest.MonkeyPatch
@pytest.mark.parametrize("fake_router", [[(409, f"this actor runs task 'x' (echo {SECRET})")]], indirect=True)
def test_a_409_is_raised_at_once_with_the_response_text_redacted(monkeypatch: pytest.MonkeyPatch, fake_router: FakeRouter) -> None:
monkeypatch.setenv("OUSAST_ROUTER_URL", fake_router.url)
with open_router() as router, pytest.raises(StartError, match="HTTP 409.*runs task 'x'") as caught:
with open_router("kind-test") as router, pytest.raises(StartError, match="HTTP 409.*runs task 'x'") as caught:
router.start_task("default", "t", "r", {"DEEPSEEK_API_KEY": SECRET})
assert len(fake_router.requests) == 1 and SECRET not in str(caught.value)
assert router.starter("default", "t", "r", {})() is not None, "the starter returns the refusal instead of raising"
@@ -153,7 +161,7 @@ def test_a_restart_after_re_resume_accepts_a_running_command(monkeypatch: pytest
"""First start: 'already started' is a refusal. After a re-resume the actor may not have rebooted; then the
runner's 'already started on this boot' means the command still runs, not a failure."""
monkeypatch.setenv("OUSAST_ROUTER_URL", fake_router.url)
with open_router() as router:
with open_router("kind-test") as router:
start = router.starter("default", "t", "r", {})
assert start() is not None, "a first start that finds the command running is a refusal"
assert start() is None, "a re-sent start after a re-resume is not"
@@ -165,7 +173,7 @@ def test_a_restart_after_re_resume_accepts_a_running_command(monkeypatch: pytest
def test_start_gives_up_after_the_start_timeout(monkeypatch: pytest.MonkeyPatch, fake_router: FakeRouter) -> None:
monkeypatch.setenv("OUSAST_ROUTER_URL", fake_router.url)
monkeypatch.setenv("OUSAST_START_TIMEOUT", "1")
with open_router() as router, pytest.raises(StartError, match="OUSAST_START_TIMEOUT.*workspaces not materialised"):
with open_router("kind-test") as router, pytest.raises(StartError, match="OUSAST_START_TIMEOUT.*workspaces not materialised"):
router.start_task("default", "t", "r", {})
assert len(fake_router.requests) >= 2
@@ -173,7 +181,7 @@ def test_start_gives_up_after_the_start_timeout(monkeypatch: pytest.MonkeyPatch,
def test_connection_errors_are_retried_until_the_timeout(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("OUSAST_ROUTER_URL", "http://127.0.0.1:9")
monkeypatch.setenv("OUSAST_START_TIMEOUT", "1")
with open_router() as router, pytest.raises(StartError, match="within 1s"):
with open_router("kind-test") as router, pytest.raises(StartError, match="within 1s"):
router.start_task("default", "t", "r", {})