Skip to content

Refactor Keycloak direct-access-grants into shared helper; harden eval port-forwards; add cluster/dry flags to ibac comparison - #48

Open
yoavkatz wants to merge 3 commits into
mainfrom
refactor/keycloak-direct-access-helper
Open

Refactor Keycloak direct-access-grants into shared helper; harden eval port-forwards; add cluster/dry flags to ibac comparison#48
yoavkatz wants to merge 3 commits into
mainfrom
refactor/keycloak-direct-access-helper

Conversation

@yoavkatz

Copy link
Copy Markdown
Contributor

Summary

  • Extract shared Keycloak helper. The "enable Direct Access Grants for the rossoctl client" logic was duplicated across deploy-agent.sh and deploy-benchmark.sh. It now lives in a new libsh/keycloak-direct-access.sh as enable_direct_access_grants, which resolves master-realm admin creds from env vars → the keycloak-initial-admin secret → admin/admin defaults, and exits with a clear diagnostic on any failure.
  • Fix delete-all-deployments.sh. Its token requests use grant_type=password against the rossoctl client, which requires Direct Access Grants to be enabled first. It now sources the helper and calls it before requesting a token.
  • Harden evaluate-benchmark.sh port-forwards. Before binding a local port, detect a stale (or suspended) leftover kubectl port-forward holding it and clean it up — refusing to touch any non-kubectl process. Port-forward output is captured to a temp file so the real error is surfaced when startup fails; temp files are removed on cleanup.
  • Add cluster/dry flags to run-ibac-comparison.sh. New --kind / --openshift DOMAIN / --in-cluster cluster flags and a --dry run mode, all forwarded verbatim to deploy-and-evaluate.sh (which already validates and supports them).

Testing

  • bash -n syntax-checks pass on all six changed scripts.
  • Verified downstream deploy-and-evaluate.sh accepts the forwarded --dry, --kind, --openshift, and --in-cluster flags.

yoavkatz and others added 3 commits July 28, 2026 11:08
…l port-forwards; add cluster/dry flags to ibac comparison

- Extract the duplicated "enable Direct Access Grants for the rossoctl
  client" logic from deploy-agent.sh and deploy-benchmark.sh into a new
  libsh/keycloak-direct-access.sh helper (enable_direct_access_grants).
  Resolves master-realm admin creds from env vars, the
  keycloak-initial-admin secret, then admin/admin defaults.
- delete-all-deployments.sh now calls the helper too: its token requests
  use grant_type=password against the rossoctl client, which requires
  Direct Access Grants to be enabled first.
- evaluate-benchmark.sh: detect and clean up stale kubectl port-forwards
  holding a local port before binding, refusing to touch non-kubectl
  processes; capture port-forward output to surface the real error on
  startup failure.
- run-ibac-comparison.sh: add --kind/--openshift/--in-cluster cluster
  flags and a --dry run mode, forwarded to deploy-and-evaluate.sh.

