Skip to content

feat: kube-rbac-proxy sidecar container for eval-hub#716

Open
nbs-rh wants to merge 8 commits intotrustyai-explainability:mainfrom
nbs-rh:eh_auth_sidecar
Open

feat: kube-rbac-proxy sidecar container for eval-hub#716
nbs-rh wants to merge 8 commits intotrustyai-explainability:mainfrom
nbs-rh:eh_auth_sidecar

Conversation

@nbs-rh
Copy link
Copy Markdown
Contributor

@nbs-rh nbs-rh commented Apr 23, 2026

Related to https://redhat.atlassian.net/browse/RHOAIENG-56638

  • EvalHub API service pod ( in the control plane) includes an additional container kube-rbac-proxy which handles authentication/authorization and invokes downstream EH app on localhost with the X-User and X-Tenant headers. This is to be consistent with other RHAI services in terms of authentication/authorization.
  • EH health APIs are also routed through kube-rbac-proxy but authentication/authorization is skipped for these routes.

Tests

  1. Both the containers (evalhub and kube-rbac-proxy) come up and the pod is healthy:
Events:
  Type    Reason          Age   From               Message
  ----    ------          ----  ----               -------
  Normal  Scheduled       19m   default-scheduler  Successfully assigned prabhu/evalhub-644df647c-zls4w to ip-10-0-72-204.ec2.internal
  Normal  AddedInterface  19m   multus             Add eth0 [10.129.0.200/23] from ovn-kubernetes
  Normal  Pulling         19m   kubelet            Pulling image "quay.io/evalhub/evalhub:latest"
  Normal  Pulled          19m   kubelet            Successfully pulled image "quay.io/evalhub/evalhub:latest" in 518ms (518ms including waiting). Image size: 239070906 bytes.
  Normal  Created         19m   kubelet            Created container: evalhub
  Normal  Started         19m   kubelet            Started container evalhub
  Normal  Pulled          19m   kubelet            Container image "quay.io/rh-ee-nbs/nbs-dev:kube-rbac-proxy_v1" already present on machine
  Normal  Created         19m   kubelet            Created container: kube-rbac-proxy
  Normal  Started         19m   kubelet            Started container kube-rbac-proxy
  1. Authorization scenarios
Test scenario details

EvalHub + kube-rbac-proxy Integration Test Results

Configuration Summary

Pod: evalhub-644df647c-zls4w

Namespace: prabhu

kube-rbac-proxy Configuration:

--secure-listen-address=0.0.0.0:8443
--upstream=http://127.0.0.1:8444/
--config-file=/etc/kube-rbac-proxy/auth.yaml
--tls-cert-file=/etc/tls/private/tls.crt
--tls-private-key-file=/etc/tls/private/tls.key
--proxy-endpoints-port=9443
--ignore-paths=/api/v1/health
--auth-header-fields-enabled
--auth-header-user-field-name=X-User
--v=0

evalhub Configuration:

service:
  disable_auth: true
  port: 8444

TEST 1: Authentication - Valid ServiceAccount Token

Objective: Verify that kube-rbac-proxy accepts valid Kubernetes ServiceAccount tokens

Request:

curl -k -H "Authorization: Bearer <evalhub-client-token>" \
     -H "X-Tenant: prabhu" \
     https://evalhub-prabhu.apps.rosa.prabhu-comhub.xqmp.p3.openshiftapps.com/api/v1/evaluations/providers

Response Status: 200

Response Body (first 500 chars):

