ops(ax): worker pool, digest-pinned runner image, CLI from the deployed checkout

Substrate installs no WorkerPool (resume failed: no free workers) and rejects
images not pinned by digest; ax's default runner image on gcr.io needs
credentials, so the smoke task runs our runner (repo-facts over a one-file
workspace). A release ax CLI skewed from the server (UpdateTask unimplemented).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
This commit is contained in:
Marius Ciepluch
2026-09-29 18:37:25 +02:00
co-authored by Claude Opus 5.5
parent e6d5124ffc
commit d579b724b3
3 changed files with 72 additions and 13 deletions
@@ -1,12 +1,15 @@
# Minimal Task for the bring-up smoke test: default runner image, a files-only workspace, no MCP or skills.
# Bring-up smoke test: our runner image runs the model-free repo-facts task over a files-only workspace.
apiVersion: ax.io/v1alpha1
kind: Task
metadata:
name: ousast-smoke
atespace: default
spec:
image: "gcr.io/ax-substrate/ate-images/ax-task-runner@sha256:3a0dea6ad8b55278685db58aca6e37dc4ba04056831d45bef3aaeafdca43cac6"
command: ["sh", "-c", "cat /workspace/hello.txt && echo smoke-ok"]
image: "${RUNNER_IMAGE}" # digest-pinned (Substrate rejects tags); rendered by up.sh
command: ["repo-facts"]
env:
- name: OUSAST_OUTPUT_DIR
value: "/workspace/.ousast-out/smoke"
resources:
requests:
cpu: "100m"
@@ -25,5 +28,5 @@ metadata:
atespace: default
spec:
files:
- path: "hello.txt"
content: "hello from openultrasast\n"
- path: "hello.py"
content: "def hello():\n return greet()\n"
+41 -8
View File
@@ -20,20 +20,53 @@ clone ax https://github.com/google/ax.git
step() { printf '\n== %s (%s)\n' "$1" "$(date +%H:%M:%S)"; }
step "kind cluster '$KIND_CLUSTER_NAME' + registry $KO_DOCKER_REPO"
(cd "$SRC/substrate" && hack/create-kind-cluster.sh) # its helpers resolve paths from the git toplevel
# Each step is skipped when already done: the substrate helper DELETES an existing cluster before creating one.
CTX="kind-$KIND_CLUSTER_NAME"
ready() { kubectl --context "$CTX" -n "$1" get pods --no-headers 2>/dev/null | awk '$2 ~ /^[0-9]+\/[0-9]+$/ {n++; split($2,a,"/"); if (a[1]!=a[2] && $3!="Completed") bad++} END {exit !(n>0 && bad==0)}'; }
if kind get clusters 2>/dev/null | grep -qx "$KIND_CLUSTER_NAME"; then
echo "cluster exists; kept"
else
(cd "$SRC/substrate" && hack/create-kind-cluster.sh) # its helpers resolve paths from the git toplevel
fi
step "agent substrate (ate-system)"
(cd "$SRC/substrate" && hack/install-ate-kind.sh --deploy-ate-system)
kubectl -n ate-system wait --for=condition=Ready pod --all --timeout=900s || true
kubectl -n ate-system get pods
if ready ate-system; then echo "substrate ready; kept"; else
(cd "$SRC/substrate" && hack/install-ate-kind.sh --deploy-ate-system)
fi
kubectl --context "$CTX" -n ate-system get pods
step "ax control plane (ax-system)"
(cd "$SRC/ax" && make deploy AX_IMAGE_REPO="$KO_DOCKER_REPO")
kubectl -n ax-system wait --for=condition=Ready pod --all --timeout=600s || true
kubectl -n ax-system get pods
if ready ax-system; then echo "ax ready; kept"; else
(cd "$SRC/ax" && make deploy AX_IMAGE_REPO="$KO_DOCKER_REPO")
kubectl --context "$CTX" -n ax-system wait --for=condition=Ready pod --all --timeout=600s
fi
kubectl --context "$CTX" -n ax-system get pods
step "worker pool (gVisor)"
SUBSTRATE_VERSION="$(kubectl --context "$CTX" get nodes -o jsonpath='{.items[0].metadata.labels.ate\.dev/substrate-version}')"
export SUBSTRATE_VERSION
envsubst < "$(dirname "$0")/workerpool.yaml.tmpl" > "$SRC/workerpool.yaml"
(cd "$SRC/substrate" && KO_DOCKER_REPO="$KO_DOCKER_REPO" ko apply -f "$SRC/workerpool.yaml")
kubectl --context "$CTX" -n ax-system wait --for=condition=Ready pod -l ate.dev/worker-pool=ousast-pool --timeout=600s \
|| kubectl --context "$CTX" -n ax-system get pods
step "runner image $KO_DOCKER_REPO/ousast-runner"
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
docker build -q -f "$REPO_ROOT/plane/Dockerfile.runner" -t "$KO_DOCKER_REPO/ousast-runner:dev" "$REPO_ROOT"
docker push -q "$KO_DOCKER_REPO/ousast-runner:dev"
RUNNER_IMAGE="$(docker inspect --format '{{range .RepoDigests}}{{println .}}{{end}}' "$KO_DOCKER_REPO/ousast-runner:dev" | grep "^$KO_DOCKER_REPO/ousast-runner@" | head -1)"
[ -n "$RUNNER_IMAGE" ] || { echo "no digest for the runner image" >&2; exit 2; }
export RUNNER_IMAGE; echo "$RUNNER_IMAGE" > "$SRC/runner-image" # the reconciler reads this pin
echo "runner image $RUNNER_IMAGE"
step "ax CLI from the deployed checkout (a release CLI skews from the server)"
(cd "$SRC/ax" && go install ./cmd/ax)
step "smoke task"
ax apply -f "$(dirname "$0")/smoke-task.yaml"
ax delete task ousast-smoke >/dev/null 2>&1 || true
envsubst < "$(dirname "$0")/smoke-task.yaml.tmpl" > "$SRC/smoke-task.yaml"
ax apply -f "$SRC/smoke-task.yaml"
ax resume task ousast-smoke || true
ax get tasks
step "footprint"
+23
View File
@@ -0,0 +1,23 @@
# Warm gVisor workers for ax tasks on this host (Agent Substrate WorkerPool). ax's per-task ActorTemplates
# set no workerSelector, so every pool of the matching sandbox class is eligible. Two workers of 1 CPU /
# 1.5 GiB fit the 7 GB host beside the control planes; an actor occupies a whole worker.
apiVersion: ate.dev/v1alpha1
kind: WorkerPool
metadata:
name: ousast-pool
namespace: ax-system
labels:
workload: ousast
spec:
replicas: 2
workerImage: ko://github.com/agent-substrate/substrate/cmd/ateom-gvisor
template:
nodeSelector:
ate.dev/substrate-version: "${SUBSTRATE_VERSION}"
resources:
limits:
cpu: "1"
memory: 1536Mi
requests:
cpu: 250m
memory: 1536Mi