Skip to content

Commit 1e5efd0

Browse files
committed
Refactor Gateway source processing.
1 parent aa138f2 commit 1e5efd0

File tree

2 files changed

+74
-105
lines changed

2 files changed

+74
-105
lines changed

source/gateway.go

+74-104
Original file line numberDiff line numberDiff line change
@@ -223,17 +223,59 @@ func (src *gatewayRouteSource) Endpoints(ctx context.Context) ([]*endpoint.Endpo
223223
continue
224224
}
225225

226-
// Get Route hostnames and their targets.
227-
hostTargets, err := resolver.resolve(rt)
228-
if err != nil {
229-
return nil, err
230-
}
231-
if len(hostTargets) == 0 {
226+
// Get Gateway Listeners associated with Route.
227+
gwListeners := resolver.resolve(rt)
228+
if len(gwListeners) == 0 {
232229
log.Debugf("No endpoints could be generated from %s %s/%s", src.rtKind, meta.Namespace, meta.Name)
233230
continue
234231
}
235232

236-
// Create endpoints from hostnames and targets.
233+
// Create endpoints for Route and associated Gateway Listeners
234+
rtHosts := rt.Hostnames()
235+
if len(rtHosts) == 0 {
236+
// This means that the route doesn't specify a hostname and should use any provided by
237+
// attached Gateway Listeners.
238+
rtHosts = []v1.Hostname{""}
239+
}
240+
241+
hostTargets := make(map[string]endpoint.Targets)
242+
for gateway, listeners := range gwListeners {
243+
var hosts []string
244+
for _, listener := range listeners {
245+
// Find all overlapping hostnames between the Route and Listener.
246+
gwHost := getVal(listener.Hostname, "")
247+
for _, rtHost := range rtHosts {
248+
host, ok := gwMatchingHost(string(gwHost), string(rtHost))
249+
if !ok || host == "" {
250+
continue
251+
}
252+
hosts = append(hosts, host)
253+
}
254+
}
255+
// TODO: The ignore-hostname-annotation flag help says "valid only when using fqdn-template"
256+
// but other sources don't check if fqdn-template is set. Which should it be?
257+
if !src.ignoreHostnameAnnotation {
258+
hosts = append(hosts, getHostnamesFromAnnotations(annots)...)
259+
}
260+
// TODO: The combine-fqdn-annotation flag is similarly vague.
261+
if src.fqdnTemplate != nil && (len(hosts) == 0 || src.combineFQDNAnnotation) {
262+
templated, err := execTemplate(src.fqdnTemplate, rt.Object())
263+
if err != nil {
264+
return nil, err
265+
}
266+
hosts = append(hosts, templated...)
267+
}
268+
for _, host := range hosts {
269+
override := getTargetsFromTargetAnnotation(gateway.Annotations)
270+
hostTargets[host] = append(hostTargets[host], override...)
271+
if len(override) == 0 {
272+
for _, addr := range gateway.Status.Addresses {
273+
hostTargets[host] = append(hostTargets[host], addr.Value)
274+
}
275+
}
276+
}
277+
}
278+
237279
resource := fmt.Sprintf("%s/%s/%s", kind, meta.Namespace, meta.Name)
238280
providerSpecific, setIdentifier := getProviderSpecificAnnotations(annots)
239281
ttl := getTTLFromAnnotations(annots, resource)
@@ -287,25 +329,21 @@ func newGatewayRouteResolver(src *gatewayRouteSource, gateways []*v1beta1.Gatewa
287329
}
288330
}
289331

