From 45546f14a4062c88189e6cc5276a259a878be5fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Burzy=C5=84ski?= Date: Mon, 29 Apr 2024 12:34:36 +0200 Subject: [PATCH] feat(httproute): handle URLRewrite filter's hostname rewrite --- .../translator/subtranslator/httproute.go | 77 ++++++++++----- .../subtranslator/httproute_test.go | 95 +++++++++++++------ test/conformance/gateway_conformance_test.go | 2 + 3 files changed, 123 insertions(+), 51 deletions(-) diff --git a/internal/dataplane/translator/subtranslator/httproute.go b/internal/dataplane/translator/subtranslator/httproute.go index 7b59b3cda2..43bead0047 100644 --- a/internal/dataplane/translator/subtranslator/httproute.go +++ b/internal/dataplane/translator/subtranslator/httproute.go @@ -439,14 +439,12 @@ func generatePluginsFromHTTPRouteFilters( pluginNamesFromExtensionRef = append(pluginNamesFromExtensionRef, plugin) case gatewayapi.HTTPRouteFilterURLRewrite: - plugin, routeModifier, err := generateRequestTransformerForURLRewrite(filter.URLRewrite, path, expressionsRouterEnabled) + plugins, routeModifiers, err := generateRequestTransformerForURLRewrite(filter.URLRewrite, path, expressionsRouterEnabled) if err != nil { return httpRouteFiltersOriginatedPlugins{}, err } - kongPlugins = append(kongPlugins, plugin) - if routeModifier != nil { - kongRouteModifiers = append(kongRouteModifiers, routeModifier) - } + kongPlugins = append(kongPlugins, plugins...) + kongRouteModifiers = append(kongRouteModifiers, routeModifiers...) case gatewayapi.HTTPRouteFilterRequestMirror: // not supported @@ -632,36 +630,67 @@ func generateRequestTransformerForURLRewrite( filter *gatewayapi.HTTPURLRewriteFilter, path string, expressionsRouterEnabled bool, -) (kong.Plugin, kongRouteModifier, error) { +) ([]kong.Plugin, []kongRouteModifier, error) { if filter == nil { - return kong.Plugin{}, nil, fmt.Errorf("%s is not provided", gatewayapi.HTTPRouteFilterURLRewrite) + return nil, nil, fmt.Errorf("%s is not provided", gatewayapi.HTTPRouteFilterURLRewrite) } - if filter.Path == nil && filter.Hostname == nil { - return kong.Plugin{}, nil, fmt.Errorf("%s missing Path and Hostname", gatewayapi.HTTPRouteFilterURLRewrite) + return nil, nil, fmt.Errorf("%s missing Path and Hostname", gatewayapi.HTTPRouteFilterURLRewrite) } + var ( + plugins []kong.Plugin + routeModifiers []kongRouteModifier + ) if filter.Path != nil { - switch filter.Path.Type { - case gatewayapi.FullPathHTTPPathModifier: - plugin, err := generateRequestTransformerForURLRewriteFullPath(filter) - if err != nil { - return kong.Plugin{}, nil, fmt.Errorf("failed to generate request-transformer plugin for %s: %w", gatewayapi.HTTPRouteFilterURLRewrite, err) - } - return plugin, nil, nil - - case gatewayapi.PrefixMatchHTTPPathModifier: - plugin, routeModifier := generateRequestTransformerForURLRewritePrefixMatch(filter, path, expressionsRouterEnabled) - return plugin, routeModifier, nil + plugin, modifier, err := generateRequestTransformerForURLRewritePath(filter, path, expressionsRouterEnabled) + if err != nil { + return nil, nil, fmt.Errorf("failed to generate request-transformer plugin for Path: %w", err) + } + plugins = append(plugins, plugin) + if modifier != nil { + routeModifiers = append(routeModifiers, modifier) } } - - // TODO: https://github.com/Kong/kubernetes-ingress-controller/issues/3685 if filter.Hostname != nil { - return kong.Plugin{}, nil, fmt.Errorf("unsupported hostname replace for %s", gatewayapi.HTTPRouteFilterURLRewrite) + plugins = append(plugins, generateRequestTransformerForURLRewriteHostname(filter)) } - return kong.Plugin{}, nil, fmt.Errorf("invalid %s config", gatewayapi.HTTPRouteFilterURLRewrite) + return plugins, routeModifiers, nil +} + +func generateRequestTransformerForURLRewritePath( + filter *gatewayapi.HTTPURLRewriteFilter, + path string, + expressionsRouterEnabled bool, +) (kong.Plugin, kongRouteModifier, error) { + switch filter.Path.Type { + case gatewayapi.FullPathHTTPPathModifier: + plugin, err := generateRequestTransformerForURLRewriteFullPath(filter) + if err != nil { + return kong.Plugin{}, nil, fmt.Errorf("failed to generate request-transformer plugin for %s: %w", gatewayapi.HTTPRouteFilterURLRewrite, err) + } + return plugin, nil, nil + + case gatewayapi.PrefixMatchHTTPPathModifier: + plugin, routeModifier := generateRequestTransformerForURLRewritePrefixMatch(filter, path, expressionsRouterEnabled) + return plugin, routeModifier, nil + default: + return kong.Plugin{}, nil, fmt.Errorf("unsupported path type %s for %s", filter.Path.Type, gatewayapi.HTTPRouteFilterURLRewrite) + } +} + +func generateRequestTransformerForURLRewriteHostname( + filter *gatewayapi.HTTPURLRewriteFilter, +) kong.Plugin { + return kong.Plugin{ + Name: kong.String("request-transformer"), + Config: kong.Configuration{ + "replace": map[string]string{ + "host": string(*filter.Hostname), + }, + }, + } } // generateRequestTransformerForURLRewriteFullPath generates a request-transformer plugin for the URLRewrite filter diff --git a/internal/dataplane/translator/subtranslator/httproute_test.go b/internal/dataplane/translator/subtranslator/httproute_test.go index d47288cbc5..4df406fd24 100644 --- a/internal/dataplane/translator/subtranslator/httproute_test.go +++ b/internal/dataplane/translator/subtranslator/httproute_test.go @@ -2,7 +2,6 @@ package subtranslator import ( "errors" - "fmt" "testing" "github.com/kong/go-kong/kong" @@ -292,7 +291,7 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { modifier *gatewayapi.HTTPURLRewriteFilter firstMatchPath string expectedKongRouteModification kongstate.Route - expected kong.Plugin + expected []kong.Plugin expectedErr error }{ { @@ -303,14 +302,14 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { ReplaceFullPath: lo.ToPtr("/new-path"), }, }, - expected: kong.Plugin{ + expected: []kong.Plugin{{ Name: lo.ToPtr("request-transformer"), Config: kong.Configuration{ "replace": map[string]string{ "uri": "/new-path", }, }, - }, + }}, expectedErr: nil, }, { @@ -322,14 +321,14 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { }, }, firstMatchPath: "/prefix", - expected: kong.Plugin{ + expected: []kong.Plugin{{ Name: lo.ToPtr("request-transformer"), Config: kong.Configuration{ "replace": map[string]string{ "uri": "/new$(uri_captures[1])", }, }, - }, + }}, expectedKongRouteModification: kongstate.Route{ Route: kong.Route{ Paths: []*string{ @@ -348,14 +347,14 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { }, }, firstMatchPath: "/prefix", - expected: kong.Plugin{ + expected: []kong.Plugin{{ Name: lo.ToPtr("request-transformer"), Config: kong.Configuration{ "replace": map[string]string{ "uri": `$(uri_captures[1] == nil and "/" or uri_captures[1])`, }, }, - }, + }}, expectedKongRouteModification: kongstate.Route{ Route: kong.Route{ Paths: []*string{ @@ -374,14 +373,14 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { }, }, firstMatchPath: "", - expected: kong.Plugin{ + expected: []kong.Plugin{{ Name: lo.ToPtr("request-transformer"), Config: kong.Configuration{ "replace": map[string]string{ "uri": `/prefix$(uri_captures[1] == nil and "" or "/" .. uri_captures[1])`, }, }, - }, + }}, expectedKongRouteModification: kongstate.Route{ Route: kong.Route{ Paths: []*string{ @@ -400,14 +399,14 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { }, }, firstMatchPath: "/prefix", - expected: kong.Plugin{ + expected: []kong.Plugin{{ Name: lo.ToPtr("request-transformer"), Config: kong.Configuration{ "replace": map[string]string{ "uri": `$(uri_captures[1] == nil and "/" or uri_captures[1])`, }, }, - }, + }}, expectedKongRouteModification: kongstate.Route{ Route: kong.Route{ Paths: []*string{ @@ -426,14 +425,14 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { }, }, firstMatchPath: "/", - expected: kong.Plugin{ + expected: []kong.Plugin{{ Name: lo.ToPtr("request-transformer"), Config: kong.Configuration{ "replace": map[string]string{ "uri": `$(uri_captures[1] == nil and "/" or "/" .. uri_captures[1])`, }, }, - }, + }}, expectedKongRouteModification: kongstate.Route{ Route: kong.Route{ Paths: []*string{ @@ -452,14 +451,14 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { }, }, firstMatchPath: "/", - expected: kong.Plugin{ + expected: []kong.Plugin{{ Name: lo.ToPtr("request-transformer"), Config: kong.Configuration{ "replace": map[string]string{ "uri": `/new-prefix$(uri_captures[1] == nil and "" or "/" .. uri_captures[1])`, }, }, - }, + }}, expectedKongRouteModification: kongstate.Route{ Route: kong.Route{ Paths: []*string{ @@ -478,14 +477,14 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { }, }, firstMatchPath: "/prefix/", - expected: kong.Plugin{ + expected: []kong.Plugin{{ Name: lo.ToPtr("request-transformer"), Config: kong.Configuration{ "replace": map[string]string{ "uri": `/new-prefix$(uri_captures[1])`, }, }, - }, + }}, expectedKongRouteModification: kongstate.Route{ Route: kong.Route{ Paths: []*string{ @@ -495,31 +494,73 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { }, }, }, - // TODO: https://github.com/Kong/kubernetes-ingress-controller/issues/3685 { - name: "valid URLRewriteFilter with unsupported", + name: "URLRewriteFilter with hostname", modifier: &gatewayapi.HTTPURLRewriteFilter{ - Hostname: lo.ToPtr(gatewayapi.PreciseHostname("hostname")), + Hostname: lo.ToPtr(gatewayapi.PreciseHostname("replaced.host")), + }, + expected: []kong.Plugin{{ + Name: lo.ToPtr("request-transformer"), + Config: kong.Configuration{ + "replace": map[string]string{ + "host": "replaced.host", + }, + }, + }}, + }, + { + name: "URLRewriteFilter with hostname and path", + modifier: &gatewayapi.HTTPURLRewriteFilter{ + Path: &gatewayapi.HTTPPathModifier{ + Type: gatewayapi.PrefixMatchHTTPPathModifier, + ReplacePrefixMatch: lo.ToPtr("/new-prefix"), + }, + Hostname: lo.ToPtr(gatewayapi.PreciseHostname("replaced.host")), + }, + firstMatchPath: "/prefix", + expected: []kong.Plugin{ + { + Name: lo.ToPtr("request-transformer"), + Config: kong.Configuration{ + "replace": map[string]string{ + "uri": `/new-prefix$(uri_captures[1])`, + }, + }, + }, + { + Name: lo.ToPtr("request-transformer"), + Config: kong.Configuration{ + "replace": map[string]string{ + "host": "replaced.host", + }, + }, + }, + }, + expectedKongRouteModification: kongstate.Route{ + Route: kong.Route{ + Paths: []*string{ + lo.ToPtr("~/prefix$"), + lo.ToPtr("~/prefix(/.*)"), + }, + }, }, - expected: kong.Plugin{}, - expectedErr: fmt.Errorf("unsupported hostname replace for %s", gatewayapi.HTTPRouteFilterURLRewrite), }, { name: "nil URLRewriteFilter", modifier: nil, - expected: kong.Plugin{}, + expected: nil, expectedErr: errors.New("URLRewrite is not provided"), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - plugin, routeModifier, err := generateRequestTransformerForURLRewrite(tc.modifier, tc.firstMatchPath, false) + plugins, routeModifiers, err := generateRequestTransformerForURLRewrite(tc.modifier, tc.firstMatchPath, false) require.Equal(t, tc.expectedErr, err) - require.Equal(t, tc.expected, plugin) + require.Equal(t, tc.expected, plugins) route := kongstate.Route{} - if routeModifier != nil { + for _, routeModifier := range routeModifiers { routeModifier(&route) } require.Equal(t, tc.expectedKongRouteModification, route) diff --git a/test/conformance/gateway_conformance_test.go b/test/conformance/gateway_conformance_test.go index c5f47acc3f..24e5ed3b07 100644 --- a/test/conformance/gateway_conformance_test.go +++ b/test/conformance/gateway_conformance_test.go @@ -32,6 +32,7 @@ var traditionalRoutesSupportedFeatures = []suite.SupportedFeature{ // extended features suite.SupportHTTPRouteResponseHeaderModification, suite.SupportHTTPRoutePathRewrite, + suite.SupportHTTPRouteHostRewrite, // TODO: https://github.com/Kong/kubernetes-ingress-controller/issues/5868 // Temporarily disabled and tracking through the following issue. // suite.SupportHTTPRouteBackendTimeout, @@ -46,6 +47,7 @@ var expressionRoutesSupportedFeatures = []suite.SupportedFeature{ suite.SupportHTTPRouteMethodMatching, suite.SupportHTTPRouteResponseHeaderModification, suite.SupportHTTPRoutePathRewrite, + suite.SupportHTTPRouteHostRewrite, // TODO: https://github.com/Kong/kubernetes-ingress-controller/issues/5868 // Temporarily disabled and tracking through the following issue. // suite.SupportHTTPRouteBackendTimeout,