feat(keycloak): Gateway-API as an alternative to the two Ingresses

Group 3 of the #114 sweep, first chart. keycloak has TWO Ingress objects -
the public hostname and the admin hostname - so it gets TWO HTTPRoutes, with
httpRoute.admin.parentRefs falling back to httpRoute.parentRefs exactly as
ingress.admin.className falls back to ingress.className: "which data path" is
a property of the Gateway in this API, not of the route, so an internal
Gateway here is what an internal ingress class is in the other mode.

The admin route stays gated on ingress.admin.enabled, because that value is
not an Ingress switch - it also decides whether KC_HOSTNAME_ADMIN is set on
the Deployment, i.e. whether the admin hostname exists at all. Switching to
Gateway mode therefore must not require turning it off.

FIXED ALONG THE WAY: the admin Ingress did not hang off ingress.enabled.
Measured on origin/main, `helm template -f ci/keycloak/test-values.yaml --set
ingress.enabled=false` emitted kc-keycloak-admin-ingress - so
"ingress.enabled: false" did not mean "no Ingress", and ingress.clusterIssuer
stayed required for an object nobody asked for. It also made Gateway mode
impossible: the admin Ingress would have gone on claiming the admin host next
to the admin HTTPRoute, one host over two data paths, which is the exact
conflict the mutual exclusion exists to prevent. The default render is
unchanged; what changed is a combination that could not be coherent.

WHAT THIS MODE DROPS, MEASURED ON THE LIVE CLUSTER rather than inferred. On
2026-10-12 BOTH of this chart's Ingress objects in jk-cluster carried

    nginx.ingress.kubernetes.io/proxy-buffer-size:    16k
    nginx.ingress.kubernetes.io/proxy-buffers-number: 4

set by the OPERATOR through ingress.annotations and ingress.admin.annotations
- the chart sets neither, which is why reading the chart alone does not reveal
them. The Gateway API has no annotation channel and no counterpart for proxy
buffer sizing; it is implementation policy, not routing. Per decision 1 of
#114 the chart renders no policy object, so this is NAMED and not rebuilt, in
values.yaml, in the schema and in the Gateway branch of NOTES.txt, with the
consequence spelled out in Keycloak's own terms: large headers (JWTs, SAML
assertions, long Set-Cookie chains on the login flow) make a too-small buffer
answer 502 at the PROXY while Keycloak logs the request as successful.

Named and deliberately not touched: four further Ingress objects in the same
namespace (keycloak-realm-*-ingress, with app-root) belong to the Helm release
jk-cluster-config-fleet-cd-keycloak-realm-ingress, i.e. to another repository.
They point at this release's Service under four further hostnames. This chart
renders exactly the two routes its own two Ingresses cover.

Minor: additive, defaults unchanged. Empty helm template diff against
origin/main with ci/keycloak/test-values.yaml.

Part of #114
This commit is contained in:
jklein authored and jklein committed 2026-10-13 08:59:09 +00:00
1 parent d36112aaf6
commit d0f0d49a93
7 files changed
+470 -13

No files matched your search

+52
View File
@@ -0,0 +1,52 @@
# Render-only overlay for ci/run-httproute-match-test.sh, applied LAST.
#
# Its one job is to put this chart into Gateway mode, because the convention
# ships with httpRoute.enabled: false and the test would otherwise be green
# over nothing. A chart that has an overlay here and still renders no
# HTTPRoute FAILS the test - that is the whole point of the file existing.
#
# An overlay and not a change to ci/keycloak/test-values.yaml: the install-test
# runs exactly ONE configuration per chart, and this chart's fixture is the
# expensive and informative one - two replicas against a throwaway PostgreSQL,
# two init containers, `kc.sh build` under readOnlyRootFilesystem, and the
# Infinispan cluster forming over jdbc-ping (see that file for the four things
# only a real install can prove). Trading all of it for a CRD schema check that
# cyberchef's fixture already performs against the real Gateway-API CRDs would
# be a bad swap. The install keeps the two Ingresses; the two routes are held
# here, at render level, which is where their silent failure modes live.
#
# ingress.enabled: false is not optional here. The two modes are mutually
# exclusive and the chart aborts its own render when both are true, so an
# overlay that only switched httpRoute on would turn this test red.
#
# WHAT THIS OVERLAY COVERS, WHICH IS BOTH ROUTING OBJECTS:
#
# - ingress.domain comes from the fixture (keycloak.ci.example.com), so the
# PUBLIC route renders with its hostname from that single source of truth
# (httpRoute.hostnames stays empty on purpose - the fallback is the path
# that every installation will actually use).
# - ingress.admin.enabled is true in the fixture with
# ingress.admin.domain set, so the ADMIN route renders too, and its
# parentRefs fall back to httpRoute.parentRefs below - the empty-admin
# branch, which is the one an installation hits unless it wants an
# internal Gateway.
# - both routes' backendRef is checked against the serving Service of the
# same render. Both point at the SAME Service, by PORT NUMBER
# (keycloak.ports.http) rather than by the name "http" the Ingress uses,
# because backendRefs takes a PortNumber - so this overlay is what holds
# that number against the Service's.
#
# NOT covered here, and it cannot be: that either hostname answers. No Gateway
# controller runs in CI, no route reaches "Accepted: True", and the
# proxy-buffer-size/proxy-buffers-number annotations this chart's live
# installation relies on have no counterpart in this mode at all - that loss is
# documented in values.yaml and printed by NOTES.txt, and documentation is not
# something a test can assert.
ingress:
enabled: false
httpRoute:
enabled: true
parentRefs:
- name: public
namespace: gateway
sectionName: https