mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 03:49:26 +00:00
fix(plane): apply a Task after the Workspaces it binds; end-to-end smoke Run
On the live cluster ax created the Task before its Workspace in the same apply and the runner saw an empty AX_WORKSPACES_YAML. Tests now find rendered documents by kind and name, not position. ops/ax/smoke-run.sh renders a model-free repo-facts Run with the current runner digest and executes it through the reconciler. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
14ea48b1ea
commit
30e4778b11
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
# Render the end-to-end smoke Run with the current runner digest and execute it through the reconciler on ax.
|
||||
set -euo pipefail
|
||||
export PATH="$HOME/go/bin:$HOME/.local/bin:$PATH"
|
||||
SRC="${OUSAST_AX_SRC:-$HOME/.cache/ousast/ax-src}"
|
||||
RUNNER_IMAGE="$(cat "$SRC/runner-image")"; export RUNNER_IMAGE
|
||||
KIND_GW="$(docker network inspect kind --format '{{range .IPAM.Config}}{{if .Gateway}}{{.Gateway}} {{end}}{{end}}' | tr ' ' '\n' | grep -m1 '\.')"
|
||||
export OUSAST_ARTIFACT_HOST="${OUSAST_ARTIFACT_HOST:-$KIND_GW}" # sandboxes reach the host via the kind gateway
|
||||
envsubst < "$(dirname "$0")/smoke-run.yaml.tmpl" > "$SRC/smoke-run.yaml"
|
||||
REPO="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
"$REPO/.venv/bin/ousast" plane run "$SRC/smoke-run.yaml" "$@"
|
||||
"$REPO/.venv/bin/ousast" plane status ax-e2e-smoke
|
||||
@@ -0,0 +1,39 @@
|
||||
# End-to-end smoke of the plane on ax: `ousast plane run` submits one model-free repo-facts Task, resumes it,
|
||||
# starts it through Substrate's router and receives its artifacts on the host. Rendered by ops/ax/smoke-run.sh.
|
||||
apiVersion: ax.io/v1alpha1
|
||||
kind: Workspace
|
||||
metadata:
|
||||
name: ousast-e2e-ws
|
||||
atespace: default
|
||||
spec:
|
||||
files:
|
||||
- path: "app/views.py"
|
||||
content: "from app.db import run_query\n\ndef handler(request):\n return run_query(request.args['q'])\n"
|
||||
- path: "app/db.py"
|
||||
content: "def run_query(sql):\n return cursor.execute(sql)\n"
|
||||
---
|
||||
apiVersion: ax.io/v1alpha1
|
||||
kind: Task
|
||||
metadata:
|
||||
name: ousast-e2e-facts
|
||||
atespace: default
|
||||
spec:
|
||||
image: "${RUNNER_IMAGE}"
|
||||
command: ["repo-facts"]
|
||||
resources:
|
||||
requests: {cpu: "250m", memory: "512Mi"}
|
||||
limits: {cpu: "1", memory: "1Gi"}
|
||||
workspaces:
|
||||
- name: ousast-e2e-ws
|
||||
path: "/workspace/case"
|
||||
---
|
||||
apiVersion: openultrasast.io/v1alpha1
|
||||
kind: Run
|
||||
metadata:
|
||||
name: ax-e2e-smoke
|
||||
spec:
|
||||
tasks:
|
||||
- name: facts
|
||||
task: ousast-e2e-facts
|
||||
outputs: [facts.json, summary.json]
|
||||
budget: {usd: 0, calls: 0}
|
||||
@@ -167,7 +167,7 @@ def render_task(run: Run, entry: RunTask, manifests: Manifests, base: Path, arti
|
||||
spec["workspaces"] = bindings
|
||||
if task.debug:
|
||||
spec["debug"] = True
|
||||
return [_doc("Task", ax_name, spec, task.metadata.atespace), *docs]
|
||||
return [*docs, _doc("Task", ax_name, spec, task.metadata.atespace)] # ax snapshots bound Workspaces at Task create
|
||||
|
||||
|
||||
class Ax:
|
||||
|
||||
@@ -73,8 +73,9 @@ def test_validation_run_resolves_and_renders_every_task(tmp_path: Path) -> None:
|
||||
assert not non_ax_fields(doc["spec"], AX_FIELDS[doc["kind"]], doc["kind"])
|
||||
assert set(doc["metadata"]) <= {"name", "atespace"}
|
||||
assert len(doc["metadata"]["name"]) <= 63, doc["metadata"]["name"]
|
||||
assert docs[0]["metadata"]["name"] not in ax_names
|
||||
ax_names.add(docs[0]["metadata"]["name"])
|
||||
assert docs[-1]["kind"] == "Task"
|
||||
assert docs[-1]["metadata"]["name"] not in ax_names
|
||||
ax_names.add(docs[-1]["metadata"]["name"])
|
||||
|
||||
|
||||
def test_inputs_carry_the_validation_set() -> None:
|
||||
|
||||
@@ -365,18 +365,20 @@ def test_chain_runs_in_order_with_inputs_env_and_model(fake: Fake, tmp_path: Pat
|
||||
assert env["OUSAST_MODEL"] == "deepseek-flash"
|
||||
assert json.loads(env["OUSAST_MODEL_PARAMS"]) == {"cache_hit_per_m": 0.07, "input_per_m": 0.27, "output_per_m": 1.1}
|
||||
kinds = [(d["kind"], d["metadata"]["name"]) for d in verify["docs"]]
|
||||
assert kinds == [("Task", "chain-verify"), ("Model", "deepseek-flash"), ("Workspace", "case-pin"), ("Workspace", "chain-verify-inputs")]
|
||||
inputs_ws = verify["docs"][3]
|
||||
assert kinds == [("Model", "deepseek-flash"), ("Workspace", "case-pin"), ("Workspace", "chain-verify-inputs"), ("Task", "chain-verify")]
|
||||
inputs_ws = next(d for d in verify["docs"] if d["metadata"]["name"] == "chain-verify-inputs")
|
||||
assert inputs_ws["spec"]["files"] == [{"path": "repo-facts/facts.json", "content": json.dumps({"a.py": {"functions": ["f"]}})}]
|
||||
assert verify["docs"][0]["spec"]["workspaces"][-1] == {"name": "chain-verify-inputs", "path": "/workspace/.ousast-in/verify"}
|
||||
assert "annotations" not in verify["docs"][0]["metadata"], "ax's ObjectMeta has no annotations"
|
||||
assert verify["docs"][-1]["spec"]["workspaces"][-1] == {"name": "chain-verify-inputs", "path": "/workspace/.ousast-in/verify"}
|
||||
assert "annotations" not in verify["docs"][-1]["metadata"], "ax's ObjectMeta has no annotations"
|
||||
assert json.loads(env["OUSAST_GIT_PINS"]) == {"case-pin/repo": PIN}, "the Workspace annotation reaches the task as env"
|
||||
assert verify["docs"][2]["spec"]["git"] == [{"name": "repo", "repo": "https://example.invalid/repo.git", "dir": "src", "depth": 1}]
|
||||
assert verify["docs"][2]["metadata"] == {"name": "case-pin", "atespace": "default"}
|
||||
assert next(d for d in verify["docs"] if d["metadata"]["name"] == "case-pin")["spec"]["git"] == [
|
||||
{"name": "repo", "repo": "https://example.invalid/repo.git", "dir": "src", "depth": 1}
|
||||
]
|
||||
assert next(d for d in verify["docs"] if d["metadata"]["name"] == "case-pin")["metadata"] == {"name": "case-pin", "atespace": "default"}
|
||||
assert "OUSAST_GIT_PINS" not in fake.applied("chain", "agree")["env"], "no bound workspace, no pins"
|
||||
facts = fake.applied("chain", "repo-facts")
|
||||
assert "OUSAST_MODEL" not in facts["env"] and "OUSAST_BUDGET_USD" not in facts["env"]
|
||||
assert facts["docs"][0]["spec"]["image"] == "ousast-runner:dev"
|
||||
assert facts["docs"][-1]["spec"]["image"] == "ousast-runner:dev"
|
||||
delivered = reconciler.run_dir("chain") / "verify" / "agreed.json"
|
||||
assert json.loads(delivered.read_text()) == {"agreed": ["a.py:f"]}
|
||||
assert state_of("chain")["tasks"]["verify"]["usd"] == 0.5 and state_of("chain")["tasks"]["verify"]["model"] == "deepseek-flash"
|
||||
@@ -505,7 +507,7 @@ def test_start_is_sent_once_after_resume_with_the_models_credential(fake: Fake,
|
||||
for s in starts:
|
||||
name = s["body"]["task"]
|
||||
assert accepted[name] < s["at"] < delivered[name.removeprefix("sig-")], f"{name}: resume, then start, then delivery"
|
||||
verify_model = fake.applied("sig", "verify")["docs"][1]
|
||||
verify_model = next(d for d in fake.applied("sig", "verify")["docs"] if d["kind"] == "Model")
|
||||
assert verify_model["spec"]["secretKey"] == {"name": "deepseek", "key": "DEEPSEEK_API_KEY"}, "the manifest names the key"
|
||||
assert "DEEPSEEK_API_KEY" not in fake.applied("sig", "verify")["env"], "the rendered Task carries no credential"
|
||||
rendered = str(reconciler.run_dir("sig") / "verify" / "task.yaml")
|
||||
@@ -696,6 +698,18 @@ def test_rendered_documents_carry_only_ax_fields(tmp_path: Path) -> None:
|
||||
assert "openultrasast.io/" not in yaml.safe_dump(rendered["metadata"]), where
|
||||
|
||||
|
||||
def test_the_task_is_applied_after_everything_it_binds(tmp_path: Path) -> None:
|
||||
"""ax copies the bound Workspaces into the task at create time: a Task applied before its Workspace ran with
|
||||
an empty AX_WORKSPACES_YAML on the live cluster (2026-09-29). The Task is the last document of each apply."""
|
||||
(tmp_path / "repo-facts").mkdir()
|
||||
(tmp_path / "repo-facts" / "facts.json").write_text("{}", encoding="utf-8")
|
||||
run, manifests = reconciler.load_run(write_run(tmp_path, "order"))
|
||||
for entry in run.tasks:
|
||||
docs = reconciler.render_task(run, entry, manifests, tmp_path, "http://h:1/")
|
||||
assert [d["kind"] for d in docs].count("Task") == 1
|
||||
assert docs[-1]["kind"] == "Task", [d["kind"] for d in docs]
|
||||
|
||||
|
||||
def test_reconciler_is_under_500_lines_and_holds_no_pipeline_logic() -> None:
|
||||
text = RECONCILER.read_text(encoding="utf-8")
|
||||
assert len(text.splitlines()) < 500, "Req 3.4: the reconciler stays under 500 lines"
|
||||
|
||||
@@ -584,7 +584,7 @@ def test_git_commits_annotation_round_trips_to_the_checkout(
|
||||
"apiVersion: openultrasast.io/v1alpha1\nkind: Run\nmetadata: {name: rt}\nspec: {tasks: [{name: stub, task: stub}]}\n"
|
||||
)
|
||||
run, manifests = reconciler.load_run(manifest)
|
||||
task_doc, *rest = reconciler.render_task(run, run.tasks[0], manifests, tmp_path, "unused")
|
||||
*rest, task_doc = reconciler.render_task(run, run.tasks[0], manifests, tmp_path, "unused")
|
||||
assert rest[0]["metadata"] == {"name": "case"}, "annotations are stripped before ax"
|
||||
assert rest[0]["spec"]["git"] == [{"name": "code", "repo": repo.as_uri(), "branch": "main"}], "no commit for ax"
|
||||
overrides = {"OUSAST_OUTPUT_DIR": str(output)}
|
||||
|
||||
Reference in New Issue
Block a user