Skip to content

Commit

Permalink
fix: set proper destination port for TCPRoute and UDPRoute (#4928)
Browse files Browse the repository at this point in the history
Co-authored-by: Patryk Małek <patryk.malek@konghq.com>
  • Loading branch information
programmer04 and pmalek committed Oct 25, 2023
1 parent 4efc818 commit 5ab69eb
Show file tree
Hide file tree
Showing 11 changed files with 720 additions and 180 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ Adding a new version? You'll need three changes:
[#4813](https://github.com/Kong/kubernetes-ingress-controller/pull/4813)
- Fixed an incorrect watch, set in UDPRoute controller watching UDProute status updates.
[#4835](https://github.com/Kong/kubernetes-ingress-controller/pull/4835)
- Fixed setting proper destination port for TCPRoute and UDPRoute, now field `SectionName`
for `TCPRoute` and `UDPRoute` works as expected. It **breaks** some configurations that
relied on matching multiple Gateway's listener ports to ports of services automatically.
[#4928](https://github.com/Kong/kubernetes-ingress-controller/pull/4928)

### Changed

Expand Down
32 changes: 32 additions & 0 deletions internal/dataplane/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,38 @@ func (p *Parser) getCerts(secretsToSNIs SecretNameToSNIs) []certWrapper {
return certs
}

func (p *Parser) getGatewayListeningPorts(
routeNamespace string,
protocol gatewayapi.ProtocolType,
prs []gatewayapi.ParentReference,
) []gatewayapi.PortNumber {
var gwPorts []gatewayapi.PortNumber
for _, pr := range prs {
// When namespace is explicitly specified in the parentRef,
// it should be used instead of namespace of the whole Route.
ns := string(lo.FromPtr(pr.Namespace))
if ns == "" {
ns = routeNamespace
}
gw, err := p.storer.GetGateway(ns, string(pr.Name))
if err != nil {
continue // Skip when attached Gateway is not found.
}

// Get explicitly referenced Gateway listening ports by ParentReference configuration.
// If no sectionName is specified, all ports are used (according to the specification
// "When unspecified (empty string), this will reference the entire resource." - see
// https://github.com/kubernetes-sigs/gateway-api/blob/ebe9f31ef27819c3b29f698a3e9b91d279453c59/apis/v1/shared_types.go#L107).
gwPorts = append(gwPorts, lo.FilterMap(gw.Spec.Listeners, func(l gatewayapi.Listener, _ int) (gatewayapi.PortNumber, bool) {
if (pr.SectionName == nil || *pr.SectionName == l.Name) && protocol == l.Protocol {
return l.Port, true
}
return 0, false
})...)
}
return gwPorts
}

func mergeCerts(logger logr.Logger, certLists ...[]certWrapper) []kongstate.Certificate {
snisSeen := make(map[string]string)
certsSeen := make(map[string]certWrapper)
Expand Down
68 changes: 27 additions & 41 deletions internal/dataplane/parser/translate_routes_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"

"github.com/kong/go-kong/kong"
"github.com/samber/lo"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/kong/kubernetes-ingress-controller/v2/internal/dataplane/kongstate"
Expand All @@ -31,67 +32,66 @@ type tRouteRule interface {
gatewayapi.UDPRouteRule | gatewayapi.TCPRouteRule | gatewayapi.TLSRouteRule
}

// getBackendRefs returns BackendRefs from TCPRouteRule, UDPRouteRule or TLSRouteRule.
func getBackendRefs[TRouteRule tRouteRule](t TRouteRule) ([]gatewayapi.BackendRef, error) {
// checkBackendRefs checks if BackendRefs are properly configured for
// TCPRouteRule, UDPRouteRule or TLSRouteRule.
func checkBackendRefs[TRouteRule tRouteRule](t TRouteRule) error {
// This is necessary because as of go1.18 (and go1.19) one cannot use common
// struct fields in generic code.
//
// Related golang issue: https://github.com/golang/go/issues/48522
switch tt := any(t).(type) {
case gatewayapi.UDPRouteRule:
if len(tt.BackendRefs) == 0 {
return nil, errors.New("UDPRoute rules must include at least one backendRef")
return errors.New("UDPRoute rules must include at least one backendRef")
}
return tt.BackendRefs, nil
case gatewayapi.TCPRouteRule:
if len(tt.BackendRefs) == 0 {
return nil, errors.New("TCPRoute rules must include at least one backendRef")
return errors.New("TCPRoute rules must include at least one backendRef")
}
return tt.BackendRefs, nil
case gatewayapi.TLSRouteRule:
// TLSRoutes don't require BackendRefs.
return tt.BackendRefs, nil
}

// This should never happen because we use type constraints on what types
// are accepted.
return nil, nil
return nil
}

// generateKongRoutesFromRouteRule converts a Gateway Route (TCP, UDP or TLS) rule
// to one or more Kong Route objects to route traffic to services.
func generateKongRoutesFromRouteRule[T tRoute, TRule tRouteRule](
route T,
gwPorts []gatewayapi.PortNumber,
ruleNumber int,
rule TRule,
) ([]kongstate.Route, error) {
backendRefs, err := getBackendRefs(rule)
if err != nil {
if err := checkBackendRefs(rule); err != nil {
return []kongstate.Route{}, err
}

tags := util.GenerateTagsForObject(route)
return []kongstate.Route{
{
Ingress: util.FromK8sObject(route),
Route: routeToKongRoute(route, backendRefs, ruleNumber, tags),
Route: routeToKongRoute(route, gwPorts, ruleNumber, util.GenerateTagsForObject(route)),
},
}, nil
}

// routeToKongRoute converts Gateway Route to kong.Route.
func routeToKongRoute[TRoute tTCPorUDPorTLSRoute](
r TRoute,
backendRefs []gatewayapi.BackendRef,
gwPorts []gatewayapi.PortNumber,
ruleNumber int,
tags []*string,
) kong.Route {
destinations := lo.Map(gwPorts, func(p gatewayapi.PortNumber, _ int) *kong.CIDRPort {
return &kong.CIDRPort{
Port: kong.Int(int(p)),
}
})

var kr kong.Route
switch rr := any(r).(type) {
case *gatewayapi.UDPRoute:
kr = udpRouteToKongRoute(rr, backendRefs, ruleNumber)
kr = udpRouteToKongRoute(rr, destinations, ruleNumber)
case *gatewayapi.TCPRoute:
kr = tcpRouteToKongRoute(rr, backendRefs, ruleNumber)
kr = tcpRouteToKongRoute(rr, destinations, ruleNumber)
case *gatewayapi.TLSRoute:
kr = tlsRouteToKongRoute(rr, ruleNumber)
default:
Expand All @@ -104,44 +104,30 @@ func routeToKongRoute[TRoute tTCPorUDPorTLSRoute](

func udpRouteToKongRoute(
r *gatewayapi.UDPRoute,
backendRefs []gatewayapi.BackendRef,
destinations []*kong.CIDRPort,
ruleNumber int,
) kong.Route {
return kong.Route{
Name: kong.String(
generateRouteName(udpRouteType, r.Namespace, r.Name, ruleNumber)),
generateRouteName(udpRouteType, r.Namespace, r.Name, ruleNumber),
),
Protocols: kong.StringSlice("udp"),
Destinations: backendRefsToKongCIDRPorts(backendRefs),
Destinations: destinations,
}
}

func tcpRouteToKongRoute(
r *gatewayapi.TCPRoute,
backendRefs []gatewayapi.BackendRef,
destinations []*kong.CIDRPort,
ruleNumber int,
) kong.Route {
return kong.Route{
Name: kong.String(
generateRouteName(tcpRouteType, r.Namespace, r.Name, ruleNumber)),
generateRouteName(tcpRouteType, r.Namespace, r.Name, ruleNumber),
),
Protocols: kong.StringSlice("tcp"),
Destinations: backendRefsToKongCIDRPorts(backendRefs),
}
}

func backendRefsToKongCIDRPorts(backendRefs []gatewayapi.BackendRef) []*kong.CIDRPort {
destinations := make([]*kong.CIDRPort, 0, len(backendRefs))
for _, backendRef := range backendRefs {
if backendRef.Port == nil {
continue // Should we propagate the error?
}

destinations = append(destinations,
&kong.CIDRPort{
Port: kong.Int(int(*backendRef.Port)),
},
)
Destinations: destinations,
}
return destinations
}

func tlsRouteToKongRoute(r *gatewayapi.TLSRoute, ruleNumber int) kong.Route {
Expand Down

0 comments on commit 5ab69eb

Please sign in to comment.