Skip to content

Commit b6a9d79

Browse files
backport/2.0.x: Fix(translator): Remove the cleanup of nulls in plugin configuration in DBLess mode (#2475)
Co-authored-by: Tao Yi <tao.yi@konghq.com>
1 parent 853238e commit b6a9d79

File tree

4 files changed

+106
-43
lines changed

4 files changed

+106
-43
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@
4040

4141
- Fix `DataPlane`'s volumes and volume mounts patching when specified by user
4242
[#2425](https://github.com/Kong/kong-operator/pull/2425)
43+
- Do not cleanup `null`s in the configuration of plugins with Kong running in
44+
DBLess mode in the translator of ingress-controller. This enables user to use
45+
explicit `null`s in plugins.
46+
[#2459](https://github.com/Kong/kong-operator/pull/2459)
4347

4448
## [v2.0.4]
4549

ingress-controller/internal/dataplane/sendconfig/inmemory_schema.go

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -28,55 +28,12 @@ func (DefaultContentToDBLessConfigConverter) Convert(content *file.Content) DBLe
2828
// DBLess schema does not support decK's Info section.
2929
dblessConfig.Info = nil
3030

31-
// DBLess schema does not support nulls in plugin configs.
32-
cleanUpNullsInPluginConfigs(&dblessConfig.Content)
33-
3431
// DBLess schema does not 1-1 match decK's schema for ConsumerGroups.
3532
convertConsumerGroups(&dblessConfig)
3633

3734
return dblessConfig
3835
}
3936

40-
// cleanUpNullsInPluginConfigs removes null values from plugins' configs.
41-
func cleanUpNullsInPluginConfigs(state *file.Content) {
42-
for _, s := range state.Services {
43-
for _, p := range s.Plugins {
44-
for k, v := range p.Config {
45-
if v == nil {
46-
delete(p.Config, k)
47-
}
48-
}
49-
}
50-
for _, r := range state.Routes {
51-
for _, p := range r.Plugins {
52-
for k, v := range p.Config {
53-
if v == nil {
54-
delete(p.Config, k)
55-
}
56-
}
57-
}
58-
}
59-
}
60-
61-
for _, c := range state.Consumers {
62-
for _, p := range c.Plugins {
63-
for k, v := range p.Config {
64-
if v == nil {
65-
delete(p.Config, k)
66-
}
67-
}
68-
}
69-
}
70-
71-
for _, p := range state.Plugins {
72-
for k, v := range p.Config {
73-
if v == nil {
74-
delete(p.Config, k)
75-
}
76-
}
77-
}
78-
}
79-
8037
// convertConsumerGroups drops consumer groups related fields that are not supported in DBLess schema:
8138
// - Content.Consumers[].Groups,
8239
// - Content.ConsumerGroups[].Plugins

ingress-controller/internal/dataplane/sendconfig/inmemory_schema_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ func TestDefaultContentToDBLessConfigConverter(t *testing.T) {
279279
Plugin: kong.Plugin{
280280
Name: kong.String("p1"),
281281
Config: kong.Configuration{
282+
"config1": nil,
282283
"config2": "value2",
283284
},
284285
},
@@ -294,6 +295,7 @@ func TestDefaultContentToDBLessConfigConverter(t *testing.T) {
294295
Plugin: kong.Plugin{
295296
Name: kong.String("p1"),
296297
Config: kong.Configuration{
298+
"config1": nil,
297299
"config2": "value2",
298300
},
299301
},
@@ -311,6 +313,7 @@ func TestDefaultContentToDBLessConfigConverter(t *testing.T) {
311313
Plugin: kong.Plugin{
312314
Name: kong.String("p1"),
313315
Config: kong.Configuration{
316+
"config1": nil,
314317
"config2": "value2",
315318
},
316319
},
@@ -328,6 +331,7 @@ func TestDefaultContentToDBLessConfigConverter(t *testing.T) {
328331
Plugin: kong.Plugin{
329332
Name: kong.String("p1"),
330333
Config: kong.Configuration{
334+
"config1": nil,
331335
"config2": "value2",
332336
},
333337
},

ingress-controller/test/integration/plugin_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ import (
2525
configurationv1 "github.com/kong/kubernetes-configuration/v2/api/configuration/v1"
2626
"github.com/kong/kubernetes-configuration/v2/pkg/clientset"
2727

28+
"github.com/kong/kong-operator/ingress-controller/internal/adminapi"
2829
"github.com/kong/kong-operator/ingress-controller/internal/annotations"
2930
"github.com/kong/kong-operator/ingress-controller/internal/gatewayapi"
3031
"github.com/kong/kong-operator/ingress-controller/internal/labels"
32+
managercfg "github.com/kong/kong-operator/ingress-controller/pkg/manager/config"
3133
"github.com/kong/kong-operator/ingress-controller/test"
3234
"github.com/kong/kong-operator/ingress-controller/test/consts"
3335
"github.com/kong/kong-operator/ingress-controller/test/internal/helpers"
@@ -716,3 +718,99 @@ func TestPluginCrossNamespaceReference(t *testing.T) {
716718
assert.True(c, resp.StatusCode == http.StatusTeapot)
717719
}, ingressWait, waitTick)
718720
}
721+
722+
func TestPluginNullInConfig(t *testing.T) {
723+
ctx := t.Context()
724+
725+
t.Parallel()
726+
ns, cleaner := helpers.Setup(ctx, t, env)
727+
728+
t.Log("deploying a minimal HTTP container deployment to test Ingress routes")
729+
container := generators.NewContainer("httpbin", test.HTTPBinImage, test.HTTPBinPort)
730+
deployment := generators.NewDeploymentForContainer(container)
731+
deployment, err := env.Cluster().Client().AppsV1().Deployments(ns.Name).Create(ctx, deployment, metav1.CreateOptions{})
732+
require.NoError(t, err)
733+
cleaner.Add(deployment)
734+
735+
t.Logf("exposing deployment %s via service", deployment.Name)
736+
service := generators.NewServiceForDeployment(deployment, corev1.ServiceTypeLoadBalancer)
737+
service, err = env.Cluster().Client().CoreV1().Services(ns.Name).Create(ctx, service, metav1.CreateOptions{})
738+
require.NoError(t, err)
739+
cleaner.Add(service)
740+
741+
t.Logf("creating an ingress for service %s with ingress.class %s", service.Name, consts.IngressClass)
742+
ingress := generators.NewIngressForService("/test_plugin_essentials", map[string]string{
743+
"konghq.com/strip-path": "true",
744+
}, service)
745+
ingress.Spec.IngressClassName = kong.String(consts.IngressClass)
746+
ingress, err = env.Cluster().Client().NetworkingV1().Ingresses(ns.Name).Create(ctx, ingress, metav1.CreateOptions{})
747+
require.NoError(t, err)
748+
cleaner.Add(ingress)
749+
750+
t.Log("waiting for routes from Ingress to be operational")
751+
assert.Eventually(t, func() bool {
752+
resp, err := helpers.DefaultHTTPClient().Get(fmt.Sprintf("%s/test_plugin_essentials", proxyHTTPURL))
753+
if err != nil {
754+
t.Logf("WARNING: error while waiting for %s: %v", proxyHTTPURL, err)
755+
return false
756+
}
757+
defer resp.Body.Close()
758+
if resp.StatusCode == http.StatusOK {
759+
// now that the ingress backend is routable, make sure the contents we're getting back are what we expect
760+
// Expected: "<title>httpbin.org</title>"
761+
b := new(bytes.Buffer)
762+
n, err := b.ReadFrom(resp.Body)
763+
require.NoError(t, err)
764+
require.True(t, n > 0)
765+
return strings.Contains(b.String(), "<title>httpbin.org</title>")
766+
}
767+
return false
768+
}, ingressWait, waitTick)
769+
770+
t.Log("Creating a plugin with `null` in its configuration")
771+
772+
kongplugin := &configurationv1.KongPlugin{
773+
ObjectMeta: metav1.ObjectMeta{
774+
Namespace: ns.Name,
775+
Name: "plugin-datadog",
776+
},
777+
InstanceName: "plugin-with-null",
778+
PluginName: "datadog",
779+
Config: apiextensionsv1.JSON{
780+
Raw: []byte(`{"host":"localhost","port":8125,"prefix":null}`),
781+
},
782+
}
783+
c, err := clientset.NewForConfig(env.Cluster().Config())
784+
require.NoError(t, err)
785+
kongplugin, err = c.ConfigurationV1().KongPlugins(ns.Name).Create(ctx, kongplugin, metav1.CreateOptions{})
786+
require.NoError(t, err)
787+
cleaner.Add(kongplugin)
788+
789+
t.Logf("Updating Ingress to use plugin %s", kongplugin.Name)
790+
require.Eventually(t, func() bool {
791+
ingress, err := env.Cluster().Client().NetworkingV1().Ingresses(ns.Name).Get(ctx, ingress.Name, metav1.GetOptions{})
792+
if err != nil {
793+
return false
794+
}
795+
ingress.Annotations[annotations.AnnotationPrefix+annotations.PluginsKey] = kongplugin.Name
796+
_, err = env.Cluster().Client().NetworkingV1().Ingresses(ns.Name).Update(ctx, ingress, metav1.UpdateOptions{})
797+
return err == nil
798+
}, ingressWait, waitTick)
799+
800+
t.Logf("Checking the configuration of the plugin %s in Kong", kongplugin.Name)
801+
require.Eventually(t, func() bool {
802+
kc, err := adminapi.NewKongAPIClient(proxyAdminURL.String(), managercfg.AdminAPIClientConfig{}, consts.KongTestPassword)
803+
require.NoError(t, err, "failed to create Kong client")
804+
plugins, err := kc.Plugins.ListAll(ctx)
805+
require.NoError(t, err, "failed to list plugins")
806+
if len(plugins) != 1 {
807+
return false
808+
}
809+
plugin := plugins[0]
810+
if plugin.Name == nil || *plugin.Name != "datadog" {
811+
return false
812+
}
813+
configPrefix, ok := plugin.Config["prefix"]
814+
return ok && configPrefix == nil
815+
}, ingressWait, waitTick, "failed to find 'datadog' plugin with null in config.prefix in Kong")
816+
}

0 commit comments

Comments
 (0)