Signed-off-by: Yoav Katz <katz@il.ibm.com>
secret mode previously ran a client_credentials grant for the mlflow
service account, but mlflow-oidc-auth authorizes reads from its own user
DB (not the token's group claim), so the traces API returned 403. Switch
to a password (direct-access) grant for a real MLflow user (default:
admin), matching how the other deploy scripts authenticate.

- analyze-run.sh: obtain the token via a password grant built from
  keycloak_api_url() and the mlflow-oauth-secret client id/secret; resolve
  the user password from KEYCLOAK_PASSWORD or the rossoctl-test-user
  secret; enable Direct Access Grants on the mlflow client first.
- Default MLFLOW_LOCAL_PORT to 8085: 8080 is served by the kind ingress
  for keycloak.localtest.me (127.0.0.1), so binding there would shadow
  Keycloak and send the token request to MLflow.
- Detect the downloader's exit 75 (token rejected) via PIPESTATUS and
  print an actionable hint (log into the MLflow UI once), re-echoing the
  original invocation.
- download_mlflow_traces.py: raise MLflowAuthError on 401/403 and exit 75
  so the wrapper can distinguish "no MLflow user record yet" from a
  generic failure.
- keycloak-direct-access.sh: parameterize enable_direct_access_grants with
  an optional CLIENT_ID (defaults to rossoctl) so analyze-run can target
  the mlflow client.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Yoav Katz <katz@il.ibm.com>
Optionally tee the raw traces JSON from download_mlflow_traces.py into a
timestamped file under the given directory before piping to the analyzer,
so the saved copy and the analysis come from the same download. Off by
default; the directory is created if needed.

Also drop a stray leftover `env` debug line that dumped the full
environment before the download pipeline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Yoav Katz <katz@il.ibm.com>
@kellyaa

kellyaa commented Jul 30, 2026

Copy link
Copy Markdown
Member

Tested this on a local Kind cluster (kind-rossoctl) against a live Keycloak. All four changes work as described, and the core fix is verifiably real. One bug worth fixing before merge, plus two minor notes.

The core fix is confirmed

I proved the delete-all-deployments.sh fix with a before/after against the same cluster state — set directAccessGrantsEnabled=false on the rossoctl client, then ran both versions:

  • main's version → ⚠ Warning: Fetched password from cluster but authentication failed, exit 1
  • this branch → enables the grant, obtains a token, exit 0

So the password-grant prerequisite really was broken on main, and this fixes it.

What else I verified

Keycloak helper — works against live Keycloak, and the flag genuinely persists (checked via the admin API, not just an HTTP 200 on the PUT). All three failure paths exit 1 with a useful diagnostic: wrong admin password, nonexistent client, unreachable Keycloak.

Port-forward hardening — both branches behave correctly:

  • a real stale kubectl port-forward holding the port → detected, killed, port freed
  • a non-kubectl process holding the port (a Python listener) → refused to kill it, exited 1 and printed the offending process. The safety guard does what it claims.

--dry / cluster flags — dry run completes and flags forward correctly. Confirmed delete-all-deployments.sh does accept --kind/--openshift, so that forwarding is valid.

Refactor fidelitybash -n clean on all 7 shell scripts, py_compile clean on the Python. The extracted helper is a faithful superset of the two copies it replaces: it adopts deploy-benchmark.sh's stronger version, so deploy-agent.sh picks up credential resolution and real error handling it previously lacked (it used to silently swallow every failure). The single shellcheck warning in evaluate-benchmark.sh (SC2155) is pre-existing and unrelated.

Bug: analyze-run.sh doesn't receive the cluster flag

run-ibac-comparison.sh forwards CLUSTER_FLAG to delete-all-deployments.sh and deploy-and-evaluate.sh, but not to the final analyze-run.sh call. analyze-run.sh does accept cluster flags and defaults to kind when none is given (analyze-run.sh:146-148), so with --openshift DOMAIN the comparison step will target a Kind-style MLflow URL and fail — after both benchmark runs have already completed, which is the expensive way to find out.

Dry run with --openshift apps.mycluster.example.com shows the flag missing:

[DRY RUN] Would execute:
.../analyze-run.sh -c gsm8k-10-parallel-1-cejqm-ibac\,gsm8k-10-parallel-1-cejqm

One-line fix:

run_step "$SCRIPT_DIR/analyze-run.sh" -c "${EXPERIMENT_PLUGIN},${EXPERIMENT_BASE}" ${CLUSTER_FLAG[@]+"${CLUSTER_FLAG[@]}"}

This is invisible on kind, which is probably why it slipped through — the testing notes are syntax-checks only, no cluster run.

Minor notes

  1. Misleading diagnostic when Keycloak is unreachable. A connection failure prints Set KEYCLOAK_ADMIN_PASSWORD in your .env..., blaming credentials for what is actually a network problem:

    Error: Could not obtain master-realm admin token from Keycloak
      Response:
      Set KEYCLOAK_ADMIN_PASSWORD in your .env if the master realm admin password is not 'admin'.
    

    Worth distinguishing an empty response (unreachable) from a rejected one (bad creds).

  2. analyze-run.sh (+123/-26) and download_mlflow_traces.py aren't mentioned in the PR description — together that's the largest diff here. The change itself looks good: a distinct exit code 75 for "MLflow rejected the token", raised via MLflowAuthError and handled at analyze-run.sh:514, with urllib.error correctly imported. Worth adding to the summary so reviewers know to look at it.

One caveat on my testing

My keycloak-initial-admin secret happens to contain admin/admin*, so the secret-fallback path and the hardcoded admin/admin default are indistinguishable on this cluster. I confirmed the env-var override path works, but the secret-resolution path is not independently proven — that needs a cluster with a non-default admin password (e.g. a real RHBK deployment).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants