Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion exgentic_a2a_runner/AUTHBRIDGE_PIPELINE_SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ This proposal generalizes the deploy scripts so they drive the pipeline by **ena
- Backwards compatibility with the previous `--ibac` / `--authbridge` flags or their env-var aliases. They are removed.
- Building a full YAML editor in shell. Pipeline ordering rules and slot dependency validation stay in AuthBridge (`pipeline.New`).
- Exposing every plugin config field as a CLI flag. We ship sensible defaults; advanced users override via env vars or `--plugin-config-file`.
- Changing the operator. The base config still comes from `kagenti-operator`; we only overlay.
- Changing the operator. The base config still comes from `rossoctl-operator`; we only overlay.

## 3. Proposed CLI surface

Expand Down
8 changes: 4 additions & 4 deletions exgentic_a2a_runner/IMPLEMENTATION_CHECKLIST.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,12 @@

The implementation is ready for testing with:
- Real Exgentic MCP server
- Kagenti generalist agent
- Rossoctl generalist agent
- OTLP collector for telemetry

### To Test:
1. Set up Exgentic MCP server
2. Deploy Kagenti agent with A2A endpoint
2. Deploy Rossoctl agent with A2A endpoint
3. Configure environment variables
4. Run: `uv run exgentic-a2a-runner`
5. Verify session creation, execution, evaluation, and cleanup
Expand All @@ -196,9 +196,9 @@ The implementation is ready for testing with:
All components have been successfully implemented following the requirements from GitHub Issue #963. The harness:

- ✅ Integrates with Exgentic MCP server
- ✅ Communicates with Kagenti agents via A2A
- ✅ Communicates with Rossoctl agents via A2A
- ✅ Implements the exact execution model specified
- ✅ Provides comprehensive observability
- ✅ Is fully documented and ready for deployment

The implementation is **COMPLETE** and ready for integration testing with actual Exgentic MCP server and Kagenti agents.
The implementation is **COMPLETE** and ready for integration testing with actual Exgentic MCP server and Rossoctl agents.
10 changes: 5 additions & 5 deletions exgentic_a2a_runner/analyze-run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,14 @@ Options:
--mlflow-port PORT Remote MLflow service port to forward
--mlflow-tls MLflow serves HTTPS on the forwarded port
--mlflow-workspace NAME Send x-mlflow-workspace header
--auth-mode MODE Token source: secret (kagenti oauth secret) or oc-token (oc whoami -t)
--auth-mode MODE Token source: secret (rossoctl oauth secret) or oc-token (oc whoami -t)
-h, --help Show this help message

The MLflow location, TLS, workspace, auth mode, and experiment id all DEFAULT
from the cluster mode, so a plain --kind or --openshift needs no other flags:

--kind --openshift
namespace kagenti-system redhat-ods-applications
namespace rossoctl-system redhat-ods-applications
service mlflow mlflow
remote port 5000 8443
tls off (http) on (https)
Expand Down Expand Up @@ -131,9 +131,9 @@ fi
# already set the value wins; only unset values fall back to the mode default.
case "$CLUSTER_MODE" in
kind)
# kagenti's kind MLflow: HTTP on port 5000 in kagenti-system, no
# rossoctl's kind MLflow: HTTP on port 5000 in rossoctl-system, no
# workspace header, client-credentials secret flow for auth.
MLFLOW_NAMESPACE="${MLFLOW_NAMESPACE:-kagenti-system}"
MLFLOW_NAMESPACE="${MLFLOW_NAMESPACE:-rossoctl-system}"
MLFLOW_SERVICE="${MLFLOW_SERVICE:-mlflow}"
MLFLOW_REMOTE_PORT="${MLFLOW_REMOTE_PORT:-5000}"
MLFLOW_TLS="${MLFLOW_TLS:-false}"
Expand Down Expand Up @@ -277,7 +277,7 @@ cleanup_port_forward() {
fi
}

