From 54e8fa9790cb0734721094c956756f83ffe18c16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Burzy=C5=84ski?= Date: Wed, 8 May 2024 17:38:25 +0200 Subject: [PATCH] add strongly typed transformer plugin config --- .../translator/subtranslator/httproute.go | 227 ++++++++----- .../subtranslator/httproute_atc_test.go | 12 +- .../subtranslator/httproute_test.go | 307 ++++++++---------- 3 files changed, 288 insertions(+), 258 deletions(-) diff --git a/internal/dataplane/translator/subtranslator/httproute.go b/internal/dataplane/translator/subtranslator/httproute.go index 72499245b4..a61c60076e 100644 --- a/internal/dataplane/translator/subtranslator/httproute.go +++ b/internal/dataplane/translator/subtranslator/httproute.go @@ -8,6 +8,7 @@ import ( "strings" "dario.cat/mergo" + "github.com/google/go-cmp/cmp" "github.com/kong/go-kong/kong" "github.com/samber/lo" @@ -407,14 +408,13 @@ func generatePluginsFromHTTPRouteFilters( filters []gatewayapi.HTTPRouteFilter, path string, tags []*string, - // As of now, expressions router is not supported for URLRewrite with PrefixMatchHTTPPathModifier. - // TODO: https://github.com/Kong/kubernetes-ingress-controller/issues/3686 expressionsRouterEnabled bool, ) (httpRouteFiltersOriginatedPlugins, error) { if len(filters) == 0 { return httpRouteFiltersOriginatedPlugins{}, nil } var ( + transformerPlugins []transformerPlugin kongPlugins []kong.Plugin pluginNamesFromExtensionRef []string kongRouteModifiers []kongRouteModifier @@ -423,13 +423,15 @@ func generatePluginsFromHTTPRouteFilters( for _, filter := range filters { switch filter.Type { case gatewayapi.HTTPRouteFilterRequestHeaderModifier: - kongPlugins = append(kongPlugins, generateRequestHeaderModifierKongPlugin(filter.RequestHeaderModifier)) + transformerPlugins = append(transformerPlugins, generateRequestHeaderModifierKongPlugin(filter.RequestHeaderModifier)) case gatewayapi.HTTPRouteFilterRequestRedirect: - kongPlugins = append(kongPlugins, generateRequestRedirectKongPlugin(filter.RequestRedirect, path)...) + kongPlugin, transformerPlugin := generateRequestRedirectKongPlugin(filter.RequestRedirect, path) + kongPlugins = append(kongPlugins, kongPlugin) + transformerPlugins = append(transformerPlugins, transformerPlugin) case gatewayapi.HTTPRouteFilterResponseHeaderModifier: - kongPlugins = append(kongPlugins, generateResponseHeaderModifierKongPlugin(filter.ResponseHeaderModifier)) + transformerPlugins = append(transformerPlugins, generateResponseHeaderModifierKongPlugin(filter.ResponseHeaderModifier)) case gatewayapi.HTTPRouteFilterExtensionRef: plugin, err := generateExtensionRefKongPlugin(filter.ExtensionRef) @@ -443,7 +445,7 @@ func generatePluginsFromHTTPRouteFilters( if err != nil { return httpRouteFiltersOriginatedPlugins{}, err } - kongPlugins = append(kongPlugins, plugins...) + transformerPlugins = append(transformerPlugins, plugins...) kongRouteModifiers = append(kongRouteModifiers, routeModifiers...) case gatewayapi.HTTPRouteFilterRequestMirror: @@ -451,17 +453,19 @@ func generatePluginsFromHTTPRouteFilters( return httpRouteFiltersOriginatedPlugins{}, fmt.Errorf("httpFilter %s unsupported", filter.Type) } } - for _, p := range kongPlugins { - // This plugin is derived from an HTTPRoute filter, not a KongPlugin, so we apply tags indicating that - // HTTPRoute as the parent Kubernetes resource for these generated plugins. - p.Tags = tags - } - // It's possible the above loop generates multiple plugins of the same type, so we need to merge them. + // It's possible the above loop generates multiple transformerPlugins of the same type, so we need to merge them. // It can happen for example when both RequestHeaderModifier and HTTPRouteFilterURLRewrite filters are present. - kongPlugins, err := mergePluginsOfTheSameType(kongPlugins) + transformerPlugins, err := mergePluginsOfTheSameType(transformerPlugins) if err != nil { - return httpRouteFiltersOriginatedPlugins{}, fmt.Errorf("failed to merge plugins of the same type: %w", err) + return httpRouteFiltersOriginatedPlugins{}, fmt.Errorf("failed to merge transformerPlugins of the same type: %w", err) + } + kongPlugins = append(kongPlugins, transformerPluginsToKongPlugins(transformerPlugins)...) + + for _, p := range kongPlugins { + // This plugin is derived from an HTTPRoute filter, not a KongPlugin, so we apply tags indicating that + // HTTPRoute as the parent Kubernetes resource for these generated transformerPlugins. + p.Tags = tags } if len(pluginNamesFromExtensionRef) > 0 { @@ -475,37 +479,61 @@ func generatePluginsFromHTTPRouteFilters( } // mergePluginsOfTheSameType merges plugins of the same type into a single plugin with merged configurations. -func mergePluginsOfTheSameType(plugins []kong.Plugin) ([]kong.Plugin, error) { - pluginsByName := lo.GroupBy(plugins, func(p kong.Plugin) string { - return *p.Name // Name is effectively a plugin type. +func mergePluginsOfTheSameType(plugins []transformerPlugin) ([]transformerPlugin, error) { + pluginsByName := lo.GroupBy(plugins, func(p transformerPlugin) transformerPluginType { + return p.Type // Name is effectively a plugin type. }) for pluginName, plugins := range pluginsByName { plugins := plugins // If we produced multiple plugins of the same type, we need to merge their configurations now. if len(plugins) > 1 { - // Use the first plugin as the base for the merged plugin. - mergedPlugin := *plugins[0].DeepCopy() - // Merge the configurations of the other plugins into the base plugin (thus skipping the first one). - // If we merged the first one into itself, it could result in duplicate entries in slices because of - // the `mergo.WithAppendSlice` option. - for _, plugin := range plugins[1:] { - if err := mergo.Merge(&mergedPlugin.Config, plugin.Config, mergo.WithAppendSlice); err != nil { + mergedPlugin := transformerPlugin{} + for _, plugin := range plugins { + if err := mergo.Merge(&mergedPlugin, plugin, mergo.WithAppendSlice); err != nil { // Should never happen as we're passing the same type of objects. return nil, fmt.Errorf("failed to merge %q plugin configurations: %w", pluginName, err) } } - pluginsByName[pluginName] = []kong.Plugin{mergedPlugin} + pluginsByName[pluginName] = []transformerPlugin{mergedPlugin} } } // Sort the plugins by name to ensure that the order is deterministic. mergedPlugins := lo.Flatten(lo.Values(pluginsByName)) sort.Slice(mergedPlugins, func(i, j int) bool { - return *mergedPlugins[i].Name < *mergedPlugins[j].Name + return mergedPlugins[i].Type < mergedPlugins[j].Type }) return mergedPlugins, nil } +func transformerPluginsToKongPlugins(plugins []transformerPlugin) []kong.Plugin { + kongPlugins := make([]kong.Plugin, 0, len(plugins)) + for _, plugin := range plugins { + kongPlugins = append(kongPlugins, transformerPluginToKongPlugin(plugin)) + } + return kongPlugins +} + +func transformerPluginToKongPlugin(plugin transformerPlugin) kong.Plugin { + res := kong.Plugin{ + Name: kong.String(string(plugin.Type)), + Config: kong.Configuration{}, + } + if !cmp.Equal(plugin.Replace, transformerPluginReplaceConfig{}) { + res.Config["replace"] = plugin.Replace + } + if !cmp.Equal(plugin.Add, transformerPluginConfig{}) { + res.Config["add"] = plugin.Add + } + if !cmp.Equal(plugin.Append, transformerPluginConfig{}) { + res.Config["append"] = plugin.Append + } + if !cmp.Equal(plugin.Remove, transformerPluginConfig{}) { + res.Config["remove"] = plugin.Remove + } + return res +} + func generateKongRouteModifierFromExtensionRef(pluginNamesFromExtensionRef []string) kongRouteModifier { return func(route *kongstate.Route) { if route.Ingress.Annotations == nil { @@ -525,9 +553,8 @@ func generateKongRouteModifierFromExtensionRef(pluginNamesFromExtensionRef []str // generateRequestRedirectKongPlugin generates configurations of plugins to satisfy the specification // of request redirect filter. -func generateRequestRedirectKongPlugin(modifier *gatewayapi.HTTPRequestRedirectFilter, path string) []kong.Plugin { - plugins := make([]kong.Plugin, 2) - plugins[0] = kong.Plugin{ +func generateRequestRedirectKongPlugin(modifier *gatewayapi.HTTPRequestRedirectFilter, path string) (kong.Plugin, transformerPlugin) { + requestTerminationPlugin := kong.Plugin{ Name: kong.String("request-termination"), Config: kong.Configuration{ "status_code": modifier.StatusCode, @@ -556,16 +583,14 @@ func generateRequestRedirectKongPlugin(modifier *gatewayapi.HTTPRequestRedirectF locationHeader = fmt.Sprintf("Location: %s", path) } - plugins[1] = kong.Plugin{ - Name: kong.String("response-transformer"), - Config: kong.Configuration{ - "add": map[string][]string{ - "headers": {locationHeader}, - }, + transformerPlugin := transformerPlugin{ + Type: transformerPluginTypeResponse, + Add: transformerPluginConfig{ + Headers: []string{locationHeader}, }, } - return plugins + return requestTerminationPlugin, transformerPlugin } func generateExtensionRefKongPlugin(modifier *gatewayapi.LocalObjectReference) (string, error) { @@ -577,50 +602,80 @@ func generateExtensionRefKongPlugin(modifier *gatewayapi.LocalObjectReference) ( // generateRequestHeaderModifierKongPlugin converts a gatewayapi.HTTPRequestHeaderFilter into a // kong.Plugin of type request-transformer. -func generateRequestHeaderModifierKongPlugin(modifier *gatewayapi.HTTPHeaderFilter) kong.Plugin { - return generateHeaderModifierKongPlugin(modifier, "request-transformer") +func generateRequestHeaderModifierKongPlugin(modifier *gatewayapi.HTTPHeaderFilter) transformerPlugin { + return generateHeaderModifierKongPlugin(modifier, transformerPluginTypeRequest) } // generateResponseHeaderModifierKongPlugin converts a gatewayapi.HTTPResponseHeaderFilter into a // kong.Plugin of type response-transformer. -func generateResponseHeaderModifierKongPlugin(modifier *gatewayapi.HTTPHeaderFilter) kong.Plugin { - return generateHeaderModifierKongPlugin(modifier, "response-transformer") +func generateResponseHeaderModifierKongPlugin(modifier *gatewayapi.HTTPHeaderFilter) transformerPlugin { + return generateHeaderModifierKongPlugin(modifier, transformerPluginTypeResponse) +} + +type transformerPluginType string + +const ( + transformerPluginTypeRequest transformerPluginType = "request-transformer" + transformerPluginTypeResponse transformerPluginType = "response-transformer" +) + +// transformerPlugin is a configuration for request-transformer and response-transformer plugins. +type transformerPlugin struct { + // Type is the type of the transformer plugin (request-transformer or response-transformer). + Type transformerPluginType `json:"-"` + + Replace transformerPluginReplaceConfig `json:"replace,omitempty"` + Add transformerPluginConfig `json:"add,omitempty"` + Append transformerPluginConfig `json:"append,omitempty"` + Remove transformerPluginConfig `json:"remove,omitempty"` } -func generateHeaderModifierKongPlugin(modifier *gatewayapi.HTTPHeaderFilter, pluginName string) kong.Plugin { - plugin := kong.Plugin{ - Name: kong.String(pluginName), - Config: make(kong.Configuration), +// transformerPluginConfig is a configuration for request-transformer and response-transformer plugins' +// "add", "append", and "remove" fields. +type transformerPluginConfig struct { + Headers []string `json:"headers,omitempty"` +} + +// transformerPluginReplaceConfig is a configuration for request-transformer and response-transformer plugins' +// "replace" field. +type transformerPluginReplaceConfig struct { + Headers []string `json:"headers,omitempty"` + URI string `json:"uri,omitempty"` +} + +func generateHeaderModifierKongPlugin(modifier *gatewayapi.HTTPHeaderFilter, pluginType transformerPluginType) transformerPlugin { + plugin := transformerPlugin{ + Type: pluginType, } // modifier.Set is converted to a pair composed of "replace" and "add" if modifier.Set != nil { - setModifiers := make([]interface{}, 0, len(modifier.Set)) + setModifiers := make([]string, 0, len(modifier.Set)) for _, s := range modifier.Set { setModifiers = append(setModifiers, kongHeaderFormatter(s)) } - plugin.Config["replace"] = map[string]interface{}{ - "headers": setModifiers, + plugin.Replace = transformerPluginReplaceConfig{ + Headers: setModifiers, } - plugin.Config["add"] = map[string]interface{}{ - "headers": setModifiers, + plugin.Add = transformerPluginConfig{ + Headers: setModifiers, } } // modifier.Add is converted to "append" if modifier.Add != nil { - appendModifiers := make([]interface{}, 0, len(modifier.Add)) + appendModifiers := make([]string, 0, len(modifier.Add)) for _, a := range modifier.Add { appendModifiers = append(appendModifiers, kongHeaderFormatter(a)) } - plugin.Config["append"] = map[string]interface{}{ - "headers": appendModifiers, + plugin.Append = transformerPluginConfig{ + Headers: appendModifiers, } } if modifier.Remove != nil { - plugin.Config["remove"] = map[string]interface{}{ - "headers": modifier.Remove, + plugin.Remove = transformerPluginConfig{ + Headers: modifier.Remove, } } @@ -635,7 +690,7 @@ func generateRequestTransformerForURLRewrite( filter *gatewayapi.HTTPURLRewriteFilter, path string, expressionsRouterEnabled bool, -) ([]kong.Plugin, []kongRouteModifier, error) { +) ([]transformerPlugin, []kongRouteModifier, error) { if filter == nil { return nil, nil, fmt.Errorf("%s is not provided", gatewayapi.HTTPRouteFilterURLRewrite) } @@ -644,7 +699,7 @@ func generateRequestTransformerForURLRewrite( } var ( - plugins []kong.Plugin + plugins []transformerPlugin routeModifiers []kongRouteModifier ) if filter.Path != nil { @@ -668,12 +723,12 @@ func generateRequestTransformerForURLRewritePath( filter *gatewayapi.HTTPURLRewriteFilter, path string, expressionsRouterEnabled bool, -) (kong.Plugin, kongRouteModifier, error) { +) (transformerPlugin, 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 transformerPlugin{}, nil, fmt.Errorf("failed to generate request-transformer plugin for %s: %w", gatewayapi.HTTPRouteFilterURLRewrite, err) } return plugin, nil, nil @@ -681,25 +736,23 @@ func generateRequestTransformerForURLRewritePath( 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) + return transformerPlugin{}, 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{ - "headers": { - fmt.Sprintf("host:%s", string(*filter.Hostname)), - }, +) transformerPlugin { + return transformerPlugin{ + Type: transformerPluginTypeRequest, + Replace: transformerPluginReplaceConfig{ + Headers: []string{ + fmt.Sprintf("host:%s", string(*filter.Hostname)), }, - "add": map[string][]string{ - "headers": { - fmt.Sprintf("host:%s", string(*filter.Hostname)), - }, + }, + Add: transformerPluginConfig{ + Headers: []string{ + fmt.Sprintf("host:%s", string(*filter.Hostname)), }, }, } @@ -707,17 +760,15 @@ func generateRequestTransformerForURLRewriteHostname( // generateRequestTransformerForURLRewriteFullPath generates a request-transformer plugin for the URLRewrite filter // with a FullPathHTTPPathModifier. -func generateRequestTransformerForURLRewriteFullPath(filter *gatewayapi.HTTPURLRewriteFilter) (kong.Plugin, error) { +func generateRequestTransformerForURLRewriteFullPath(filter *gatewayapi.HTTPURLRewriteFilter) (transformerPlugin, error) { if filter.Path.ReplaceFullPath == nil { - return kong.Plugin{}, fmt.Errorf("%s missing ReplaceFullPath", gatewayapi.HTTPRouteFilterURLRewrite) + return transformerPlugin{}, fmt.Errorf("%s missing ReplaceFullPath", gatewayapi.HTTPRouteFilterURLRewrite) } - return kong.Plugin{ - Name: kong.String("request-transformer"), - Config: kong.Configuration{ - "replace": map[string]string{ - "uri": *filter.Path.ReplaceFullPath, - }, + return transformerPlugin{ + Type: transformerPluginTypeRequest, + Replace: transformerPluginReplaceConfig{ + URI: *filter.Path.ReplaceFullPath, }, }, nil } @@ -728,19 +779,17 @@ func generateRequestTransformerForURLRewritePrefixMatch( filter *gatewayapi.HTTPURLRewriteFilter, path string, expressionsRouterEnabled bool, -) (kong.Plugin, kongRouteModifier) { +) (transformerPlugin, kongRouteModifier) { // Normalize the path before passing it down. path = normalizePath(path) - return kong.Plugin{ - Name: kong.String("request-transformer"), - Config: kong.Configuration{ - "replace": map[string]string{ - "uri": generateRequestTransformerReplaceURIForURLRewritePrefixMatch( - filter.Path.ReplacePrefixMatch, - path, - ), - }, + return transformerPlugin{ + Type: transformerPluginTypeRequest, + Replace: transformerPluginReplaceConfig{ + URI: generateRequestTransformerReplaceURIForURLRewritePrefixMatch( + filter.Path.ReplacePrefixMatch, + path, + ), }, }, generateKongRouteModifierForURLRewritePrefixMatch(path, expressionsRouterEnabled) } diff --git a/internal/dataplane/translator/subtranslator/httproute_atc_test.go b/internal/dataplane/translator/subtranslator/httproute_atc_test.go index 46ad8e656b..4cacd722d1 100644 --- a/internal/dataplane/translator/subtranslator/httproute_atc_test.go +++ b/internal/dataplane/translator/subtranslator/httproute_atc_test.go @@ -138,8 +138,8 @@ func TestGenerateKongExpressionRoutesFromHTTPRouteMatches(t *testing.T) { { Name: kong.String("response-transformer"), Config: kong.Configuration{ - "add": map[string][]string{ - "headers": {`Location: http://a.foo.com:80/exact/0`}, + "add": transformerPluginConfig{ + Headers: []string{`Location: http://a.foo.com:80/exact/0`}, }, }, }, @@ -164,8 +164,8 @@ func TestGenerateKongExpressionRoutesFromHTTPRouteMatches(t *testing.T) { { Name: kong.String("response-transformer"), Config: kong.Configuration{ - "add": map[string][]string{ - "headers": {`Location: http://a.foo.com:80/exact/1`}, + "add": transformerPluginConfig{ + Headers: []string{`Location: http://a.foo.com:80/exact/1`}, }, }, }, @@ -200,8 +200,8 @@ func TestGenerateKongExpressionRoutesFromHTTPRouteMatches(t *testing.T) { { Name: kong.String("request-transformer"), Config: kong.Configuration{ - "append": map[string]interface{}{ - "headers": []interface{}{"foo:bar"}, + "append": transformerPluginConfig{ + Headers: []string{"foo:bar"}, }, }, }, diff --git a/internal/dataplane/translator/subtranslator/httproute_test.go b/internal/dataplane/translator/subtranslator/httproute_test.go index b6310cafdb..43c258056c 100644 --- a/internal/dataplane/translator/subtranslator/httproute_test.go +++ b/internal/dataplane/translator/subtranslator/httproute_test.go @@ -53,23 +53,23 @@ func TestGeneratePluginsFromHTTPRouteFilters(t *testing.T) { { Name: kong.String("request-transformer"), Config: kong.Configuration{ - "add": map[string]interface{}{ - "headers": []interface{}{ + "add": transformerPluginConfig{ + Headers: []string{ "header-to-set:bar", }, }, - "append": map[string]interface{}{ - "headers": []interface{}{ + "append": transformerPluginConfig{ + Headers: []string{ "header-to-add:foo", }, }, - "remove": map[string]interface{}{ - "headers": []string{ + "remove": transformerPluginConfig{ + Headers: []string{ "header-to-remove", }, }, - "replace": map[string]interface{}{ - "headers": []interface{}{ + "replace": transformerPluginReplaceConfig{ + Headers: []string{ "header-to-set:bar", }, }, @@ -99,8 +99,8 @@ func TestGeneratePluginsFromHTTPRouteFilters(t *testing.T) { { Name: kong.String("response-transformer"), Config: kong.Configuration{ - "add": map[string][]string{ - "headers": { + "add": transformerPluginConfig{ + Headers: []string{ "Location: http://example.org:80/test", }, }, @@ -134,23 +134,23 @@ func TestGeneratePluginsFromHTTPRouteFilters(t *testing.T) { { Name: kong.String("response-transformer"), Config: kong.Configuration{ - "add": map[string]interface{}{ - "headers": []interface{}{ + "add": transformerPluginConfig{ + Headers: []string{ "header-to-set:bar", }, }, - "append": map[string]interface{}{ - "headers": []interface{}{ + "append": transformerPluginConfig{ + Headers: []string{ "header-to-add:foo", }, }, - "remove": map[string]interface{}{ - "headers": []string{ + "remove": transformerPluginConfig{ + Headers: []string{ "header-to-remove", }, }, - "replace": map[string]interface{}{ - "headers": []interface{}{ + "replace": transformerPluginReplaceConfig{ + Headers: []string{ "header-to-set:bar", }, }, @@ -185,7 +185,7 @@ func TestGeneratePluginsFromHTTPRouteFilters(t *testing.T) { }, }, }, - expectedPlugins: []kong.Plugin{}, + expectedPlugins: nil, }, { name: "invalid extensionrefs filter group", @@ -244,14 +244,14 @@ func TestGeneratePluginsFromHTTPRouteFilters(t *testing.T) { { Name: kong.String("request-transformer"), Config: kong.Configuration{ - "add": map[string]interface{}{ - "headers": []interface{}{ + "add": transformerPluginConfig{ + Headers: []string{ "header-to-set:bar", }, }, - "replace": map[string]interface{}{ - "uri": "/new$(uri_captures[1])", - "headers": []interface{}{ + "replace": transformerPluginReplaceConfig{ + URI: "/new$(uri_captures[1])", + Headers: []string{ "header-to-set:bar", }, }, @@ -291,7 +291,7 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { modifier *gatewayapi.HTTPURLRewriteFilter firstMatchPath string expectedKongRouteModification kongstate.Route - expected []kong.Plugin + expected []transformerPlugin expectedErr error }{ { @@ -302,12 +302,10 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { ReplaceFullPath: lo.ToPtr("/new-path"), }, }, - expected: []kong.Plugin{{ - Name: lo.ToPtr("request-transformer"), - Config: kong.Configuration{ - "replace": map[string]string{ - "uri": "/new-path", - }, + expected: []transformerPlugin{{ + Type: transformerPluginTypeRequest, + Replace: transformerPluginReplaceConfig{ + URI: "/new-path", }, }}, expectedErr: nil, @@ -321,12 +319,10 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { }, }, firstMatchPath: "/prefix", - expected: []kong.Plugin{{ - Name: lo.ToPtr("request-transformer"), - Config: kong.Configuration{ - "replace": map[string]string{ - "uri": "/new$(uri_captures[1])", - }, + expected: []transformerPlugin{{ + Type: transformerPluginTypeRequest, + Replace: transformerPluginReplaceConfig{ + URI: "/new$(uri_captures[1])", }, }}, expectedKongRouteModification: kongstate.Route{ @@ -347,12 +343,10 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { }, }, firstMatchPath: "/prefix", - 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])`, - }, + expected: []transformerPlugin{{ + Type: transformerPluginTypeRequest, + Replace: transformerPluginReplaceConfig{ + URI: `$(uri_captures[1] == nil and "/" or uri_captures[1])`, }, }}, expectedKongRouteModification: kongstate.Route{ @@ -373,12 +367,10 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { }, }, firstMatchPath: "", - 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])`, - }, + expected: []transformerPlugin{{ + Type: transformerPluginTypeRequest, + Replace: transformerPluginReplaceConfig{ + URI: `/prefix$(uri_captures[1] == nil and "" or "/" .. uri_captures[1])`, }, }}, expectedKongRouteModification: kongstate.Route{ @@ -399,12 +391,10 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { }, }, firstMatchPath: "/prefix", - 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])`, - }, + expected: []transformerPlugin{{ + Type: transformerPluginTypeRequest, + Replace: transformerPluginReplaceConfig{ + URI: `$(uri_captures[1] == nil and "/" or uri_captures[1])`, }, }}, expectedKongRouteModification: kongstate.Route{ @@ -425,12 +415,10 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { }, }, firstMatchPath: "/", - 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])`, - }, + expected: []transformerPlugin{{ + Type: transformerPluginTypeRequest, + Replace: transformerPluginReplaceConfig{ + URI: `$(uri_captures[1] == nil and "/" or "/" .. uri_captures[1])`, }, }}, expectedKongRouteModification: kongstate.Route{ @@ -451,12 +439,10 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { }, }, firstMatchPath: "/", - 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])`, - }, + expected: []transformerPlugin{{ + Type: transformerPluginTypeRequest, + Replace: transformerPluginReplaceConfig{ + URI: `/new-prefix$(uri_captures[1] == nil and "" or "/" .. uri_captures[1])`, }, }}, expectedKongRouteModification: kongstate.Route{ @@ -477,12 +463,10 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { }, }, firstMatchPath: "/prefix/", - expected: []kong.Plugin{{ - Name: lo.ToPtr("request-transformer"), - Config: kong.Configuration{ - "replace": map[string]string{ - "uri": `/new-prefix$(uri_captures[1])`, - }, + expected: []transformerPlugin{{ + Type: transformerPluginTypeRequest, + Replace: transformerPluginReplaceConfig{ + URI: `/new-prefix$(uri_captures[1])`, }, }}, expectedKongRouteModification: kongstate.Route{ @@ -499,18 +483,16 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { modifier: &gatewayapi.HTTPURLRewriteFilter{ Hostname: lo.ToPtr(gatewayapi.PreciseHostname("replaced.host")), }, - expected: []kong.Plugin{{ - Name: lo.ToPtr("request-transformer"), - Config: kong.Configuration{ - "replace": map[string][]string{ - "headers": { - "host:replaced.host", - }, + expected: []transformerPlugin{{ + Type: transformerPluginTypeRequest, + Replace: transformerPluginReplaceConfig{ + Headers: []string{ + "host:replaced.host", }, - "add": map[string][]string{ - "headers": { - "host:replaced.host", - }, + }, + Add: transformerPluginConfig{ + Headers: []string{ + "host:replaced.host", }, }, }}, @@ -525,27 +507,24 @@ func TestGenerateRequestTransformerForURLRewrite(t *testing.T) { Hostname: lo.ToPtr(gatewayapi.PreciseHostname("replaced.host")), }, firstMatchPath: "/prefix", - expected: []kong.Plugin{ + expected: []transformerPlugin{ { - Name: lo.ToPtr("request-transformer"), - Config: kong.Configuration{ - "replace": map[string]string{ - "uri": `/new-prefix$(uri_captures[1])`, - }, + Type: transformerPluginTypeRequest, + + Replace: transformerPluginReplaceConfig{ + URI: `/new-prefix$(uri_captures[1])`, }, }, { - Name: lo.ToPtr("request-transformer"), - Config: kong.Configuration{ - "replace": map[string][]string{ - "headers": { - "host:replaced.host", - }, + Type: transformerPluginTypeRequest, + Replace: transformerPluginReplaceConfig{ + Headers: []string{ + "host:replaced.host", }, - "add": map[string][]string{ - "headers": { - "host:replaced.host", - }, + }, + Add: transformerPluginConfig{ + Headers: []string{ + "host:replaced.host", }, }, }, @@ -621,149 +600,151 @@ func TestGenerateKongRouteModifierForURLRewritePrefixMatch_ExpressionsRouter(t * func TestMergePluginsOfTheSameType(t *testing.T) { testCases := []struct { name string - plugins []kong.Plugin - expected []kong.Plugin + plugins []transformerPlugin + expected []transformerPlugin }{ { name: "no plugins", - plugins: []kong.Plugin{}, - expected: []kong.Plugin{}, + plugins: []transformerPlugin{}, + expected: []transformerPlugin{}, }, { name: "single plugin", - plugins: []kong.Plugin{ + plugins: []transformerPlugin{ { - Name: lo.ToPtr("plugin1"), + Type: transformerPluginTypeRequest, }, }, - expected: []kong.Plugin{ + expected: []transformerPlugin{ { - Name: lo.ToPtr("plugin1"), + Type: transformerPluginTypeRequest, }, }, }, { name: "multiple plugins of different types", - plugins: []kong.Plugin{ - { - Name: lo.ToPtr("plugin1"), - }, + plugins: []transformerPlugin{ { - Name: lo.ToPtr("plugin2"), + Type: transformerPluginTypeRequest, }, { - Name: lo.ToPtr("plugin3"), + Type: transformerPluginTypeResponse, }, }, - expected: []kong.Plugin{ + expected: []transformerPlugin{ { - Name: lo.ToPtr("plugin1"), + Type: transformerPluginTypeRequest, }, { - Name: lo.ToPtr("plugin2"), - }, - { - Name: lo.ToPtr("plugin3"), + Type: transformerPluginTypeResponse, }, }, }, { name: "multiple plugins of the same types", - plugins: []kong.Plugin{ + plugins: []transformerPlugin{ { - Name: lo.ToPtr("plugin1"), + Type: transformerPluginTypeRequest, }, { - Name: lo.ToPtr("plugin1"), + Type: transformerPluginTypeResponse, }, { - Name: lo.ToPtr("plugin2"), + Type: transformerPluginTypeRequest, }, { - Name: lo.ToPtr("plugin2"), + Type: transformerPluginTypeResponse, }, }, - expected: []kong.Plugin{ + expected: []transformerPlugin{ { - Name: lo.ToPtr("plugin1"), + Type: transformerPluginTypeRequest, }, { - Name: lo.ToPtr("plugin2"), + Type: transformerPluginTypeResponse, }, }, }, { name: "multiple plugins of the same types with different configurations - configuration is merged", - plugins: []kong.Plugin{ + plugins: []transformerPlugin{ { - Name: lo.ToPtr("plugin1"), - Config: kong.Configuration{ - "key1": "value1", + Type: transformerPluginTypeRequest, + Add: transformerPluginConfig{ + Headers: []string{"header1:value1"}, + }, + Replace: transformerPluginReplaceConfig{ + URI: "path1", }, }, { - Name: lo.ToPtr("plugin1"), - Config: kong.Configuration{ - "key2": "value2", + Type: transformerPluginTypeRequest, + Add: transformerPluginConfig{ + Headers: []string{"header2:value2"}, + }, + Replace: transformerPluginReplaceConfig{ + URI: "path2", }, }, }, - expected: []kong.Plugin{ + expected: []transformerPlugin{ { - Name: lo.ToPtr("plugin1"), - Config: kong.Configuration{ - "key1": "value1", - "key2": "value2", + Type: transformerPluginTypeRequest, + Add: transformerPluginConfig{ + Headers: []string{"header1:value1", "header2:value2"}, + }, + Replace: transformerPluginReplaceConfig{ + URI: "path1", }, }, }, }, { name: "multiple plugins of the same types with same configuration keys - configuration is merged and the first wins", - plugins: []kong.Plugin{ + plugins: []transformerPlugin{ { - Name: lo.ToPtr("plugin1"), - Config: kong.Configuration{ - "key1": "value1", + Type: transformerPluginTypeRequest, + Replace: transformerPluginReplaceConfig{ + URI: "path1", }, }, { - Name: lo.ToPtr("plugin1"), - Config: kong.Configuration{ - "key1": "value2", + Type: transformerPluginTypeRequest, + Replace: transformerPluginReplaceConfig{ + URI: "path2", }, }, }, - expected: []kong.Plugin{ + expected: []transformerPlugin{ { - Name: lo.ToPtr("plugin1"), - Config: kong.Configuration{ - "key1": "value1", + Type: transformerPluginTypeRequest, + Replace: transformerPluginReplaceConfig{ + URI: "path1", }, }, }, }, { - name: "multiple plugins of the same types with same configuration keys - slices are appended", - plugins: []kong.Plugin{ + name: "multiple plugins of the same types with same configuration keys, first URI empty", + plugins: []transformerPlugin{ { - Name: lo.ToPtr("plugin1"), - Config: kong.Configuration{ - "key1": []interface{}{"value1"}, + Type: transformerPluginTypeRequest, + Replace: transformerPluginReplaceConfig{ + URI: "", }, }, { - Name: lo.ToPtr("plugin1"), - Config: kong.Configuration{ - "key1": []interface{}{"value2"}, + Type: transformerPluginTypeRequest, + Replace: transformerPluginReplaceConfig{ + URI: "path2", }, }, }, - expected: []kong.Plugin{ + expected: []transformerPlugin{ { - Name: lo.ToPtr("plugin1"), - Config: kong.Configuration{ - "key1": []interface{}{"value1", "value2"}, + Type: transformerPluginTypeRequest, + Replace: transformerPluginReplaceConfig{ + URI: "path2", }, }, },