Skip to content

Commit 58a1e06

Browse files
committed
merge conflict
1 parent 60f8334 commit 58a1e06

File tree

13 files changed

+529
-66
lines changed

13 files changed

+529
-66
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ require (
4242
k8s.io/klog/v2 v2.130.1
4343
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738
4444
sigs.k8s.io/controller-runtime v0.19.3
45+
sigs.k8s.io/gateway-api v1.2.0
4546
sigs.k8s.io/yaml v1.4.0
4647
)
4748

@@ -128,7 +129,6 @@ require (
128129
github.com/mattn/go-colorable v0.1.13 // indirect
129130
github.com/mattn/go-isatty v0.0.20 // indirect
130131
github.com/mattn/go-runewidth v0.0.9 // indirect
131-
github.com/miekg/dns v1.1.62 // indirect
132132
github.com/mitchellh/copystructure v1.2.0 // indirect
133133
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
134134
github.com/mitchellh/reflectwalk v1.0.2 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,8 @@ oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo=
623623
oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo=
624624
sigs.k8s.io/controller-runtime v0.19.3 h1:XO2GvC9OPftRst6xWCpTgBZO04S2cbp0Qqkj8bX1sPw=
625625
sigs.k8s.io/controller-runtime v0.19.3/go.mod h1:j4j87DqtsThvwTv5/Tc5NFRyyF/RF0ip4+62tbTSIUM=
626+
sigs.k8s.io/gateway-api v1.2.0 h1:LrToiFwtqKTKZcZtoQPTuo3FxhrrhTgzQG0Te+YGSo8=
627+
sigs.k8s.io/gateway-api v1.2.0/go.mod h1:EpNfEXNjiYfUJypf0eZ0P5iXA9ekSGWaS1WgPaM42X0=
626628
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
627629
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
628630
sigs.k8s.io/kustomize/api v0.18.0 h1:hTzp67k+3NEVInwz5BHyzc9rGxIauoXferXyjv5lWPo=

pkg/gateway/routeutils/backend.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package routeutils
2+
3+
import v1 "k8s.io/api/core/v1"
4+
5+
type BackendDescription interface {
6+
GetService() *v1.Service
7+
GetWeight() int32
8+
GetPort() int
9+
}
10+
11+
var _ BackendDescription = &backendDescriptionImpl{}
12+
13+
type backendDescriptionImpl struct {
14+
}
15+
16+
func (b backendDescriptionImpl) GetService() *v1.Service {
17+
//TODO implement me
18+
panic("implement me")
19+
}
20+
21+
func (b backendDescriptionImpl) GetWeight() int32 {
22+
//TODO implement me
23+
panic("implement me")
24+
}
25+
26+
func (b backendDescriptionImpl) GetPort() int {
27+
//TODO implement me
28+
panic("implement me")
29+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package routeutils
2+
3+
import (
4+
"context"
5+
"sigs.k8s.io/controller-runtime/pkg/client"
6+
gwv1 "sigs.k8s.io/gateway-api/apis/v1"
7+
)
8+
9+
const (
10+
TCPRouteKind = "TCPRoute"
11+
UDPRouteKind = "UDPRoute"
12+
TLSRouteKind = "TLSRoute"
13+
HTTPRouteKind = "HTTPRoute"
14+
GRPCRouteKind = "GRPCRoute"
15+
)
16+
17+
var allRoutes = map[string]func(context context.Context, client client.Client) ([]RouteDescriptor, error){
18+
TCPRouteKind: ListTCPRoutes,
19+
UDPRouteKind: ListUDPRoutes,
20+
TLSRouteKind: ListTLSRoutes,
21+
HTTPRouteKind: nil,
22+
GRPCRouteKind: nil,
23+
}
24+
25+
var defaultProtocolToRouteKindMap = map[gwv1.ProtocolType]string{
26+
gwv1.TCPProtocolType: TCPRouteKind,
27+
gwv1.UDPProtocolType: UDPRouteKind,
28+
gwv1.TLSProtocolType: TLSRouteKind,
29+
gwv1.HTTPProtocolType: HTTPRouteKind,
30+
gwv1.HTTPSProtocolType: HTTPRouteKind,
31+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package routeutils
2+
3+
type RouteDescriptor interface {
4+
GetRouteNamespace() string
5+
GetRouteName() string
6+
GetRouteKind() string
7+
GetAttachedRules() []BackendDescription
8+
GetRawRoute() interface{}
9+
}

pkg/gateway/routeutils/loader.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package routeutils
2+
3+
import (
4+
"context"
5+
"k8s.io/apimachinery/pkg/util/sets"
6+
"sigs.k8s.io/controller-runtime/pkg/client"
7+
gwv1 "sigs.k8s.io/gateway-api/apis/v1"
8+
)
9+
10+
type LoadRouteFilter interface {
11+
IsApplicable(kind string) bool
12+
}
13+
14+
type routeFilterImpl struct {
15+
acceptedKinds sets.Set[string]
16+
}
17+
18+
func (r *routeFilterImpl) IsApplicable(kind string) bool {
19+
return r.acceptedKinds.Has(kind)
20+
}
21+
22+
var L4RouteFilter LoadRouteFilter = &routeFilterImpl{
23+
acceptedKinds: sets.New(UDPRouteKind, TCPRouteKind, TLSRouteKind),
24+
}
25+
26+
var L7RouteFilter LoadRouteFilter = &routeFilterImpl{
27+
acceptedKinds: sets.New(HTTPRouteKind, GRPCRouteKind),
28+
}
29+
30+
type Loader interface {
31+
LoadRoutesForGateway(ctx context.Context, client client.Client, gw *gwv1.Gateway, filter LoadRouteFilter) (map[int][]RouteDescriptor, error)
32+
}
33+
34+
var _ Loader = &loaderImpl{}
35+
36+
type loaderImpl struct {
37+
mapper ListenerToRouteMapper
38+
}
39+
40+
func (l *loaderImpl) LoadRoutesForGateway(ctx context.Context, k8sclient client.Client, gw *gwv1.Gateway, filter LoadRouteFilter) (map[int][]RouteDescriptor, error) {
41+
// 1. Load all relevant routes according to the filter
42+
loadedRoutes := make([]RouteDescriptor, 0)
43+
for route, loader := range allRoutes {
44+
if filter.IsApplicable(route) {
45+
data, err := loader(ctx, k8sclient)
46+
if err != nil {
47+
return nil, err
48+
}
49+
loadedRoutes = append(loadedRoutes, data...)
50+
}
51+
}
52+
53+
// 2. Remove routes that aren't granted attachment by the listener.
54+
// Map any routes that are granted attachment to the listener port that allows the attachment.
55+
return l.mapper.Map(ctx, k8sclient, gw, loadedRoutes)
56+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package routeutils
2+
3+
import (
4+
"context"
5+
v1 "k8s.io/api/core/v1"
6+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
7+
"k8s.io/apimachinery/pkg/util/sets"
8+
"sigs.k8s.io/controller-runtime/pkg/client"
9+
gwv1 "sigs.k8s.io/gateway-api/apis/v1"
10+
)
11+
12+
type ListenerToRouteMapper interface {
13+
Map(context context.Context, client client.Client, gw *gwv1.Gateway, routes []RouteDescriptor) (map[int][]RouteDescriptor, error)
14+
}
15+
16+
var _ ListenerToRouteMapper = &listenerToRouteMapper{}
17+
18+
type listenerToRouteMapper struct {
19+
}
20+
21+
func (ltr *listenerToRouteMapper) Map(ctx context.Context, k8sclient client.Client, gw *gwv1.Gateway, routes []RouteDescriptor) (map[int][]RouteDescriptor, error) {
22+
result := make(map[int][]RouteDescriptor)
23+
24+
// Approach is to greedily add as many relevant routes to each listener.
25+
for _, listener := range gw.Spec.Listeners {
26+
for _, route := range routes {
27+
allowedAttachment, err := ltr.listenerAllowsAttachment(ctx, k8sclient, gw, listener, route)
28+
if err != nil {
29+
return nil, err
30+
}
31+
32+
if allowedAttachment {
33+
result[int(listener.Port)] = append(result[int(listener.Port)], route)
34+
}
35+
}
36+
}
37+
return result, nil
38+
}
39+
40+
func (ltr *listenerToRouteMapper) listenerAllowsAttachment(ctx context.Context, k8sclient client.Client, gw *gwv1.Gateway, listener gwv1.Listener, route RouteDescriptor) (bool, error) {
41+
namespaceOK, err := ltr.namespaceCheck(ctx, k8sclient, gw, listener, route)
42+
if err != nil {
43+
return false, err
44+
}
45+
46+
if !namespaceOK {
47+
return false, nil
48+
}
49+
50+
if !ltr.kindCheck(listener, route) {
51+
return false, nil
52+
}
53+
return true, nil
54+
}
55+
56+
func (ltr *listenerToRouteMapper) namespaceCheck(ctx context.Context, k8sclient client.Client, gw *gwv1.Gateway, listener gwv1.Listener, route RouteDescriptor) (bool, error) {
57+
if listener.AllowedRoutes == nil {
58+
return gw.Namespace == route.GetRouteNamespace(), nil
59+
}
60+
61+
var allowedNamespaces gwv1.FromNamespaces
62+
63+
if listener.AllowedRoutes.Namespaces == nil || listener.AllowedRoutes.Namespaces.From == nil {
64+
allowedNamespaces = gwv1.NamespacesFromSame
65+
} else {
66+
allowedNamespaces = *listener.AllowedRoutes.Namespaces.From
67+
}
68+
69+
switch allowedNamespaces {
70+
case gwv1.NamespacesFromSame:
71+
if gw.Namespace != route.GetRouteNamespace() {
72+
return false, nil
73+
}
74+
break
75+
case gwv1.NamespacesFromSelector:
76+
if listener.AllowedRoutes.Namespaces.Selector == nil {
77+
return false, nil
78+
}
79+
// This should be executed off the client-go cache, hence we do not need to perform local caching.
80+
namespaces, err := ltr.getNamespacesFromSelector(ctx, k8sclient, listener.AllowedRoutes.Namespaces.Selector)
81+
if err != nil {
82+
return false, err
83+
}
84+
85+
if !namespaces.Has(route.GetRouteNamespace()) {
86+
return false, nil
87+
}
88+
break
89+
case gwv1.NamespacesFromAll:
90+
// Nothing to check
91+
break
92+
default:
93+
// Unclear what to do in this case, let's try to be best effort and just ignore this value.
94+
return false, nil
95+
}
96+
97+
return false, nil
98+
}
99+
100+
func (ltr *listenerToRouteMapper) kindCheck(listener gwv1.Listener, route RouteDescriptor) bool {
101+
102+
var allowedRoutes sets.Set[string]
103+
104+
// Allowed Routes being null defaults to no checking required.
105+
if listener.AllowedRoutes.Kinds == nil {
106+
allowedRoutes = sets.New[string](defaultProtocolToRouteKindMap[listener.Protocol])
107+
} else {
108+
// TODO - Not sure how to handle versioning (correctly) here.
109+
// So going to ignore the group checks for now :x
110+
allowedRoutes = sets.New[string]()
111+
for _, v := range listener.AllowedRoutes.Kinds {
112+
allowedRoutes.Insert(string(v.Kind))
113+
}
114+
}
115+
return allowedRoutes.Has(route.GetRouteKind())
116+
}
117+
118+
func (ltr *listenerToRouteMapper) getNamespacesFromSelector(context context.Context, k8sclient client.Client, selector *metav1.LabelSelector) (sets.Set[string], error) {
119+
namespaceList := v1.NamespaceList{}
120+
121+
convertedSelector, err := metav1.LabelSelectorAsSelector(selector)
122+
if err != nil {
123+
return nil, err
124+
}
125+
listOpts := client.ListOptions{LabelSelector: convertedSelector}
126+
127+
err = k8sclient.List(context, &namespaceList, &listOpts)
128+
if err != nil {
129+
return nil, err
130+
}
131+
132+
namespaces := sets.New[string]()
133+
134+
for _, ns := range namespaceList.Items {
135+
namespaces.Insert(ns.Name)
136+
}
137+
138+
return namespaces, nil
139+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package routeutils
2+
3+
import v1 "k8s.io/api/core/v1"
4+
5+
type Rule interface {
6+
GetSectionName() string
7+
GetService() *v1.Service
8+
GetHostnames() []string
9+
GetWeight() int32
10+
}
11+
12+
var _ Rule = &ruleImpl{}
13+
14+
type ruleImpl struct{}
15+
16+
func (s *ruleImpl) GetHostnames() []string {
17+
//TODO implement me
18+
panic("implement me")
19+
}
20+
21+
func (s *ruleImpl) GetSectionName() string {
22+
//TODO implement me
23+
panic("implement me")
24+
}
25+
26+
func (s *ruleImpl) GetService() *v1.Service {
27+
//TODO implement me
28+
panic("implement me")
29+
}
30+
31+
func (s *ruleImpl) GetWeight() int32 {
32+
//TODO implement me
33+
panic("implement me")
34+
}

pkg/gateway/routeutils/tcp.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package routeutils
2+
3+
import (
4+
"context"
5+
"sigs.k8s.io/controller-runtime/pkg/client"
6+
gwalpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
7+
)
8+
9+
type tcpRouteDescription struct {
10+
route *gwalpha2.TCPRoute
11+
}
12+
13+
func (t *tcpRouteDescription) GetRouteKind() string {
14+
return TCPRouteKind
15+
}
16+
17+
func (t *tcpRouteDescription) GetRouteNamespace() string {
18+
return t.route.Namespace
19+
}
20+
21+
func (t *tcpRouteDescription) GetRouteName() string {
22+
return t.route.Name
23+
}
24+
25+
func convertTCPRoute(r gwalpha2.TCPRoute) *tcpRouteDescription {
26+
return &tcpRouteDescription{route: &r}
27+
}
28+
29+
func (t *tcpRouteDescription) GetAttachedRules() []BackendDescription {
30+
//TODO implement me
31+
panic("implement me")
32+
}
33+
34+
func (t *tcpRouteDescription) GetRawRoute() interface{} {
35+
return t.route
36+
}
37+
38+
var _ RouteDescriptor = &tcpRouteDescription{}
39+
40+
// Can we use an indexer here to query more efficiently?
41+
42+
func ListTCPRoutes(context context.Context, client client.Client) ([]RouteDescriptor, error) {
43+
routeList := &gwalpha2.TCPRouteList{}
44+
err := client.List(context, routeList)
45+
if err != nil {
46+
return nil, err
47+
}
48+
49+
result := make([]RouteDescriptor, 0)
50+
51+
for _, item := range routeList.Items {
52+
result = append(result, convertTCPRoute(item))
53+
}
54+
55+
return result, err
56+
}

0 commit comments

Comments
 (0)