From 9443c1720fbab3fd3ba26658594c759725fa70d6 Mon Sep 17 00:00:00 2001 From: Jakub Warczarek Date: Mon, 16 Dec 2024 17:13:14 +0100 Subject: [PATCH] chore(refactor): streamline logic for HTTPRoute translation and validation --- .../admission/validation/gateway/httproute.go | 32 ++++++++++- .../translator/subtranslator/httproute.go | 57 ++++++------------- .../translator/subtranslator/httproute_atc.go | 6 +- .../subtranslator/httproute_atc_test.go | 2 +- .../translator/translate_httproute.go | 46 --------------- 5 files changed, 53 insertions(+), 90 deletions(-) diff --git a/internal/admission/validation/gateway/httproute.go b/internal/admission/validation/gateway/httproute.go index e7987719b6..8f0488049c 100644 --- a/internal/admission/validation/gateway/httproute.go +++ b/internal/admission/validation/gateway/httproute.go @@ -6,14 +6,17 @@ import ( "strings" "github.com/kong/go-kong/kong" + "github.com/samber/lo" apierrors "k8s.io/apimachinery/pkg/api/errors" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/kong/kubernetes-ingress-controller/v3/internal/admission/validation" gatewaycontroller "github.com/kong/kubernetes-ingress-controller/v3/internal/controllers/gateway" + "github.com/kong/kubernetes-ingress-controller/v3/internal/dataplane/kongstate" "github.com/kong/kubernetes-ingress-controller/v3/internal/dataplane/translator" "github.com/kong/kubernetes-ingress-controller/v3/internal/dataplane/translator/subtranslator" "github.com/kong/kubernetes-ingress-controller/v3/internal/gatewayapi" + "github.com/kong/kubernetes-ingress-controller/v3/internal/util" ) type routeValidator interface { @@ -190,15 +193,40 @@ func validateWithKongGateway( // Use KIC translator that works both for traditional and expressions based routes. var kongRoutes []kong.Route var errMsgs []string + // Gather the K8s object information and hostnames from the HTTPRoute. + objectInfo := util.FromK8sObject(httproute) + hostnames := lo.Map(httproute.Spec.Hostnames, func(h gatewayapi.Hostname, _ int) string { + return string(h) + }) for _, rule := range httproute.Spec.Rules { translation := subtranslator.KongRouteTranslation{ Name: "validation-attempt", Matches: rule.Matches, Filters: rule.Filters, } - routes, err := translator.GenerateKongRouteFromTranslation( - httproute, translation, translatorFeatures.ExpressionRoutes, + + var ( + routes []kongstate.Route + err error ) + tags := util.GenerateTagsForObject(httproute, util.AdditionalTagsK8sNamedRouteRule(translation.OptionalNamedRouteRules...)...) + if translatorFeatures.ExpressionRoutes { + routes, err = subtranslator.GenerateKongExpressionRoutesFromTranslation( + translation, + objectInfo, + hostnames, + tags, + ) + } else { + routes, err = subtranslator.GenerateKongRoutesFromHTTPRouteMatches( + translation.Name, + translation.Matches, + translation.Filters, + objectInfo, + lo.ToSlicePtr(hostnames), + tags, + ) + } if err != nil { errMsgs = append(errMsgs, err.Error()) continue diff --git a/internal/dataplane/translator/subtranslator/httproute.go b/internal/dataplane/translator/subtranslator/httproute.go index cd9ae91273..3cab111249 100644 --- a/internal/dataplane/translator/subtranslator/httproute.go +++ b/internal/dataplane/translator/subtranslator/httproute.go @@ -702,29 +702,28 @@ func GenerateKongRoutesFromHTTPRouteMatches( hostnames []*string, tags []*string, ) ([]kongstate.Route, error) { + r := kongstate.Route{ + Ingress: ingressObjectInfo, + Route: kong.Route{ + Name: kong.String(routeName), + Protocols: kong.StringSlice("http", "https"), + PreserveHost: kong.Bool(true), + Tags: tags, + }, + } + // Attach any hostnames associated with the HTTPRoute. + if len(hostnames) > 0 { + r.Hosts = hostnames + } + // It's acceptable for an HTTPRoute to have no matches in the rulesets, + // but only backends as long as there are hostnames. In this case, we + // match all traffic based on the hostname and leave all other routing + // options default. + // For rules with no hostnames, we generate a "catch-all" route for it. if len(matches) == 0 { - // it's acceptable for an HTTPRoute to have no matches in the rulesets, - // but only backends as long as there are hostnames. In this case, we - // match all traffic based on the hostname and leave all other routing - // options default. - // for rules with no hostnames, we generate a "catch-all" route for it. - r := kongstate.Route{ - Ingress: ingressObjectInfo, - Route: kong.Route{ - Name: kong.String(routeName), - Protocols: kong.StringSlice("http", "https"), - PreserveHost: kong.Bool(true), - Tags: tags, - }, - } - r.Hosts = append(r.Hosts, hostnames...) - return []kongstate.Route{r}, nil } - r := generateKongstateHTTPRoute(routeName, ingressObjectInfo, hostnames) - r.Tags = tags - // convert header matching from HTTPRoute to Route format headers, err := convertGatewayMatchHeadersToKongRouteMatchHeaders(matches[0].Headers) if err != nil { @@ -775,26 +774,6 @@ func GenerateKongRoutesFromHTTPRouteMatches( return routes, nil } -func generateKongstateHTTPRoute(routeName string, ingressObjectInfo util.K8sObjectInfo, hostnames []*string) kongstate.Route { - // build the route object using the method and pathing information - r := kongstate.Route{ - Ingress: ingressObjectInfo, - Route: kong.Route{ - Name: kong.String(routeName), - Protocols: kong.StringSlice("http", "https"), - PreserveHost: kong.Bool(true), - // metadata tags aren't added here, they're added by the caller - }, - } - - // attach any hostnames associated with the httproute - if len(hostnames) > 0 { - r.Hosts = hostnames - } - - return r -} - // convertGatewayMatchHeadersToKongRouteMatchHeaders takes an input list of Gateway APIs HTTPHeaderMatch // and converts these header matching rules to the format expected by go-kong. func convertGatewayMatchHeadersToKongRouteMatchHeaders(headers []gatewayapi.HTTPHeaderMatch) (map[string][]string, error) { diff --git a/internal/dataplane/translator/subtranslator/httproute_atc.go b/internal/dataplane/translator/subtranslator/httproute_atc.go index c20772bcb7..0dfb1fba44 100644 --- a/internal/dataplane/translator/subtranslator/httproute_atc.go +++ b/internal/dataplane/translator/subtranslator/httproute_atc.go @@ -16,9 +16,11 @@ import ( "github.com/kong/kubernetes-ingress-controller/v3/internal/util" ) -// GenerateKongExpressionRoutesFromHTTPRouteMatches generates Kong routes from HTTPRouteRule +// GenerateKongExpressionRoutesFromTranslation generates Kong routes from HTTPRouteRule // pointing to a specific backend. -func GenerateKongExpressionRoutesFromHTTPRouteMatches( +// NOTICE: currently it's used only for validation in internal/admission/validation/gateway/httproute.go file, +// for generation of actual config sent to Kong Gateway see translateHTTPRouteRulesMetaToKongstateService function. +func GenerateKongExpressionRoutesFromTranslation( translation KongRouteTranslation, ingressObjectInfo util.K8sObjectInfo, hostnames []string, diff --git a/internal/dataplane/translator/subtranslator/httproute_atc_test.go b/internal/dataplane/translator/subtranslator/httproute_atc_test.go index b105147829..e9e3d73f07 100644 --- a/internal/dataplane/translator/subtranslator/httproute_atc_test.go +++ b/internal/dataplane/translator/subtranslator/httproute_atc_test.go @@ -252,7 +252,7 @@ func TestGenerateKongExpressionRoutesFromHTTPRouteMatches(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - routes, err := GenerateKongExpressionRoutesFromHTTPRouteMatches( + routes, err := GenerateKongExpressionRoutesFromTranslation( KongRouteTranslation{ Name: tc.routeName, Matches: tc.matches, diff --git a/internal/dataplane/translator/translate_httproute.go b/internal/dataplane/translator/translate_httproute.go index d4386ae3a1..3b51c4424e 100644 --- a/internal/dataplane/translator/translate_httproute.go +++ b/internal/dataplane/translator/translate_httproute.go @@ -8,10 +8,8 @@ import ( "github.com/samber/lo" k8stypes "k8s.io/apimachinery/pkg/types" - "github.com/kong/kubernetes-ingress-controller/v3/internal/dataplane/kongstate" "github.com/kong/kubernetes-ingress-controller/v3/internal/dataplane/translator/subtranslator" "github.com/kong/kubernetes-ingress-controller/v3/internal/gatewayapi" - "github.com/kong/kubernetes-ingress-controller/v3/internal/util" ) // ----------------------------------------------------------------------------- @@ -110,15 +108,6 @@ func (t *Translator) ingressRulesFromHTTPRoutesWithCombinedService(httpRoutes [] // Translate HTTPRoute - Utils // ----------------------------------------------------------------------------- -// getHTTPRouteHostnamesAsSliceOfStrings translates the hostnames defined in an -// HTTPRoute specification into a []*string slice, which is the type required by translating to matchers -// in expression based routes. -func getHTTPRouteHostnamesAsSliceOfStrings(httproute *gatewayapi.HTTPRoute) []string { - return lo.Map(httproute.Spec.Hostnames, func(h gatewayapi.Hostname, _ int) string { - return string(h) - }) -} - // getHTTPRouteHostnamesAsSliceOfStringPointers translates the hostnames defined // in an HTTPRoute specification into a []*string slice, which is the type required // by kong.Route{}. @@ -127,38 +116,3 @@ func getHTTPRouteHostnamesAsSliceOfStringPointers(httproute *gatewayapi.HTTPRout return kong.String(string(h)) }) } - -// GenerateKongRouteFromTranslation generates Kong routes from HTTPRoute -// pointing to a specific backend. It is used for both traditional and expression based routes. -func GenerateKongRouteFromTranslation( - httproute *gatewayapi.HTTPRoute, - translation subtranslator.KongRouteTranslation, - expressionRoutes bool, -) ([]kongstate.Route, error) { - // Gather the k8s object information and hostnames from the HTTPRoute. - objectInfo := util.FromK8sObject(httproute) - tags := util.GenerateTagsForObject(httproute, util.AdditionalTagsK8sNamedRouteRule(translation.OptionalNamedRouteRules...)...) - - // translate to expression based routes when expressionRoutes is enabled. - if expressionRoutes { - // get the hostnames from the HTTPRoute - hostnames := getHTTPRouteHostnamesAsSliceOfStrings(httproute) - return subtranslator.GenerateKongExpressionRoutesFromHTTPRouteMatches( - translation, - objectInfo, - hostnames, - tags, - ) - } - - // get the hostnames from the HTTPRoute - hostnames := getHTTPRouteHostnamesAsSliceOfStringPointers(httproute) - return subtranslator.GenerateKongRoutesFromHTTPRouteMatches( - translation.Name, - translation.Matches, - translation.Filters, - objectInfo, - hostnames, - tags, - ) -}