Skip to content

Commit f5bee1d

Browse files
committed
refactor with specIvalid
Signed-off-by: zirain <zirain2009@gmail.com>
1 parent 8a6b56c commit f5bee1d

4 files changed

Lines changed: 294 additions & 162 deletions

File tree

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: 93 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,94 @@ 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)
@@ -223,51 +311,13 @@ func (t *Translator) ProcessListeners(gateways []*GatewayContext, xdsIR resource
223311
// don't block valid ones during conflict detection.
224312
for _, gateway := range gateways {
225313
for _, listener := range gateway.listeners {
226-
// Process protocol & supported kinds
227-
switch listener.Protocol {
228-
case gwapiv1.TLSProtocolType:
229-
if listener.TLS != nil {
230-
switch *listener.TLS.Mode {
231-
case gwapiv1.TLSModePassthrough:
232-
t.validateAllowedRoutes(listener, resource.KindTLSRoute)
233-
case gwapiv1.TLSModeTerminate:
234-
t.validateAllowedRoutes(listener, resource.KindTCPRoute, resource.KindTLSRoute)
235-
default:
236-
t.validateAllowedRoutes(listener, resource.KindTCPRoute, resource.KindTLSRoute)
237-
}
238-
} else {
239-
t.validateAllowedRoutes(listener, resource.KindTCPRoute, resource.KindTLSRoute)
240-
}
241-
case gwapiv1.HTTPProtocolType, gwapiv1.HTTPSProtocolType:
242-
t.validateAllowedRoutes(listener, resource.KindHTTPRoute, resource.KindGRPCRoute)
243-
case gwapiv1.TCPProtocolType:
244-
t.validateAllowedRoutes(listener, resource.KindTCPRoute)
245-
case gwapiv1.UDPProtocolType:
246-
t.validateAllowedRoutes(listener, resource.KindUDPRoute)
247-
default:
248-
listener.SetSupportedKinds()
249-
listener.SetCondition(
250-
gwapiv1.ListenerConditionAccepted,
251-
metav1.ConditionFalse,
252-
gwapiv1.ListenerReasonUnsupportedProtocol,
253-
fmt.Sprintf("Protocol %s is unsupported, must be %s, %s, %s or %s.", listener.Protocol,
254-
gwapiv1.HTTPProtocolType, gwapiv1.HTTPSProtocolType, gwapiv1.TCPProtocolType, gwapiv1.UDPProtocolType),
255-
)
256-
}
257-
258-
// Validate allowed namespaces
259-
t.validateAllowedNamespaces(listener)
260-
261-
// Process TLS configuration
262-
t.validateTLSConfiguration(listener, resources)
263-
264-
// Process Hostname configuration
265-
t.validateHostName(listener)
314+
listener.specValid = t.validateListenerSpec(listener, resources)
266315
}
267316
}
268317

269318
// Phase 2: Run conflict detection.
270319
// Only listeners that haven't been marked as invalid will participate in conflict resolution.
320+
t.validateConflictedProtocolsListeners(gateways)
271321
t.validateConflictedLayer7Listeners(gateways)
272322
t.validateConflictedLayer4Listeners(gateways, gwapiv1.TCPProtocolType)
273323
t.validateConflictedLayer4Listeners(gateways, gwapiv1.UDPProtocolType)
@@ -286,9 +336,9 @@ func (t *Translator) ProcessListeners(gateways []*GatewayContext, xdsIR resource
286336
t.processProxyObservability(gateway, xdsIR[irKey], infraIR[irKey].Proxy, resources)
287337

288338
for _, listener := range gateway.listeners {
289-
// Process conditions and check if the listener is ready
290-
isReady := t.validateListenerConditions(listener)
291-
if !isReady {
339+
// Finalize listener conditions and check readiness.
340+
t.validateListenerConditions(listener)
341+
if !listener.IsReady() {
292342
// Skip invalid listeners from IR building
293343
continue
294344
}

internal/gatewayapi/testdata/gateway-with-two-listeners-with-same-port-http-tcp-protocol.out.yaml

Lines changed: 11 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,18 @@ gateways:
4545
kind: HTTPRoute
4646
- group: gateway.networking.k8s.io
4747
kind: GRPCRoute
48-
- attachedRoutes: 1
48+
- attachedRoutes: 0
4949
conditions:
5050
- lastTransitionTime: null
51-
message: Sending translated listener configuration to the data plane
52-
reason: Programmed
51+
message: All listeners for a given port must use a compatible protocol
52+
reason: ProtocolConflict
5353
status: "True"
54-
type: Programmed
54+
type: Conflicted
5555
- lastTransitionTime: null
56-
message: Listener has been successfully translated
57-
reason: Accepted
58-
status: "True"
59-
type: Accepted
56+
message: Listener is invalid, see other Conditions for details.
57+
reason: Invalid
58+
status: "False"
59+
type: Programmed
6060
- lastTransitionTime: null
6161
message: Listener references have been resolved
6262
reason: ResolvedRefs
@@ -110,12 +110,6 @@ infraIR:
110110
name: http-80
111111
protocol: HTTP
112112
servicePort: 80
113-
- name: envoy-gateway/gateway-1/tcp
114-
ports:
115-
- containerPort: 10080
116-
name: tcp-80
117-
protocol: TCP
118-
servicePort: 80
119113
metadata:
120114
labels:
121115
gateway.envoyproxy.io/owning-gateway-name: gateway-1
@@ -143,9 +137,9 @@ tcpRoutes:
143137
parents:
144138
- conditions:
145139
- lastTransitionTime: null
146-
message: Route is accepted
147-
reason: Accepted
148-
status: "True"
140+
message: Multiple routes on the same TCP listener
141+
reason: UnsupportedValue
142+
status: "False"
149143
type: Accepted
150144
- lastTransitionTime: null
151145
message: Resolved all the Object references for the Route
@@ -233,38 +227,3 @@ xdsIR:
233227
ipFamily: IPv4
234228
path: /ready
235229
port: 19003
236-
tcp:
237-
- address: 0.0.0.0
238-
externalPort: 80
239-
metadata:
240-
kind: Gateway
241-
name: gateway-1
242-
namespace: envoy-gateway
243-
sectionName: tcp
244-
name: envoy-gateway/gateway-1/tcp
245-
port: 10080
246-
routes:
247-
- destination:
248-
metadata:
249-
kind: TCPRoute
250-
name: tcproute-1
251-
namespace: default
252-
name: tcproute/default/tcproute-1/rule/-1
253-
settings:
254-
- addressType: IP
255-
endpoints:
256-
- host: 7.7.7.7
257-
port: 8163
258-
metadata:
259-
kind: Service
260-
name: service-1
261-
namespace: default
262-
sectionName: "8163"
263-
name: tcproute/default/tcproute-1/rule/-1/backend/0
264-
protocol: TCP
265-
weight: 1
266-
metadata:
267-
kind: TCPRoute
268-
name: tcproute-1
269-
namespace: default
270-
name: tcproute/default/tcproute-1

0 commit comments

Comments
 (0)