Skip to content

Commit

Permalink
feat(parser) generate prioritized routes from GRPCRoutes (#4364)
Browse files Browse the repository at this point in the history
  • Loading branch information
randmonkey authored Jul 25, 2023
1 parent ee80518 commit 7528e7b
Show file tree
Hide file tree
Showing 8 changed files with 1,789 additions and 2 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ Adding a new version? You'll need three changes:
them to expression based Kong routes. The assigning method follows the
[specification on priorities of matches in `HTTPRoute`][httproute-specification].
[#4296](https://github.com/Kong/kubernetes-ingress-controller/pull/4296)
- Assign priorities to routes translated from GRPCRoutes when the parser translates
them to expression based Kong routes. The priority order follows the
[specification on match priorities in GRPCRoute][grpcroute-specification].
[#4364](https://github.com/Kong/kubernetes-ingress-controller/pull/4364)
- When a translated Kong configuration is empty in DB-less mode, the controller
will now send the configuration with a single empty `Upstream`. This is to make
Gateways using `/status/ready` as their health check ready after receiving the
Expand Down Expand Up @@ -145,6 +149,7 @@ Adding a new version? You'll need three changes:

[gojson]: https://github.com/goccy/go-json
[httproute-specification]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRoute
[grpcroute-specification]: https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GRPCRouteRule

## [2.10.4]

Expand Down
77 changes: 77 additions & 0 deletions internal/dataplane/parser/translate_grpcroute.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package parser
import (
"fmt"

"github.com/bombsimon/logrusr/v2"
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"

Expand All @@ -25,6 +26,11 @@ func (p *Parser) ingressRulesFromGRPCRoutes() ingressRules {
return result
}

if p.featureFlags.ExpressionRoutes {
p.ingressRulesFromGRPCRoutesUsingExpressionRoutes(grpcRouteList, &result)
return result
}

var errs []error
for _, grpcroute := range grpcRouteList {
if err := p.ingressRulesFromGRPCRoute(&result, grpcroute); err != nil {
Expand Down Expand Up @@ -79,6 +85,77 @@ func (p *Parser) ingressRulesFromGRPCRoute(result *ingressRules, grpcroute *gate
return nil
}

// ingressRulesFromGRPCRoutesUsingExpressionRoutes translates GRPCRoutes to expression based routes
// when ExpressionRoutes feature flag is enabled.
// Because we need to assign different priorities based on the hostname and match in the specification of GRPCRoutes,
// We need to split the GRPCRoutes into ones with only one hostname and one match, then assign priority to them
// and finally translate the split GRPCRoutes into Kong services and routes with assigned priorities.
func (p *Parser) ingressRulesFromGRPCRoutesUsingExpressionRoutes(grpcRoutes []*gatewayv1alpha2.GRPCRoute, result *ingressRules) {
// first, split GRPCRoutes by hostname and match.
splitGRPCRouteMatches := []translators.SplitGRPCRouteMatch{}
// record GRPCRoutes passing the validation and get translated.
// after they are translated, register the success event in the parser.
translatedGRPCRoutes := []*gatewayv1alpha2.GRPCRoute{}
for _, grpcRoute := range grpcRoutes {
if len(grpcRoute.Spec.Rules) == 0 {
p.registerTranslationFailure(
translators.ErrRouteValidationNoRules.Error(),
grpcRoute,
)
continue
}
splitGRPCRouteMatches = append(splitGRPCRouteMatches, translators.SplitGRPCRoute(grpcRoute)...)
translatedGRPCRoutes = append(translatedGRPCRoutes, grpcRoute)
}

// assign priorities to split GRPCRoutes.
splitGRPCRouteMatchesWithPriorities := translators.AssignRoutePriorityToSplitGRPCRouteMatches(logrusr.New(p.logger), splitGRPCRouteMatches)
// generate Kong service and route from each split GRPC route with its assigned priority of Kong route.
for _, splitGRPCRouteMatchWithPriority := range splitGRPCRouteMatchesWithPriorities {
p.ingressRulesFromGRPCRouteWithPriority(result, splitGRPCRouteMatchWithPriority)
}

// register successful parses of GRPCRoutes.
for _, grpcRoute := range translatedGRPCRoutes {
p.registerSuccessfullyParsedObject(grpcRoute)
}
}

func (p *Parser) ingressRulesFromGRPCRouteWithPriority(
rules *ingressRules,
splitGRPCRouteMatchWithPriority translators.SplitGRPCRouteMatchToPriority,
) {
match := splitGRPCRouteMatchWithPriority.Match
grpcRoute := splitGRPCRouteMatchWithPriority.Match.Source
// (very unlikely that) the rule index split from the source GRPCRoute is larger then length of original rules.
if len(grpcRoute.Spec.Rules) <= match.RuleIndex {
p.logger.Infof("WARN: split rule index %d is larger then the length of rules in source GRPCRoute %d",
match.RuleIndex, len(grpcRoute.Spec.Rules))
return
}
grpcRouteRule := grpcRoute.Spec.Rules[match.RuleIndex]
backendRefs := grpcBackendRefsToBackendRefs(grpcRouteRule.BackendRefs)

serviceName := translators.KongServiceNameFromSplitGRPCRouteMatch(match)

kongService, _ := generateKongServiceFromBackendRefWithName(
p.logger,
p.storer,
rules,
serviceName,
grpcRoute,
"grpcs",
backendRefs...,
)
kongService.Routes = append(
kongService.Routes,
translators.KongExpressionRouteFromSplitGRPCRouteMatchWithPriority(splitGRPCRouteMatchWithPriority),
)
// cache the service to avoid duplicates in further loop iterations
rules.ServiceNameToServices[serviceName] = kongService
rules.ServiceNameToParent[serviceName] = grpcRoute
}

func grpcBackendRefsToBackendRefs(grpcBackendRef []gatewayv1alpha2.GRPCBackendRef) []gatewayv1beta1.BackendRef {
backendRefs := make([]gatewayv1beta1.BackendRef, 0, len(grpcBackendRef))

Expand Down
Loading

0 comments on commit 7528e7b

Please sign in to comment.