Skip to content

Commit 46b11c4

Browse files
committed
skip invalid listener
Signed-off-by: zirain <zirain2009@gmail.com>
1 parent 9f25066 commit 46b11c4

41 files changed

Lines changed: 663 additions & 900 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

internal/gatewayapi/contexts.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ type ListenerContext struct {
146146

147147
namespaceSelector labels.Selector
148148

149+
// specValid indicates whether per-listener spec validation succeeded.
150+
// Conflict detection should only consider listeners with specValid=true.
151+
specValid bool
152+
149153
tls ListenerTLSConfig
150154

151155
httpIR *ir.HTTPListener

internal/gatewayapi/listener.go

Lines changed: 123 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -217,19 +217,118 @@ func validClientCertificateRef(ref *gwapiv1.SecretObjectReference) error {
217217
return nil
218218
}
219219

220+
// allowedRouteKindsForProtocol returns the route kinds supported by the given listener protocol.
221+
func allowedRouteKindsForProtocol(protocol gwapiv1.ProtocolType, tlsMode *gwapiv1.TLSModeType) []gwapiv1.Kind {
222+
switch protocol {
223+
case gwapiv1.HTTPProtocolType, gwapiv1.HTTPSProtocolType:
224+
return []gwapiv1.Kind{resource.KindHTTPRoute, resource.KindGRPCRoute}
225+
case gwapiv1.TLSProtocolType:
226+
if tlsMode != nil && *tlsMode == gwapiv1.TLSModePassthrough {
227+
return []gwapiv1.Kind{resource.KindTLSRoute}
228+
}
229+
// Terminate mode or unspecified defaults to accept both TCP and TLS routes
230+
return []gwapiv1.Kind{resource.KindTCPRoute, resource.KindTLSRoute}
231+
case gwapiv1.TCPProtocolType:
232+
return []gwapiv1.Kind{resource.KindTCPRoute}
233+
case gwapiv1.UDPProtocolType:
234+
return []gwapiv1.Kind{resource.KindUDPRoute}
235+
default:
236+
return nil
237+
}
238+
}
239+
240+
// validateProtocolRules validates protocol-specific constraints (hostname, TLS requirements).
241+
// Returns true if all constraints are satisfied, false otherwise.
242+
func validateProtocolRules(listener *ListenerContext) bool {
243+
switch listener.Protocol {
244+
case gwapiv1.HTTPProtocolType, gwapiv1.HTTPSProtocolType, gwapiv1.TLSProtocolType,
245+
gwapiv1.TCPProtocolType, gwapiv1.UDPProtocolType:
246+
// All supported protocols pass basic validation here.
247+
// Protocol-specific constraints (TLS, hostname) are validated in
248+
// validateTLSConfiguration and validateHostName respectively,
249+
// where they can set proper conditions.
250+
return true
251+
252+
default:
253+
// Unsupported protocol handled separately
254+
return false
255+
}
256+
}
257+
258+
func (t *Translator) validateListenerSpec(listener *ListenerContext, resources *resource.Resources) bool {
259+
// Validate listener spec directly without relying on conditions.
260+
// Start with valid assumption and invalidate on failures.
261+
262+
// Phase 1: Validate fundamental rules
263+
specValid := t.validateAllowedNamespaces(listener)
264+
if !validateProtocolRules(listener) {
265+
specValid = false
266+
}
267+
268+
// Phase 2: Validate allowed routes based on protocol
269+
if listener.Protocol == gwapiv1.HTTPProtocolType ||
270+
listener.Protocol == gwapiv1.HTTPSProtocolType ||
271+
listener.Protocol == gwapiv1.TCPProtocolType ||
272+
listener.Protocol == gwapiv1.UDPProtocolType ||
273+
listener.Protocol == gwapiv1.TLSProtocolType {
274+
var tlsMode *gwapiv1.TLSModeType
275+
if listener.TLS != nil {
276+
tlsMode = listener.TLS.Mode
277+
}
278+
allowedKinds := allowedRouteKindsForProtocol(listener.Protocol, tlsMode)
279+
if !t.validateAllowedRoutes(listener, allowedKinds...) {
280+
specValid = false
281+
}
282+
} else {
283+
// Unsupported protocol
284+
specValid = false
285+
listener.SetSupportedKinds()
286+
listener.SetCondition(
287+
gwapiv1.ListenerConditionAccepted,
288+
metav1.ConditionFalse,
289+
gwapiv1.ListenerReasonUnsupportedProtocol,
290+
fmt.Sprintf("Protocol %s is unsupported, must be %s, %s, %s or %s.", listener.Protocol,
291+
gwapiv1.HTTPProtocolType, gwapiv1.HTTPSProtocolType, gwapiv1.TCPProtocolType, gwapiv1.UDPProtocolType),
292+
)
293+
}
294+
295+
// Phase 3: Validate TLS configuration details
296+
if !t.validateTLSConfiguration(listener, resources) {
297+
specValid = false
298+
}
299+
300+
// Phase 4: Validate Hostname configuration
301+
if !t.validateHostName(listener) {
302+
specValid = false
303+
}
304+
305+
return specValid
306+
}
307+
220308
func (t *Translator) ProcessListeners(gateways []*GatewayContext, xdsIR resource.XdsIRMap, infraIR resource.InfraIRMap, resources *resource.Resources) {
221309
// Infra IR proxy ports must be unique.
222310
foundPorts := make(map[string][]*protocolPort)
311+
312+
// Phase 1: Validate each listener's spec independently.
313+
// This must happen before conflict resolution so that invalid listeners
314+
// don't block valid ones during conflict detection.
315+
for _, gateway := range gateways {
316+
for _, listener := range gateway.listeners {
317+
listener.specValid = t.validateListenerSpec(listener, resources)
318+
}
319+
}
320+
321+
// Phase 2: Run conflict detection.
322+
// Only listeners that haven't been marked as invalid will participate in conflict resolution.
323+
t.validateConflictedProtocolsListeners(gateways)
223324
t.validateConflictedLayer7Listeners(gateways)
224325
t.validateConflictedLayer4Listeners(gateways, gwapiv1.TCPProtocolType)
225326
t.validateConflictedLayer4Listeners(gateways, gwapiv1.UDPProtocolType)
226327
if t.MergeGateways {
227328
t.validateConflictedMergedListeners(gateways)
228329
}
229330

230-
// Iterate through all listeners to validate spec
231-
// and compute status for each, and add valid ones
232-
// to the Xds IR.
331+
// Phase 3: Build IR for valid listeners.
233332
for _, gateway := range gateways {
234333
irKey := t.getIRKey(gateway.Gateway)
235334

@@ -240,49 +339,12 @@ func (t *Translator) ProcessListeners(gateways []*GatewayContext, xdsIR resource
240339
t.processProxyObservability(gateway, xdsIR[irKey], infraIR[irKey].Proxy, resources)
241340

242341
for _, listener := range gateway.listeners {
243-
// Process protocol & supported kinds
244-
switch listener.Protocol {
245-
case gwapiv1.TLSProtocolType:
246-
if listener.TLS != nil {
247-
switch *listener.TLS.Mode {
248-
case gwapiv1.TLSModePassthrough:
249-
t.validateAllowedRoutes(listener, resource.KindTLSRoute)
250-
case gwapiv1.TLSModeTerminate:
251-
t.validateAllowedRoutes(listener, resource.KindTCPRoute, resource.KindTLSRoute)
252-
default:
253-
t.validateAllowedRoutes(listener, resource.KindTCPRoute, resource.KindTLSRoute)
254-
}
255-
} else {
256-
t.validateAllowedRoutes(listener, resource.KindTCPRoute, resource.KindTLSRoute)
257-
}
258-
case gwapiv1.HTTPProtocolType, gwapiv1.HTTPSProtocolType:
259-
t.validateAllowedRoutes(listener, resource.KindHTTPRoute, resource.KindGRPCRoute)
260-
case gwapiv1.TCPProtocolType:
261-
t.validateAllowedRoutes(listener, resource.KindTCPRoute)
262-
case gwapiv1.UDPProtocolType:
263-
t.validateAllowedRoutes(listener, resource.KindUDPRoute)
264-
default:
265-
listener.SetSupportedKinds()
266-
listener.SetCondition(
267-
gwapiv1.ListenerConditionAccepted,
268-
metav1.ConditionFalse,
269-
gwapiv1.ListenerReasonUnsupportedProtocol,
270-
fmt.Sprintf("Protocol %s is unsupported, must be %s, %s, %s or %s.", listener.Protocol,
271-
gwapiv1.HTTPProtocolType, gwapiv1.HTTPSProtocolType, gwapiv1.TCPProtocolType, gwapiv1.UDPProtocolType),
272-
)
273-
}
274-
275-
// Validate allowed namespaces
276-
t.validateAllowedNamespaces(listener)
277-
278-
// Process TLS configuration
279-
t.validateTLSConfiguration(listener, resources)
280-
281-
// Process Hostname configuration
282-
t.validateHostName(listener)
283-
284-
// Process conditions and check if the listener is ready
342+
// Finalize listener conditions and check readiness.
285343
t.validateListenerConditions(listener)
344+
if !listener.IsReady() {
345+
// Skip invalid listeners from IR building
346+
continue
347+
}
286348

287349
// Skip listeners with invalid frontend TLS validation as they are not functional.
288350
if listener.frontendTLSValidationInvalid() {
@@ -425,10 +487,18 @@ func checkOverlappingHostnames(httpsListeners []*ListenerContext) {
425487
if overlappingListeners[i] != nil {
426488
continue
427489
}
490+
// Skip listeners that are already marked as invalid from per-listener validation
491+
if hasInvalidCondition(httpsListeners[i]) {
492+
continue
493+
}
428494
for j := i + 1; j < len(httpsListeners); j++ {
429495
if overlappingListeners[j] != nil {
430496
continue
431497
}
498+
// Skip listeners that are already marked as invalid from per-listener validation
499+
if hasInvalidCondition(httpsListeners[j]) {
500+
continue
501+
}
432502
if httpsListeners[i].Port != httpsListeners[j].Port {
433503
continue
434504
}
@@ -507,10 +577,18 @@ func checkOverlappingCertificates(httpsListeners []*ListenerContext) {
507577
if overlappingListeners[i] != nil {
508578
continue
509579
}
580+
// Skip listeners that are already marked as invalid from per-listener validation
581+
if hasInvalidCondition(httpsListeners[i]) {
582+
continue
583+
}
510584
for j := i + 1; j < len(httpsListeners); j++ {
511585
if overlappingListeners[j] != nil {
512586
continue
513587
}
588+
// Skip listeners that are already marked as invalid from per-listener validation
589+
if hasInvalidCondition(httpsListeners[j]) {
590+
continue
591+
}
514592
if httpsListeners[i].Port != httpsListeners[j].Port {
515593
continue
516594
}

internal/gatewayapi/route.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2282,7 +2282,7 @@ func (t *Translator) processAllowedListenersForParentRefs(
22822282
}
22832283
parentRefCtx.SetListeners(allowedListeners...)
22842284

2285-
if !HasReadyListener(selectedListeners) {
2285+
if !HasReadyListener(allowedListeners) {
22862286
routeStatus := GetRouteStatus(routeContext)
22872287
status.SetRouteStatusCondition(routeStatus,
22882288
parentRefCtx.routeParentStatusIdx,

internal/gatewayapi/testdata/backendtrafficpolicy-status-conditions.out.yaml

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -508,12 +508,6 @@ infraIR:
508508
name: http-80
509509
protocol: HTTP
510510
servicePort: 80
511-
- name: envoy-gateway/gateway-2/https
512-
ports:
513-
- containerPort: 10443
514-
name: https-443
515-
protocol: HTTPS
516-
servicePort: 443
517511
- name: envoy-gateway/gateway-2/tcp
518512
ports:
519513
- containerPort: 10053
@@ -735,20 +729,6 @@ xdsIR:
735729
namespace: envoy-gateway
736730
name: grpcroute/envoy-gateway/grpcroute-1/rule/0/match/0/*
737731
traffic: {}
738-
- address: 0.0.0.0
739-
externalPort: 443
740-
hostnames:
741-
- '*'
742-
metadata:
743-
kind: Gateway
744-
name: gateway-2
745-
namespace: envoy-gateway
746-
sectionName: https
747-
name: envoy-gateway/gateway-2/https
748-
path:
749-
escapedSlashesAction: UnescapeAndRedirect
750-
mergeSlashes: true
751-
port: 10443
752732
readyListener:
753733
address: 0.0.0.0
754734
ipFamily: IPv4

internal/gatewayapi/testdata/backendtrafficpolicy-with-routing-type-listener.out.yaml

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,6 @@ infraIR:
218218
name: http-80
219219
protocol: HTTP
220220
servicePort: 80
221-
- name: envoy-gateway/gateway-1/https
222-
ports:
223-
- containerPort: 10443
224-
name: https-443
225-
protocol: HTTPS
226-
servicePort: 443
227221
metadata:
228222
labels:
229223
gateway.envoyproxy.io/owning-gateway-name: gateway-1
@@ -309,20 +303,6 @@ xdsIR:
309303
name: ""
310304
prefix: /foo
311305
traffic: {}
312-
- address: 0.0.0.0
313-
externalPort: 443
314-
hostnames:
315-
- '*'
316-
metadata:
317-
kind: Gateway
318-
name: gateway-1
319-
namespace: envoy-gateway
320-
sectionName: https
321-
name: envoy-gateway/gateway-1/https
322-
path:
323-
escapedSlashesAction: UnescapeAndRedirect
324-
mergeSlashes: true
325-
port: 10443
326306
readyListener:
327307
address: 0.0.0.0
328308
ipFamily: IPv4

internal/gatewayapi/testdata/clienttrafficpolicy-status-conditions.out.yaml

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -453,12 +453,6 @@ infraIR:
453453
name: http-80
454454
protocol: HTTP
455455
servicePort: 80
456-
- name: envoy-gateway/gateway-2/https
457-
ports:
458-
- containerPort: 10443
459-
name: https-443
460-
protocol: HTTPS
461-
servicePort: 443
462456
- name: envoy-gateway/gateway-2/tcp
463457
ports:
464458
- containerPort: 10053
@@ -596,20 +590,6 @@ xdsIR:
596590
escapedSlashesAction: UnescapeAndRedirect
597591
mergeSlashes: true
598592
port: 10080
599-
- address: 0.0.0.0
600-
externalPort: 443
601-
hostnames:
602-
- '*'
603-
metadata:
604-
kind: Gateway
605-
name: gateway-2
606-
namespace: envoy-gateway
607-
sectionName: https
608-
name: envoy-gateway/gateway-2/https
609-
path:
610-
escapedSlashesAction: UnescapeAndRedirect
611-
mergeSlashes: true
612-
port: 10443
613593
readyListener:
614594
address: 0.0.0.0
615595
ipFamily: IPv4

internal/gatewayapi/testdata/envoyextensionpolicy-status-conditions.out.yaml

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -508,12 +508,6 @@ infraIR:
508508
name: http-80
509509
protocol: HTTP
510510
servicePort: 80
511-
- name: envoy-gateway/gateway-2/https
512-
ports:
513-
- containerPort: 10443
514-
name: https-443
515-
protocol: HTTPS
516-
servicePort: 443
517511
- name: envoy-gateway/gateway-2/tcp
518512
ports:
519513
- containerPort: 10053
@@ -727,20 +721,6 @@ xdsIR:
727721
name: grpcroute-1
728722
namespace: envoy-gateway
729723
name: grpcroute/envoy-gateway/grpcroute-1/rule/0/match/0/*
730-
- address: 0.0.0.0
731-
externalPort: 443
732-
hostnames:
733-
- '*'
734-
metadata:
735-
kind: Gateway
736-
name: gateway-2
737-
namespace: envoy-gateway
738-
sectionName: https
739-
name: envoy-gateway/gateway-2/https
740-
path:
741-
escapedSlashesAction: UnescapeAndRedirect
742-
mergeSlashes: true
743-
port: 10443
744724
readyListener:
745725
address: 0.0.0.0
746726
ipFamily: IPv4

0 commit comments

Comments
 (0)