# secret mode: kagenti's client-credentials flow. Reads mlflow-oauth-secret and
# secret mode: rossoctl's client-credentials flow. Reads mlflow-oauth-secret and
# execs into the MLflow pod to exchange it for an access token.
get_token_from_secret() {
echo "Obtaining OAuth token via mlflow-oauth-secret..."
Expand Down
2 changes: 1 addition & 1 deletion exgentic_a2a_runner/authbridge/apply-pipeline.sh
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ fi
# --- Pre-flight: ConfigMap exists.
if ! kubectl -n "$NAMESPACE" get configmap "$CM_NAME" >/dev/null 2>&1; then
echo "ERROR: ConfigMap $NAMESPACE/$CM_NAME not found." >&2
echo " The kagenti operator should create this when the agent pod is admitted." >&2
echo " The rossoctl operator should create this when the agent pod is admitted." >&2
echo " Check: kubectl -n $NAMESPACE get pods -l app.kubernetes.io/name=$AGENT_NAME" >&2
exit 1
fi
Expand Down
2 changes: 1 addition & 1 deletion exgentic_a2a_runner/authbridge/plugins/token-exchange.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# This fragment is intentionally minimal so the overlay can keep the
# plugin in the resolved selection without re-rendering the operator
# config. Per-route exchange rules live in the `authproxy-routes`
# ConfigMap (see kagenti-extensions/authbridge/CLAUDE.md). Per-plugin
# ConfigMap (see rossoctl-extensions/authbridge/CLAUDE.md). Per-plugin
# overrides may be supplied via --plugin-config-file.
#
# Mutually exclusive with token-broker (both claim
Expand Down
56 changes: 28 additions & 28 deletions exgentic_a2a_runner/delete-all-deployments.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/bash
# Delete all agent and benchmark (tool) deployments from a Kagenti namespace via the API.
# Delete all agent and benchmark (tool) deployments from a Rossoctl namespace via the API.
# Usage: ./delete-all-deployments.sh [OPTIONS]
# Example: ./delete-all-deployments.sh --openshift apps.mycluster.example.com
# Example: ./delete-all-deployments.sh --kind
Expand Down Expand Up @@ -37,15 +37,15 @@ usage() {
cat <<EOF
Usage: $0 [OPTIONS]
Delete all agent and benchmark deployments in a Kagenti namespace via the API.
Delete all agent and benchmark deployments in a Rossoctl namespace via the API.
Cluster selection (exactly one required):
--kind Target local Kind cluster (default for local dev)
--openshift <domain> Target OpenShift cluster with given ingress domain
--in-cluster Run as a Kubernetes Job pod (uses in-cluster DNS)
Options:
--namespace <ns> Kagenti namespace (default: team1)
--namespace <ns> Rossoctl namespace (default: team1)
--keycloak-user <user> Keycloak username (default: admin)
--keycloak-pass <pass> Keycloak password (overrides KEYCLOAK_PASSWORD env)
--dry-run List what would be deleted without deleting anything
Expand Down Expand Up @@ -102,14 +102,14 @@ source "$SCRIPT_DIR/libsh/urls.sh"
source "$SCRIPT_DIR/libsh/check-kubectl-context.sh"
check_kubectl_context

KAGENTI_API="$(kagenti_api_url)"
ROSSOCTL_API="$(rossoctl_api_url)"
KEYCLOAK_API="$(keycloak_api_url)"

echo "=========================================="
echo " Delete All Deployments"
echo "=========================================="
echo " Namespace: $NAMESPACE"
echo " Kagenti API: $KAGENTI_API"
echo " Rossoctl API: $ROSSOCTL_API"
echo " Dry run: $DRY_RUN"
echo "=========================================="
echo ""
Expand All @@ -119,28 +119,28 @@ echo "Step 1: Getting Keycloak authentication token..."

if [ "$KEYCLOAK_PASSWORD" = "unknown" ]; then
echo "Step 1.5: Attempting to fetch Keycloak password from cluster..."
KAGENTI_PASSWORD=$("$KUBECTL_BIN" get secret kagenti-test-user -n keycloak -o jsonpath='{.data.password}' 2>/dev/null | base64 -d 2>/dev/null || echo "")
if [ -n "$KAGENTI_PASSWORD" ]; then
TEST_AUTH=$(curl -s -X POST "$KEYCLOAK_API/realms/kagenti/protocol/openid-connect/token" \
ROSSOCTL_PASSWORD=$("$KUBECTL_BIN" get secret rossoctl-test-user -n keycloak -o jsonpath='{.data.password}' 2>/dev/null | base64 -d 2>/dev/null || echo "")
if [ -n "$ROSSOCTL_PASSWORD" ]; then
TEST_AUTH=$(curl -s -X POST "$KEYCLOAK_API/realms/rossoctl/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=$KEYCLOAK_USERNAME" \
-d "password=$KAGENTI_PASSWORD" \
-d "password=$ROSSOCTL_PASSWORD" \
-d "grant_type=password" \
-d "client_id=kagenti" 2>/dev/null || echo "")
-d "client_id=rossoctl" 2>/dev/null || echo "")
if echo "$TEST_AUTH" | grep -q "access_token"; then
KEYCLOAK_PASSWORD="$KAGENTI_PASSWORD"
KEYCLOAK_PASSWORD="$ROSSOCTL_PASSWORD"
echo "✓ Fetched Keycloak password from cluster"
else
echo "⚠ Warning: Fetched password from cluster but authentication failed"
exit 1
fi
else
TEST_AUTH=$(curl -s -X POST "$KEYCLOAK_API/realms/kagenti/protocol/openid-connect/token" \
TEST_AUTH=$(curl -s -X POST "$KEYCLOAK_API/realms/rossoctl/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=$KEYCLOAK_USERNAME" \
-d "password=admin" \
-d "grant_type=password" \
-d "client_id=kagenti" 2>/dev/null || echo "")
-d "client_id=rossoctl" 2>/dev/null || echo "")
if echo "$TEST_AUTH" | grep -q "access_token"; then
KEYCLOAK_PASSWORD="admin"
echo "✓ Using default Keycloak password"
Expand All @@ -151,12 +151,12 @@ if [ "$KEYCLOAK_PASSWORD" = "unknown" ]; then
fi
fi

TOKEN_RESPONSE=$(curl -s -X POST "$KEYCLOAK_API/realms/kagenti/protocol/openid-connect/token" \
TOKEN_RESPONSE=$(curl -s -X POST "$KEYCLOAK_API/realms/rossoctl/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=$KEYCLOAK_USERNAME" \
-d "password=$KEYCLOAK_PASSWORD" \
-d "grant_type=password" \
-d "client_id=kagenti" || echo "TOKEN_ERROR")
-d "client_id=rossoctl" || echo "TOKEN_ERROR")

if [ "$TOKEN_RESPONSE" = "TOKEN_ERROR" ]; then
echo "Error: Failed to reach Keycloak at $KEYCLOAK_API"
Expand All @@ -172,13 +172,13 @@ fi
echo "✓ Authentication token obtained"
echo ""

# Step 2: Verify Kagenti backend is accessible
echo "Step 2: Verifying Kagenti backend accessibility at $KAGENTI_API..."
if ! curl -s --max-time 10 "$KAGENTI_API/api/v1/namespaces" >/dev/null 2>&1; then
echo "Error: Kagenti backend is not accessible at $KAGENTI_API"
# Step 2: Verify Rossoctl backend is accessible
echo "Step 2: Verifying Rossoctl backend accessibility at $ROSSOCTL_API..."
if ! curl -s --max-time 10 "$ROSSOCTL_API/api/v1/namespaces" >/dev/null 2>&1; then
echo "Error: Rossoctl backend is not accessible at $ROSSOCTL_API"
exit 1
fi
echo "Kagenti backend is accessible"
echo "Rossoctl backend is accessible"
echo ""

# Step 3: Discover deployments via kubectl
Expand Down Expand Up @@ -222,11 +222,11 @@ wait_for_gone() {
local elapsed=0 max=30 code
while true; do
code=$(curl -s --max-time 5 -o /dev/null -w "%{http_code}" \
"$KAGENTI_API$api_path" \
"$ROSSOCTL_API$api_path" \
-H "Authorization: Bearer $ACCESS_TOKEN") || code="000"
[ "$code" = "404" ] && echo "$resource_type '$name' confirmed gone" && return 0
if [ "$elapsed" -ge "$max" ]; then
echo "$resource_type '$name' still present after ${max}s — Kagenti cleanup stalled"
echo "$resource_type '$name' still present after ${max}s — Rossoctl cleanup stalled"
return 1
fi
sleep 2
Expand All @@ -240,15 +240,15 @@ if [ -n "$AGENT_NAMES" ]; then
echo "$AGENT_NAMES" | while read -r name; do
[ -z "$name" ] && continue
echo -n " Deleting agent '$name'... "
HTTP=$(curl -s --max-time 30 -w "%{http_code}" -o /tmp/kagenti_del_agent.txt \
-X DELETE "$KAGENTI_API/api/v1/agents/$NAMESPACE/$name" \
HTTP=$(curl -s --max-time 30 -w "%{http_code}" -o /tmp/rossoctl_del_agent.txt \
-X DELETE "$ROSSOCTL_API/api/v1/agents/$NAMESPACE/$name" \
-H "Authorization: Bearer $ACCESS_TOKEN") || HTTP="000"
if [ "$HTTP" = "200" ] || [ "$HTTP" = "404" ]; then
echo "done (HTTP $HTTP)"
[ "$HTTP" = "200" ] && wait_for_gone "agent" "$name" "/api/v1/agents/$NAMESPACE/$name"
else
echo "FAILED (HTTP $HTTP)"
cat /tmp/kagenti_del_agent.txt
cat /tmp/rossoctl_del_agent.txt
fi
done
else
Expand All @@ -262,15 +262,15 @@ if [ -n "$TOOL_NAMES" ]; then
echo "$TOOL_NAMES" | while read -r name; do
[ -z "$name" ] && continue
echo -n " Deleting tool '$name'... "
HTTP=$(curl -s --max-time 30 -w "%{http_code}" -o /tmp/kagenti_del_tool.txt \
-X DELETE "$KAGENTI_API/api/v1/tools/$NAMESPACE/$name" \
HTTP=$(curl -s --max-time 30 -w "%{http_code}" -o /tmp/rossoctl_del_tool.txt \
-X DELETE "$ROSSOCTL_API/api/v1/tools/$NAMESPACE/$name" \
-H "Authorization: Bearer $ACCESS_TOKEN") || HTTP="000"
if [ "$HTTP" = "200" ] || [ "$HTTP" = "404" ]; then
echo "done (HTTP $HTTP)"
[ "$HTTP" = "200" ] && wait_for_gone "tool" "$name" "/api/v1/tools/$NAMESPACE/$name"
else
echo "FAILED (HTTP $HTTP)"
cat /tmp/kagenti_del_tool.txt
cat /tmp/rossoctl_del_tool.txt
fi
done
else
Expand Down
Loading