Skip to content

Commit 7bc5d9c

Browse files
committed
gatewayapi: ignore unsupported listeners in protocol conflict checks
Signed-off-by: zirain <zirain2009@gmail.com>
1 parent 73d90aa commit 7bc5d9c

4 files changed

Lines changed: 209 additions & 1 deletion

File tree

internal/gatewayapi/testdata/listenerset-protocol-conflict.in.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,30 @@ listenerSets:
133133
allowedRoutes:
134134
namespaces:
135135
from: All
136+
# The first listener is invalid due to unsupported protocol and should not block
137+
# the following valid listener on the same port.
138+
- apiVersion: gateway.networking.k8s.io/v1
139+
kind: ListenerSet
140+
metadata:
141+
name: listenerset-invalid-first-should-not-block-valid
142+
namespace: gateway-conformance-infra
143+
spec:
144+
parentRef:
145+
kind: Gateway
146+
group: gateway.networking.k8s.io
147+
name: gateway-with-listenerset-protocol-conflict
148+
namespace: gateway-conformance-infra
149+
listeners:
150+
- name: invalid-unsupported-protocol-first
151+
port: 81
152+
protocol: SCTP
153+
allowedRoutes:
154+
namespaces:
155+
from: All
156+
- name: valid-http-after-invalid
157+
port: 81
158+
protocol: HTTP
159+
hostname: "valid-http-after-invalid.com"
160+
allowedRoutes:
161+
namespaces:
162+
from: All

internal/gatewayapi/testdata/listenerset-protocol-conflict.out.yaml

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ gateways:
2525
port: 80
2626
protocol: HTTP
2727
status:
28-
attachedListenerSets: 2
28+
attachedListenerSets: 3
2929
listeners:
3030
- attachedRoutes: 0
3131
conditions:
@@ -83,6 +83,12 @@ infraIR:
8383
name: http-80
8484
protocol: HTTP
8585
servicePort: 80
86+
- name: gateway-conformance-infra/gateway-with-listenerset-protocol-conflict/gateway-conformance-infra/listenerset-invalid-first-should-not-block-valid/valid-http-after-invalid
87+
ports:
88+
- containerPort: 10081
89+
name: http-81
90+
protocol: HTTP
91+
servicePort: 81
8692
metadata:
8793
labels:
8894
gateway.envoyproxy.io/owning-gateway-name: gateway-with-listenerset-protocol-conflict
@@ -426,6 +432,90 @@ listenerSets:
426432
supportedKinds:
427433
- group: gateway.networking.k8s.io
428434
kind: TCPRoute
435+
- apiVersion: gateway.networking.k8s.io/v1
436+
kind: ListenerSet
437+
metadata:
438+
name: listenerset-invalid-first-should-not-block-valid
439+
namespace: gateway-conformance-infra
440+
spec:
441+
listeners:
442+
- allowedRoutes:
443+
namespaces:
444+
from: All
445+
name: invalid-unsupported-protocol-first
446+
port: 81
447+
protocol: SCTP
448+
- allowedRoutes:
449+
namespaces:
450+
from: All
451+
hostname: valid-http-after-invalid.com
452+
name: valid-http-after-invalid
453+
port: 81
454+
protocol: HTTP
455+
parentRef:
456+
group: gateway.networking.k8s.io
457+
kind: Gateway
458+
name: gateway-with-listenerset-protocol-conflict
459+
namespace: gateway-conformance-infra
460+
status:
461+
conditions:
462+
- lastTransitionTime: null
463+
message: Some listeners are invalid
464+
reason: ListenersNotValid
465+
status: "True"
466+
type: Accepted
467+
- lastTransitionTime: null
468+
message: Some listeners are invalid
469+
reason: Programmed
470+
status: "True"
471+
type: Programmed
472+
listeners:
473+
- attachedRoutes: 0
474+
conditions:
475+
- lastTransitionTime: null
476+
message: Protocol SCTP is unsupported, must be HTTP, HTTPS, TCP or UDP.
477+
reason: UnsupportedProtocol
478+
status: "False"
479+
type: Accepted
480+
- lastTransitionTime: null
481+
message: Listener is invalid, see other Conditions for details.
482+
reason: ListenersNotValid
483+
status: "False"
484+
type: Programmed
485+
- lastTransitionTime: null
486+
message: Listener references have been resolved
487+
reason: ResolvedRefs
488+
status: "True"
489+
type: ResolvedRefs
490+
name: invalid-unsupported-protocol-first
491+
- attachedRoutes: 0
492+
conditions:
493+
- lastTransitionTime: null
494+
message: Sending translated listener configuration to the data plane
495+
reason: Programmed
496+
status: "True"
497+
type: Programmed
498+
- lastTransitionTime: null
499+
message: Listener has been successfully translated
500+
reason: Accepted
501+
status: "True"
502+
type: Accepted
503+
- lastTransitionTime: null
504+
message: Listener references have been resolved
505+
reason: ResolvedRefs
506+
status: "True"
507+
type: ResolvedRefs
508+
- lastTransitionTime: null
509+
message: No conflicts detected
510+
reason: NoConflicts
511+
status: "False"
512+
type: Conflicted
513+
name: valid-http-after-invalid
514+
supportedKinds:
515+
- group: gateway.networking.k8s.io
516+
kind: HTTPRoute
517+
- group: gateway.networking.k8s.io
518+
kind: GRPCRoute
429519
xdsIR:
430520
gateway-conformance-infra/gateway-with-listenerset-protocol-conflict:
431521
accessLog:
@@ -523,6 +613,20 @@ xdsIR:
523613
escapedSlashesAction: UnescapeAndRedirect
524614
mergeSlashes: true
525615
port: 10080
616+
- address: 0.0.0.0
617+
externalPort: 81
618+
hostnames:
619+
- valid-http-after-invalid.com
620+
metadata:
621+
kind: Gateway
622+
name: gateway-with-listenerset-protocol-conflict
623+
namespace: gateway-conformance-infra
624+
sectionName: valid-http-after-invalid
625+
name: gateway-conformance-infra/gateway-with-listenerset-protocol-conflict/gateway-conformance-infra/listenerset-invalid-first-should-not-block-valid/valid-http-after-invalid
626+
path:
627+
escapedSlashesAction: UnescapeAndRedirect
628+
mergeSlashes: true
629+
port: 10081
526630
readyListener:
527631
address: 0.0.0.0
528632
ipFamily: IPv4

