Skip to content

feat: run deploy and evaluate scripts inside the Kagenti cluster #25

Description

@yoavkatz

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

  1. exgentic_a2a_runner/lib/urls.sh — shared URL helper functions sourced by all scripts
  2. exgentic_a2a_runner/Dockerfile — runner image with curl, jq, bash, python3.12, uv, pre-built venv; no kubectl
  3. Gate image sync on [ -z "$KUBERNETES_SERVICE_HOST" ] in deploy-benchmark.sh and deploy-agent.sh
  4. Replace kubectl wait with curl retry loops in evaluate-benchmark.sh
  5. Gate port-forward setup on [ -z "$KUBERNETES_SERVICE_HOST" ] in evaluate-benchmark.sh
  6. Gate context check on [ -z "$KUBERNETES_SERVICE_HOST" ] in evaluate-benchmark.sh
  7. exgentic_a2a_runner/k8s/job.yaml — Kubernetes Job with runner image and secret injection (OPENAI_API_KEY, KEYCLOAK_PASSWORD, etc.), default ServiceAccount

Verification

  1. Local path unchanged: run deploy-and-evaluate.sh on a laptop with KinD — behavior identical to before
  2. Build image: docker build -t ghcr.io/exgentic/runner:test exgentic_a2a_runner/
  3. Launch Job: kubectl apply -f exgentic_a2a_runner/k8s/job.yaml
  4. kubectl logs -f job/<name> — should see all 3 steps execute
  5. Verify benchmark pod and agent pod become Ready in team1
  6. Verify evaluation produces results

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    New/ToDo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions