Skip to content

Commit 63ee945

Browse files
fix: process top-level plugins and routes (#1555)
* fix: process top-level plugins and routes * fix: enhance plugin processing with ordering and service checks * fix: clean up whitespace in processTopLevelEntities function --------- Co-authored-by: Prashansa Kulshrestha <[email protected]>
1 parent 1dd6c2e commit 63ee945

11 files changed

+341
-142
lines changed

kong2kic/global_plugin.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ func populateKICKongClusterPlugins(content *file.Content, file *KICContent) erro
2121
if plugin.ConsumerGroup != nil {
2222
continue
2323
}
24+
// skip this plugin instance if it is linked to a service or route
25+
if plugin.Service != nil || plugin.Route != nil {
26+
continue
27+
}
2428
var kongClusterPlugin configurationv1.KongClusterPlugin
2529
kongClusterPlugin.APIVersion = ConfigurationKongHQv1
2630
kongClusterPlugin.Kind = KongClusterPluginKind

kong2kic/kong2kic.go

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
package kong2kic
22

33
import (
4-
"crypto/sha256"
5-
"fmt"
6-
"strings"
7-
8-
"github.com/gosimple/slug"
94
"github.com/kong/go-database-reconciler/pkg/file"
105
)
116

@@ -18,6 +13,7 @@ var targetKICVersionAPI = KICV3GATEWAY
1813

1914
func MarshalKongToKIC(content *file.Content, builderType string, format string) ([]byte, error) {
2015
targetKICVersionAPI = builderType
16+
processTopLevelEntities(content)
2117
kicContent := convertKongToKIC(content, builderType)
2218
return kicContent.marshalKICContentToFormat(format)
2319
}
@@ -27,27 +23,3 @@ func convertKongToKIC(content *file.Content, builderType string) *KICContent {
2723
director := newDirector(builder)
2824
return director.buildManifests(content)
2925
}
30-
31-
// utility function to make sure that objectmeta.name is always
32-
// compatible with kubernetes naming conventions.
33-
func calculateSlug(input string) string {
34-
// Use the slug library to create a slug
35-
slugStr := slug.Make(input)
36-
37-
// Replace underscores with dashes
38-
slugStr = strings.ReplaceAll(slugStr, "_", "-")
39-
40-
// If the resulting string has more than 63 characters
41-
if len(slugStr) > 63 {
42-
// Calculate the sha256 sum of the string
43-
hash := sha256.Sum256([]byte(slugStr))
44-
45-
// Truncate the slug to 53 characters
46-
slugStr = slugStr[:53]
47-
48-
// Replace the last 10 characters with the first 10 characters of the sha256 sum
49-
slugStr = slugStr[:len(slugStr)-10] + fmt.Sprintf("%x", hash)[:10]
50-
}
51-
52-
return slugStr
53-
}

kong2kic/route.go

Lines changed: 92 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -190,73 +190,16 @@ func addPluginsToRoute(
190190
ingress *k8snetv1.Ingress,
191191
kicContent *KICContent,
192192
) error {
193+
ownerName := *service.Name + "-" + *route.Name
193194
for _, plugin := range route.Plugins {
194195
if plugin.Name == nil {
195196
log.Println("Plugin name is empty. Please provide a name for the plugin.")
196197
continue
197198
}
198-
pluginName := *plugin.Name
199-
kongPlugin := configurationv1.KongPlugin{
200-
TypeMeta: metav1.TypeMeta{
201-
APIVersion: ConfigurationKongHQv1,
202-
Kind: KongPluginKind,
203-
},
204-
ObjectMeta: metav1.ObjectMeta{
205-
Name: calculateSlug(*service.Name + "-" + *route.Name + "-" + pluginName),
206-
Annotations: map[string]string{IngressClass: ClassName},
207-
},
208-
PluginName: pluginName,
209-
}
210199

211-
// Populate plugin fields
212-
if plugin.Enabled != nil {
213-
kongPlugin.Disabled = !*plugin.Enabled
214-
}
215-
if plugin.RunOn != nil {
216-
kongPlugin.RunOn = *plugin.RunOn
217-
}
218-
if plugin.Ordering != nil {
219-
kongPlugin.Ordering = &kong.PluginOrdering{
220-
Before: plugin.Ordering.Before,
221-
After: plugin.Ordering.After,
222-
}
223-
}
224-
if plugin.Protocols != nil {
225-
var protocols []string
226-
for _, protocol := range plugin.Protocols {
227-
if protocol != nil {
228-
protocols = append(protocols, *protocol)
229-
}
230-
}
231-
kongPlugin.Protocols = configurationv1.StringsToKongProtocols(protocols)
232-
}
233-
if plugin.Tags != nil {
234-
var tags []string
235-
for _, tag := range plugin.Tags {
236-
if tag != nil {
237-
tags = append(tags, *tag)
238-
}
239-
}
240-
kongPlugin.ObjectMeta.Annotations[KongHQTags] = strings.Join(tags, ",")
241-
}
242-
243-
configJSON, err := json.Marshal(plugin.Config)
244-
if err != nil {
200+
if err := processPlugin(plugin, ownerName, ingress.ObjectMeta.Annotations, kicContent); err != nil {
245201
return err
246202
}
247-
kongPlugin.Config = apiextensionsv1.JSON{
248-
Raw: configJSON,
249-
}
250-
251-
// Add plugin reference to ingress annotations
252-
pluginsAnnotation := ingress.ObjectMeta.Annotations[KongHQPlugins]
253-
if pluginsAnnotation == "" {
254-
ingress.ObjectMeta.Annotations[KongHQPlugins] = kongPlugin.ObjectMeta.Name
255-
} else {
256-
ingress.ObjectMeta.Annotations[KongHQPlugins] = pluginsAnnotation + "," + kongPlugin.ObjectMeta.Name
257-
}
258-
259-
kicContent.KongPlugins = append(kicContent.KongPlugins, kongPlugin)
260203
}
261204
return nil
262205
}
@@ -518,43 +461,103 @@ func addBackendRefs(httpRoute *k8sgwapiv1.HTTPRoute, service file.FService, rout
518461
}
519462

520463
func addPluginsToGatewayAPIRoute(
521-
service file.FService, route *file.FRoute, httpRoute k8sgwapiv1.HTTPRoute, kicContent *KICContent,
464+
service file.FService,
465+
route *file.FRoute,
466+
httpRoute k8sgwapiv1.HTTPRoute,
467+
kicContent *KICContent,
522468
) error {
469+
// Process route-level plugins
523470
for _, plugin := range route.Plugins {
524-
var kongPlugin configurationv1.KongPlugin
525-
kongPlugin.APIVersion = ConfigurationKongHQv1
526-
kongPlugin.Kind = KongPluginKind
527-
if plugin.Name != nil && route.Name != nil && service.Name != nil {
528-
kongPlugin.ObjectMeta.Name = calculateSlug(*service.Name + "-" + *route.Name + "-" + *plugin.Name)
529-
} else {
530-
log.Println("Service name, route name or plugin name is empty. This is not recommended." +
531-
"Please, provide a name for the service, route and the plugin before generating Kong Ingress Controller manifests.")
532-
continue
471+
if err := processGatewayAPIPlugin(plugin, service, route, &httpRoute, kicContent); err != nil {
472+
return err
533473
}
534-
kongPlugin.ObjectMeta.Annotations = map[string]string{IngressClass: ClassName}
535-
kongPlugin.PluginName = *plugin.Name
474+
}
475+
return nil
476+
}
536477

537-
var configJSON apiextensionsv1.JSON
538-
var err error
539-
configJSON.Raw, err = json.Marshal(plugin.Config)
540-
if err != nil {
541-
return err
478+
// processGatewayAPIPlugin handles creating and configuring a KongPlugin for Gateway API routes
479+
func processGatewayAPIPlugin(
480+
plugin *file.FPlugin,
481+
service file.FService,
482+
route *file.FRoute,
483+
httpRoute *k8sgwapiv1.HTTPRoute,
484+
kicContent *KICContent,
485+
) error {
486+
// Skip plugins without a name
487+
if plugin.Name == nil || route.Name == nil || service.Name == nil {
488+
log.Println("Service name, route name or plugin name is empty. This is not recommended. " +
489+
"Please provide a name for the service, route and the plugin before generating Kong Ingress Controller manifests.")
490+
return nil
491+
}
492+
493+
// Create the KongPlugin
494+
kongPlugin := configurationv1.KongPlugin{
495+
TypeMeta: metav1.TypeMeta{
496+
APIVersion: ConfigurationKongHQv1,
497+
Kind: KongPluginKind,
498+
},
499+
ObjectMeta: metav1.ObjectMeta{
500+
Name: calculateSlug(*service.Name + "-" + *route.Name + "-" + *plugin.Name),
501+
Annotations: map[string]string{IngressClass: ClassName},
502+
},
503+
PluginName: *plugin.Name,
504+
}
505+
506+
// Set plugin configuration
507+
configJSON, err := json.Marshal(plugin.Config)
508+
if err != nil {
509+
return err
510+
}
511+
kongPlugin.Config = apiextensionsv1.JSON{Raw: configJSON}
512+
513+
// Populate additional plugin fields if they exist
514+
if plugin.Enabled != nil {
515+
kongPlugin.Disabled = !*plugin.Enabled
516+
}
517+
if plugin.RunOn != nil {
518+
kongPlugin.RunOn = *plugin.RunOn
519+
}
520+
if plugin.Ordering != nil {
521+
kongPlugin.Ordering = &kong.PluginOrdering{
522+
Before: plugin.Ordering.Before,
523+
After: plugin.Ordering.After,
542524
}
543-
kongPlugin.Config = configJSON
544-
545-
// add plugins as extensionRef under filters for every rule
546-
for i := range httpRoute.Spec.Rules {
547-
httpRoute.Spec.Rules[i].Filters = append(httpRoute.Spec.Rules[i].Filters, k8sgwapiv1.HTTPRouteFilter{
548-
ExtensionRef: &k8sgwapiv1.LocalObjectReference{
549-
Name: k8sgwapiv1.ObjectName(kongPlugin.ObjectMeta.Name),
550-
Kind: KongPluginKind,
551-
Group: ConfigurationKongHQ,
552-
},
553-
Type: k8sgwapiv1.HTTPRouteFilterExtensionRef,
554-
})
525+
}
526+
if plugin.Protocols != nil {
527+
var protocols []string
528+
for _, protocol := range plugin.Protocols {
529+
if protocol != nil {
530+
protocols = append(protocols, *protocol)
531+
}
532+
}
533+
kongPlugin.Protocols = configurationv1.StringsToKongProtocols(protocols)
534+
}
535+
if plugin.Tags != nil {
536+
var tags []string
537+
for _, tag := range plugin.Tags {
538+
if tag != nil {
539+
tags = append(tags, *tag)
540+
}
555541
}
542+
if kongPlugin.ObjectMeta.Annotations == nil {
543+
kongPlugin.ObjectMeta.Annotations = make(map[string]string)
544+
}
545+
kongPlugin.ObjectMeta.Annotations[KongHQTags] = strings.Join(tags, ",")
546+
}
556547

557-
kicContent.KongPlugins = append(kicContent.KongPlugins, kongPlugin)
548+
// Add plugins as extensionRef under filters for every rule
549+
for i := range httpRoute.Spec.Rules {
550+
httpRoute.Spec.Rules[i].Filters = append(httpRoute.Spec.Rules[i].Filters, k8sgwapiv1.HTTPRouteFilter{
551+
ExtensionRef: &k8sgwapiv1.LocalObjectReference{
552+
Name: k8sgwapiv1.ObjectName(kongPlugin.ObjectMeta.Name),
553+
Kind: KongPluginKind,
554+
Group: ConfigurationKongHQ,
555+
},
556+
Type: k8sgwapiv1.HTTPRouteFilterExtensionRef,
557+
})
558558
}
559+
560+
// Add the KongPlugin to the content
561+
kicContent.KongPlugins = append(kicContent.KongPlugins, kongPlugin)
559562
return nil
560563
}

kong2kic/service.go

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -132,28 +132,48 @@ func addAnnotationsFromService(service *file.FService, annotations map[string]st
132132
addTagsToAnnotations(service.Tags, annotations)
133133
}
134134

135-
// Helper function to add plugins to a service
135+
// processPlugin is a helper function that processes a single plugin for a service
136+
func processPlugin(
137+
plugin *file.FPlugin,
138+
ownerName string,
139+
annotations map[string]string,
140+
kicContent *KICContent,
141+
) error {
142+
if plugin.Name == nil {
143+
log.Println("Plugin name is empty. Please provide a name for the plugin.")
144+
return nil
145+
}
146+
147+
// Create a KongPlugin
148+
kongPlugin, err := createKongPlugin(plugin, ownerName)
149+
if err != nil {
150+
return err
151+
}
152+
if kongPlugin == nil {
153+
return nil
154+
}
155+
156+
// Add the plugin name to the service annotations
157+
addPluginToAnnotations(kongPlugin.ObjectMeta.Name, annotations)
158+
159+
// Append the KongPlugin to KIC content
160+
kicContent.KongPlugins = append(kicContent.KongPlugins, *kongPlugin)
161+
return nil
162+
}
163+
164+
// addPluginsToService adds plugins from both service-level and top-level plugin configurations
136165
func addPluginsToService(service *file.FService, k8sService *k8scorev1.Service, kicContent *KICContent) error {
137-
for _, plugin := range service.Plugins {
138-
if plugin.Name == nil || service.Name == nil {
139-
log.Println("Service name or plugin name is empty. Please provide names for both.")
140-
continue
141-
}
166+
if service.Name == nil {
167+
log.Println("Service name is empty. Please provide a name for the service.")
168+
return nil
169+
}
170+
ownerName := *service.Name
142171

143-
// Create a KongPlugin
144-
kongPlugin, err := createKongPlugin(plugin, *service.Name)
145-
if err != nil {
172+
// Process service-level plugins
173+
for _, plugin := range service.Plugins {
174+
if err := processPlugin(plugin, ownerName, k8sService.ObjectMeta.Annotations, kicContent); err != nil {
146175
return err
147176
}
148-
if kongPlugin == nil {
149-
continue
150-
}
151-
152-
// Add the plugin name to the service annotations
153-
addPluginToAnnotations(kongPlugin.ObjectMeta.Name, k8sService.ObjectMeta.Annotations)
154-
155-
// Append the KongPlugin to KIC content
156-
kicContent.KongPlugins = append(kicContent.KongPlugins, *kongPlugin)
157177
}
158178
return nil
159179
}

kong2kic/testdata/route-gw-v2-output-expected.yaml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
11
apiVersion: configuration.konghq.com/v1
2+
config:
3+
limit_by: consumer
4+
minute: 5
5+
policy: local
6+
kind: KongPlugin
7+
metadata:
8+
annotations:
9+
kubernetes.io/ingress.class: kong
10+
name: simple-service-simple-route-rate-limiting
11+
plugin: rate-limiting
12+
---
13+
apiVersion: configuration.konghq.com/v1
214
config:
315
aws_key: my_key
416
aws_region: us-west-2
@@ -22,6 +34,12 @@ spec:
2234
- backendRefs:
2335
- name: simple-service
2436
port: 8080
37+
filters:
38+
- extensionRef:
39+
group: configuration.konghq.com
40+
kind: KongPlugin
41+
name: simple-service-simple-route-rate-limiting
42+
type: ExtensionRef
2543
matches:
2644
- path:
2745
type: PathPrefix
@@ -173,6 +191,22 @@ spec:
173191
type: PathPrefix
174192
value: /v1/yet-another-example
175193
---
194+
apiVersion: gateway.networking.k8s.io/v1beta1
195+
kind: HTTPRoute
196+
metadata:
197+
name: simple-service-top-route
198+
spec:
199+
parentRefs:
200+
- name: kong
201+
rules:
202+
- backendRefs:
203+
- name: simple-service
204+
port: 8080
205+
matches:
206+
- path:
207+
type: PathPrefix
208+
value: /tr1
209+
---
176210
apiVersion: v1
177211
kind: Service
178212
metadata:

0 commit comments

Comments
 (0)