internal/gatewayapi/validate.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,12 @@ func (t *Translator) validateConflictedProtocolsListeners(gateways []*GatewayCon
743743
for _, gateway := range gateways {
744744
portListenerInfo := map[gwapiv1.PortNumber]*portListeners{}
745745
for _, listener := range gateway.listeners {
746+
// Skip unsupported protocols here so invalid listeners do not
747+
// participate in protocol winner selection for the port.
748+
if !isSupportedListenerProtocol(listener.Protocol) {
749+
continue
750+
}
751+
746752
if portListenerInfo[listener.Port] == nil {
747753
portListenerInfo[listener.Port] = &portListeners{
748754
hostnames: map[string]int{},
@@ -888,6 +894,16 @@ func getProtocolForListener(listener *ListenerContext) string {
888894
return protocol
889895
}
890896

897+
func isSupportedListenerProtocol(protocol gwapiv1.ProtocolType) bool {
898+
switch protocol {
899+
case gwapiv1.HTTPProtocolType, gwapiv1.HTTPSProtocolType, gwapiv1.TLSProtocolType,
900+
gwapiv1.TCPProtocolType, gwapiv1.UDPProtocolType:
901+
return true
902+
default:
903+
return false
904+
}
905+
}
906+
891907
func (t *Translator) validateConflictedLayer4Listeners(gateways []*GatewayContext, protocols ...gwapiv1.ProtocolType) {
892908
// Iterate through all layer-4(TCP UDP) listeners and check if there are more than one listener on the same port
893909
for _, gateway := range gateways {
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright Envoy Gateway Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
// The full text of the Apache license is available in the LICENSE file at
4+
// the root of the repo.
5+
6+
package gatewayapi
7+
8+
import (
9+
"testing"
10+
11+
"github.com/stretchr/testify/require"
12+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13+
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
14+
)
15+
16+
func TestValidateConflictedProtocolsListenersIgnoresUnsupportedProtocols(t *testing.T) {
17+
unsupported := gwapiv1.ProtocolType("INVALID")
18+
19+
gateway := &gwapiv1.Gateway{}
20+
gateway.Status.Listeners = []gwapiv1.ListenerStatus{{}, {}}
21+
22+
gatewayCtx := &GatewayContext{Gateway: gateway}
23+
gatewayCtx.listeners = []*ListenerContext{
24+
{
25+
Listener: &gwapiv1.Listener{
26+
Name: "invalid",
27+
Port: 80,
28+
Protocol: unsupported,
29+
},
30+
gateway: gatewayCtx,
31+
listenerStatusIdx: 0,
32+
},
33+
{
34+
Listener: &gwapiv1.Listener{
35+
Name: "http",
36+
Port: 80,
37+
Protocol: gwapiv1.HTTPProtocolType,
38+
},
39+
gateway: gatewayCtx,
40+
listenerStatusIdx: 1,
41+
},
42+
}
43+
44+
translator := &Translator{}
45+
translator.validateConflictedProtocolsListeners([]*GatewayContext{gatewayCtx})
46+
47+
httpConds := gatewayCtx.Status.Listeners[1].Conditions
48+
require.False(t, hasListenerCondition(httpConds, gwapiv1.ListenerConditionConflicted, gwapiv1.ListenerReasonProtocolConflict, metav1.ConditionTrue))
49+
require.False(t, hasListenerCondition(httpConds, gwapiv1.ListenerConditionAccepted, gwapiv1.ListenerReasonProtocolConflict, metav1.ConditionFalse))
50+
require.False(t, hasListenerCondition(httpConds, gwapiv1.ListenerConditionProgrammed, gwapiv1.ListenerReasonProtocolConflict, metav1.ConditionFalse))
51+
}
52+
53+
func hasListenerCondition(conditions []metav1.Condition, condType gwapiv1.ListenerConditionType, reason gwapiv1.ListenerConditionReason, status metav1.ConditionStatus) bool {
54+
for _, cond := range conditions {
55+
if cond.Type == string(condType) && cond.Reason == string(reason) && cond.Status == status {
56+
return true
57+
}
58+
}
59+
60+
return false
61+
}

0 commit comments

Comments
 (0)