290-
func (c *gatewayRouteResolver) resolve(rt gatewayRoute) (map[string]endpoint.Targets, error) {
291-
rtHosts, err := c.hosts(rt)
292-
if err != nil {
293-
return nil, err
294-
}
295-
hostTargets := make(map[string]endpoint.Targets)
332+
func (c *gatewayRouteResolver) resolve(rt gatewayRoute) map[*v1beta1.Gateway][]*v1.Listener {
333+
gwListeners := map[*v1beta1.Gateway][]*v1.Listener{}
296334

297335
meta := rt.Metadata()
298336
for _, rps := range rt.RouteStatus().Parents {
299337
// Confirm the Parent is the standard Gateway kind.
300338
ref := rps.ParentRef
301-
group := strVal((*string)(ref.Group), gatewayGroup)
302-
kind := strVal((*string)(ref.Kind), gatewayKind)
339+
group := getVal(ref.Group, gatewayGroup)
340+
kind := getVal(ref.Kind, gatewayKind)
303341
if group != gatewayGroup || kind != gatewayKind {
304342
log.Debugf("Unsupported parent %s/%s for %s %s/%s", group, kind, c.src.rtKind, meta.Namespace, meta.Name)
305343
continue
306344
}
307345
// Lookup the Gateway and its Listeners.
308-
namespace := strVal((*string)(ref.Namespace), meta.Namespace)
346+
namespace := getVal((*string)(ref.Namespace), meta.Namespace)
309347
gw, ok := c.gws[namespacedName(namespace, string(ref.Name))]
310348
if !ok {
311349
log.Debugf("Gateway %s/%s not found for %s %s/%s", namespace, ref.Name, c.src.rtKind, meta.Namespace, meta.Name)
@@ -318,7 +356,7 @@ func (c *gatewayRouteResolver) resolve(rt gatewayRoute) (map[string]endpoint.Tar
318356
}
319357
// Match the Route to all possible Listeners.
320358
match := false
321-
section := sectionVal(ref.SectionName, "")
359+
section := getVal(ref.SectionName, "")
322360
listeners := gw.listeners[section]
323361
for i := range listeners {
324362
lis := &listeners[i]
@@ -327,78 +365,35 @@ func (c *gatewayRouteResolver) resolve(rt gatewayRoute) (map[string]endpoint.Tar
327365
continue
328366
}
329367
// Confirm that the Listener and Route ports match, if specified.
330-
// EXPERIMENTAL: https://gateway-api.sigs.k8s.io/geps/gep-957/
331368
if ref.Port != nil && *ref.Port != lis.Port {
332369
continue
333370
}
334371
// Confirm that the Listener allows the Route (based on namespace and kind).
335372
if !c.routeIsAllowed(gw.gateway, lis, rt) {
336373
continue
337374
}
338-
// Find all overlapping hostnames between the Route and Listener.
339-
// For {TCP,UDP}Routes, all annotation-generated hostnames should match since the Listener doesn't specify a hostname.
340-
// For {HTTP,TLS}Routes, hostnames (including any annotation-generated) will be required to match any Listeners specified hostname.
341-
gwHost := ""
342-
if lis.Hostname != nil {
343-
gwHost = string(*lis.Hostname)
344-
}
345-
for _, rtHost := range rtHosts {
346-
if gwHost == "" && rtHost == "" {
347-
// For {HTTP,TLS}Routes, this means the Route and the Listener both allow _any_ hostnames.
348-
// For {TCP,UDP}Routes, this should always happen since neither specifies hostnames.
349-
continue
350-
}
351-
host, ok := gwMatchingHost(gwHost, rtHost)
352-
if !ok {
353-
continue
354-
}
355-
override := getTargetsFromTargetAnnotation(gw.gateway.Annotations)
356-
hostTargets[host] = append(hostTargets[host], override...)
357-
if len(override) == 0 {
358-
for _, addr := range gw.gateway.Status.Addresses {
359-
hostTargets[host] = append(hostTargets[host], addr.Value)
375+
// Confirm that the Listener and Route hostnames overlap, if specified.
376+
overlap := true
377+
if len(rt.Hostnames()) != 0 {
378+
gwHost := getVal(lis.Hostname, "")
379+
for _, rtHost := range rt.Hostnames() {
380+
_, overlap = gwMatchingHost(string(gwHost), string(rtHost))
381+
if overlap {
382+
break
360383
}
361384
}
362-
match = true
363385
}
386+
if !overlap {
387+
continue
388+
}
389+
gwListeners[gw.gateway] = append(gwListeners[gw.gateway], lis)
390+
match = true
364391
}
365392
if !match {
366-
log.Debugf("Gateway %s/%s section %q does not match %s %s/%s hostnames %q", namespace, ref.Name, section, c.src.rtKind, meta.Namespace, meta.Name, rtHosts)
393+
log.Debugf("Gateway %s/%s section %q does not match %s %s/%s hostnames %q", namespace, ref.Name, section, c.src.rtKind, meta.Namespace, meta.Name, rt.Hostnames())
367394
}
368395
}
369-
// If a Gateway has multiple matching Listeners for the same host, then we'll
370-
// add its IPs to the target list multiple times and should dedupe them.
371-
for host, targets := range hostTargets {
372-
hostTargets[host] = uniqueTargets(targets)
373-
}
374-
return hostTargets, nil
375-
}
376-
377-
func (c *gatewayRouteResolver) hosts(rt gatewayRoute) ([]string, error) {
378-
var hostnames []string
379-
for _, name := range rt.Hostnames() {
380-
hostnames = append(hostnames, string(name))
381-
}
382-
// TODO: The ignore-hostname-annotation flag help says "valid only when using fqdn-template"
383-
// but other sources don't check if fqdn-template is set. Which should it be?
384-
if !c.src.ignoreHostnameAnnotation {
385-
hostnames = append(hostnames, getHostnamesFromAnnotations(rt.Metadata().Annotations)...)
386-
}
387-
// TODO: The combine-fqdn-annotation flag is similarly vague.
388-
if c.src.fqdnTemplate != nil && (len(hostnames) == 0 || c.src.combineFQDNAnnotation) {
389-
hosts, err := execTemplate(c.src.fqdnTemplate, rt.Object())
390-
if err != nil {
391-
return nil, err
392-
}
393-
hostnames = append(hostnames, hosts...)
394-
}
395-
// This means that the route doesn't specify a hostname and should use any provided by
396-
// attached Gateway Listeners. This is only useful for {HTTP,TLS}Routes, but it doesn't
397-
// break {TCP,UDP}Routes.
398-
if len(rt.Hostnames()) == 0 {
399-
hostnames = append(hostnames, "")
400-
}
401-
return hostnames, nil
396+
return gwListeners
402397
}
403398

404399
func (c *gatewayRouteResolver) routeIsAllowed(gw *v1beta1.Gateway, lis *v1.Listener, rt gatewayRoute) bool {
@@ -445,7 +440,7 @@ func (c *gatewayRouteResolver) routeIsAllowed(gw *v1beta1.Gateway, lis *v1.Liste
445440
}
446441
gvk := rt.Object().GetObjectKind().GroupVersionKind()
447442
for _, gk := range allow.Kinds {
448-
group := strVal((*string)(gk.Group), gatewayGroup)
443+
group := string(getVal(gk.Group, gatewayGroup))
449444
if gvk.Group == group && gvk.Kind == string(gk.Kind) {
450445
return true
451446
}
@@ -462,24 +457,6 @@ func gwRouteIsAccepted(conds []metav1.Condition) bool {
462457
return false
463458
}
464459

465-
func uniqueTargets(targets endpoint.Targets) endpoint.Targets {
466-
if len(targets) < 2 {
467-
return targets
468-
}
469-
sort.Strings([]string(targets))
470-
prev := targets[0]
471-
n := 1
472-
for _, v := range targets[1:] {
473-
if v == prev {
474-
continue
475-
}
476-
prev = v
477-
targets[n] = v
478-
n++
479-
}
480-
return targets[:n]
481-
}
482-
483460
// gwProtocolMatches returns whether a and b are the same protocol,
484461
// where HTTP and HTTPS are considered the same.
485462
// and TLS and TCP are considered the same.
@@ -583,16 +560,9 @@ func isAlphaNum(b byte) bool {
583560
}
584561
}
585562

586-
func strVal(ptr *string, def string) string {
587-
if ptr == nil || *ptr == "" {
588-
return def
589-
}
590-
return *ptr
591-
}
592-
593-
func sectionVal(ptr *v1.SectionName, def v1.SectionName) v1.SectionName {
594-
if ptr == nil || *ptr == "" {
595-
return def
563+
func getVal[T any](ptr *T, def T) T {
564+
if ptr == nil {
565+
return T(def)
596566
}
597567
return *ptr
598568
}

source/gateway_httproute_test.go

-1
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,6 @@ func TestGatewayHTTPRouteSourceEndpoints(t *testing.T) {
483483
},
484484
},
485485
{
486-
// EXPERIMENTAL: https://gateway-api.sigs.k8s.io/geps/gep-957/
487486
title: "PortNumberMatch",
488487
config: Config{},
489488
namespaces: namespaces("default"),

0 commit comments

Comments
 (0)