Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(httproute): handle URLRewrite filter's hostname rewrite #5952

Merged
merged 3 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,9 @@ Adding a new version? You'll need three changes:
- Add support in `HTTPRoute`s for `URLRewrite`:
- `FullPathRewrite` [#5855](https://github.com/Kong/kubernetes-ingress-controller/pull/5855)
- `ReplacePrefixMatch` for both router modes:
- `traditional_compatible`: [#5895](https://github.com/Kong/kubernetes-ingress-controller/pull/5895)
- `expressions`: [#5940](https://github.com/Kong/kubernetes-ingress-controller/pull/5940)
- `traditional_compatible` [#5895](https://github.com/Kong/kubernetes-ingress-controller/pull/5895)
- `expressions` [#5940](https://github.com/Kong/kubernetes-ingress-controller/pull/5940)
- `Hostname` [#5952](https://github.com/Kong/kubernetes-ingress-controller/pull/5952)
- DB mode now supports Event reporting for resources that failed to apply.
[#5785](https://github.com/Kong/kubernetes-ingress-controller/pull/5785)
- Improve validation - reject `Ingresses`, `Services` or `KongConsumers` that have multiple instances
Expand Down
101 changes: 69 additions & 32 deletions internal/dataplane/translator/subtranslator/httproute.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -482,11 +480,12 @@ func mergePluginsOfTheSameType(plugins []kong.Plugin) ([]kong.Plugin, error) {
return *p.Name // 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 {
mergedPlugin := *plugins[0].DeepCopy()
for _, plugin := range plugins {
if err := mergo.Merge(&mergedPlugin.Config, plugin.Config); err != nil {
for _, plugin := range plugins[1:] {
if err := mergo.Merge(&mergedPlugin.Config, plugin.Config, mergo.WithAppendSlice); err != nil {
czeslavo marked this conversation as resolved.
Show resolved Hide resolved
// 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)
}
Expand Down Expand Up @@ -592,31 +591,31 @@ func generateHeaderModifierKongPlugin(modifier *gatewayapi.HTTPHeaderFilter, plu

// modifier.Set is converted to a pair composed of "replace" and "add"
if modifier.Set != nil {
setModifiers := make([]string, 0, len(modifier.Set))
setModifiers := make([]interface{}, 0, len(modifier.Set))
for _, s := range modifier.Set {
setModifiers = append(setModifiers, kongHeaderFormatter(s))
}
plugin.Config["replace"] = map[string][]string{
plugin.Config["replace"] = map[string]interface{}{
"headers": setModifiers,
}
plugin.Config["add"] = map[string][]string{
plugin.Config["add"] = map[string]interface{}{
"headers": setModifiers,
}
}

// modifier.Add is converted to "append"
if modifier.Add != nil {
appendModifiers := make([]string, 0, len(modifier.Add))
appendModifiers := make([]interface{}, 0, len(modifier.Add))
for _, a := range modifier.Add {
appendModifiers = append(appendModifiers, kongHeaderFormatter(a))
}
plugin.Config["append"] = map[string][]string{
plugin.Config["append"] = map[string]interface{}{
"headers": appendModifiers,
}
}

if modifier.Remove != nil {
plugin.Config["remove"] = map[string][]string{
plugin.Config["remove"] = map[string]interface{}{
rainest marked this conversation as resolved.
Show resolved Hide resolved
"headers": modifier.Remove,
}
}
Expand All @@ -632,36 +631,74 @@ 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 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)
}
}

return kong.Plugin{}, nil, fmt.Errorf("invalid %s config", 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)),
},
},
"add": map[string][]string{
"headers": {
fmt.Sprintf("host:%s", string(*filter.Hostname)),
},
},
},
}
}

// generateRequestTransformerForURLRewriteFullPath generates a request-transformer plugin for the URLRewrite filter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,8 @@ func TestGenerateKongExpressionRoutesFromHTTPRouteMatches(t *testing.T) {
{
Name: kong.String("request-transformer"),
Config: kong.Configuration{
"append": map[string][]string{
"headers": {"foo:bar"},
"append": map[string]interface{}{
"headers": []interface{}{"foo:bar"},
},
},
},
Expand Down
Loading
Loading