diff --git a/internal/dataplane/sendconfig/inmemory_schema.go b/internal/dataplane/sendconfig/inmemory_schema.go index 7687143574..28bfb3e236 100644 --- a/internal/dataplane/sendconfig/inmemory_schema.go +++ b/internal/dataplane/sendconfig/inmemory_schema.go @@ -28,55 +28,12 @@ func (DefaultContentToDBLessConfigConverter) Convert(content *file.Content) DBLe // DBLess schema does not support decK's Info section. dblessConfig.Content.Info = nil - // DBLess schema does not support nulls in plugin configs. - cleanUpNullsInPluginConfigs(&dblessConfig.Content) - // DBLess schema does not 1-1 match decK's schema for ConsumerGroups. convertConsumerGroups(&dblessConfig) return dblessConfig } -// cleanUpNullsInPluginConfigs removes null values from plugins' configs. -func cleanUpNullsInPluginConfigs(state *file.Content) { - for _, s := range state.Services { - for _, p := range s.Plugins { - for k, v := range p.Config { - if v == nil { - delete(p.Config, k) - } - } - } - for _, r := range state.Routes { - for _, p := range r.Plugins { - for k, v := range p.Config { - if v == nil { - delete(p.Config, k) - } - } - } - } - } - - for _, c := range state.Consumers { - for _, p := range c.Plugins { - for k, v := range p.Config { - if v == nil { - delete(p.Config, k) - } - } - } - } - - for _, p := range state.Plugins { - for k, v := range p.Config { - if v == nil { - delete(p.Config, k) - } - } - } -} - // convertConsumerGroups drops consumer groups related fields that are not supported in DBLess schema: // - Content.Consumers[].Groups, // - Content.ConsumerGroups[].Plugins diff --git a/internal/dataplane/sendconfig/inmemory_schema_test.go b/internal/dataplane/sendconfig/inmemory_schema_test.go index a5aabb9d4a..6d77b59109 100644 --- a/internal/dataplane/sendconfig/inmemory_schema_test.go +++ b/internal/dataplane/sendconfig/inmemory_schema_test.go @@ -279,6 +279,7 @@ func TestDefaultContentToDBLessConfigConverter(t *testing.T) { Plugin: kong.Plugin{ Name: kong.String("p1"), Config: kong.Configuration{ + "config1": nil, "config2": "value2", }, }, @@ -294,6 +295,7 @@ func TestDefaultContentToDBLessConfigConverter(t *testing.T) { Plugin: kong.Plugin{ Name: kong.String("p1"), Config: kong.Configuration{ + "config1": nil, "config2": "value2", }, }, @@ -311,6 +313,7 @@ func TestDefaultContentToDBLessConfigConverter(t *testing.T) { Plugin: kong.Plugin{ Name: kong.String("p1"), Config: kong.Configuration{ + "config1": nil, "config2": "value2", }, }, @@ -328,6 +331,7 @@ func TestDefaultContentToDBLessConfigConverter(t *testing.T) { Plugin: kong.Plugin{ Name: kong.String("p1"), Config: kong.Configuration{ + "config1": nil, "config2": "value2", }, }, diff --git a/test/integration/plugin_test.go b/test/integration/plugin_test.go index 356709b121..a138a9d133 100644 --- a/test/integration/plugin_test.go +++ b/test/integration/plugin_test.go @@ -5,6 +5,7 @@ package integration import ( "bytes" "context" + "encoding/json" "fmt" "net/http" "strings" @@ -13,6 +14,7 @@ import ( "github.com/kong/go-kong/kong" "github.com/kong/kubernetes-testing-framework/pkg/clusters" "github.com/kong/kubernetes-testing-framework/pkg/utils/kubernetes/generators" + "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" @@ -26,6 +28,7 @@ import ( "github.com/kong/kubernetes-ingress-controller/v2/test" "github.com/kong/kubernetes-ingress-controller/v2/test/consts" "github.com/kong/kubernetes-ingress-controller/v2/test/internal/helpers" + "github.com/kong/kubernetes-ingress-controller/v2/test/internal/testenv" ) func TestPluginEssentials(t *testing.T) { @@ -304,3 +307,139 @@ func TestPluginOrdering(t *testing.T) { require.NoError(t, clusters.DeleteIngress(ctx, env.Cluster(), ns.Name, ingress)) helpers.EventuallyExpectHTTP404WithNoRoute(t, proxyURL, "/test_plugin_ordering", ingressWait, waitTick, nil) } + +func TestPluginNullInConfig(t *testing.T) { + ctx := context.Background() + t.Parallel() + ns, cleaner := helpers.Setup(ctx, t, env) + + t.Log("deploying a minimal HTTP container deployment to test Ingress routes") + container := generators.NewContainer("httpbin", test.HTTPBinImage, test.HTTPBinPort) + deployment := generators.NewDeploymentForContainer(container) + deployment, err := env.Cluster().Client().AppsV1().Deployments(ns.Name).Create(ctx, deployment, metav1.CreateOptions{}) + require.NoError(t, err) + cleaner.Add(deployment) + + t.Logf("exposing deployment %s via service", deployment.Name) + service := generators.NewServiceForDeployment(deployment, corev1.ServiceTypeLoadBalancer) + service, err = env.Cluster().Client().CoreV1().Services(ns.Name).Create(ctx, service, metav1.CreateOptions{}) + require.NoError(t, err) + cleaner.Add(service) + + t.Logf("creating an ingress for service %s with ingress.class %s", service.Name, consts.IngressClass) + ingress := generators.NewIngressForService("/test_plugin_null_in_config", map[string]string{ + "konghq.com/strip-path": "true", + }, service) + ingress.Spec.IngressClassName = kong.String(consts.IngressClass) + ingress, err = env.Cluster().Client().NetworkingV1().Ingresses(ns.Name).Create(ctx, ingress, metav1.CreateOptions{}) + require.NoError(t, err) + cleaner.Add(ingress) + + t.Log("waiting for routes from Ingress to be operational") + assert.Eventually(t, func() bool { + resp, err := helpers.DefaultHTTPClient().Get(fmt.Sprintf("%s/test_plugin_null_in_config", proxyURL)) + if err != nil { + t.Logf("WARNING: error while waiting for %s: %v", proxyURL, err) + return false + } + defer resp.Body.Close() + if resp.StatusCode == http.StatusOK { + // now that the ingress backend is routable, make sure the contents we're getting back are what we expect + // Expected: "