diff --git a/internal/dataplane/parser/translate_errors.go b/internal/dataplane/parser/translate_errors.go deleted file mode 100644 index eb0570b69e..0000000000 --- a/internal/dataplane/parser/translate_errors.go +++ /dev/null @@ -1,11 +0,0 @@ -package parser - -import "errors" - -// REVIEW: use error variables in translator package instead? - -var ( - errRouteValidationNoRules = errors.New("no rules provided") - errRouteValidationQueryParamMatchesUnsupported = errors.New("query param matches are not yet supported") - errRouteValidationNoMatchRulesOrHostnamesSpecified = errors.New("no match rules or hostnames specified") -) diff --git a/internal/dataplane/parser/translate_grpcroute.go b/internal/dataplane/parser/translate_grpcroute.go index 0c228b08f2..6eca194f13 100644 --- a/internal/dataplane/parser/translate_grpcroute.go +++ b/internal/dataplane/parser/translate_grpcroute.go @@ -9,6 +9,7 @@ import ( gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" "github.com/kong/kubernetes-ingress-controller/v2/internal/dataplane/kongstate" + "github.com/kong/kubernetes-ingress-controller/v2/internal/dataplane/parser/translators" "github.com/kong/kubernetes-ingress-controller/v2/internal/util" ) @@ -53,7 +54,7 @@ func (p *Parser) ingressRulesFromGRPCRoute(result *ingressRules, grpcroute *gate spec := grpcroute.Spec if len(spec.Rules) == 0 { - return errRouteValidationNoRules + return translators.ErrRouteValidationNoRules } // each rule may represent a different set of backend services that will be accepting diff --git a/internal/dataplane/parser/translate_httproute.go b/internal/dataplane/parser/translate_httproute.go index 11d11821f7..f311ab60f2 100644 --- a/internal/dataplane/parser/translate_httproute.go +++ b/internal/dataplane/parser/translate_httproute.go @@ -61,7 +61,7 @@ func validateHTTPRoute(httproute *gatewayv1beta1.HTTPRoute) error { // are invalid somehow make it past validation (e.g. the webhook is not enabled) we can // at least try to provide a helpful message about the situation in the manager logs. if len(spec.Rules) == 0 { - return errRouteValidationNoRules + return translators.ErrRouteValidationNoRules } return nil @@ -137,22 +137,18 @@ func (p *Parser) ingressRulesFromHTTPRouteLegacyFallback(httproute *gatewayv1bet // HTTPRoute specification into a []*string slice, which is the type required by translating to matchers // in expression based routes. func getHTTPRouteHostnamesAsSliceOfStrings(httproute *gatewayv1beta1.HTTPRoute) []string { - hostnames := make([]string, 0, len(httproute.Spec.Hostnames)) - for _, hostname := range httproute.Spec.Hostnames { - hostnames = append(hostnames, string(hostname)) - } - return hostnames + return lo.Map(httproute.Spec.Hostnames, func(h gatewayv1beta1.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{}. func getHTTPRouteHostnamesAsSliceOfStringPointers(httproute *gatewayv1beta1.HTTPRoute) []*string { - hostnames := make([]*string, 0, len(httproute.Spec.Hostnames)) - for _, hostname := range httproute.Spec.Hostnames { - hostnames = append(hostnames, kong.String(string(hostname))) - } - return hostnames + return lo.Map(httproute.Spec.Hostnames, func(h gatewayv1beta1.Hostname, _ int) *string { + return kong.String(string(h)) + }) } // generateKongRoutesFromHTTPRouteRule converts an HTTPRoute rule to one or more @@ -242,9 +238,7 @@ func generateKongRouteFromTranslation( // get the hostnames from the HTTPRoute hostnames := getHTTPRouteHostnamesAsSliceOfStrings(httproute) return translators.GenerateKongExpressionRoutesFromHTTPRouteMatches( - translation.Name, - translation.Matches, - translation.Filters, + translation, objectInfo, hostnames, tags, @@ -294,7 +288,7 @@ func generateKongRoutesFromHTTPRouteMatches( // however in this case there must actually be some present hostnames // configured for the HTTPRoute or else it's not valid. if len(hostnames) == 0 { - return []kongstate.Route{}, errRouteValidationNoMatchRulesOrHostnamesSpecified + return []kongstate.Route{}, translators.ErrRouteValidationNoMatchRulesOrHostnamesSpecified } // otherwise apply the hostnames to the route @@ -305,7 +299,7 @@ func generateKongRoutesFromHTTPRouteMatches( // TODO: implement query param matches (https://github.com/Kong/kubernetes-ingress-controller/issues/2778) if len(matches[0].QueryParams) > 0 { - return []kongstate.Route{}, errRouteValidationQueryParamMatchesUnsupported + return []kongstate.Route{}, translators.ErrRouteValidationQueryParamMatchesUnsupported } r := generateKongstateHTTPRoute(routeName, ingressObjectInfo, hostnames) diff --git a/internal/dataplane/parser/translate_httproute_test.go b/internal/dataplane/parser/translate_httproute_test.go index 589c786d98..153da502de 100644 --- a/internal/dataplane/parser/translate_httproute_test.go +++ b/internal/dataplane/parser/translate_httproute_test.go @@ -14,6 +14,7 @@ import ( gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" "github.com/kong/kubernetes-ingress-controller/v2/internal/dataplane/kongstate" + "github.com/kong/kubernetes-ingress-controller/v2/internal/dataplane/parser/translators" "github.com/kong/kubernetes-ingress-controller/v2/internal/store" "github.com/kong/kubernetes-ingress-controller/v2/internal/util" "github.com/kong/kubernetes-ingress-controller/v2/internal/util/builder" @@ -143,7 +144,7 @@ func getIngressRulesFromHTTPRoutesCommonTestCases() []testCaseIngressRulesFromHT } }, errs: []error{ - errRouteValidationNoMatchRulesOrHostnamesSpecified, + translators.ErrRouteValidationNoMatchRulesOrHostnamesSpecified, }, }, { @@ -233,7 +234,7 @@ func getIngressRulesFromHTTPRoutesCommonTestCases() []testCaseIngressRulesFromHT } }, errs: []error{ - errRouteValidationNoRules, + translators.ErrRouteValidationNoRules, }, }, { @@ -263,7 +264,7 @@ func getIngressRulesFromHTTPRoutesCommonTestCases() []testCaseIngressRulesFromHT } }, errs: []error{ - errRouteValidationQueryParamMatchesUnsupported, + translators.ErrRouteValidationQueryParamMatchesUnsupported, }, }, { diff --git a/internal/dataplane/parser/translate_tcproute.go b/internal/dataplane/parser/translate_tcproute.go index 3063292026..8965a3a4ef 100644 --- a/internal/dataplane/parser/translate_tcproute.go +++ b/internal/dataplane/parser/translate_tcproute.go @@ -4,6 +4,8 @@ import ( "fmt" gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + + "github.com/kong/kubernetes-ingress-controller/v2/internal/dataplane/parser/translators" ) // ----------------------------------------------------------------------------- @@ -51,7 +53,7 @@ func (p *Parser) ingressRulesFromTCPRoute(result *ingressRules, tcproute *gatewa // are invalid somehow make it past validation (e.g. the webhook is not enabled) we can // at least try to provide a helpful message about the situation in the manager logs. if len(spec.Rules) == 0 { - return errRouteValidationNoRules + return translators.ErrRouteValidationNoRules } // each rule may represent a different set of backend services that will be accepting diff --git a/internal/dataplane/parser/translate_tlsroute.go b/internal/dataplane/parser/translate_tlsroute.go index 1166b13d5c..e6edffbddc 100644 --- a/internal/dataplane/parser/translate_tlsroute.go +++ b/internal/dataplane/parser/translate_tlsroute.go @@ -8,6 +8,7 @@ import ( gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" + "github.com/kong/kubernetes-ingress-controller/v2/internal/dataplane/parser/translators" "github.com/kong/kubernetes-ingress-controller/v2/internal/store" ) @@ -55,7 +56,7 @@ func (p *Parser) ingressRulesFromTLSRoute(result *ingressRules, tlsroute *gatewa return fmt.Errorf("no hostnames provided") } if len(spec.Rules) == 0 { - return errRouteValidationNoRules + return translators.ErrRouteValidationNoRules } tlsPassthrough, err := p.isTLSRoutePassthrough(tlsroute) diff --git a/internal/dataplane/parser/translate_udproute.go b/internal/dataplane/parser/translate_udproute.go index 19b0019574..8bfb5cf62a 100644 --- a/internal/dataplane/parser/translate_udproute.go +++ b/internal/dataplane/parser/translate_udproute.go @@ -4,6 +4,8 @@ import ( "fmt" gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + + "github.com/kong/kubernetes-ingress-controller/v2/internal/dataplane/parser/translators" ) // ----------------------------------------------------------------------------- @@ -51,7 +53,7 @@ func (p *Parser) ingressRulesFromUDPRoute(result *ingressRules, udproute *gatewa // are invalid somehow make it past validation (e.g. the webhook is not enabled) we can // at least try to provide a helpful message about the situation in the manager logs. if len(spec.Rules) == 0 { - return errRouteValidationNoRules + return translators.ErrRouteValidationNoRules } // each rule may represent a different set of backend services that will be accepting diff --git a/internal/dataplane/parser/translators/httproute_atc.go b/internal/dataplane/parser/translators/httproute_atc.go index ab858bf8b4..4c1cb3ec5c 100644 --- a/internal/dataplane/parser/translators/httproute_atc.go +++ b/internal/dataplane/parser/translators/httproute_atc.go @@ -17,9 +17,7 @@ import ( // GenerateKongExpressionRoutesFromHTTPRouteMatches generates Kong routes from HTTPRouteRule // pointing to a specific backend. func GenerateKongExpressionRoutesFromHTTPRouteMatches( - routeName string, - matches []gatewayv1beta1.HTTPRouteMatch, - filters []gatewayv1beta1.HTTPRouteFilter, + translation KongRouteTranslation, ingressObjectInfo util.K8sObjectInfo, hostnames []string, tags []*string, @@ -28,14 +26,14 @@ func GenerateKongExpressionRoutesFromHTTPRouteMatches( r := kongstate.Route{ Ingress: ingressObjectInfo, Route: kong.Route{ - Name: kong.String(routeName), + Name: kong.String(translation.Name), PreserveHost: kong.Bool(true), Tags: tags, }, ExpressionRoutes: true, } - if len(matches) == 0 { + if len(translation.Matches) == 0 { if len(hostnames) == 0 { return []kongstate.Route{}, ErrRouteValidationNoMatchRulesOrHostnamesSpecified } @@ -45,48 +43,17 @@ func GenerateKongExpressionRoutesFromHTTPRouteMatches( return []kongstate.Route{r}, nil } - _, hasRedirectFilter := lo.Find(filters, func(filter gatewayv1beta1.HTTPRouteFilter) bool { + _, hasRedirectFilter := lo.Find(translation.Filters, func(filter gatewayv1beta1.HTTPRouteFilter) bool { return filter.Type == gatewayv1beta1.HTTPRouteFilterRequestRedirect }) // if the rule has request redirect filter(s), we need to generate a route for each match to // attach plugins for the filter. if hasRedirectFilter { - routes := make([]kongstate.Route, 0, len(matches)) - for _, match := range matches { - matchRoute := kongstate.Route{ - Ingress: ingressObjectInfo, - Route: kong.Route{ - Name: kong.String(routeName), - PreserveHost: kong.Bool(true), - Tags: tags, - }, - ExpressionRoutes: true, - } - // generate matcher for this HTTPRoute Match. - matcher := atc.And(generateMatcherFromHTTPRouteMatch(match)) - - // add matcher from parent httproute (hostnames, protocols, SNIs) to be ANDed with the matcher from match. - matchersFromParent := matchersFromParentHTTPRoute(hostnames, ingressObjectInfo.Annotations) - for _, m := range matchersFromParent { - matcher.And(m) - } - atc.ApplyExpression(&matchRoute.Route, matcher, 1) - - // we need to extract the path to configure redirect path of the plugins for request redirect filter. - path := "" - if match.Path != nil && match.Path.Value != nil { - path = *match.Path.Value - } - plugins := GeneratePluginsFromHTTPRouteFilters(filters, path, tags) - matchRoute.Plugins = plugins - - routes = append(routes, matchRoute) - } - return routes, nil + return generateKongExpressionRoutesWithRequestRedirectFilter(translation, ingressObjectInfo, hostnames, tags) } // if we do not need to generate a kong route for each match, we OR matchers from all matches together. - routeMatcher := atc.And(atc.Or(generateMatchersFromHTTPRouteMatches(matches)...)) + routeMatcher := atc.And(atc.Or(generateMatchersFromHTTPRouteMatches(translation.Matches)...)) // add matcher from parent httproute (hostnames, protocols, SNIs) to be ANDed with the matcher from match. matchersFromParent := matchersFromParentHTTPRoute(hostnames, ingressObjectInfo.Annotations) for _, matcher := range matchersFromParent { @@ -95,11 +62,51 @@ func GenerateKongExpressionRoutesFromHTTPRouteMatches( atc.ApplyExpression(&r.Route, routeMatcher, 1) // generate plugins. - plugins := GeneratePluginsFromHTTPRouteFilters(filters, "", tags) + plugins := GeneratePluginsFromHTTPRouteFilters(translation.Filters, "", tags) r.Plugins = plugins return []kongstate.Route{r}, nil } +func generateKongExpressionRoutesWithRequestRedirectFilter( + translation KongRouteTranslation, + ingressObjectInfo util.K8sObjectInfo, + hostnames []string, + tags []*string, +) ([]kongstate.Route, error) { + routes := make([]kongstate.Route, 0, len(translation.Matches)) + for _, match := range translation.Matches { + matchRoute := kongstate.Route{ + Ingress: ingressObjectInfo, + Route: kong.Route{ + Name: kong.String(translation.Name), + PreserveHost: kong.Bool(true), + Tags: tags, + }, + ExpressionRoutes: true, + } + // generate matcher for this HTTPRoute Match. + matcher := atc.And(generateMatcherFromHTTPRouteMatch(match)) + + // add matcher from parent httproute (hostnames, protocols, SNIs) to be ANDed with the matcher from match. + matchersFromParent := matchersFromParentHTTPRoute(hostnames, ingressObjectInfo.Annotations) + for _, m := range matchersFromParent { + matcher.And(m) + } + atc.ApplyExpression(&matchRoute.Route, matcher, 1) + + // we need to extract the path to configure redirect path of the plugins for request redirect filter. + path := "" + if match.Path != nil && match.Path.Value != nil { + path = *match.Path.Value + } + plugins := GeneratePluginsFromHTTPRouteFilters(translation.Filters, path, tags) + matchRoute.Plugins = plugins + + routes = append(routes, matchRoute) + } + return routes, nil +} + func generateMatchersFromHTTPRouteMatches(matches []gatewayv1beta1.HTTPRouteMatch) []atc.Matcher { ret := make([]atc.Matcher, 0, len(matches)) for _, match := range matches { diff --git a/internal/dataplane/parser/translators/httproute_atc_test.go b/internal/dataplane/parser/translators/httproute_atc_test.go index b52c07ce8e..c8d29d14b8 100644 --- a/internal/dataplane/parser/translators/httproute_atc_test.go +++ b/internal/dataplane/parser/translators/httproute_atc_test.go @@ -234,9 +234,11 @@ func TestGenerateKongExpressionRoutesFromHTTPRouteMatches(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { routes, err := GenerateKongExpressionRoutesFromHTTPRouteMatches( - tc.routeName, - tc.matches, - tc.filters, + KongRouteTranslation{ + Name: tc.routeName, + Matches: tc.matches, + Filters: tc.filters, + }, tc.ingressObjectInfo, tc.hostnames, kong.StringSlice(tc.tags...),