Skip to content

Commit 1fa2c97

Browse files
committed
skip invalid listener
Signed-off-by: zirain <zirain2009@gmail.com>
1 parent fa81778 commit 1fa2c97

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
@@ -138,6 +138,10 @@ type ListenerContext struct {
138138

139139
namespaceSelector labels.Selector
140140

141+
// specValid indicates whether per-listener spec validation succeeded.
142+
// Conflict detection should only consider listeners with specValid=true.
143+
specValid bool
144+
141145
tls ListenerTLSConfig
142146

143147
httpIR *ir.HTTPListener

internal/gatewayapi/listener.go

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

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

227-
// Iterate through all listeners to validate spec
228-
// and compute status for each, and add valid ones
229-
// to the Xds IR.
328+
// Phase 3: Build IR for valid listeners.
230329
for _, gateway := range gateways {
231330
irKey := t.getIRKey(gateway.Gateway)
232331

@@ -237,49 +336,12 @@ func (t *Translator) ProcessListeners(gateways []*GatewayContext, xdsIR resource
237336
t.processProxyObservability(gateway, xdsIR[irKey], infraIR[irKey].Proxy, resources)
238337

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

284346
// Skip listeners with invalid frontend TLS validation as they are not functional.
285347
if listener.frontendTLSValidationInvalid() {
@@ -422,10 +484,18 @@ func checkOverlappingHostnames(httpsListeners []*ListenerContext) {
422484
if overlappingListeners[i] != nil {
423485
continue
424486
}
487+
// Skip listeners that are already marked as invalid from per-listener validation
488+
if hasInvalidCondition(httpsListeners[i]) {
489+
continue
490+
}
425491
for j := i + 1; j < len(httpsListeners); j++ {
426492
if overlappingListeners[j] != nil {
427493
continue
428494
}
495+
// Skip listeners that are already marked as invalid from per-listener validation
496+
if hasInvalidCondition(httpsListeners[j]) {
497+
continue
498+
}
429499
if httpsListeners[i].Port != httpsListeners[j].Port {
430500
continue
431501
}
@@ -504,10 +574,18 @@ func checkOverlappingCertificates(httpsListeners []*ListenerContext) {
504574
if overlappingListeners[i] != nil {
505575
continue
506576
}
577+
// Skip listeners that are already marked as invalid from per-listener validation
578+
if hasInvalidCondition(httpsListeners[i]) {
579+
continue
580+
}
507581
for j := i + 1; j < len(httpsListeners); j++ {
508582
if overlappingListeners[j] != nil {
509583
continue
510584
}
585+
// Skip listeners that are already marked as invalid from per-listener validation
586+
if hasInvalidCondition(httpsListeners[j]) {
587+
continue
588+
}
511589
if httpsListeners[i].Port != httpsListeners[j].Port {
512590
continue
513591
}

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)