Context
Currently deploy-benchmark.sh, deploy-agent.sh, and evaluate-benchmark.sh (orchestrated by deploy-and-evaluate.sh) run on a local developer machine. They assume a local shell environment with access to kubectl connected to a KinD cluster named kind-kagenti, a local container runtime (podman/docker), and kind for image syncing.
This issue tracks the work to run these scripts inside a container in the Kagenti cluster itself — as a Kubernetes Job or Pod, rather than from a developer laptop.
What works already (no changes needed)
All HTTP-based steps already work from inside the cluster:
- Keycloak auth (
http://keycloak.localtest.me:8080) — reachable via Istio ingress
- Kagenti API calls (
http://kagenti-api.localtest.me:8080) — same
- MCP health checks — same
- GitHub raw content fetches — standard internet egress
What needs to change
1. Image pull + KinD sync (biggest structural change)
deploy-benchmark.sh and deploy-agent.sh both pull images locally, check whether the KinD node already has them, and sync via kind load image-archive. This entire flow is local-machine-centric:
- No
podman/docker socket is available inside a cluster container
kind load image-archive requires exec access to the KinD node container — impossible from inside the cluster
- Image-ID comparison via
podman/docker exec kagenti-control-plane crictl inspecti ... won't work
Resolution: Gate the image sync block on [ -z "$KUBERNETES_SERVICE_HOST" ]. When in-cluster, skip straight to using $REMOTE_IMAGE_NAME directly. The --local-image flag remains functional for local use.
Affected: deploy-benchmark.sh:104–202, deploy-agent.sh:205–303.
2. kubectl — removed from in-cluster path
All cluster interaction goes through the Kagenti API and Keycloak over HTTP. No kubectl is needed in-cluster, so no ServiceAccount or RBAC is required. The Job uses the default ServiceAccount.
kubectl wait readiness checks are replaced with curl retry loops against the service endpoints directly (see item 3).
3. Port-forwards are meaningless from inside the cluster
evaluate-benchmark.sh sets up:
- OTEL collector:
localhost:4327 → otel-collector.kagenti-system:4317
- Prometheus:
localhost:9191 → prometheus.istio-system:9090
Resolution: Gate port-forward setup on [ -z "$KUBERNETES_SERVICE_HOST" ]. When in-cluster, use cluster DNS directly via dedicated URL helper functions.
Affected: evaluate-benchmark.sh:218–262.
4. *.localtest.me URLs don't resolve from inside the cluster
localtest.me is a DNS wildcard that resolves to 127.0.0.1. From inside a pod, this points to the container's own loopback — not the ingress.
Resolution: A shared lib/urls.sh with named functions for each service. Each function checks KUBERNETES_SERVICE_HOST and returns the appropriate URL.
| Current (localtest.me) |
In-cluster replacement |
http://kagenti-api.localtest.me:8080 |
http://kagenti-api.kagenti-system.svc.cluster.local:8080 |
http://keycloak.localtest.me:8080 |
http://keycloak.keycloak.svc.cluster.local:8080 |
http://{tool}.team1.localtest.me:8080 |
http://{tool}-mcp.team1.svc.cluster.local:8000 |
http://{agent}.team1.localtest.me:8080 |
http://{agent}.team1.svc.cluster.local:8080 |
http://mcp-gateway-istio.gateway-system.localtest.me:8080 |
http://mcp-gateway-istio.gateway-system.svc.cluster.local:8080 |
5. Cluster context check — remove when in-cluster
evaluate-benchmark.sh:126–145 checks kubectl config current-context and prompts interactively if not kind-kagenti. Inside a pod there is no named context, and interactive prompts hang forever.
Resolution: Gate on [ -z "$KUBERNETES_SERVICE_HOST" ]. Skip entirely when in-cluster.
6. Python/uv runner containerization
evaluate-benchmark.sh:314–373 runs the Python harness via uv sync + uv run. The container image bakes in the source and pre-installs dependencies at build time — no uv sync at runtime.
Deliverables
exgentic_a2a_runner/lib/urls.sh — shared URL helper functions sourced by all scripts
exgentic_a2a_runner/Dockerfile — runner image with curl, jq, bash, python3.12, uv, pre-built venv; no kubectl
- Gate image sync on
[ -z "$KUBERNETES_SERVICE_HOST" ] in deploy-benchmark.sh and deploy-agent.sh
- Replace
kubectl wait with curl retry loops in evaluate-benchmark.sh
- Gate port-forward setup on
[ -z "$KUBERNETES_SERVICE_HOST" ] in evaluate-benchmark.sh
- Gate context check on
[ -z "$KUBERNETES_SERVICE_HOST" ] in evaluate-benchmark.sh
exgentic_a2a_runner/k8s/job.yaml — Kubernetes Job with runner image and secret injection (OPENAI_API_KEY, KEYCLOAK_PASSWORD, etc.), default ServiceAccount
Verification
- Local path unchanged: run
deploy-and-evaluate.sh on a laptop with KinD — behavior identical to before
- Build image:
docker build -t ghcr.io/exgentic/runner:test exgentic_a2a_runner/
- Launch Job:
kubectl apply -f exgentic_a2a_runner/k8s/job.yaml
kubectl logs -f job/<name> — should see all 3 steps execute
- Verify benchmark pod and agent pod become Ready in
team1
- Verify evaluation produces results
Context
Currently
deploy-benchmark.sh,deploy-agent.sh, andevaluate-benchmark.sh(orchestrated bydeploy-and-evaluate.sh) run on a local developer machine. They assume a local shell environment with access tokubectlconnected to a KinD cluster namedkind-kagenti, a local container runtime (podman/docker), andkindfor image syncing.This issue tracks the work to run these scripts inside a container in the Kagenti cluster itself — as a Kubernetes Job or Pod, rather than from a developer laptop.
What works already (no changes needed)
All HTTP-based steps already work from inside the cluster:
http://keycloak.localtest.me:8080) — reachable via Istio ingresshttp://kagenti-api.localtest.me:8080) — sameWhat needs to change
1. Image pull + KinD sync (biggest structural change)
deploy-benchmark.shanddeploy-agent.shboth pull images locally, check whether the KinD node already has them, and sync viakind load image-archive. This entire flow is local-machine-centric:podman/dockersocket is available inside a cluster containerkind load image-archiverequires exec access to the KinD node container — impossible from inside the clusterpodman/docker exec kagenti-control-plane crictl inspecti ...won't workResolution: Gate the image sync block on
[ -z "$KUBERNETES_SERVICE_HOST" ]. When in-cluster, skip straight to using$REMOTE_IMAGE_NAMEdirectly. The--local-imageflag remains functional for local use.Affected:
deploy-benchmark.sh:104–202,deploy-agent.sh:205–303.2.
kubectl— removed from in-cluster pathAll cluster interaction goes through the Kagenti API and Keycloak over HTTP. No
kubectlis needed in-cluster, so no ServiceAccount or RBAC is required. The Job uses the default ServiceAccount.kubectl waitreadiness checks are replaced withcurlretry loops against the service endpoints directly (see item 3).3. Port-forwards are meaningless from inside the cluster
evaluate-benchmark.shsets up:localhost:4327 → otel-collector.kagenti-system:4317localhost:9191 → prometheus.istio-system:9090Resolution: Gate port-forward setup on
[ -z "$KUBERNETES_SERVICE_HOST" ]. When in-cluster, use cluster DNS directly via dedicated URL helper functions.Affected:
evaluate-benchmark.sh:218–262.4.
*.localtest.meURLs don't resolve from inside the clusterlocaltest.meis a DNS wildcard that resolves to127.0.0.1. From inside a pod, this points to the container's own loopback — not the ingress.Resolution: A shared
lib/urls.shwith named functions for each service. Each function checksKUBERNETES_SERVICE_HOSTand returns the appropriate URL.http://kagenti-api.localtest.me:8080http://kagenti-api.kagenti-system.svc.cluster.local:8080http://keycloak.localtest.me:8080http://keycloak.keycloak.svc.cluster.local:8080http://{tool}.team1.localtest.me:8080http://{tool}-mcp.team1.svc.cluster.local:8000http://{agent}.team1.localtest.me:8080http://{agent}.team1.svc.cluster.local:8080http://mcp-gateway-istio.gateway-system.localtest.me:8080http://mcp-gateway-istio.gateway-system.svc.cluster.local:80805. Cluster context check — remove when in-cluster
evaluate-benchmark.sh:126–145checkskubectl config current-contextand prompts interactively if notkind-kagenti. Inside a pod there is no named context, and interactive prompts hang forever.Resolution: Gate on
[ -z "$KUBERNETES_SERVICE_HOST" ]. Skip entirely when in-cluster.6. Python/uv runner containerization
evaluate-benchmark.sh:314–373runs the Python harness viauv sync+uv run. The container image bakes in the source and pre-installs dependencies at build time — nouv syncat runtime.Deliverables
exgentic_a2a_runner/lib/urls.sh— shared URL helper functions sourced by all scriptsexgentic_a2a_runner/Dockerfile— runner image withcurl,jq,bash,python3.12,uv, pre-built venv; nokubectl[ -z "$KUBERNETES_SERVICE_HOST" ]indeploy-benchmark.shanddeploy-agent.shkubectl waitwithcurlretry loops inevaluate-benchmark.sh[ -z "$KUBERNETES_SERVICE_HOST" ]inevaluate-benchmark.sh[ -z "$KUBERNETES_SERVICE_HOST" ]inevaluate-benchmark.shexgentic_a2a_runner/k8s/job.yaml— Kubernetes Job with runner image and secret injection (OPENAI_API_KEY, KEYCLOAK_PASSWORD, etc.), default ServiceAccountVerification
deploy-and-evaluate.shon a laptop with KinD — behavior identical to beforedocker build -t ghcr.io/exgentic/runner:test exgentic_a2a_runner/kubectl apply -f exgentic_a2a_runner/k8s/job.yamlkubectl logs -f job/<name>— should see all 3 steps executeteam1