From 39fe815cd9a948910e88146fb51eaabee6af44bc Mon Sep 17 00:00:00 2001 From: Mattia Lavacca Date: Wed, 11 Oct 2023 16:55:46 +0200 Subject: [PATCH 1/3] feat: Kong plugins feature Signed-off-by: Mattia Lavacca --- pkg/i2gw/providers/kong/annotations.go | 1 + pkg/i2gw/providers/kong/converter.go | 1 + pkg/i2gw/providers/kong/converter_test.go | 13 ++- .../providers/kong/method_matching_test.go | 2 +- pkg/i2gw/providers/kong/plugins.go | 82 +++++++++++++++++++ 5 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 pkg/i2gw/providers/kong/plugins.go diff --git a/pkg/i2gw/providers/kong/annotations.go b/pkg/i2gw/providers/kong/annotations.go index 41864622..4d6c5cd9 100644 --- a/pkg/i2gw/providers/kong/annotations.go +++ b/pkg/i2gw/providers/kong/annotations.go @@ -23,6 +23,7 @@ const ( headersKey = "headers" methodsKey = "methods" + pluginsKey = "plugins" ) func kongAnnotation(suffix string) string { diff --git a/pkg/i2gw/providers/kong/converter.go b/pkg/i2gw/providers/kong/converter.go index dd47c40d..c683af23 100644 --- a/pkg/i2gw/providers/kong/converter.go +++ b/pkg/i2gw/providers/kong/converter.go @@ -36,6 +36,7 @@ func newConverter(conf *i2gw.ProviderConf) *converter { featureParsers: []i2gw.FeatureParser{ headerMatchingFeature, methodMatchingFeature, + pluginsFeature, }, } } diff --git a/pkg/i2gw/providers/kong/converter_test.go b/pkg/i2gw/providers/kong/converter_test.go index 08f2c890..fcc43670 100644 --- a/pkg/i2gw/providers/kong/converter_test.go +++ b/pkg/i2gw/providers/kong/converter_test.go @@ -42,7 +42,7 @@ func Test_ToGateway(t *testing.T) { expectedErrors field.ErrorList }{ { - name: "header matching, method matching, single ingress rule", + name: "header matching, method matching, plugin, single ingress rule", ingresses: []networkingv1.Ingress{ { ObjectMeta: metav1.ObjectMeta{ @@ -51,6 +51,7 @@ func Test_ToGateway(t *testing.T) { Annotations: map[string]string{ "konghq.com/headers.key1": "val1", "konghq.com/methods": "GET,POST", + "konghq.com/plugins": "plugin1", }, }, Spec: networkingv1.IngressSpec{ @@ -131,6 +132,16 @@ func Test_ToGateway(t *testing.T) { Method: ptrTo(gatewayv1beta1.HTTPMethodPost), }, }, + Filters: []gatewayv1beta1.HTTPRouteFilter{ + { + Type: gatewayv1beta1.HTTPRouteFilterExtensionRef, + ExtensionRef: &gatewayv1beta1.LocalObjectReference{ + Group: gatewayv1beta1.Group("configuration.konghq.com/v1"), + Kind: gatewayv1beta1.Kind("KongPlugin"), + Name: gatewayv1beta1.ObjectName("plugin1"), + }, + }, + }, BackendRefs: []gatewayv1beta1.HTTPBackendRef{ { BackendRef: gatewayv1beta1.BackendRef{ diff --git a/pkg/i2gw/providers/kong/method_matching_test.go b/pkg/i2gw/providers/kong/method_matching_test.go index a353bc61..4f9e024b 100644 --- a/pkg/i2gw/providers/kong/method_matching_test.go +++ b/pkg/i2gw/providers/kong/method_matching_test.go @@ -29,7 +29,7 @@ import ( gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) -func TestPathMatchingFeature(t *testing.T) { +func TestMethodMatchingFeature(t *testing.T) { iPrefix := networkingv1.PathTypePrefix testCases := []struct { diff --git a/pkg/i2gw/providers/kong/plugins.go b/pkg/i2gw/providers/kong/plugins.go new file mode 100644 index 00000000..ad676d8b --- /dev/null +++ b/pkg/i2gw/providers/kong/plugins.go @@ -0,0 +1,82 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kong + +import ( + "strings" + + "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" + "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/validation/field" + gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +// pluginsFeature parses the Kong Ingress Controller plugins annotation and converts it +// into HTTPRoutes rule's ExtensionRef filters. +// It's possible to define a list of plugins to attach to the same HTTPRoute by setting +// a comma-separated list. +// +// Example: konghq.com/plugins: "plugin1,plugin2" +func pluginsFeature(ingressResources i2gw.InputResources, gatewayResources *i2gw.GatewayResources) field.ErrorList { + ruleGroups := common.GetRuleGroups(ingressResources.Ingresses) + for _, rg := range ruleGroups { + for _, rule := range rg.Rules { + key := types.NamespacedName{Namespace: rule.Ingress.Namespace, Name: common.NameFromHost(rg.Host)} + httpRoute, ok := gatewayResources.HTTPRoutes[key] + if !ok { + panic("HTTPRoute does not exist - this should never happen") + } + filters := parsePluginsAnnotation(rule.Ingress.ObjectMeta.Namespace, rule.Ingress.ObjectMeta.Name, rule.Ingress.Annotations) + patchHTTPRoutePlugins(&httpRoute, filters) + } + } + return nil +} + +func parsePluginsAnnotation(ingressNamespace, ingressName string, annotations map[string]string) []gatewayv1beta1.HTTPRouteFilter { + filters := make([]gatewayv1beta1.HTTPRouteFilter, 0) + mkey := kongAnnotation(pluginsKey) + for key, val := range annotations { + if key == mkey { + filtersValues := strings.Split(val, ",") + for _, v := range filtersValues { + if v == "" { + continue + } + filters = append(filters, gatewayv1beta1.HTTPRouteFilter{ + Type: gatewayv1beta1.HTTPRouteFilterExtensionRef, + ExtensionRef: &gatewayv1beta1.LocalObjectReference{ + Group: gatewayv1beta1.Group("configuration.konghq.com/v1"), + Kind: gatewayv1beta1.Kind("KongPlugin"), + Name: gatewayv1beta1.ObjectName(v), + }, + }) + } + } + } + return filters +} + +func patchHTTPRoutePlugins(httpRoute *gatewayv1beta1.HTTPRoute, extensionRefs []gatewayv1beta1.HTTPRouteFilter) { + for i := range httpRoute.Spec.Rules { + if httpRoute.Spec.Rules[i].Filters == nil { + httpRoute.Spec.Rules[i].Filters = make([]gatewayv1beta1.HTTPRouteFilter, 0) + } + httpRoute.Spec.Rules[i].Filters = append(httpRoute.Spec.Rules[i].Filters, extensionRefs...) + } +} From a0bca0b49bc3f1ef2d87e1dba1aa06fb2df9c2e9 Mon Sep 17 00:00:00 2001 From: Mattia Lavacca Date: Wed, 11 Oct 2023 16:57:41 +0200 Subject: [PATCH 2/3] feat: Kong README.md updated Signed-off-by: Mattia Lavacca --- pkg/i2gw/providers/kong/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/i2gw/providers/kong/README.md b/pkg/i2gw/providers/kong/README.md index 47b320bb..16055d1d 100644 --- a/pkg/i2gw/providers/kong/README.md +++ b/pkg/i2gw/providers/kong/README.md @@ -12,5 +12,8 @@ Current supported annotations: in the annotation key after `.`, and the annotations value can contain multiple header values separated by commas. All the header values for a specific header name are intended to be ORed. Example: `konghq.com/headers.x-routing: "alpha,bravo"`. +- `konghq.com/plugins`: If specified, the values of this annotation are used to + configure plugins on the associated ingress rules. Multiple plugins can be specified + by separating values with commas. Example: `konghq.com/plugins: "plugin1,plugin2"`. If you are reliant on any annotations not listed above, please open an issue. From 7452b69bfc1aa3e051030960de05d306f420c0db Mon Sep 17 00:00:00 2001 From: Mattia Lavacca Date: Fri, 20 Oct 2023 17:41:24 +0200 Subject: [PATCH 3/3] address review's comments Signed-off-by: Mattia Lavacca --- pkg/i2gw/providers/kong/converter_test.go | 4 ++-- pkg/i2gw/providers/kong/plugins.go | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/pkg/i2gw/providers/kong/converter_test.go b/pkg/i2gw/providers/kong/converter_test.go index fcc43670..c0436856 100644 --- a/pkg/i2gw/providers/kong/converter_test.go +++ b/pkg/i2gw/providers/kong/converter_test.go @@ -136,8 +136,8 @@ func Test_ToGateway(t *testing.T) { { Type: gatewayv1beta1.HTTPRouteFilterExtensionRef, ExtensionRef: &gatewayv1beta1.LocalObjectReference{ - Group: gatewayv1beta1.Group("configuration.konghq.com/v1"), - Kind: gatewayv1beta1.Kind("KongPlugin"), + Group: kongPluginGroup, + Kind: kongPluginKind, Name: gatewayv1beta1.ObjectName("plugin1"), }, }, diff --git a/pkg/i2gw/providers/kong/plugins.go b/pkg/i2gw/providers/kong/plugins.go index ad676d8b..d3c054e1 100644 --- a/pkg/i2gw/providers/kong/plugins.go +++ b/pkg/i2gw/providers/kong/plugins.go @@ -26,6 +26,11 @@ import ( gatewayv1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) +const ( + kongPluginGroup gatewayv1beta1.Group = "configuration.konghq.com/v1" + kongPluginKind gatewayv1beta1.Kind = "KongPlugin" +) + // pluginsFeature parses the Kong Ingress Controller plugins annotation and converts it // into HTTPRoutes rule's ExtensionRef filters. // It's possible to define a list of plugins to attach to the same HTTPRoute by setting @@ -61,8 +66,8 @@ func parsePluginsAnnotation(ingressNamespace, ingressName string, annotations ma filters = append(filters, gatewayv1beta1.HTTPRouteFilter{ Type: gatewayv1beta1.HTTPRouteFilterExtensionRef, ExtensionRef: &gatewayv1beta1.LocalObjectReference{ - Group: gatewayv1beta1.Group("configuration.konghq.com/v1"), - Kind: gatewayv1beta1.Kind("KongPlugin"), + Group: kongPluginGroup, + Kind: kongPluginKind, Name: gatewayv1beta1.ObjectName(v), }, })