fix: clear error for hostname-less tenant listeners#220
Open
ecv wants to merge 1 commit into
Open
Conversation
Tenant Gateways whose listeners use the managed ports (80/443) without a hostname were rejected at admission with the opaque upstream CEL message "Combination of port, protocol and hostname must be unique for each listener", even for a single listener. Root cause: the mutating defaulter injects default-http/default-https listeners with no hostname at creation. A tenant listener on the same port/protocol without a hostname produces a duplicate (port, protocol, hostname) tuple, which the upstream Gateway API CRD CEL rejects. CRD CEL validation runs after mutating webhooks but before validating webhooks, so the equivalent check in validateListeners is preempted and never surfaces at creation. Reject hostname-less tenant (non-default) listeners in the defaulting webhook, before injecting the defaults, so the operator returns an actionable message instead of leaking the CEL error. Closes #219
Author
|
Setting to draft until the bug this fixes is reproduced with end to end tests in this repo. |
Author
|
E2E reproduction of the bug this PR fixes: #221 (separate branch, test-only, no production code). It reproduces the misleading |
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
Closes #219.
Tenant Gateways (class
datum-external-global-proxy) whose listeners use the managed ports (80/443) without a hostname were rejected at admission with the opaque upstream CEL message:— even for a single listener, which can't collide with anything.
Root cause
The mutating defaulter (
GatewayCustomDefaulter.Default) injectsdefault-http(80/HTTP) anddefault-https(443/HTTPS) with no hostname at creation (the canonical<uid>.<targetDomain>hostname is stamped later by the controller, since the UID isn't available in the create webhook). A tenant listener on the same port/protocol without a hostname produces a duplicate(port, protocol, hostname=∅)tuple, which the upstream Gateway API CRD CEL rejects.Admission order is mutating webhook → CRD schema/CEL validation → validating webhook, so the CEL fires before
validateListenersruns. That makes the existingvalidateListenershostname-required branch (gateway_validation.go) effectively unreachable for tenant listeners at creation — the opaque CEL error always wins.Fix
Reject hostname-less tenant (non-default) listeners in the defaulting webhook, before injecting the defaults — the defaulter returns
error, which denies admission before the CEL stage, so the operator surfaces an actionable message:default-named listeners are exempt (they're overwritten by defaulting and stamped by the controller), and bare gateways (no listeners) are unaffected.This is the design-intent-aligned option from the issue discussion (#3): the design already mandates a distinguishing hostname on tenant listeners (ports are locked to 80/443, so hostname is the only uniqueness axis), and the injected defaults are meant to coexist with hostname-bearing tenant listeners. It does not dedupe/suppress the default listeners (#1, would drop the canonical hostname) and does not try to stamp a hostname at create (#2, UID unavailable then).
Tests
TestValidateTenantListenerHostnames: bare gateway, hostname-less tenant listener (rejected), hostname-bearing tenant listener (ok),default-named listeners (exempt).go build ./...,go vet, and the validation package tests pass.Validation note
Reproduced the original failure against a live project control plane with
kubectl apply --dry-run=server: a single 80/HTTP listener with no hostname was rejected with the CEL "must be unique" message; adding a hostname made it apply cleanly — confirming the diagnosis.Refs datum-cloud/infra#2804.