Repository: kubernetes-sigs/ingress2gateway
Pull request #72 (2023-10-11 15:00:36)

Kong plugins feature
**What type of PR is this?**
<!--
Add one of the following kinds:
/kind bug
/kind cleanup
/kind documentation
/kind feature
/kind test

Optionally add one or more of the following kinds if applicable:
/kind deprecation
/kind failing-test
/kind flake
/kind regression
-->

**What this PR does / why we need it**:

Kong plugins feature. The `konhq.com/plugins` ingress annotation is converted to a list of `ExtensionRef` filters.

**Which issue(s) this PR fixes**:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->
Fixes #

**Does this PR introduce a user-facing change?**:
<!--
If no, just write "NONE" in the release-note block below.
If yes, please enter a release note below:
-->
```release-note
[Kong Provider] Add support for converting the `konghq.com/plugins` ingress annotation to a list of `ExtensionRef` HTTPRoute filters.
```


k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/ingress2gateway/pull/72#" title="Author self-approved">mlavacca</a>*
**Once this PR has been reviewed and has the lgtm label**, please assign [robscott](https://github.com/robscott) for approval. For more information see [the Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fingress2gateway).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes-sigs/ingress2gateway/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["robscott"]} -->
k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/ingress2gateway/pull/72#pullrequestreview-1684680933" title="Approved">levikobi</a>*, *<a href="https://github.com/kubernetes-sigs/ingress2gateway/pull/72#" title="Author self-approved">mlavacca</a>*
**Once this PR has been reviewed and has the lgtm label**, please assign [robscott](https://github.com/robscott) for approval. For more information see [the Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fingress2gateway).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes-sigs/ingress2gateway/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["robscott"]} -->

Patch:
diff --git a/README.md b/README.md
index 488c322..6ffb121 100644
--- a/README.md
+++ b/README.md
@@ -16,15 +16,7 @@ translated to [Gateway API](https://gateway-api.sigs.k8s.io/) directly.
 
 Note: Ingress2gateway is not intended to copy annotations from Ingress to Gateway API.
 
-## Installation
-
-If you have a Go development environment locally, you can install ingress2gateway with `go install github.com/kubernetes-sigs/ingress2gateway@v0.1.0`
-
-This will put `ingress2gateway` binary in `$(go env GOPATH)/bin`
-
-Alternatively, you can download the binary at the [releases page](https://github.com/kubernetes-sigs/ingress2gateway/releases)
-
-### Build from Source
+## Build from Source
 
 1. Ensure that your system meets the following requirements:
 
diff --git a/pkg/i2gw/providers/kong/README.md b/pkg/i2gw/providers/kong/README.md
index 47b320b..16055d1 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.
diff --git a/pkg/i2gw/providers/kong/annotations.go b/pkg/i2gw/providers/kong/annotations.go
index 4186462..4d6c5cd 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 dd47c40..c683af2 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/plugins.go b/pkg/i2gw/providers/kong/plugins.go
new file mode 100644
index 0000000..d3c054e
--- /dev/null
+++ b/pkg/i2gw/providers/kong/plugins.go
@@ -0,0 +1,87 @@
+/*
+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"
+)
+
+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
+// 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: kongPluginGroup,
+						Kind:  kongPluginKind,
+						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...)
+	}
+}


Test patch:
diff --git a/pkg/i2gw/providers/kong/converter_test.go b/pkg/i2gw/providers/kong/converter_test.go
index 08f2c89..c043685 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: kongPluginGroup,
+											Kind:  kongPluginKind,
+											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 a353bc6..4f9e024 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 {