fix(keycloak): stabilize audience mapper registration for existing scopes - #360
Merged
Merged
Conversation
…opes Match the Python AuthBridge sidecar pattern: for existing scopes, never POST a new mapper—only verify via GET and update via PUT. This prevents Keycloak ghost-409 cascades that corrupted mapper state on every reconcile. Key changes: - getOrCreateAudienceClientScope: skip ensureAudienceMapper for existing scopes; only POST mappers when creating a brand-new scope - verifyAudienceMapper: use createAudienceMapperBestEffort (treats 409 as success after verifying actual state) instead of ensureAudienceMapper - createAudienceMapperBestEffort: on 409, verify mapper exists via GET before returning success; handles ghost-conflict gracefully - updateAudienceMapperIfNeeded: use best-effort POST for delete+recreate path to avoid recursive ensureAudienceMapper calls Fixes rossoctl#358 Signed-off-by: Paolo Dettori <paolo@us.ibm.com> Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <dettori@us.ibm.com>
Signed-off-by: Paolo Dettori <paolo@us.ibm.com> Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <dettori@us.ibm.com>
cwiklik
approved these changes
May 14, 2026
cwiklik
left a comment
Collaborator
There was a problem hiding this comment.
Clean fix that correctly aligns the operator with the Python AuthBridge sidecar pattern: only POST mappers for brand-new scopes, verify via GET+PUT for existing ones. The createAudienceMapperBestEffort + ghost-409 handling is sound. Good test coverage for the corrupted-mapper and repair scenarios.
All 15 CI checks pass. 2 commits, both DCO-signed.
- Return error for non-409/non-2xx responses so the controller re-queues immediately rather than waiting for the next periodic reconcile - Fix comment: verifyAudienceMapper runs in the same reconcile, not next Signed-off-by: Paolo Dettori <paolo@us.ibm.com> Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <dettori@us.ibm.com>
3 tasks
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
createAudienceMapperBestEffort: on 409, verifies mapper state via GET before returning success; gracefully handles Keycloak ghost-conflictsRoot Cause
The operator called
ensureAudienceMapper(a POST) on every reconcile for existing scopes. The Python AuthBridge sidecar only POSTs a mapper when creating a brand-new scope. Repeated POSTs trigger 409 conflicts, and if a mapper ever enters a corrupted state, the 409 cascades into ghost-conflict entries in Keycloak's DB that persist across pod restarts.Test plan
go test ./internal/keycloak/ -run TestEnsureAudienceScope)localhost/kagenti-operator:fix-358)oidc-audience-mapperwith SPIFFE audience after all operationsFixes #358
Assisted-By: Claude Code