Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: set proper destination port for TCPRoute and UDPRoute #4928

Merged
merged 6 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
pmalek marked this conversation as resolved.
Show resolved Hide resolved
})...)
}
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