@@ -4,6 +4,7 @@ package integration
44
55import (
66 "bytes"
7+ "encoding/json"
78 "fmt"
89 "io"
910 "net/http"
@@ -15,6 +16,7 @@ import (
1516 "github.com/kong/go-kong/kong"
1617 "github.com/kong/kubernetes-testing-framework/pkg/clusters"
1718 "github.com/kong/kubernetes-testing-framework/pkg/utils/kubernetes/generators"
19+ "github.com/samber/lo"
1820 "github.com/stretchr/testify/assert"
1921 "github.com/stretchr/testify/require"
2022 corev1 "k8s.io/api/core/v1"
@@ -25,12 +27,15 @@ import (
2527 configurationv1 "github.com/kong/kubernetes-configuration/v2/api/configuration/v1"
2628 "github.com/kong/kubernetes-configuration/v2/pkg/clientset"
2729
30+ "github.com/kong/kubernetes-ingress-controller/v3/internal/adminapi"
2831 "github.com/kong/kubernetes-ingress-controller/v3/internal/annotations"
2932 "github.com/kong/kubernetes-ingress-controller/v3/internal/gatewayapi"
3033 "github.com/kong/kubernetes-ingress-controller/v3/internal/labels"
34+ managercfg "github.com/kong/kubernetes-ingress-controller/v3/pkg/manager/config"
3135 "github.com/kong/kubernetes-ingress-controller/v3/test"
3236 "github.com/kong/kubernetes-ingress-controller/v3/test/consts"
3337 "github.com/kong/kubernetes-ingress-controller/v3/test/internal/helpers"
38+ "github.com/kong/kubernetes-ingress-controller/v3/test/internal/testenv"
3439)
3540
3641func TestPluginEssentials (t * testing.T ) {
@@ -182,7 +187,7 @@ func TestPluginConfigPatch(t *testing.T) {
182187 cleaner .Add (service )
183188
184189 t .Logf ("creating an ingress for service %s with ingress.class %s" , service .Name , consts .IngressClass )
185- ingress := generators .NewIngressForService ("/test_plugin_essentials " , map [string ]string {
190+ ingress := generators .NewIngressForService ("/test_plugin_config_patch " , map [string ]string {
186191 "konghq.com/strip-path" : "true" ,
187192 }, service )
188193 ingress .Spec .IngressClassName = kong .String (consts .IngressClass )
@@ -191,7 +196,7 @@ func TestPluginConfigPatch(t *testing.T) {
191196
192197 t .Log ("waiting for routes from Ingress to be operational" )
193198 assert .Eventually (t , func () bool {
194- resp , err := helpers .DefaultHTTPClient ().Get (fmt .Sprintf ("%s/test_plugin_essentials " , proxyHTTPURL ))
199+ resp , err := helpers .DefaultHTTPClient ().Get (fmt .Sprintf ("%s/test_plugin_config_patch " , proxyHTTPURL ))
195200 if err != nil {
196201 t .Logf ("WARNING: error while waiting for %s: %v" , proxyHTTPURL , err )
197202 return false
@@ -293,7 +298,7 @@ func TestPluginConfigPatch(t *testing.T) {
293298
294299 t .Logf ("validating that plugin %s was successfully configured" , kongplugin .Name )
295300 assert .Eventually (t , func () bool {
296- resp , err := helpers .DefaultHTTPClient ().Get (fmt .Sprintf ("%s/test_plugin_essentials " , proxyHTTPURL ))
301+ resp , err := helpers .DefaultHTTPClient ().Get (fmt .Sprintf ("%s/test_plugin_config_patch " , proxyHTTPURL ))
297302 if err != nil {
298303 t .Logf ("WARNING: error while waiting for %s: %v" , proxyHTTPURL , err )
299304 return false
@@ -316,7 +321,7 @@ func TestPluginConfigPatch(t *testing.T) {
316321
317322 t .Logf ("validating that clusterplugin %s was successfully configured" , kongclusterplugin .Name )
318323 assert .Eventually (t , func () bool {
319- resp , err := helpers .DefaultHTTPClient ().Get (fmt .Sprintf ("%s/test_plugin_essentials " , proxyHTTPURL ))
324+ resp , err := helpers .DefaultHTTPClient ().Get (fmt .Sprintf ("%s/test_plugin_config_patch " , proxyHTTPURL ))
320325 if err != nil {
321326 t .Logf ("WARNING: error while waiting for %s: %v" , proxyHTTPURL , err )
322327 return false
@@ -328,7 +333,7 @@ func TestPluginConfigPatch(t *testing.T) {
328333
329334 t .Log ("deleting Ingress and waiting for routes to be torn down" )
330335 require .NoError (t , clusters .DeleteIngress (ctx , env .Cluster (), ns .Name , ingress ))
331- helpers .EventuallyExpectHTTP404WithNoRoute (t , proxyHTTPURL , proxyHTTPURL .Host , "/test_plugin_essentials " , ingressWait , waitTick , nil )
336+ helpers .EventuallyExpectHTTP404WithNoRoute (t , proxyHTTPURL , proxyHTTPURL .Host , "/test_plugin_config_patch " , ingressWait , waitTick , nil )
332337}
333338
334339func TestPluginOrdering (t * testing.T ) {
@@ -712,3 +717,117 @@ func TestPluginCrossNamespaceReference(t *testing.T) {
712717 assert .True (c , resp .StatusCode == http .StatusTeapot )
713718 }, ingressWait , waitTick )
714719}
720+
721+ func TestPluginNullInConfig (t * testing.T ) {
722+ ctx := t .Context ()
723+
724+ t .Parallel ()
725+ ns , cleaner := helpers .Setup (ctx , t , env )
726+
727+ t .Log ("deploying a minimal HTTP container deployment to test Ingress routes" )
728+ container := generators .NewContainer ("httpbin" , test .HTTPBinImage , test .HTTPBinPort )
729+ deployment := generators .NewDeploymentForContainer (container )
730+ deployment , err := env .Cluster ().Client ().AppsV1 ().Deployments (ns .Name ).Create (ctx , deployment , metav1.CreateOptions {})
731+ require .NoError (t , err )
732+ cleaner .Add (deployment )
733+
734+ t .Logf ("exposing deployment %s via service" , deployment .Name )
735+ service := generators .NewServiceForDeployment (deployment , corev1 .ServiceTypeLoadBalancer )
736+ service , err = env .Cluster ().Client ().CoreV1 ().Services (ns .Name ).Create (ctx , service , metav1.CreateOptions {})
737+ require .NoError (t , err )
738+ cleaner .Add (service )
739+
740+ t .Logf ("creating an ingress for service %s with ingress.class %s" , service .Name , consts .IngressClass )
741+ ingress := generators .NewIngressForService ("/test_plugin_null_in_config" , map [string ]string {
742+ "konghq.com/strip-path" : "true" ,
743+ }, service )
744+ ingress .Spec .IngressClassName = kong .String (consts .IngressClass )
745+ ingress , err = env .Cluster ().Client ().NetworkingV1 ().Ingresses (ns .Name ).Create (ctx , ingress , metav1.CreateOptions {})
746+ require .NoError (t , err )
747+ cleaner .Add (ingress )
748+
749+ t .Log ("waiting for routes from Ingress to be operational" )
750+ assert .Eventually (t , func () bool {
751+ resp , err := helpers .DefaultHTTPClient ().Get (fmt .Sprintf ("%s/test_plugin_null_in_config" , proxyHTTPURL ))
752+ if err != nil {
753+ t .Logf ("WARNING: error while waiting for %s: %v" , proxyHTTPURL , err )
754+ return false
755+ }
756+ defer resp .Body .Close ()
757+ if resp .StatusCode == http .StatusOK {
758+ // now that the ingress backend is routable, make sure the contents we're getting back are what we expect
759+ // Expected: "<title>httpbin.org</title>"
760+ b := new (bytes.Buffer )
761+ n , err := b .ReadFrom (resp .Body )
762+ require .NoError (t , err )
763+ require .True (t , n > 0 )
764+ return strings .Contains (b .String (), "<title>httpbin.org</title>" )
765+ }
766+ return false
767+ }, ingressWait , waitTick )
768+
769+ t .Log ("Creating a plugin with `null` in its configuration" )
770+
771+ kongplugin := & configurationv1.KongPlugin {
772+ ObjectMeta : metav1.ObjectMeta {
773+ Namespace : ns .Name ,
774+ Name : "plugin-datadog" ,
775+ },
776+ InstanceName : "plugin-with-null" ,
777+ PluginName : "datadog" ,
778+ Config : apiextensionsv1.JSON {
779+ Raw : []byte (`{"host":"localhost","port":8125,"prefix":null}` ),
780+ },
781+ }
782+ c , err := clientset .NewForConfig (env .Cluster ().Config ())
783+ require .NoError (t , err )
784+ kongplugin , err = c .ConfigurationV1 ().KongPlugins (ns .Name ).Create (ctx , kongplugin , metav1.CreateOptions {})
785+ require .NoError (t , err )
786+ cleaner .Add (kongplugin )
787+
788+ t .Logf ("Updating Ingress to use plugin %s" , kongplugin .Name )
789+ require .Eventually (t , func () bool {
790+ ingress , err := env .Cluster ().Client ().NetworkingV1 ().Ingresses (ns .Name ).Get (ctx , ingress .Name , metav1.GetOptions {})
791+ if err != nil {
792+ return false
793+ }
794+ ingress .Annotations [annotations .AnnotationPrefix + annotations .PluginsKey ] = kongplugin .Name
795+ _ , err = env .Cluster ().Client ().NetworkingV1 ().Ingresses (ns .Name ).Update (ctx , ingress , metav1.UpdateOptions {})
796+ return err == nil
797+ }, ingressWait , waitTick )
798+
799+ t .Logf ("Checking the configuration of the plugin %s in Kong" , kongplugin .Name )
800+ kc , err := adminapi .NewKongAPIClient (proxyAdminURL .String (), managercfg.AdminAPIClientConfig {}, consts .KongTestPassword )
801+ require .NoError (t , err , "failed to create Kong client" )
802+ // For integration tests in enterprise edition and postgres DB backed Kong gateway,
803+ // the tests are run in "notdefault" workspace of Kong.
804+ if testenv .DBMode () != testenv .DBModeOff && testenv .KongEnterpriseEnabled () {
805+ kc .SetWorkspace (consts .KongTestWorkspace )
806+ }
807+ require .Eventually (t , func () bool {
808+ plugins , err := kc .Plugins .ListAll (ctx )
809+ require .NoError (t , err , "failed to list plugins" )
810+
811+ datadogPlugin , found := lo .Find (plugins , func (p * kong.Plugin ) bool {
812+ return p .Name != nil && * p .Name == "datadog"
813+ })
814+ if ! found {
815+ t .Logf ("datadog plugin not found. %d plugins found: %s" ,
816+ len (plugins ),
817+ strings .Join (
818+ lo .Map (plugins , func (p * kong.Plugin , _ int ) string {
819+ return lo .FromPtrOr (p .Name , "_" )
820+ }),
821+ "," ),
822+ )
823+ return false
824+ }
825+
826+ configJSON , err := json .Marshal (datadogPlugin .Config )
827+ require .NoError (t , err )
828+ t .Logf ("Configuration of datadog plugin: %s" , string (configJSON ))
829+
830+ configPrefix , ok := datadogPlugin .Config ["prefix" ]
831+ return ok && configPrefix == nil
832+ }, ingressWait , waitTick , "failed to find 'datadog' plugin with null in config.prefix in Kong" )
833+ }
0 commit comments