{"first":{"href":"/api/v1/evaluations/providers"},"limit":50,"total_count":3,"items":[{"resource":{"id":"lm_evaluation_harness","created_at":"2026-04-27T10:13:38.012789Z","updated_at":"2026-04-27T10:13:38.012789Z","owner":"system"},"name":"LM Evaluation Harness","description":"Comprehensive evaluation framework for language models with 180 benchmarks
","title":"LM Evaluation Harness","benchmarks":[{"id":"arc_easy","name":"Basic science Q&A","description":"Grade-school science questions testing b
...

evalhub Logs (showing received headers):

{"level":"info","ts":"2026-04-27T11:21:08.011Z","caller":"handlers/providers.go:117","msg":"Request started","request_id":"f968aadc-48f4-4731-b5fe-cb6a02a14ca6","method":"GET","uri":"/api/v1/evaluations/providers","user_agent":"curl/8.7.1","remote_addr":"127.0.0.1:32944","tenant":"prabhu","user":"system:serviceaccount:prabhu:evalhub-client","filter":"{\"limit\":50,\"offset\":0,\"params\":map[name: owner: tags:]}"}
{"level":"info","ts":"2026-04-27T11:21:08.014Z","caller":"server/execution_context.go:166","msg":"Request successful","request_id":"f968aadc-48f4-4731-b5fe-cb6a02a14ca6","method":"GET","uri":"/api/v1/evaluations/providers","user_agent":"curl/8.7.1","remote_addr":"127.0.0.1:32944","tenant":"prabhu","user":"system:serviceaccount:prabhu:evalhub-client","code":200,"duration":0.003096457}

Result: ✅ PASS - Authentication successful, HTTP 200 OK

Evidence:

  • HTTP Status Code: 200
  • evalhub logs show: "tenant":"prabhu","user":"system:serviceaccount:prabhu:evalhub-client"
  • X-User header added by kube-rbac-proxy from authenticated token identity
  • X-Tenant header passed through from client request

TEST 2: Authentication - Invalid/Missing Token

Objective: Verify that kube-rbac-proxy rejects requests without valid authentication

Request:

curl -k -H "X-Tenant: prabhu" \
     https://evalhub-prabhu.apps.rosa.prabhu-comhub.xqmp.p3.openshiftapps.com/api/v1/evaluations/providers

Response Status: 401

Response Body:

Unauthorized

Result: ✅ PASS - Authentication failed as expected, HTTP 401 Unauthorized

Evidence:

  • HTTP Status Code: 401
  • Response body: "Unauthorized"
  • Request never reached evalhub (blocked by kube-rbac-proxy)

TEST 3: Authorization - Configured Endpoint (Allowed)

Objective: Verify that authorized endpoints in auth.yaml configuration are accessible

auth.yaml configuration includes:

authorization:
  endpoints:
    - path: /api/v1/evaluations/providers
      mappings:
        - resources:
            - rewrites:
                byHttpHeader:
                  name: X-Tenant
              resourceAttributes:
                namespace: "{{.FromHeader}}"
                apiGroup: trustyai.opendatahub.io
                resource: providers
                verb: "{{.FromMethod}}"

Request:

curl -k -H "Authorization: Bearer <token>" \
     -H "X-Tenant: prabhu" \
     https://evalhub-prabhu.apps.rosa.prabhu-comhub.xqmp.p3.openshiftapps.com/api/v1/evaluations/providers

Response Status: 200

Response Body (truncated):

{"first":{"href":"/api/v1/evaluations/providers"},"limit":50,"total_count":3,"items":[{"resource":{"id":"lm_evaluation_harness","created_at":"2026-04-27T10:13:38.012789Z","updated_at":"2026-04-27T10:13:38.012789Z","owner":"system"},"name":"LM Evaluation Harness","description":"Comprehensive evaluati...

Result: ✅ PASS - Authorization successful for configured endpoint, HTTP 200 OK

Evidence:

  • HTTP Status Code: 200
  • kube-rbac-proxy performed SubjectAccessReview for resource: providers in namespace: prabhu
  • Request successfully forwarded to evalhub

TEST 4: Authorization - Unconfigured Endpoint (Blocked)

Objective: Verify that endpoints NOT in auth.yaml configuration are blocked

Request:

curl -k -H "Authorization: Bearer <token>" \
     -H "X-Tenant: prabhu" \
     https://evalhub-prabhu.apps.rosa.prabhu-comhub.xqmp.p3.openshiftapps.com/openapi.yaml

Response Status: 403

Response Body:

Forbidden (user=system:serviceaccount:prabhu:evalhub-client, verb=get, resource=, subresource=)

Result: ✅ PASS - Authorization blocked for unconfigured endpoint, HTTP 403 Forbidden

Evidence:

  • HTTP Status Code: 403
  • Response shows: Forbidden (user=system:serviceaccount:prabhu:evalhub-client, verb=get, resource=, subresource=)
  • kube-rbac-proxy blocked request before reaching evalhub
  • /openapi.yaml is NOT configured in auth.yaml

TEST 5: Health Endpoint Bypass (--ignore-paths)

Objective: Verify that /api/v1/health bypasses authentication and authorization checks for liveness/readiness probes

kube-rbac-proxy configuration:

--ignore-paths=/api/v1/health

Test 5a: Health endpoint WITH Authorization header

curl -k -H "Authorization: Bearer <token>" \
     https://evalhub-prabhu.apps.rosa.prabhu-comhub.xqmp.p3.openshiftapps.com/api/v1/health

Response Status: 200

Response Body:

{"status":"healthy","timestamp":"2026-04-27T11:24:11.362238185Z","build":"0.4.0","build_date":"2026-04-27T08:49:30.137Z"}

Test 5b: Health endpoint WITHOUT Authorization header (Kubelet probe scenario)

curl -k https://evalhub-prabhu.apps.rosa.prabhu-comhub.xqmp.p3.openshiftapps.com/api/v1/health

Response Status: 200

Response Body:

{"status":"healthy","timestamp":"2026-04-27T11:36:00.168231836Z","build":"0.4.0","build_date":"2026-04-27T08:49:30.137Z"}

Result: ✅ PASS - Health endpoint bypassed authentication and authorization, HTTP 200 OK in both cases

Evidence:

  • HTTP Status Code: 200 (both with and without Authorization header)
  • Response shows healthy status in both cases
  • --ignore-paths=/api/v1/health flag allows kubelet probes to succeed without authentication or RBAC checks
  • Critical for Kubernetes liveness/readiness probes which don't include authentication
  • Request bypasses kube-rbac-proxy auth layer completely for this path

TEST 6: Header Forwarding - X-User and X-Tenant

Objective: Verify that kube-rbac-proxy adds X-User header and forwards X-Tenant header to evalhub

kube-rbac-proxy configuration:

--auth-header-fields-enabled
--auth-header-user-field-name=X-User

Request:

curl -k -H "Authorization: Bearer <evalhub-client-token>" \
     -H "X-Tenant: prabhu" \
     https://evalhub-prabhu.apps.rosa.prabhu-comhub.xqmp.p3.openshiftapps.com/api/v1/evaluations/providers

evalhub Application Logs:

{"level":"info","ts":"2026-04-27T11:24:37.363Z","caller":"handlers/providers.go:117","msg":"Request started","request_id":"58eba93d-98e8-41f3-b268-6975e7119fbc","method":"GET","uri":"/api/v1/evaluations/providers","user_agent":"curl/8.7.1","remote_addr":"127.0.0.1:56634","tenant":"prabhu","user":"system:serviceaccount:prabhu:evalhub-client","filter":"{\"limit\":50,\"offset\":0,\"params\":map[name: owner: tags:]}"}

Extracted Headers from evalhub logs:

  • X-User: "user":"system:serviceaccount:prabhu:evalhub-client"
  • X-Tenant: "tenant":"prabhu"

Result: ✅ PASS - Both headers correctly received by evalhub

Evidence:

  • X-User header shows: "user":"system:serviceaccount:prabhu:evalhub-client"
    • This was ADDED by kube-rbac-proxy from the authenticated token identity
  • X-Tenant header shows: "tenant":"prabhu"
    • This was PASSED THROUGH from the client request
  • evalhub receives both headers and logs them in execution context

TEST 7: evalhub Authentication Disabled

Objective: Verify that evalhub has authentication disabled (DisableAuth: true)

ConfigMap Configuration:

service:
  disable_auth: true
  eval_init_image: quay.io/evalhub/evalhub:latest
  eval_sidecar_image: quay.io/evalhub/evalhub:latest

evalhub Startup Log:

{"level":"info","ts":"2026-04-27T10:13:38.032Z","caller":"eval_hub/main.go:146","msg":"Server starting","server_port":8444,"version":"0.4.0","build":"0.4.0","build_date":"2026-04-27T08:49:30.137Z","validator":true,"local":false,"tls":false,"mlflow_tracking":true,"otel":false,"prometheus":true,"authentication":false}

Extracted Authentication Status:

  • "authentication":false}

Result: ✅ PASS - evalhub authentication is disabled

Evidence:

  • ConfigMap shows: disable_auth: true
  • evalhub startup log shows: "authentication":false
  • evalhub accepts requests from kube-rbac-proxy without performing its own authentication
  • Authentication is delegated entirely to kube-rbac-proxy

TEST 8: TLS Termination Architecture

Objective: Verify TLS architecture - kube-rbac-proxy terminates TLS, evalhub uses HTTP internally

evalhub Server Configuration:

{"level":"info","ts":"2026-04-27T10:13:38.033Z","caller":"server/server.go:509","msg":"Server starting","addr":"127.0.0.1:8444","tls":false}

Extracted Configuration:

  • "tls":false}
  • "addr":"127.0.0.1:8444"

kube-rbac-proxy Configuration:

Upstream: http://127.0.0.1:8444/ (HTTP, not HTTPS)
Listen: 0.0.0.0:8443 (HTTPS with TLS certificates)

Result: ✅ PASS - Correct TLS architecture in place

Evidence:

  • evalhub listens on HTTP: "tls":false, "addr":"127.0.0.1:8444"
  • kube-rbac-proxy terminates TLS on port 8443
  • kube-rbac-proxy forwards to upstream via HTTP: http://127.0.0.1:8444/
  • Both containers on localhost, no need for TLS between them
  • External clients connect via HTTPS through OpenShift Route → Service → kube-rbac-proxy

TEST 9: Authorization - Multiple Unconfigured Endpoints

Objective: Verify authorization blocks multiple unconfigured endpoints

Test 9a: /docs endpoint

curl -k -H "Authorization: Bearer <token>" -H "X-Tenant: prabhu" \
     https://evalhub-prabhu.apps.rosa.prabhu-comhub.xqmp.p3.openshiftapps.com/docs

Response: HTTP 403 - Forbidden (user=system:serviceaccount:prabhu:evalhub-client, verb=get, resource=, subresource=)

Test 9b: /metrics endpoint

curl -k -H "Authorization: Bearer <token>" -H "X-Tenant: prabhu" \
     https://evalhub-prabhu.apps.rosa.prabhu-comhub.xqmp.p3.openshiftapps.com/metrics

Response: HTTP 403 - Forbidden (user=system:serviceaccount:prabhu:evalhub-client, verb=get, resource=, subresource=)

Result: ✅ PASS - All unconfigured endpoints blocked

Evidence:

  • /docs: HTTP 403 Forbidden
  • /metrics: HTTP 403 Forbidden
  • /openapi.yaml: HTTP 403 Forbidden (from Test 4)
  • Only endpoints explicitly configured in auth.yaml are allowed


TEST 10: Format 1 (Global Fallback) Authorization

Objective: Verify that kube-rbac-proxy supports Format 1 (global fallback) authorization and that it works when endpoint-specific rules (Format 2) are not present

Configuration Change

Original auth.yaml (Format 2 only):

authorization:
  endpoints:
    - path: /api/v1/evaluations/providers
      mappings:
        - resources:
            - rewrites:
                byHttpHeader:
                  name: X-Tenant
              resourceAttributes:
                namespace: "{{.FromHeader}}"
                apiGroup: trustyai.opendatahub.io
                resource: providers
                verb: "{{.FromMethod}}"
    # ... other endpoints

Updated auth.yaml (Format 1 + Format 2):

authorization:
  rewrites:
    byHttpHeader:
      name: X-Tenant
  resourceAttributes:
    namespace: "{{ .Value }}"
    apiGroup: trustyai.opendatahub.io
    resource: providers
    verb: get
  endpoints:
    - path: /api/v1/evaluations/jobs/*/events
      # ... (Format 2 rules for other endpoints)
    - path: /api/v1/evaluations/collections
      # ... (Format 2 rules for collections)
    # NOTE: /api/v1/evaluations/providers endpoint rule REMOVED

Changes Made:

  1. ✅ Removed endpoint-specific rule for /api/v1/evaluations/providers
  2. ✅ Added global fallback (Format 1) at top level:
    • authorization.rewrites: Extract namespace from X-Tenant header
    • authorization.resourceAttributes: Check providers resource with get verb
  3. ✅ Updated ConfigMap: oc patch configmap evalhub-config -n prabhu
  4. ✅ Restarted pod: oc delete pod evalhub-644df647c-zls4w -n prabhu

Test Execution

Request:

curl -k -H "Authorization: Bearer <evalhub-client-token>" \
     -H "X-Tenant: prabhu" \
     https://evalhub-prabhu.apps.rosa.prabhu-comhub.xqmp.p3.openshiftapps.com/api/v1/evaluations/providers

Response Status: 200

Response Body:

{"first":{"href":"/api/v1/evaluations/providers"},"limit":50,"total_count":3,"items":[{"resource":{"id":"lm_evaluation_harness","created_at":"2026-04-27T12:03:13.177791Z","updated_at":"2026-04-27T12:03:13.177791Z","owner":"system"},"name":"LM Evaluation Harness","description":"Comprehensive evaluation framework for language models with 180 benchmarks\n","title":"LM Evaluation Harness",...}]}

evalhub Logs:

{"level":"info","ts":"2026-04-27T12:03:58.864Z","caller":"handlers/providers.go:117","msg":"Request started","request_id":"0a4bdfeb-c7a4-49f8-936e-f10ce5bffd21","method":"GET","uri":"/api/v1/evaluations/providers","user_agent":"curl/8.7.1","remote_addr":"127.0.0.1:55852","tenant":"prabhu","user":"system:serviceaccount:prabhu:evalhub-client","filter":"{\"limit\":50,\"offset\":0,\"params\":map[name: owner: tags:]}"}
{"level":"info","ts":"2026-04-27T12:03:58.871Z","caller":"server/execution_context.go:166","msg":"Request successful","request_id":"0a4bdfeb-c7a4-49f8-936e-f10ce5bffd21","method":"GET","uri":"/api/v1/evaluations/providers","user_agent":"curl/8.7.1","remote_addr":"127.0.0.1:55852","tenant":"prabhu","user":"system:serviceaccount:prabhu:evalhub-client","code":200,"duration":0.006536411}

Result: ✅ PASS - Endpoint successfully authorized through Format 1 global fallback

Evidence:

  1. ConfigMap Verification:

    $ oc get configmap evalhub-config -n prabhu -o jsonpath='{.data.auth\.yaml}' | head -15
    authorization:
      rewrites:
        byHttpHeader:
          name: X-Tenant
      resourceAttributes:
        namespace: "{{ .Value }}"
        apiGroup: trustyai.opendatahub.io
        resource: providers
        verb: get
      endpoints:
        - path: /api/v1/evaluations/jobs/*/events
          # ... other endpoints listed
        # /api/v1/evaluations/providers is NOT in the endpoints list
  2. Authorization Flow:

    Client Request: GET /api/v1/evaluations/providers
      ↓
    kube-rbac-proxy receives request
      ↓
    Step 1: Check endpoint-specific rules (Format 2)
      - Search for matching path in authorization.endpoints[]
      - Result: NO MATCH (providers endpoint removed from list)
      ↓
    Step 2: Fall back to global authorization (Format 1)
      - Use authorization.rewrites to extract namespace from X-Tenant header
      - Use authorization.resourceAttributes to build SubjectAccessReview
      ↓
    Step 3: Kubernetes SubjectAccessReview API call
      Request:
        user: system:serviceaccount:prabhu:evalhub-client
        namespace: prabhu (from X-Tenant header)
        apiGroup: trustyai.opendatahub.io
        resource: providers
        verb: get
      Response: ALLOWED (user has permission via evalhub-user RoleBinding)
      ↓
    Step 4: Add X-User header and forward to evalhub
      X-User: system:serviceaccount:prabhu:evalhub-client
      X-Tenant: prabhu (passed through)
      ↓
    evalhub processes request successfully → HTTP 200
    
  3. Success Indicators:

    • HTTP Status Code: 200 OK
    • Response contains 3 providers (lm_evaluation_harness, garak, garak-kfp)
    • evalhub logs show both headers received: "tenant":"prabhu","user":"system:serviceaccount:prabhu:evalhub-client"
    • No endpoint-specific rule exists, but authorization succeeded via Format 1
  4. Format Comparison:

    Aspect Format 2 (Before) Format 1 (After)
    Configuration Location authorization.endpoints[].path: /api/v1/evaluations/providers authorization.resourceAttributes (top-level)
    Scope Only /api/v1/evaluations/providers All endpoints without specific rules
    Verb {{.FromMethod}} (GET/POST/DELETE dynamic) get (static)
    Namespace Source {{.FromHeader}} {{ .Value }} (both from X-Tenant)
    Precedence High (endpoint-specific takes priority) Low (fallback when no match)
  5. Key Learnings:

    • ✅ Format 1 and Format 2 can coexist in the same auth.yaml
    • ✅ Format 2 (endpoint-specific) takes precedence when path matches
    • ✅ Format 1 (global fallback) handles all unmatched paths
    • ✅ Same endpoint can be authorized through either format
    • ✅ Format 1 useful for: documentation endpoints, metrics, health checks, or API endpoints with simple authorization needs
    • ✅ Format 2 useful for: fine-grained control (different verbs per HTTP method, multiple resource checks)


TEST 11: Internal Events API - POST /api/v1/evaluations/jobs/{job_id}/events

Objective: Verify that the internal events API endpoint is properly secured through kube-rbac-proxy and that requests are authorized using endpoint-specific Format 2 authorization rules

Background

The /api/v1/evaluations/jobs/{job_id}/events endpoint is documented in the private API documentation. This endpoint allows evaluation job workers to post status updates during benchmark execution.

auth.yaml configuration for this endpoint:

authorization:
  endpoints:
    - path: /api/v1/evaluations/jobs/*/events
      mappings:
        - methods: [post]
          resources:
            - rewrites:
                byHttpHeader:
                  name: X-Tenant
              resourceAttributes:
                namespace: "{{.FromHeader}}"
                apiGroup: trustyai.opendatahub.io
                resource: status-events
                verb: create

Test Setup

Step 1: Create Evaluation Job

curl -k -X POST \
  -H "Authorization: Bearer <evalhub-client-token>" \
  -H "X-Tenant: prabhu" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "test-job-for-events",
    "model": {"name": "test-model", "url": "hf://gpt2"},
    "benchmarks": [{"provider_id": "lm_evaluation_harness", "id": "arc_easy"}]
  }' \
  https://evalhub-prabhu.apps.rosa.prabhu-comhub.xqmp.p3.openshiftapps.com/api/v1/evaluations/jobs

Response Status: 202 Accepted

Response Body:

{
  "resource": {
    "id": "a5842b8a-9d8f-411f-ac6c-24f8854cc221",
    "tenant": "prabhu",
    "created_at": "2026-04-27T13:24:08.420809873Z",
    "owner": "system:serviceaccount:prabhu:evalhub-client"
  },
  "status": {
    "state": "pending",
    "message": {"message": "Evaluation job created", "message_code": "evaluation_job_created"}
  }
}

Job ID: a5842b8a-9d8f-411f-ac6c-24f8854cc221

Test Execution

Step 2: POST Event to Job

Request:

curl -k -X POST \
  -H "Authorization: Bearer <evalhub-client-token>" \
  -H "X-Tenant: prabhu" \
  -H "Content-Type: application/json" \
  -d '{
    "benchmark_status_event": {
      "id": "lm_evaluation_harness:arc_easy",
      "benchmark_id": "arc_easy",
      "provider_id": "lm_evaluation_harness",
      "status": "running",
      "message": "Benchmark execution started"
    }
  }' \
  https://evalhub-prabhu.apps.rosa.prabhu-comhub.xqmp.p3.openshiftapps.com/api/v1/evaluations/jobs/a5842b8a-9d8f-411f-ac6c-24f8854cc221/events

Response Status: 409 Conflict

Response Body:

{
  "message_code": "job_can_not_be_updated",
  "message": "The job a5842b8a-9d8f-411f-ac6c-24f8854cc221 can not be running because it is 'failed'.",
  "trace": "1ebd7d88-566e-4c5c-9bbd-275be59deaea"
}

evalhub Logs:

{"level":"info","ts":"2026-04-27T13:25:17.445Z","caller":"handlers/evaluations.go:419","msg":"Request started","request_id":"1ebd7d88-566e-4c5c-9bbd-275be59deaea","method":"POST","uri":"/api/v1/evaluations/jobs/a5842b8a-9d8f-411f-ac6c-24f8854cc221/events","user_agent":"curl/8.7.1","remote_addr":"127.0.0.1:43088","tenant":"prabhu","user":"system:serviceaccount:prabhu:evalhub-client"}
{"level":"info","ts":"2026-04-27T13:25:17.446Z","caller":"sql/evaluations.go:374","msg":"Updating evaluation job","request_id":"1ebd7d88-566e-4c5c-9bbd-275be59deaea","method":"POST","uri":"/api/v1/evaluations/jobs/a5842b8a-9d8f-411f-ac6c-24f8854cc221/events","user_agent":"curl/8.7.1","remote_addr":"127.0.0.1:43088","tenant":"prabhu","user":"system:serviceaccount:prabhu:evalhub-client","id":"a5842b8a-9d8f-411f-ac6c-24f8854cc221","status":"running"}
{"level":"info","ts":"2026-04-27T13:25:17.447Z","caller":"server/execution_context.go:166","msg":"Request successful","request_id":"1ebd7d88-566e-4c5c-9bbd-275be59deaea","method":"POST","uri":"/api/v1/evaluations/jobs/a5842b8a-9d8f-411f-ac6c-24f8854cc221/events","user_agent":"curl/8.7.1","remote_addr":"127.0.0.1:43088","tenant":"prabhu","user":"system:serviceaccount:prabhu:evalhub-client","code":409,"duration":0.001591851}
{"level":"info","ts":"2026-04-27T13:25:17.447Z","caller":"handlers/evaluations.go:456","msg":"Request failed","request_id":"1ebd7d88-566e-4c5c-9bbd-275be59deaea","method":"POST","uri":"/api/v1/evaluations/jobs/a5842b8a-9d8f-411f-ac6c-24f8854cc221/events","user_agent":"curl/8.7.1","remote_addr":"127.0.0.1:43088","tenant":"prabhu","user":"system:serviceaccount:prabhu:evalhub-client","error":"The job a5842b8a-9d8f-411f-ac6c-24f8854cc221 can not be running because it is 'failed'.","code":409,"duration":0.001634783}

Result: ✅ PASS - Authentication and authorization successful, request reached evalhub

Evidence

Note: The HTTP 409 response is from evalhub's business logic (job in 'failed' state), NOT from authorization failure. The key evidence that authorization passed is that the request reached evalhub and was processed.

  1. Authentication Success:

    • Bearer token validated by kube-rbac-proxy
    • Token identity: system:serviceaccount:prabhu:evalhub-client
  2. Authorization Success:

    kube-rbac-proxy performed SubjectAccessReview:
      user: system:serviceaccount:prabhu:evalhub-client
      namespace: prabhu (from X-Tenant header)
      apiGroup: trustyai.opendatahub.io
      resource: status-events
      verb: create
      method: POST
    
    Result: ALLOWED (request forwarded to evalhub)
    
  3. Headers Forwarded Successfully:

    • evalhub logs show: "tenant":"prabhu","user":"system:serviceaccount:prabhu:evalhub-client"
    • X-User header added by kube-rbac-proxy: system:serviceaccount:prabhu:evalhub-client
    • X-Tenant header passed through: prabhu
  4. Request Processing:

    • Request reached evalhub handler: handlers/evaluations.go:419
    • Business logic executed: attempted to update job status to "running"
    • Job state validation performed (returned 409 because job was in 'failed' state)
    • This proves the request passed all security layers
  5. Authorization Configuration:

    • Endpoint-specific rule (Format 2) applied for /api/v1/evaluations/jobs/*/events
    • Method-specific check: only POST requests to this path are authorized
    • Resource: status-events with verb create
    • Namespace extracted from X-Tenant header

Key Learnings

  1. Internal API Security:

    • ✅ Internal/private API endpoints are protected through kube-rbac-proxy
    • ✅ Different resources can be used for different endpoints (status-events vs evaluations)
    • ✅ Method-specific authorization works (only POST allowed, not GET)
  2. Authorization Flow:

    POST /api/v1/evaluations/jobs/{id}/events
      ↓
    kube-rbac-proxy matches path: /api/v1/evaluations/jobs/*/events
      ↓
    Checks method: POST ✅ (matches mapping)
      ↓
    SubjectAccessReview: status-events.create in namespace prabhu
      ↓ ALLOWED
      ↓
    Headers added/forwarded → Request sent to evalhub
      ↓
    evalhub business logic executes (returned 409 due to job state)
    
  3. Business Logic vs Authorization:

    • HTTP 409 = Business logic rejection (job in wrong state)
    • HTTP 403 = Authorization failure (would block at kube-rbac-proxy layer)
    • The fact that we got HTTP 409 proves authorization succeeded
  4. Format 2 Features Demonstrated:

    • Path pattern matching with wildcards (/api/v1/evaluations/jobs/*/events)
    • Method-specific rules (methods: [post])
    • Different resources per endpoint (status-events for events, evaluations for jobs)
    • Dynamic namespace extraction from headers

TEST SUMMARY

All Tests: ✅ PASSED (11/11)

Test # Test Name Result HTTP Status Key Verification
1 Valid Authentication ✅ PASS 200 Token accepted, headers forwarded
2 Missing Authentication ✅ PASS 401 Request blocked
3 Configured Endpoint (Format 2) ✅ PASS 200 Endpoint-specific authorization
4 Unconfigured Endpoint (openapi.yaml) ✅ PASS 403 Authorization blocked
5 Health Endpoint Bypass ✅ PASS 200 Works with/without auth header
6 Header Forwarding ✅ PASS 200 X-User added, X-Tenant passed
7 evalhub Auth Disabled ✅ PASS N/A DisableAuth: true verified
8 TLS Architecture ✅ PASS N/A kube-rbac-proxy TLS, evalhub HTTP
9 Multiple Blocked Endpoints ✅ PASS 403 /docs, /metrics blocked
10 Format 1 Global Fallback ✅ PASS 200 Fallback authorization working
11 Internal Events API (POST) ✅ PASS 409* Auth passed, business logic rejected

*HTTP 409 = Business logic rejection, not authorization failure. Request successfully passed authentication and authorization.

Key Findings:

  1. Authentication Layer (kube-rbac-proxy):

    • ✅ Successfully validates Kubernetes ServiceAccount tokens
    • ✅ Rejects requests without valid authentication (401)
    • ✅ Adds X-User header with authenticated identity
  2. Authorization Layer (kube-rbac-proxy + auth.yaml):

    • ✅ Supports both Format 1 (global fallback) and Format 2 (endpoint-specific) authorization
    • ✅ Format 2 takes precedence when endpoint path matches
    • ✅ Format 1 provides default authorization for unmatched paths
    • ✅ Enforces per-endpoint authorization based on auth.yaml configuration
    • ✅ Allows configured endpoints (/api/v1/evaluations/*)
    • ✅ Blocks unconfigured endpoints (/openapi.yaml, /docs, /metrics) with 403
    • ✅ Health endpoint bypasses authorization via --ignore-paths
    • ✅ Method-specific authorization (POST to /events, GET to /providers)
    • ✅ Path pattern matching with wildcards (/jobs/*/events)
  3. Header Forwarding:

    • ✅ X-User header added by kube-rbac-proxy from token identity
    • ✅ X-Tenant header passed through from client request
    • ✅ evalhub receives and logs both headers correctly
    • ✅ Works for both GET and POST requests
  4. Security Architecture:

    • ✅ evalhub has authentication disabled (DisableAuth: true)
    • ✅ All authentication/authorization delegated to kube-rbac-proxy
    • ✅ TLS terminated at kube-rbac-proxy, HTTP used internally
    • ✅ Defense in depth: Route (re-encrypt TLS) → kube-rbac-proxy (AuthN/AuthZ) → evalhub (business logic)
    • ✅ Internal/private APIs protected same as public APIs
  5. Authorization Format Flexibility:

    • ✅ Both Format 1 and Format 2 can coexist in same auth.yaml
    • ✅ Format 2 (endpoint-specific) overrides Format 1 (global) when path matches
    • ✅ Same endpoint can be authorized through either format (TEST 3 vs TEST 10)
    • ✅ Format 1 useful for: default authorization, documentation, metrics
    • ✅ Format 2 useful for: fine-grained per-endpoint control, method-specific rules, different resources per endpoint

Deployment Topology:

Client (curl with Bearer token + X-Tenant header)
  ↓ HTTPS
OpenShift Route (TLS re-encrypt)
  ↓ HTTPS
Service (evalhub:8443)
  ↓ HTTPS
Pod: evalhub-644df647c-9zf9x
  ├─ Container: kube-rbac-proxy (port 8443)
  │    - Validates Bearer token (AuthN)
  │    - Checks authorization:
  │      • Format 2 (endpoint-specific) if path matches
  │      • Format 1 (global fallback) otherwise
  │    - Method-specific checks (POST vs GET)
  │    - Path pattern matching (/jobs/*/events)
  │    - Adds X-User header
  │    - Forwards X-Tenant header
  │    ↓ HTTP (localhost)
  └─ Container: evalhub (port 8444)
       - Receives authenticated requests
       - Logs X-User and X-Tenant headers
       - Executes business logic
       - Returns HTTP 200/201/400/409 based on business rules

Authorization Format Comparison

Format 1 (Global Fallback) - Used in TEST 10:

authorization:
  rewrites:
    byHttpHeader:
      name: X-Tenant
  resourceAttributes:
    namespace: "{{ .Value }}"
    apiGroup: trustyai.opendatahub.io
    resource: providers
    verb: get
  • Location: Top-level authorization.rewrites and authorization.resourceAttributes
  • Scope: All endpoints without specific rules
  • Precedence: Lower (fallback when no endpoint match)
  • Use Case: Default authorization, documentation, metrics, simple API endpoints

Format 2 (Endpoint-Specific) - Used in TEST 3 and TEST 11:

authorization:
  endpoints:
    - path: /api/v1/evaluations/jobs/*/events
      mappings:
        - methods: [post]
          resources:
            - rewrites:
                byHttpHeader:
                  name: X-Tenant
              resourceAttributes:
                namespace: "{{.FromHeader}}"
                apiGroup: trustyai.opendatahub.io
                resource: status-events
                verb: create
  • Location: authorization.endpoints[].path
  • Scope: Specific endpoint paths (supports wildcards)
  • Precedence: Higher (overrides Format 1 when path matches)
  • Use Case: Fine-grained control (different verbs per HTTP method, multiple resource checks, path patterns)

Both formats successfully authorize requests, demonstrating configuration flexibility.

API Coverage Tested

API Endpoint HTTP Method Authorization Format Resource Test #
/api/v1/health GET Bypassed (--ignore-paths) N/A 5
/api/v1/evaluations/providers GET Format 1 (global fallback) providers 10
/api/v1/evaluations/collections GET Format 2 (endpoint-specific) collections 3
/api/v1/evaluations/jobs POST Format 2 (endpoint-specific) evaluations 11 (setup)
/api/v1/evaluations/jobs/{id}/events POST Format 2 (endpoint-specific) status-events 11
/openapi.yaml GET No authorization rule N/A 4
/docs GET No authorization rule N/A 9
/metrics GET No authorization rule N/A 9

Test Documentation Complete: All 11 tests passed with comprehensive evidence, request/response data, and configuration explanations.

Summary by CodeRabbit

  • New Features

    • Added a kube-rbac-proxy sidecar to EvalHub deployments for HTTPS termination and auth handling.
    • Config option to disable EvalHub authentication (DISABLE_AUTH).
  • Improvements

    • Health checks and service URL handling updated to use the new proxy/fronting behavior and loopback binding for the app.
    • Operator supports configurable proxy image, per-container resource overrides, and improved image/namespace fallback behavior.
  • Tests

    • Test suite migrated to Ginkgo v2 with added coverage for sidecar, probes, and ConfigMap-driven settings.

@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented Apr 23, 2026

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

Warning

Rate limit exceeded

@nbs-rh has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 33 minutes and 55 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d12c0531-5a83-43ea-a601-58d65bcf65ce

📥 Commits

Reviewing files that changed from the base of the PR and between 83592cc and 7872c03.

📒 Files selected for processing (4)
  • controllers/evalhub/build_test.go
  • controllers/evalhub/constants.go
  • controllers/evalhub/deployment.go
  • controllers/evalhub/deployment_test.go
📝 Walkthrough

Walkthrough

Adds a kube-rbac-proxy sidecar to EvalHub Deployments, moves EvalHub to bind to 127.0.0.1 on an internal app port (8444), shifts probe and service wiring to use the proxy, extends operator ConfigMap keys (proxy image, disable_auth, resource overrides), and migrates tests to Ginkgo/Gomega validating the two-container topology and ConfigMap fallbacks.

Changes

Cohort / File(s) Summary
Constants & Config Keys
controllers/evalhub/constants.go
Introduce evalHubAppPort = 8444, health path, kube-rbac-proxy constants, default proxy image, and ConfigMap keys for container resource overrides.
ConfigMap Generation
controllers/evalhub/configmap.go
Add ServiceConfig.DisableAuth (disable_auth), generate PORT from evalHubAppPort, and expose DISABLE_AUTH in generated config.
Deployment Implementation
controllers/evalhub/deployment.go, controllers/evalhub/deployment_operator_settings.go
Switch to kube-rbac-proxy sidecar: EvalHub bound to 127.0.0.1:evalHubAppPort, proxy listens on servicePort and forwards upstream to EvalHub; add kube-rbac-proxy container, cert/CA mounts, probe adjustments, resource merging from operator ConfigMap, and getKubeRBACProxyImage.
Tests — migration & new expectations
controllers/evalhub/*_test.go, controllers/evalhub/suite_test.go
Migrate tests to Ginkgo/Gomega, update helpers to include proxy image, assert two-container Pod (evalhub + kube-rbac-proxy), envs (API_HOST=127.0.0.1, PORT=evalHubAppPort), probe behavior on proxy, image fallback behavior, and resource propagation from operator ConfigMap.
Removed / Reworked Tests
controllers/evalhub/unit_test.go, controllers/evalhub/build_test.go, controllers/evalhub/deployment_test.go
Remove old unit test file(s) and rewrite/relocate assertions into Ginkgo suites; add findContainerByName helper and update lifecycle/integration tests for two-container layout.
Minor / Misc
controllers/evalhub/evaluation_job_failure_reconciler.go
Import order change only; no behavior change.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client
    participant KubeRBACProxy as kube-rbac-proxy
    participant EvalHub
    participant ConfigMap

    Note over Client,KubeRBACProxy: External TLS + auth handled by sidecar
    Client->>KubeRBACProxy: HTTPS request (auth headers)
    KubeRBACProxy->>KubeRBACProxy: Validate auth, terminate TLS
    KubeRBACProxy->>EvalHub: HTTP forward to http://127.0.0.1:8444/
    EvalHub->>EvalHub: Serve request on loopback
    EvalHub-->>KubeRBACProxy: HTTP response
    KubeRBACProxy-->>Client: HTTPS response

    Note over KubeRBACProxy,EvalHub: Health probe flow
    KubeRBACProxy->>EvalHub: GET /api/v1/health (upstream)
    EvalHub-->>KubeRBACProxy: 200 OK

    Note over KubeRBACProxy,ConfigMap: Image resolution
    KubeRBACProxy->>ConfigMap: Lookup proxy image (operator ConfigMap)
    ConfigMap-->>KubeRBACProxy: Image or fallback
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

kind/enhancement, feature, ok-to-test, lgtm

Suggested reviewers

  • ppadashe-psp
  • mariusdanciu
  • julpayne

Poem

🐰 I hop where proxies guard the gate,
EvalHub whispers on loopback's plate,
Sidecar hums and TLS bows low,
Health checks ping the secret flow,
A rabbit cheers: QoS and tests all go!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: kube-rbac-proxy sidecar container for eval-hub' accurately and concisely summarizes the main change: adding a kube-rbac-proxy sidecar to EvalHub, which is the central modification across all files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nbs-rh nbs-rh marked this pull request as ready for review April 27, 2026 13:00
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
controllers/evalhub/deployment_test.go (1)

47-51: Optional: use the package-level constants in this fixture too.

Same nit as suite_test.go line 127 — build_test.go and unit_test.go already use configMapEvalHubImageKey and configMapKubeRBACProxyImageKey. Using them here would make the fixture rename-safe.

♻️ Suggested change
 			Data: map[string]string{
-				"evalHubImage":    "quay.io/ruimvieira/eval-hub:test",
-				"kube-rbac-proxy": "quay.io/openshift/origin-kube-rbac-proxy:4.19",
+				configMapEvalHubImageKey:       "quay.io/ruimvieira/eval-hub:test",
+				configMapKubeRBACProxyImageKey: "quay.io/openshift/origin-kube-rbac-proxy:4.19",
 			},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@controllers/evalhub/deployment_test.go` around lines 47 - 51, The fixture in
deployment_test.go hardcodes the ConfigMap keys "evalHubImage" and
"kube-rbac-proxy"; replace those literal keys in the Data map with the
package-level constants configMapEvalHubImageKey and
configMapKubeRBACProxyImageKey so the test becomes rename-safe (locate the Data:
map[string]string{...} block in the test fixture and swap the string literals
for the constants).
controllers/evalhub/constants.go (1)

36-37: Optional: align the ConfigMap key naming style.

configMapEvalHubImageKey = "evalHubImage" (camelCase) and configMapKubeRBACProxyImageKey = "kube-rbac-proxy" (kebab-case, identical to the container name) use different conventions in the same operator ConfigMap. Consider renaming the key value to e.g. "kubeRBACProxyImage" for consistency and to remove the accidental coincidence with kubeRBACProxyContainerName. This is a public surface (operator ConfigMap key), so changing it later would be a breaking change.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@controllers/evalhub/constants.go` around lines 36 - 37, The two ConfigMap key
constants use inconsistent naming: update the value of
configMapKubeRBACProxyImageKey from "kube-rbac-proxy" to a camelCase form (e.g.
"kubeRBACProxyImage") to match configMapEvalHubImageKey; update all
references/usages of configMapKubeRBACProxyImageKey throughout the codebase to
use the new key, and ensure you do not change the constant name so callers still
reference configMapKubeRBACProxyImageKey while the string literal becomes
"kubeRBACProxyImage" (also verify there is no accidental collision with
kubeRBACProxyContainerName usage).
controllers/evalhub/deployment.go (2)

78-82: Minor: redundant fallback handling for kube-rbac-proxy image.

utils.GetImageFromConfigMapWithFallback already returns defaultKubeRBACProxyImage when the ConfigMap key is missing and reports the error to the caller. The if err != nil branch in buildDeploymentSpec then re-assigns the same fallback. It's harmless but duplicative. The same pattern is used for getEvalHubImage, so this can be deferred. Either short-circuit the assignment or change getKubeRBACProxyImage to swallow the not-found error and return nil after logging.

Also applies to: 419-426

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@controllers/evalhub/deployment.go` around lines 78 - 82, The code in
buildDeploymentSpec redundantly reassigns kubeRBACProxyImage to
defaultKubeRBACProxyImage when getKubeRBACProxyImage already returns that
default via utils.GetImageFromConfigMapWithFallback; remove the duplicate
fallback by simply assigning the returned value without overwriting on err
(i.e., let kubeRBACProxyImage, err := r.getKubeRBACProxyImage(ctx) stand and
drop the kubeRBACProxyImage = defaultKubeRBACProxyImage inside the if err != nil
block), or alternatively change getKubeRBACProxyImage/getEvalHubImage to handle
the not-found case internally (log and return the default without surfacing an
error) so callers like buildDeploymentSpec don't need to reassign the default.

219-231: Optional: use single fmt.Sprintf calls for proxy args.

Strings like "--secure-listen-address=0.0.0.0:" + fmt.Sprintf("%d", servicePort) and "--proxy-endpoints-port=" + fmt.Sprintf("%d", kubeRBACProxyHealthPort) mix concatenation and formatting. A single fmt.Sprintf is clearer and avoids the intermediate allocation.

♻️ Suggested change
 		Args: []string{
-			"--secure-listen-address=0.0.0.0:" + fmt.Sprintf("%d", servicePort),
+			fmt.Sprintf("--secure-listen-address=0.0.0.0:%d", servicePort),
 			"--upstream=" + upstreamURL,
 			"--upstream-ca-file=" + upstreamCAPath,
 			"--config-file=" + kubeRBACProxyConfigMountPath,
 			"--tls-cert-file=" + tlsSecretMountPath + "/" + tlsCertFile,
 			"--tls-private-key-file=" + tlsSecretMountPath + "/" + tlsKeyFile,
-			"--proxy-endpoints-port=" + fmt.Sprintf("%d", kubeRBACProxyHealthPort),
+			fmt.Sprintf("--proxy-endpoints-port=%d", kubeRBACProxyHealthPort),
 			"--ignore-paths=" + evalHubHealthPath,
 			"--auth-header-fields-enabled",
 			"--auth-header-user-field-name=X-User",
 			"--v=0",
 		},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@controllers/evalhub/deployment.go` around lines 219 - 231, The Args entries
in the container spec are building flags by concatenating strings with
fmt.Sprintf (see the Args slice and variables servicePort,
kubeRBACProxyHealthPort, upstreamURL, upstreamCAPath,
kubeRBACProxyConfigMountPath, tlsSecretMountPath, tlsCertFile, tlsKeyFile,
evalHubHealthPath); replace those concatenations with single fmt.Sprintf calls
for each flag (e.g. use fmt.Sprintf("--secure-listen-address=0.0.0.0:%d",
servicePort) and fmt.Sprintf("--proxy-endpoints-port=%d",
kubeRBACProxyHealthPort)) and similarly format other flags that currently
combine literals and variables so each Args element is constructed with one
fmt.Sprintf for clarity and fewer allocations.
controllers/evalhub/suite_test.go (1)

126-128: Optional: use the package-level key constants in the test fixture.

build_test.go and unit_test.go reference these via configMapEvalHubImageKey / configMapKubeRBACProxyImageKey. Using the same constants here would make tests robust to a future key-rename and avoid duplicating the literals.

♻️ Suggested change
 		Data: map[string]string{
-			"evalHubImage":    "quay.io/ruimvieira/eval-hub:test",
-			"kube-rbac-proxy": "quay.io/openshift/origin-kube-rbac-proxy:4.19",
+			configMapEvalHubImageKey:       "quay.io/ruimvieira/eval-hub:test",
+			configMapKubeRBACProxyImageKey: "quay.io/openshift/origin-kube-rbac-proxy:4.19",
 		},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@controllers/evalhub/suite_test.go` around lines 126 - 128, The test fixture
uses hard-coded map keys "evalHubImage" and "kube-rbac-proxy" which duplicates
literals; replace those literals with the package-level constants
configMapEvalHubImageKey and configMapKubeRBACProxyImageKey (the same constants
used in build_test.go and unit_test.go) so the test references the canonical
keys and remains correct if the keys are renamed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@controllers/evalhub/constants.go`:
- Around line 17-20: The comment on evalHubAppPort/evalHubHealthPath contradicts
the deployment wiring (TLS env vars and --upstream-ca-file) versus
deployment.go's use of "http://"; pick the correct protocol (TLS or plain HTTP),
make the code consistent, and update the comment to match: if you choose TLS,
ensure the eval-hub listener actually binds HTTPS and deployment.go uses
"https://" for the kube-rbac-proxy upstream and keeps the TLS env
vars/--upstream-ca-file; if you choose HTTP, remove TLS env
vars/--upstream-ca-file and change any HTTPS assumptions accordingly; update the
doc-comment for evalHubAppPort and evalHubHealthPath to reflect the chosen
protocol.

In `@controllers/evalhub/deployment.go`:
- Around line 212-227: The upstream URL in kubeRBACProxyContainer is incorrectly
built as "http://127.0.0.1:%d/" (upstreamURL) while the rest of the wiring
expects HTTPS; change the upstreamURL format to "https://127.0.0.1:%d/" so
kubeRBACProxyContainer.Args includes an HTTPS upstream that matches the existing
--upstream-ca-file, the TLS env vars/secret mounts on the eval-hub container,
the evalHubAppPort semantics, and the deployment_test.go assertion that looks
for "--upstream=https://127.0.0.1:…/". If instead you intend HTTP, update/remove
--upstream-ca-file, TLS env vars/mounts in the eval-hub container, the
constants.go comment about TLS, and adjust deployment_test.go accordingly — but
do not mix HTTP upstream with --upstream-ca-file.

---

Nitpick comments:
In `@controllers/evalhub/constants.go`:
- Around line 36-37: The two ConfigMap key constants use inconsistent naming:
update the value of configMapKubeRBACProxyImageKey from "kube-rbac-proxy" to a
camelCase form (e.g. "kubeRBACProxyImage") to match configMapEvalHubImageKey;
update all references/usages of configMapKubeRBACProxyImageKey throughout the
codebase to use the new key, and ensure you do not change the constant name so
callers still reference configMapKubeRBACProxyImageKey while the string literal
becomes "kubeRBACProxyImage" (also verify there is no accidental collision with
kubeRBACProxyContainerName usage).

In `@controllers/evalhub/deployment_test.go`:
- Around line 47-51: The fixture in deployment_test.go hardcodes the ConfigMap
keys "evalHubImage" and "kube-rbac-proxy"; replace those literal keys in the
Data map with the package-level constants configMapEvalHubImageKey and
configMapKubeRBACProxyImageKey so the test becomes rename-safe (locate the Data:
map[string]string{...} block in the test fixture and swap the string literals
for the constants).

In `@controllers/evalhub/deployment.go`:
- Around line 78-82: The code in buildDeploymentSpec redundantly reassigns
kubeRBACProxyImage to defaultKubeRBACProxyImage when getKubeRBACProxyImage
already returns that default via utils.GetImageFromConfigMapWithFallback; remove
the duplicate fallback by simply assigning the returned value without
overwriting on err (i.e., let kubeRBACProxyImage, err :=
r.getKubeRBACProxyImage(ctx) stand and drop the kubeRBACProxyImage =
defaultKubeRBACProxyImage inside the if err != nil block), or alternatively
change getKubeRBACProxyImage/getEvalHubImage to handle the not-found case
internally (log and return the default without surfacing an error) so callers
like buildDeploymentSpec don't need to reassign the default.
- Around line 219-231: The Args entries in the container spec are building flags
by concatenating strings with fmt.Sprintf (see the Args slice and variables
servicePort, kubeRBACProxyHealthPort, upstreamURL, upstreamCAPath,
kubeRBACProxyConfigMountPath, tlsSecretMountPath, tlsCertFile, tlsKeyFile,
evalHubHealthPath); replace those concatenations with single fmt.Sprintf calls
for each flag (e.g. use fmt.Sprintf("--secure-listen-address=0.0.0.0:%d",
servicePort) and fmt.Sprintf("--proxy-endpoints-port=%d",
kubeRBACProxyHealthPort)) and similarly format other flags that currently
combine literals and variables so each Args element is constructed with one
fmt.Sprintf for clarity and fewer allocations.

In `@controllers/evalhub/suite_test.go`:
- Around line 126-128: The test fixture uses hard-coded map keys "evalHubImage"
and "kube-rbac-proxy" which duplicates literals; replace those literals with the
package-level constants configMapEvalHubImageKey and
configMapKubeRBACProxyImageKey (the same constants used in build_test.go and
unit_test.go) so the test references the canonical keys and remains correct if
the keys are renamed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1d98f71b-14c6-43b1-8a14-91638f7eac99

📥 Commits

Reviewing files that changed from the base of the PR and between a5290f3 and 442cd36.

📒 Files selected for processing (8)
  • controllers/evalhub/build_test.go
  • controllers/evalhub/configmap.go
  • controllers/evalhub/constants.go
  • controllers/evalhub/deployment.go
  • controllers/evalhub/deployment_test.go
  • controllers/evalhub/evalhub_controller_test.go
  • controllers/evalhub/suite_test.go
  • controllers/evalhub/unit_test.go

Comment thread controllers/evalhub/constants.go Outdated
Comment thread controllers/evalhub/deployment.go
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
controllers/evalhub/unit_test.go (1)

26-186: ⚠️ Potential issue | 🟠 Major

Controller test style is out of policy in this updated unit test path.

The modified controller tests still use testing/testify patterns instead of Ginkgo v2 + Gomega/envtest required by repo policy.

As per coding guidelines controllers/**/*_test.go: “Use Ginkgo v2 with Gomega assertions and controller-runtime envtest for all controller tests”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@controllers/evalhub/unit_test.go` around lines 26 - 186, The unit test
TestEvalHubReconciler_reconcileDeployment in controllers/evalhub/unit_test.go
uses plain testing/testify patterns and a fake client instead of the
repo-mandated Ginkgo v2 + Gomega + controller-runtime envtest approach; convert
this test to a Ginkgo Describe/It style using Gomega assertions and set up
envtest to run real controller-runtime machinery, replacing
TestEvalHubReconciler_reconcileDeployment with a Ginkgo spec that boots an
envtest environment, registers schemes (corev1, appsv1, evalhubv1alpha1),
creates an EvalHubReconciler instance (EvalHubReconciler) against the envtest
manager, and exercises reconciler.reconcileDeployment via the reconcile loop or
by calling the reconciler with a client from the envtest manager, asserting
resources and images with Gomega matchers instead of testify.
controllers/evalhub/build_test.go (1)

71-216: ⚠️ Potential issue | 🟠 Major

Controller test style is out of policy (still testing + testify).

This updated controller test path still uses t.Run with assert/require instead of Ginkgo v2 + Gomega/envtest, which is a repository-standard compliance gap.

As per coding guidelines controllers/**/*_test.go: “Use Ginkgo v2 with Gomega assertions and controller-runtime envtest for all controller tests”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@controllers/evalhub/build_test.go` around lines 71 - 216, The tests in
controllers/evalhub/build_test.go use testing.T with testify (t.Run, assert,
require) which violates the repository policy; convert these tests to Ginkgo v2
+ Gomega and run under controller-runtime envtest. Replace each t.Run block with
Ginkgo Describe/Context/It, replace assert/require calls with Gomega Expect/Ω
matchers, and set up an envtest environment (or the repo-standard test harness)
to create the test client and resources used by EvalHubReconciler; keep
assertions about buildDeploymentSpec, EvalHubReconciler behavior, containerName,
kubeRBACProxyContainerName, defaultEvalHubImage, defaultKubeRBACProxyImage, and
findContainer logic but expressed with Gomega, and ensure test setup/teardown
uses BeforeEach/AfterEach and the shared scheme and client from envtest.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@controllers/evalhub/build_test.go`:
- Around line 71-216: The tests in controllers/evalhub/build_test.go use
testing.T with testify (t.Run, assert, require) which violates the repository
policy; convert these tests to Ginkgo v2 + Gomega and run under
controller-runtime envtest. Replace each t.Run block with Ginkgo
Describe/Context/It, replace assert/require calls with Gomega Expect/Ω matchers,
and set up an envtest environment (or the repo-standard test harness) to create
the test client and resources used by EvalHubReconciler; keep assertions about
buildDeploymentSpec, EvalHubReconciler behavior, containerName,
kubeRBACProxyContainerName, defaultEvalHubImage, defaultKubeRBACProxyImage, and
findContainer logic but expressed with Gomega, and ensure test setup/teardown
uses BeforeEach/AfterEach and the shared scheme and client from envtest.

In `@controllers/evalhub/unit_test.go`:
- Around line 26-186: The unit test TestEvalHubReconciler_reconcileDeployment in
controllers/evalhub/unit_test.go uses plain testing/testify patterns and a fake
client instead of the repo-mandated Ginkgo v2 + Gomega + controller-runtime
envtest approach; convert this test to a Ginkgo Describe/It style using Gomega
assertions and set up envtest to run real controller-runtime machinery,
replacing TestEvalHubReconciler_reconcileDeployment with a Ginkgo spec that
boots an envtest environment, registers schemes (corev1, appsv1,
evalhubv1alpha1), creates an EvalHubReconciler instance (EvalHubReconciler)
against the envtest manager, and exercises reconciler.reconcileDeployment via
the reconcile loop or by calling the reconciler with a client from the envtest
manager, asserting resources and images with Gomega matchers instead of testify.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fa3267cf-fb1b-4460-aa1f-2760be365066

📥 Commits

Reviewing files that changed from the base of the PR and between 442cd36 and 43c9e81.

📒 Files selected for processing (5)
  • controllers/evalhub/build_test.go
  • controllers/evalhub/deployment.go
  • controllers/evalhub/deployment_test.go
  • controllers/evalhub/evaluation_job_failure_reconciler.go
  • controllers/evalhub/unit_test.go
✅ Files skipped from review due to trivial changes (1)
  • controllers/evalhub/evaluation_job_failure_reconciler.go

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
controllers/evalhub/build_test.go (1)

376-378: Prefer servicePort here instead of hardcoding 8443.

The production spec is driven by the constant, so the test should be too.

Proposed fix
- Expect(port.Port).To(Equal(int32(8443)))
+ Expect(port.Port).To(Equal(int32(servicePort)))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@controllers/evalhub/build_test.go` around lines 376 - 378, The test currently
asserts a hardcoded port value (Expect(port.Port).To(Equal(int32(8443))) );
replace the magic number with the production constant by asserting
Expect(port.Port).To(Equal(int32(servicePort))) (or
Expect(...).To(Equal(int32(<package>.servicePort))) if the constant lives in
another package). Keep the other assertions (port.Name and port.TargetPort)
as-is and ensure you import or reference the servicePort symbol so the test uses
the canonical production value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@controllers/evalhub/build_test.go`:
- Line 169: The test asserts the kube-rbac-proxy upstream uses "https://" but
the sidecar in deployment.go configures an HTTP localhost hop; update the
assertion in build_test.go so Expect(strings.Join(krp.Args, "
")).To(ContainSubstring(fmt.Sprintf("--upstream=http://127.0.0.1:%d/",
evalHubAppPort))) — i.e., change the scheme to http to match krp.Args and the
upstream built in the sidecar (krp, evalHubAppPort).

In `@controllers/evalhub/deployment_test.go`:
- Around line 147-148: The test assertions currently expect the kube-rbac-proxy
upstream to use HTTPS but deployment.go builds the flag with HTTP; update the
assertions that check krp.Args (the Expect(krp.Args).To(ContainElement(...)) and
the Expect(strings.Join(krp.Args, " ")).To(ContainSubstring(...)) checks) to
assert "http://127.0.0.1:<evalHubAppPort>/" instead of "https://..." so they
match the upstream flag generated by the code that builds --upstream in
deployment.go.

---

Nitpick comments:
In `@controllers/evalhub/build_test.go`:
- Around line 376-378: The test currently asserts a hardcoded port value
(Expect(port.Port).To(Equal(int32(8443))) ); replace the magic number with the
production constant by asserting Expect(port.Port).To(Equal(int32(servicePort)))
(or Expect(...).To(Equal(int32(<package>.servicePort))) if the constant lives in
another package). Keep the other assertions (port.Name and port.TargetPort)
as-is and ensure you import or reference the servicePort symbol so the test uses
the canonical production value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 68095eff-b4d1-4b35-bc80-c983260ea94e

📥 Commits

Reviewing files that changed from the base of the PR and between 43c9e81 and 598898b.

📒 Files selected for processing (3)
  • controllers/evalhub/build_test.go
  • controllers/evalhub/deployment_test.go
  • controllers/evalhub/unit_test.go
💤 Files with no reviewable changes (1)
  • controllers/evalhub/unit_test.go

Comment thread controllers/evalhub/build_test.go Outdated
Comment thread controllers/evalhub/deployment_test.go Outdated
Comment thread controllers/evalhub/deployment.go Outdated
corev1.ResourceMemory: resource.MustParse("32Mi"),
},
Limits: corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("100m"),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nbs-rh is it to be expected that these are hard-coded here?

Data: map[string]string{
"evalHubImage": "quay.io/ruimvieira/eval-hub:test",
"evalHubImage": "quay.io/ruimvieira/eval-hub:test",
"kube-rbac-proxy": "quay.io/openshift/origin-kube-rbac-proxy:4.19",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nbs-rh should the version be hard-coded?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@julpayne we are constructing a configmap for tests here to simulate how a real version would be populated in the configmap on a cluster. So this is apt for the tests?

configMapName = "trustyai-service-operator-config"
configMapEvalHubImageKey = "evalHubImage"
configMapKubeRBACProxyImageKey = "kube-rbac-proxy"
defaultKubeRBACProxyImage = "quay.io/openshift/origin-kube-rbac-proxy:4.19"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nbs-rh hard-coded version?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the fallback if a version is not provided through the operator configmap. Since we do not own kube-rbac-proxy repo, it is better to stick to a default image that we know works well rather than use ":latest"? Btw, this version 4.19 does not have our changes with endpoint-specific SARs - it needs to be added to the configmap and here in code as default once we have an image.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
controllers/evalhub/deployment_test.go (1)

312-321: ⚠️ Potential issue | 🟠 Major

This fallback test never checks the fallback path.

After reconcileDeployment, there is no Expect on err or on the generated Deployment, so this spec currently passes whether the fallback works or not. The title also says “should fail” while the inline comment expects success. Please assert the intended default-image behavior explicitly.

Proposed fix
-It("should fail when config map is missing", func() {
+It("falls back to default images when config map is missing", func() {
 	By("Deleting config map")
 	err := k8sClient.Delete(ctx, configMap)
 	Expect(err).NotTo(HaveOccurred())

 	By("Reconciling deployment without config map")
 	err = reconciler.reconcileDeployment(ctx, evalHub, nil, nil)
-	// Deployment still succeeds because EvalHub image falls back to default
-	// when the ConfigMap is missing
+	Expect(err).NotTo(HaveOccurred())
+
+	deployment := waitForDeployment(evalHubName, testNamespace)
+	app := findContainerByName(deployment.Spec.Template.Spec.Containers, containerName)
+	krp := findContainerByName(deployment.Spec.Template.Spec.Containers, kubeRBACProxyContainerName)
+	Expect(app).NotTo(BeNil())
+	Expect(krp).NotTo(BeNil())
+	Expect(app.Image).To(Equal(defaultEvalHubImage))
+	Expect(krp.Image).To(Equal(defaultKubeRBACProxyImage))
 })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@controllers/evalhub/deployment_test.go` around lines 312 - 321, The test
currently deletes the ConfigMap and calls reconciler.reconcileDeployment but
never asserts the result; update the spec (and its description) to explicitly
check the intended fallback behavior by asserting the returned error from
reconcileDeployment (expect no error if fallback should succeed or expect error
if it should fail) and retrieve the generated Deployment (e.g., using
k8sClient.Get or the helper used elsewhere) to assert the container image equals
the default image constant; reference reconcileDeployment, evalHub, configMap,
k8sClient.Delete and the Deployment resource to locate where to add Expect(err)
and an Expect(deployment.Spec.Template.Spec.Containers[...].Image). Ensure the
test title/comment matches the asserted behavior.
controllers/evalhub/deployment.go (1)

146-147: ⚠️ Potential issue | 🟠 Major

Don't let CR env overrides break the sidecar-owned bind settings.

mergeEnvVars(defaultEnvVars, instance.Spec.Env) lets a user-provided API_HOST or PORT override the fixed 127.0.0.1:<evalHubAppPort> listener that the proxy assumes. That can re-expose eval-hub on the pod network or desynchronize it from --upstream=http://127.0.0.1:<evalHubAppPort>/, which turns the proxy into best-effort only. Please treat these keys as non-overridable.

Proposed fix
-	// Merge environment variables with CR values taking precedence
-	env := mergeEnvVars(defaultEnvVars, instance.Spec.Env)
+	// Merge environment variables with CR values taking precedence, except for
+	// proxy-owned bind settings that must remain fixed.
+	protectedEnv := map[string]struct{}{
+		"API_HOST": {},
+		"PORT":     {},
+	}
+	userEnv := make([]corev1.EnvVar, 0, len(instance.Spec.Env))
+	for _, envVar := range instance.Spec.Env {
+		if _, blocked := protectedEnv[envVar.Name]; blocked {
+			continue
+		}
+		userEnv = append(userEnv, envVar)
+	}
+	env := mergeEnvVars(defaultEnvVars, userEnv)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@controllers/evalhub/deployment.go` around lines 146 - 147, The mergeEnvVars
call allows CR overrides to stomp sidecar bind settings; prevent user-provided
API_HOST and PORT from overriding the fixed 127.0.0.1:<evalHubAppPort> listener
by treating those keys as non-overridable: either filter out API_HOST and PORT
from instance.Spec.Env before calling mergeEnvVars or modify mergeEnvVars to
accept a nonOverridableKeys set (e.g., {"API_HOST","PORT"}) and ignore any
incoming entries for those keys so defaultEnvVars retain precedence for the
evalHubAppPort-bound listener and proxy upstream.
♻️ Duplicate comments (1)
controllers/evalhub/constants.go (1)

17-20: ⚠️ Potential issue | 🟡 Minor

Update the port comment to match the current loopback protocol.

deployment.go now builds the sidecar upstream as http://127.0.0.1:<evalHubAppPort>/, so describing evalHubAppPort as the place where eval-hub “binds TLS” is stale and misleading. Please reword this as the internal loopback app port, or explicitly call out the temporary HTTP hop.

Based on learnings: in PR #716 the upstream URL from kube-rbac-proxy to evalhub is intentionally set to http://127.0.0.1:<evalHubAppPort>/ (HTTP, not HTTPS).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@controllers/evalhub/constants.go` around lines 17 - 20, The comment on
evalHubAppPort is outdated stating TLS binding; update the comment on the
constant evalHubAppPort to indicate this is the internal loopback application
port (or explicitly note the temporary HTTP hop from kube-rbac-proxy to the
sidecar upstream), reflecting that deployment.go now uses
http://127.0.0.1:<evalHubAppPort>/; also ensure the evalHubHealthPath comment
remains accurate about kube-rbac-proxy forwarding and kubelet probe behavior.
🧹 Nitpick comments (1)
controllers/evalhub/deployment.go (1)

410-428: Reuse operatorNamespace() in the image helpers.

Both image lookups duplicate the same "trustyai-service-operator-system" fallback that deployment_operator_settings.go already centralized. Using the helper here keeps image lookup and resource settings on one namespace policy.

Proposed refactor
 func (r *EvalHubReconciler) getKubeRBACProxyImage(ctx context.Context) (string, error) {
-	namespace := r.Namespace
-	if namespace == "" {
-		namespace = "trustyai-service-operator-system"
-	}
-	return utils.GetImageFromConfigMapWithFallback(ctx, r.Client, configMapKubeRBACProxyImageKey, r.effectiveOperatorConfigMapName(), namespace, defaultKubeRBACProxyImage)
+	return utils.GetImageFromConfigMapWithFallback(
+		ctx,
+		r.Client,
+		configMapKubeRBACProxyImageKey,
+		r.effectiveOperatorConfigMapName(),
+		r.operatorNamespace(),
+		defaultKubeRBACProxyImage,
+	)
 }
 
 // getEvalHubImage retrieves the EvalHub image from ConfigMap with fallback to default
 func (r *EvalHubReconciler) getEvalHubImage(ctx context.Context) (string, error) {
-	// Get the namespace where the operator is deployed (where the ConfigMap should be)
-	namespace := r.Namespace
-	if namespace == "" {
-		// Fallback to default namespace if not set
-		namespace = "trustyai-service-operator-system"
-	}
-
-	return utils.GetImageFromConfigMapWithFallback(ctx, r.Client, configMapEvalHubImageKey, r.effectiveOperatorConfigMapName(), namespace, defaultEvalHubImage)
+	return utils.GetImageFromConfigMapWithFallback(
+		ctx,
+		r.Client,
+		configMapEvalHubImageKey,
+		r.effectiveOperatorConfigMapName(),
+		r.operatorNamespace(),
+		defaultEvalHubImage,
+	)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@controllers/evalhub/deployment.go` around lines 410 - 428, Both
getKubeRBACProxyImage and getEvalHubImage duplicate the hardcoded
"trustyai-service-operator-system" fallback; replace the manual namespace
fallback logic with a call to the centralized helper (operatorNamespace or
operatorNamespace()) on the EvalHubReconciler so both functions use
r.operatorNamespace() to obtain the namespace, then pass that namespace into
utils.GetImageFromConfigMapWithFallback (keep using
configMapKubeRBACProxyImageKey, configMapEvalHubImageKey,
r.effectiveOperatorConfigMapName(), and the same default image constants).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@controllers/evalhub/deployment_test.go`:
- Around line 312-321: The test currently deletes the ConfigMap and calls
reconciler.reconcileDeployment but never asserts the result; update the spec
(and its description) to explicitly check the intended fallback behavior by
asserting the returned error from reconcileDeployment (expect no error if
fallback should succeed or expect error if it should fail) and retrieve the
generated Deployment (e.g., using k8sClient.Get or the helper used elsewhere) to
assert the container image equals the default image constant; reference
reconcileDeployment, evalHub, configMap, k8sClient.Delete and the Deployment
resource to locate where to add Expect(err) and an
Expect(deployment.Spec.Template.Spec.Containers[...].Image). Ensure the test
title/comment matches the asserted behavior.

In `@controllers/evalhub/deployment.go`:
- Around line 146-147: The mergeEnvVars call allows CR overrides to stomp
sidecar bind settings; prevent user-provided API_HOST and PORT from overriding
the fixed 127.0.0.1:<evalHubAppPort> listener by treating those keys as
non-overridable: either filter out API_HOST and PORT from instance.Spec.Env
before calling mergeEnvVars or modify mergeEnvVars to accept a
nonOverridableKeys set (e.g., {"API_HOST","PORT"}) and ignore any incoming
entries for those keys so defaultEnvVars retain precedence for the
evalHubAppPort-bound listener and proxy upstream.

---

Duplicate comments:
In `@controllers/evalhub/constants.go`:
- Around line 17-20: The comment on evalHubAppPort is outdated stating TLS
binding; update the comment on the constant evalHubAppPort to indicate this is
the internal loopback application port (or explicitly note the temporary HTTP
hop from kube-rbac-proxy to the sidecar upstream), reflecting that deployment.go
now uses http://127.0.0.1:<evalHubAppPort>/; also ensure the evalHubHealthPath
comment remains accurate about kube-rbac-proxy forwarding and kubelet probe
behavior.

---

Nitpick comments:
In `@controllers/evalhub/deployment.go`:
- Around line 410-428: Both getKubeRBACProxyImage and getEvalHubImage duplicate
the hardcoded "trustyai-service-operator-system" fallback; replace the manual
namespace fallback logic with a call to the centralized helper
(operatorNamespace or operatorNamespace()) on the EvalHubReconciler so both
functions use r.operatorNamespace() to obtain the namespace, then pass that
namespace into utils.GetImageFromConfigMapWithFallback (keep using
configMapKubeRBACProxyImageKey, configMapEvalHubImageKey,
r.effectiveOperatorConfigMapName(), and the same default image constants).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cd288ce8-3276-4484-bead-09745548dc60

📥 Commits

Reviewing files that changed from the base of the PR and between 598898b and 83592cc.

📒 Files selected for processing (5)
  • controllers/evalhub/build_test.go
  • controllers/evalhub/constants.go
  • controllers/evalhub/deployment.go
  • controllers/evalhub/deployment_operator_settings.go
  • controllers/evalhub/deployment_test.go

@nbs-rh nbs-rh requested a review from julpayne April 28, 2026 11:36
@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented Apr 28, 2026

@nbs-rh: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/trustyai-service-operator-e2e 7872c03 link true /test trustyai-service-operator-e2e

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Copy link
Copy Markdown
Collaborator

@julpayne julpayne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM (as far as I can see)

@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented Apr 28, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: julpayne

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants