Skip to content

Commit c8bbd08

Browse files
fix: handing GRPCRoute hostname from Gateway (#6166)
fix: handing GRPCRoute hostname from Gateway --------- Signed-off-by: Jintao Zhang <zhangjintao9020@gmail.com>
1 parent d62262e commit c8bbd08

File tree

5 files changed

+130
-13
lines changed

5 files changed

+130
-13
lines changed

internal/dataplane/translator/subtranslator/grpcroute.go

+60-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/kong/kubernetes-ingress-controller/v3/internal/dataplane/kongstate"
1010
"github.com/kong/kubernetes-ingress-controller/v3/internal/gatewayapi"
11+
"github.com/kong/kubernetes-ingress-controller/v3/internal/store"
1112
"github.com/kong/kubernetes-ingress-controller/v3/internal/util"
1213
)
1314

@@ -36,6 +37,7 @@ func getGRPCMatchDefaults() (
3637
func GenerateKongRoutesFromGRPCRouteRule(
3738
grpcroute *gatewayapi.GRPCRoute,
3839
ruleNumber int,
40+
storer store.Storer,
3941
) []kongstate.Route {
4042
if ruleNumber >= len(grpcroute.Spec.Rules) {
4143
return nil
@@ -67,7 +69,7 @@ func GenerateKongRoutesFromGRPCRouteRule(
6769
Tags: tags,
6870
},
6971
}
70-
if configuredHostnames := getGRPCRouteHostnamesAsSliceOfStringPointers(grpcroute); len(configuredHostnames) > 0 {
72+
if configuredHostnames := getGRPCRouteHostnamesAsSliceOfStringPointers(grpcroute, storer); len(configuredHostnames) > 0 {
7173
// Match based on hostnames.
7274
r.Hosts = configuredHostnames
7375
} else {
@@ -87,7 +89,7 @@ func GenerateKongRoutesFromGRPCRouteRule(
8789
Name: routeName(grpcroute.Namespace, grpcroute.Name, ruleNumber, matchNumber),
8890
Protocols: grpcProtocols,
8991
Tags: tags,
90-
Hosts: getGRPCRouteHostnamesAsSliceOfStringPointers(grpcroute),
92+
Hosts: getGRPCRouteHostnamesAsSliceOfStringPointers(grpcroute, storer),
9193
},
9294
}
9395

@@ -134,11 +136,64 @@ func GenerateKongRoutesFromGRPCRouteRule(
134136
// getGRPCRouteHostnamesAsSliceOfStringPointers translates the hostnames defined
135137
// in an GRPCRoute specification into a []*string slice, which is the type required
136138
// by kong.Route{}.
137-
func getGRPCRouteHostnamesAsSliceOfStringPointers(grpcroute *gatewayapi.GRPCRoute) []*string {
138-
if len(grpcroute.Spec.Hostnames) == 0 {
139+
// The hostname field is optional. If not specified, the configured value will be obtained from parentRefs.
140+
func getGRPCRouteHostnamesAsSliceOfStringPointers(grpcroute *gatewayapi.GRPCRoute, storer store.Storer) []*string {
141+
if len(grpcroute.Spec.Hostnames) > 0 {
142+
return lo.Map(grpcroute.Spec.Hostnames, func(h gatewayapi.Hostname, _ int) *string {
143+
return lo.ToPtr(string(h))
144+
})
145+
}
146+
147+
// If no hostnames are specified, we will use the hostname from the Gateway
148+
// that the GRPCRoute is attached to.
149+
namespace := grpcroute.GetNamespace()
150+
151+
if grpcroute.Spec.ParentRefs == nil {
139152
return nil
140153
}
141-
return lo.Map(grpcroute.Spec.Hostnames, func(h gatewayapi.Hostname, _ int) *string {
154+
155+
hostnames := make([]gatewayapi.Hostname, 0)
156+
for _, parentRef := range grpcroute.Spec.ParentRefs {
157+
// we only care about Gateways
158+
if parentRef.Kind != nil && *parentRef.Kind != "Gateway" {
159+
continue
160+
}
161+
162+
if parentRef.Namespace != nil {
163+
namespace = string(*parentRef.Namespace)
164+
}
165+
166+
name := string(parentRef.Name)
167+
168+
gateway, err := storer.GetGateway(namespace, name)
169+
// As parentRef has already been validated before, the error here will not actually occur.
170+
// This is where defensive programming takes place.
171+
if err != nil {
172+
// TODO: Add logging.
173+
// https://github.com/Kong/kubernetes-ingress-controller/pull/6166#discussion_r1631250776
174+
return nil
175+
}
176+
177+
if parentRef.SectionName != nil {
178+
sectionName := string(*parentRef.SectionName)
179+
180+
for _, listener := range gateway.Spec.Listeners {
181+
if string(listener.Name) == sectionName {
182+
if listener.Hostname != nil {
183+
hostnames = append(hostnames, *listener.Hostname)
184+
}
185+
}
186+
}
187+
} else {
188+
for _, listener := range gateway.Spec.Listeners {
189+
if listener.Hostname != nil {
190+
hostnames = append(hostnames, *listener.Hostname)
191+
}
192+
}
193+
}
194+
}
195+
196+
return lo.Map(hostnames, func(h gatewayapi.Hostname, _ int) *string {
142197
return lo.ToPtr(string(h))
143198
})
144199
}

internal/dataplane/translator/subtranslator/grpcroute_atc_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ func TestGenerateKongExpressionRoutesFromGRPCRouteRule(t *testing.T) {
220220
for _, tc := range testCases {
221221
tc := tc
222222
t.Run(tc.name, func(t *testing.T) {
223-
grpcroute := makeTestGRPCRoute(tc.objectName, "default", tc.annotations, tc.hostnames, []gatewayapi.GRPCRouteRule{tc.rule})
223+
grpcroute := makeTestGRPCRoute(tc.objectName, "default", tc.annotations, tc.hostnames, []gatewayapi.GRPCRouteRule{tc.rule}, nil)
224224
routes := GenerateKongExpressionRoutesFromGRPCRouteRule(grpcroute, 0)
225225
require.Equal(t, tc.expectedRoutes, routes)
226226
})

internal/dataplane/translator/subtranslator/grpcroute_test.go

+68-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/kong/kubernetes-ingress-controller/v3/internal/dataplane/kongstate"
1313
"github.com/kong/kubernetes-ingress-controller/v3/internal/gatewayapi"
14+
"github.com/kong/kubernetes-ingress-controller/v3/internal/store"
1415
"github.com/kong/kubernetes-ingress-controller/v3/internal/util"
1516
)
1617

@@ -29,6 +30,7 @@ func makeTestGRPCRoute(
2930
name string, namespace string, annotations map[string]string,
3031
hostnames []string,
3132
rules []gatewayapi.GRPCRouteRule,
33+
parentRef []gatewayapi.ParentReference,
3234
) *gatewayapi.GRPCRoute {
3335
return &gatewayapi.GRPCRoute{
3436
TypeMeta: metav1.TypeMeta{
@@ -45,6 +47,9 @@ func makeTestGRPCRoute(
4547
return gatewayapi.Hostname(h)
4648
}),
4749
Rules: rules,
50+
CommonRouteSpec: gatewayapi.CommonRouteSpec{
51+
ParentRefs: parentRef,
52+
},
4853
},
4954
}
5055
}
@@ -57,6 +62,8 @@ func TestGenerateKongRoutesFromGRPCRouteRule(t *testing.T) {
5762
hostnames []string
5863
rule gatewayapi.GRPCRouteRule
5964
expectedRoutes []kongstate.Route
65+
parentRef []gatewayapi.ParentReference
66+
storer store.Storer
6067
}{
6168
{
6269
name: "single match without hostname",
@@ -277,13 +284,70 @@ func TestGenerateKongRoutesFromGRPCRouteRule(t *testing.T) {
277284
},
278285
},
279286
},
287+
{
288+
name: "no match with hostname from gateway",
289+
objectName: "hostname-from-gateway",
290+
annotations: map[string]string{},
291+
rule: gatewayapi.GRPCRouteRule{},
292+
expectedRoutes: []kongstate.Route{
293+
{
294+
Ingress: util.K8sObjectInfo{
295+
Name: "hostname-from-gateway",
296+
Namespace: "default",
297+
Annotations: map[string]string{},
298+
GroupVersionKind: grpcRouteGVK,
299+
},
300+
Route: kong.Route{
301+
Name: kong.String("grpcroute.default.hostname-from-gateway.0.0"),
302+
Hosts: kong.StringSlice("bar.com"),
303+
Protocols: kong.StringSlice("grpc", "grpcs"),
304+
Tags: kong.StringSlice(
305+
"k8s-name:hostname-from-gateway",
306+
"k8s-namespace:default",
307+
"k8s-kind:GRPCRoute",
308+
"k8s-group:gateway.networking.k8s.io",
309+
"k8s-version:v1",
310+
),
311+
},
312+
},
313+
},
314+
parentRef: []gatewayapi.ParentReference{
315+
{
316+
Name: "gateway",
317+
Namespace: lo.ToPtr(gatewayapi.Namespace("default")),
318+
SectionName: lo.ToPtr(gatewayapi.SectionName("listener-1")),
319+
},
320+
},
321+
storer: lo.Must(store.NewFakeStore(store.FakeObjects{
322+
Gateways: []*gatewayapi.Gateway{
323+
{
324+
ObjectMeta: metav1.ObjectMeta{
325+
Name: "gateway",
326+
Namespace: "default",
327+
},
328+
TypeMeta: metav1.TypeMeta{
329+
Kind: "Gateway",
330+
APIVersion: "gateway.networking.k8s.io/v1",
331+
},
332+
Spec: gatewayapi.GatewaySpec{
333+
Listeners: []gatewayapi.Listener{
334+
{
335+
Name: "listener-1",
336+
Hostname: lo.ToPtr(gatewayapi.Hostname("bar.com")),
337+
},
338+
},
339+
},
340+
},
341+
},
342+
})),
343+
},
280344
}
281345

282346
for _, tc := range testCases {
283347
tc := tc
284348
t.Run(tc.name, func(t *testing.T) {
285-
grpcroute := makeTestGRPCRoute(tc.objectName, "default", tc.annotations, tc.hostnames, []gatewayapi.GRPCRouteRule{tc.rule})
286-
routes := GenerateKongRoutesFromGRPCRouteRule(grpcroute, 0)
349+
grpcroute := makeTestGRPCRoute(tc.objectName, "default", tc.annotations, tc.hostnames, []gatewayapi.GRPCRouteRule{tc.rule}, tc.parentRef)
350+
routes := GenerateKongRoutesFromGRPCRouteRule(grpcroute, 0, tc.storer)
287351
require.Equal(t, tc.expectedRoutes, routes)
288352
})
289353
}
@@ -325,7 +389,8 @@ func TestGetGRPCRouteHostnamesAsSliceOfStringPointers(t *testing.T) {
325389
},
326390
} {
327391
t.Run(tC.name, func(t *testing.T) {
328-
result := getGRPCRouteHostnamesAsSliceOfStringPointers(tC.grpcroute)
392+
storer := lo.Must(store.NewFakeStore(store.FakeObjects{}))
393+
result := getGRPCRouteHostnamesAsSliceOfStringPointers(tC.grpcroute, storer)
329394
require.Equal(t, tC.expected, result)
330395
})
331396
}

internal/dataplane/translator/translate_grpcroute.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func (t *Translator) ingressRulesFromGRPCRoute(result *ingressRules, grpcroute *
6060
if err != nil {
6161
return err
6262
}
63-
service.Routes = append(service.Routes, subtranslator.GenerateKongRoutesFromGRPCRouteRule(grpcroute, ruleNumber)...)
63+
service.Routes = append(service.Routes, subtranslator.GenerateKongRoutesFromGRPCRouteRule(grpcroute, ruleNumber, t.storer)...)
6464

6565
// cache the service to avoid duplicates in further loop iterations
6666
result.ServiceNameToServices[*service.Service.Name] = service

test/conformance/gateway_conformance_test.go

-3
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@ import (
2424
var skippedTestsForTraditionalRoutes = []string{
2525
// core conformance
2626
tests.HTTPRouteHeaderMatching.ShortName,
27-
// There is an issue with KIC when processing this scenario.
28-
// TODO: https://github.com/Kong/kubernetes-ingress-controller/issues/6136
29-
tests.GRPCRouteListenerHostnameMatching.ShortName,
3027
// tests.GRPCRouteHeaderMatching.ShortName and tests.GRPCExactMethodMatching.ShortName may
3128
// have some conflicts, skipping either one will still pass normally.
3229
// TODO: https://github.com/Kong/kubernetes-ingress-controller/issues/6144

0 commit comments

Comments
 (0)