Skip to content

Commit 7e176de

Browse files
authored
udproute gateway api translator (#728)
* udproute gateway api translator Signed-off-by: zhaohuabing <zhaohuabing@gmail.com> * unit tests for udproute Signed-off-by: zhaohuabing <zhaohuabing@gmail.com> * add unit test for multiple http routes on multiple http listeners Signed-off-by: zhaohuabing <zhaohuabing@gmail.com> * check if the port protocol of services matches Signed-off-by: zhaohuabing <zhaohuabing@gmail.com> * fix lint Signed-off-by: zhaohuabing <zhaohuabing@gmail.com> * address the comments Signed-off-by: zhaohuabing <zhaohuabing@gmail.com> * check conflicted layer-4 listeners Signed-off-by: zhaohuabing <zhaohuabing@gmail.com> * refactory ProcessListeners Split ProcessListeners to serveral smaller methods since it has grown too large and made it hard to read Signed-off-by: zhaohuabing <zhaohuabing@gmail.com> * fix lint Signed-off-by: zhaohuabing <zhaohuabing@gmail.com> * check if the protocol matches for TCP ports Signed-off-by: zhaohuabing <zhaohuabing@gmail.com> * fix: tests has been modified by accident Signed-off-by: zhaohuabing <zhaohuabing@gmail.com>
1 parent 13c1113 commit 7e176de

33 files changed

Lines changed: 2283 additions & 332 deletions

File tree

internal/cmd/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ func setupRunners(cfg *config.Server) error {
159159
pResources.GatewayStatuses.Close()
160160
pResources.HTTPRouteStatuses.Close()
161161
pResources.TLSRouteStatuses.Close()
162+
pResources.UDPRouteStatuses.Close()
162163
xdsIR.Close()
163164
infraIR.Close()
164165
xds.Close()

internal/gatewayapi/contexts.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ func (l *ListenerContext) IncrementAttachedRoutes() {
134134
l.gateway.Status.Listeners[l.listenerStatusIdx].AttachedRoutes++
135135
}
136136

137+
func (l *ListenerContext) AttachedRoutes() int32 {
138+
return l.gateway.Status.Listeners[l.listenerStatusIdx].AttachedRoutes
139+
}
140+
137141
func (l *ListenerContext) AllowsKind(kind v1beta1.RouteGroupKind) bool {
138142
for _, allowed := range l.gateway.Status.Listeners[l.listenerStatusIdx].SupportedKinds {
139143
if GroupDerefOr(allowed.Group, "") == GroupDerefOr(kind.Group, "") && allowed.Kind == kind.Kind {
@@ -360,6 +364,88 @@ func (t *TLSRouteContext) GetRouteParentContext(forParentRef v1beta1.ParentRefer
360364
return ctx
361365
}
362366

367+
// UDPRouteContext wraps a UDPRoute and provides helper methods for
368+
// accessing the route's parents.
369+
type UDPRouteContext struct {
370+
*v1alpha2.UDPRoute
371+
372+
parentRefs map[v1beta1.ParentReference]*RouteParentContext
373+
}
374+
375+
func (u *UDPRouteContext) GetRouteType() string {
376+
return KindUDPRoute
377+
}
378+
379+
// GetHostnames return empty string array because UDPRoute has no hostnames
380+
func (u *UDPRouteContext) GetHostnames() []string {
381+
return []string{""}
382+
}
383+
384+
func (u *UDPRouteContext) GetParentReferences() []v1beta1.ParentReference {
385+
parentReferences := make([]v1beta1.ParentReference, len(u.Spec.ParentRefs))
386+
for idx, p := range u.Spec.ParentRefs {
387+
parentReferences[idx] = UpgradeParentReference(p)
388+
}
389+
return parentReferences
390+
}
391+
392+
func (u *UDPRouteContext) GetRouteParentContext(forParentRef v1beta1.ParentReference) *RouteParentContext {
393+
if u.parentRefs == nil {
394+
u.parentRefs = make(map[v1beta1.ParentReference]*RouteParentContext)
395+
}
396+
397+
if ctx := u.parentRefs[forParentRef]; ctx != nil {
398+
return ctx
399+
}
400+
401+
var parentRef *v1beta1.ParentReference
402+
for i, p := range u.Spec.ParentRefs {
403+
p := UpgradeParentReference(p)
404+
if reflect.DeepEqual(p, forParentRef) {
405+
upgraded := UpgradeParentReference(u.Spec.ParentRefs[i])
406+
parentRef = &upgraded
407+
break
408+
}
409+
}
410+
if parentRef == nil {
411+
panic("parentRef not found")
412+
}
413+
414+
routeParentStatusIdx := -1
415+
for i := range u.Status.Parents {
416+
p := UpgradeParentReference(u.Status.Parents[i].ParentRef)
417+
defaultNamespace := v1beta1.Namespace(metav1.NamespaceDefault)
418+
if forParentRef.Namespace == nil {
419+
forParentRef.Namespace = &defaultNamespace
420+
}
421+
if p.Namespace == nil {
422+
p.Namespace = &defaultNamespace
423+
}
424+
if reflect.DeepEqual(p, forParentRef) {
425+
routeParentStatusIdx = i
426+
break
427+
}
428+
}
429+
if routeParentStatusIdx == -1 {
430+
rParentStatus := v1alpha2.RouteParentStatus{
431+
// TODO: get this value from the config
432+
ControllerName: v1alpha2.GatewayController(egv1alpha1.GatewayControllerName),
433+
ParentRef: DowngradeParentReference(forParentRef),
434+
}
435+
u.Status.Parents = append(u.Status.Parents, rParentStatus)
436+
routeParentStatusIdx = len(u.Status.Parents) - 1
437+
}
438+
439+
ctx := &RouteParentContext{
440+
ParentReference: parentRef,
441+
442+
udpRoute: u.UDPRoute,
443+
routeParentStatusIdx: routeParentStatusIdx,
444+
}
445+
u.parentRefs[forParentRef] = ctx
446+
return ctx
447+
}
448+
363449
// RouteParentContext wraps a ParentReference and provides helper methods for
364450
// setting conditions and other status information on the associated
365451
// HTTPRoute, TLSRoute etc.
@@ -370,6 +456,7 @@ type RouteParentContext struct {
370456
// a single field pointing to *v1beta1.RouteStatus.
371457
httpRoute *v1beta1.HTTPRoute
372458
tlsRoute *v1alpha2.TLSRoute
459+
udpRoute *v1alpha2.UDPRoute
373460

374461
routeParentStatusIdx int
375462
listeners []*ListenerContext
@@ -429,6 +516,25 @@ func (r *RouteParentContext) SetCondition(route RouteContext, conditionType v1be
429516
} else {
430517
r.tlsRoute.Status.Parents[r.routeParentStatusIdx].Conditions = append(r.tlsRoute.Status.Parents[r.routeParentStatusIdx].Conditions, cond)
431518
}
519+
case KindUDPRoute:
520+
for i, existing := range r.udpRoute.Status.Parents[r.routeParentStatusIdx].Conditions {
521+
if existing.Type == cond.Type {
522+
// return early if the condition is unchanged
523+
if existing.Status == cond.Status &&
524+
existing.Reason == cond.Reason &&
525+
existing.Message == cond.Message {
526+
return
527+
}
528+
idx = i
529+
break
530+
}
531+
}
532+
533+
if idx > -1 {
534+
r.udpRoute.Status.Parents[r.routeParentStatusIdx].Conditions[idx] = cond
535+
} else {
536+
r.udpRoute.Status.Parents[r.routeParentStatusIdx].Conditions = append(r.udpRoute.Status.Parents[r.routeParentStatusIdx].Conditions, cond)
537+
}
432538
}
433539
}
434540

@@ -438,6 +544,8 @@ func (r *RouteParentContext) ResetConditions(route RouteContext) {
438544
r.httpRoute.Status.Parents[r.routeParentStatusIdx].Conditions = make([]metav1.Condition, 0)
439545
case KindTLSRoute:
440546
r.tlsRoute.Status.Parents[r.routeParentStatusIdx].Conditions = make([]metav1.Condition, 0)
547+
case KindUDPRoute:
548+
r.udpRoute.Status.Parents[r.routeParentStatusIdx].Conditions = make([]metav1.Condition, 0)
441549
}
442550
}
443551

@@ -448,6 +556,8 @@ func (r *RouteParentContext) IsAccepted(route RouteContext) bool {
448556
conditions = r.httpRoute.Status.Parents[r.routeParentStatusIdx].Conditions
449557
case KindTLSRoute:
450558
conditions = r.tlsRoute.Status.Parents[r.routeParentStatusIdx].Conditions
559+
case KindUDPRoute:
560+
conditions = r.udpRoute.Status.Parents[r.routeParentStatusIdx].Conditions
451561
}
452562
for _, cond := range conditions {
453563
if cond.Type == string(v1beta1.RouteConditionAccepted) && cond.Status == metav1.ConditionTrue {

internal/gatewayapi/helpers.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ import (
1313
"sigs.k8s.io/gateway-api/apis/v1beta1"
1414
)
1515

16+
const (
17+
TCPProtocol = "TCP"
18+
UDPProtocol = "UDP"
19+
)
20+
1621
func GroupPtr(name string) *v1beta1.Group {
1722
group := v1beta1.Group(name)
1823
return &group
@@ -223,3 +228,21 @@ func hostnameMatchesWildcardHostname(hostname, wildcardHostname string) bool {
223228
wildcardMatch := strings.TrimSuffix(hostname, strings.TrimPrefix(wildcardHostname, "*"))
224229
return len(wildcardMatch) > 0
225230
}
231+
232+
func containsPort(ports []*ProtocolPort, port *ProtocolPort) bool {
233+
for _, protocolPort := range ports {
234+
if protocolPort.port == port.port && layer4Protocol(protocolPort) == layer4Protocol(port) {
235+
return true
236+
}
237+
}
238+
return false
239+
}
240+
241+
func layer4Protocol(protocolPort *ProtocolPort) string {
242+
switch protocolPort.protocol {
243+
case v1beta1.HTTPProtocolType, v1beta1.HTTPSProtocolType, v1beta1.TLSProtocolType, v1beta1.TCPProtocolType:
244+
return TCPProtocol
245+
default:
246+
return UDPProtocol
247+
}
248+
}

internal/gatewayapi/runner/runner.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ func (r *Runner) subscribeAndTranslate(ctx context.Context) {
111111
key := utils.NamespacedName(tlsRoute)
112112
r.ProviderResources.TLSRouteStatuses.Store(key, tlsRoute)
113113
}
114+
for _, udpRoute := range result.UDPRoutes {
115+
key := utils.NamespacedName(udpRoute)
116+
r.ProviderResources.UDPRouteStatuses.Store(key, udpRoute)
117+
}
114118
},
115119
)
116120
r.Logger.Info("shutting down")
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
gateways:
2+
- apiVersion: gateway.networking.k8s.io/v1beta1
3+
kind: Gateway
4+
metadata:
5+
namespace: envoy-gateway
6+
name: gateway-1
7+
spec:
8+
gatewayClassName: envoy-gateway-class
9+
listeners:
10+
- name: udp
11+
hostname: foo.com
12+
protocol: UDP
13+
port: 80
14+
allowedRoutes:
15+
namespaces:
16+
from: All
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
gateways:
2+
- apiVersion: gateway.networking.k8s.io/v1beta1
3+
kind: Gateway
4+
metadata:
5+
namespace: envoy-gateway
6+
name: gateway-1
7+
spec:
8+
gatewayClassName: envoy-gateway-class
9+
listeners:
10+
- name: udp
11+
hostname: foo.com
12+
protocol: UDP
13+
port: 80
14+
allowedRoutes:
15+
namespaces:
16+
from: All
17+
status:
18+
listeners:
19+
- name: udp
20+
supportedKinds:
21+
- group: gateway.networking.k8s.io
22+
kind: UDPRoute
23+
conditions:
24+
- type: Programmed
25+
status: "False"
26+
reason: Invalid
27+
message: Listener must not have hostname set when protocol is UDP.
28+
xdsIR:
29+
envoy-gateway-gateway-1: {}
30+
infraIR:
31+
envoy-gateway-gateway-1:
32+
proxy:
33+
metadata:
34+
labels:
35+
gateway.envoyproxy.io/owning-gateway-name: gateway-1
36+
gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
37+
name: envoy-gateway-gateway-1
38+
image: envoyproxy/envoy:translator-tests
39+
listeners:
40+
- address: ""
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
gateways:
2+
- apiVersion: gateway.networking.k8s.io/v1beta1
3+
kind: Gateway
4+
metadata:
5+
namespace: envoy-gateway
6+
name: gateway-1
7+
spec:
8+
gatewayClassName: envoy-gateway-class
9+
listeners:
10+
- name: udp
11+
protocol: UDP
12+
port: 162
13+
allowedRoutes:
14+
namespaces:
15+
from: All
16+
udpRoutes:
17+
- apiVersion: gateway.networking.k8s.io/v1alpha2
18+
kind: UDPRoute
19+
metadata:
20+
namespace: default
21+
name: udproute-1
22+
spec:
23+
parentRefs:
24+
- namespace: envoy-gateway
25+
name: gateway-1
26+
rules:
27+
- backendRefs:
28+
- name: service-1
29+
port: 8080
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
gateways:
2+
- apiVersion: gateway.networking.k8s.io/v1beta1
3+
kind: Gateway
4+
metadata:
5+
namespace: envoy-gateway
6+
name: gateway-1
7+
spec:
8+
gatewayClassName: envoy-gateway-class
9+
listeners:
10+
- name: udp
11+
protocol: UDP
12+
port: 162
13+
allowedRoutes:
14+
namespaces:
15+
from: All
16+
status:
17+
listeners:
18+
- name: udp
19+
supportedKinds:
20+
- group: gateway.networking.k8s.io
21+
kind: UDPRoute
22+
AttachedRoutes: 0
23+
conditions:
24+
- type: Programmed
25+
status: "True"
26+
reason: Programmed
27+
message: Listener is ready
28+
udpRoutes:
29+
- apiVersion: gateway.networking.k8s.io/v1alpha2
30+
kind: UDPRoute
31+
metadata:
32+
namespace: default
33+
name: udproute-1
34+
spec:
35+
parentRefs:
36+
- namespace: envoy-gateway
37+
name: gateway-1
38+
rules:
39+
- backendRefs:
40+
- name: service-1
41+
port: 8080
42+
status:
43+
parents:
44+
- parentRef:
45+
namespace: envoy-gateway
46+
name: gateway-1
47+
controllerName: gateway.envoyproxy.io/gatewayclass-controller
48+
conditions:
49+
- type: Accepted
50+
status: "True"
51+
reason: Accepted
52+
message: Route is accepted
53+
- type: ResolvedRefs
54+
status: "False"
55+
reason: PortNotFound
56+
message: UDP Port 8080 not found on service default/service-1
57+
xdsIR:
58+
envoy-gateway-gateway-1: {}
59+
60+
infraIR:
61+
envoy-gateway-gateway-1:
62+
proxy:
63+
metadata:
64+
labels:
65+
gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
66+
gateway.envoyproxy.io/owning-gateway-name: gateway-1
67+
name: envoy-gateway-gateway-1
68+
image: envoyproxy/envoy:translator-tests
69+
listeners:
70+
- address: ""
71+
ports:
72+
- name: udp
73+
protocol: "UDP"
74+
servicePort: 162
75+
containerPort: 10162

0 commit comments

Comments
 (0)