From 1871546623b72df578609652d74da6d19263e05b Mon Sep 17 00:00:00 2001 From: Mattia Lavacca Date: Tue, 16 Apr 2024 17:07:16 +0200 Subject: [PATCH] test: added integration test on HTTPS Signed-off-by: Mattia Lavacca --- pkg/utils/test/generators.go | 14 ++ test/integration/test_httproute.go | 206 +++++++++++++++++- .../zz_generated_registered_testcases.go | 3 +- 3 files changed, 221 insertions(+), 2 deletions(-) diff --git a/pkg/utils/test/generators.go b/pkg/utils/test/generators.go index 9431cca37..c9216d905 100644 --- a/pkg/utils/test/generators.go +++ b/pkg/utils/test/generators.go @@ -1,6 +1,8 @@ package test import ( + "testing" + "github.com/google/uuid" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -121,3 +123,15 @@ func GenerateGatewayConfiguration(gatewayConfigurationNSN types.NamespacedName) }, } } + +func GenerateTLSSecret(t *testing.T, namespace string, ca helpers.Cert) *corev1.Secret { + cert := helpers.CreateCert(t, "*", ca.Cert, ca.Key) + + return &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: uuid.NewString(), + Namespace: namespace, + }, + Data: helpers.TLSSecretData(t, ca, cert), + } +} diff --git a/test/integration/test_httproute.go b/test/integration/test_httproute.go index 6dfb6b614..bb2eb862c 100644 --- a/test/integration/test_httproute.go +++ b/test/integration/test_httproute.go @@ -1,12 +1,15 @@ package integration import ( + "crypto/tls" + "crypto/x509" "net/http" "testing" "time" "github.com/google/uuid" "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" @@ -21,7 +24,7 @@ import ( "github.com/kong/gateway-operator/test/helpers" ) -func TestHTTPRouteV1Beta1(t *testing.T) { +func TestHTTPRoute(t *testing.T) { t.Parallel() namespace, cleaner := helpers.SetupTestEnv(t, GetCtx(), GetEnv()) @@ -190,3 +193,204 @@ func TestHTTPRouteV1Beta1(t *testing.T) { httpRouteAccessTimeout, time.Second, ) } + +func TestHTTPRouteWithTLS(t *testing.T) { + t.Parallel() + namespace, cleaner := helpers.SetupTestEnv(t, GetCtx(), GetEnv()) + + t.Log("deploying a GatewayConfiguration to set KIC log level") + gatewayConfig := &operatorv1beta1.GatewayConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace.Name, + Name: uuid.NewString(), + }, + Spec: operatorv1beta1.GatewayConfigurationSpec{ + ControlPlaneOptions: &operatorv1beta1.ControlPlaneOptions{ + Deployment: operatorv1beta1.ControlPlaneDeploymentOptions{ + PodTemplateSpec: &corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: consts.ControlPlaneControllerContainerName, + Image: consts.DefaultControlPlaneImage, + Env: []corev1.EnvVar{ + { + Name: "CONTROLLER_LOG_LEVEL", + Value: "trace", + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + t.Logf("deploying GatewayConfiguration %s/%s to set KIC log level", gatewayConfig.Namespace, gatewayConfig.Name) + gatewayConfig, err := GetClients().OperatorClient.ApisV1beta1().GatewayConfigurations(namespace.Name).Create(GetCtx(), gatewayConfig, metav1.CreateOptions{}) + require.NoError(t, err) + cleaner.Add(gatewayConfig) + + gatewayClass := &gatewayv1.GatewayClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: uuid.NewString(), + }, + Spec: gatewayv1.GatewayClassSpec{ + ParametersRef: &gatewayv1.ParametersReference{ + Group: gatewayv1.Group(operatorv1beta1.SchemeGroupVersion.Group), + Kind: gatewayv1.Kind("GatewayConfiguration"), + Namespace: (*gatewayv1.Namespace)(&gatewayConfig.Namespace), + Name: gatewayConfig.Name, + }, + ControllerName: gatewayv1.GatewayController(vars.ControllerName()), + }, + } + t.Logf("deploying GatewayClass %s", gatewayClass.Name) + gatewayClass, err = GetClients().GatewayClient.GatewayV1().GatewayClasses().Create(GetCtx(), gatewayClass, metav1.CreateOptions{}) + require.NoError(t, err) + cleaner.Add(gatewayClass) + + gatewayNSN := types.NamespacedName{ + Name: uuid.NewString(), + Namespace: namespace.Name, + } + + ca := helpers.CreateCA(t) + secret := testutils.GenerateTLSSecret(t, namespace.Name, ca) + t.Logf("deploying Secret %s/%s", secret.Namespace, secret.Name) + secret, err = GetClients().K8sClient.CoreV1().Secrets(namespace.Name).Create(GetCtx(), secret, metav1.CreateOptions{}) + require.NoError(t, err) + + certPool := x509.NewCertPool() + certPool.AppendCertsFromPEM(ca.CertPEM.Bytes()) + httpsClient := http.Client{ + Timeout: time.Second * 10, + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: false, + RootCAs: certPool, + }, + }, + } + + gateway := testutils.GenerateGateway(gatewayNSN, gatewayClass, func(gateway *gatewayv1.Gateway) { + gateway.Spec.Listeners[0].Protocol = gatewayv1.HTTPSProtocolType + gateway.Spec.Listeners[0].Port = gatewayv1.PortNumber(443) + gateway.Spec.Listeners[0].TLS = &gatewayv1.GatewayTLSConfig{ + CertificateRefs: []gatewayv1.SecretObjectReference{ + { + Name: gatewayv1.ObjectName(secret.Name), + Namespace: lo.ToPtr(gatewayv1.Namespace(secret.Namespace)), + }, + }, + } + }) + + t.Logf("deploying Gateway %s/%s", gateway.Namespace, gateway.Name) + gateway, err = GetClients().GatewayClient.GatewayV1().Gateways(namespace.Name).Create(GetCtx(), gateway, metav1.CreateOptions{}) + require.NoError(t, err) + cleaner.Add(gateway) + + t.Logf("verifying Gateway %s/%s gets marked as Scheduled", gateway.Namespace, gateway.Name) + require.Eventually(t, testutils.GatewayIsScheduled(t, GetCtx(), gatewayNSN, clients), testutils.GatewaySchedulingTimeLimit, time.Second) + + t.Logf("verifying Gateway %s/%s gets marked as Programmed", gateway.Namespace, gateway.Name) + require.Eventually(t, testutils.GatewayIsProgrammed(t, GetCtx(), gatewayNSN, clients), testutils.GatewayReadyTimeLimit, time.Second) + t.Logf("verifying Gateway %s/%s Listeners get marked as Programmed", gateway.Namespace, gateway.Name) + require.Eventually(t, testutils.GatewayListenersAreProgrammed(t, GetCtx(), gatewayNSN, clients), testutils.GatewayReadyTimeLimit, time.Second) + + t.Logf("verifying Gateway %s/%s gets an IP address", gateway.Namespace, gateway.Name) + require.Eventually(t, testutils.GatewayIPAddressExist(t, GetCtx(), gatewayNSN, clients), testutils.SubresourceReadinessWait, time.Second) + gateway = testutils.MustGetGateway(t, GetCtx(), gatewayNSN, clients) + gatewayIPAddress := gateway.Status.Addresses[0].Value + + t.Log("deploying backend deployment (httpbin) of HTTPRoute") + container := generators.NewContainer("httpbin", testutils.HTTPBinImage, 80) + deployment := generators.NewDeploymentForContainer(container) + deployment, err = GetEnv().Cluster().Client().AppsV1().Deployments(namespace.Name).Create(GetCtx(), deployment, metav1.CreateOptions{}) + require.NoError(t, err) + + t.Logf("exposing deployment %s via service", deployment.Name) + service := generators.NewServiceForDeployment(deployment, corev1.ServiceTypeClusterIP) + _, err = GetEnv().Cluster().Client().CoreV1().Services(namespace.Name).Create(GetCtx(), service, metav1.CreateOptions{}) + require.NoError(t, err) + + httpPort := gatewayv1.PortNumber(80) + pathMatchPrefix := gatewayv1.PathMatchPathPrefix + kindService := gatewayv1.Kind("Service") + pathPrefix := "/prefix-test-http-route" + httpRoute := &gatewayv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace.Name, + Name: uuid.NewString(), + Annotations: map[string]string{ + "konghq.com/strip-path": "true", + }, + }, + Spec: gatewayv1.HTTPRouteSpec{ + CommonRouteSpec: gatewayv1.CommonRouteSpec{ + ParentRefs: []gatewayv1.ParentReference{{ + Name: gatewayv1.ObjectName(gateway.Name), + }}, + }, + Rules: []gatewayv1.HTTPRouteRule{ + { + Matches: []gatewayv1.HTTPRouteMatch{ + { + Path: &gatewayv1.HTTPPathMatch{ + Type: &pathMatchPrefix, + Value: &pathPrefix, + }, + }, + }, + BackendRefs: []gatewayv1.HTTPBackendRef{ + { + BackendRef: gatewayv1.BackendRef{ + BackendObjectReference: gatewayv1.BackendObjectReference{ + Name: gatewayv1.ObjectName(service.Name), + Port: &httpPort, + Kind: &kindService, + }, + }, + }, + }, + }, + }, + }, + } + + t.Logf("creating httproute %s/%s to access deployment %s via kong", httpRoute.Namespace, httpRoute.Name, deployment.Name) + require.EventuallyWithT(t, + func(c *assert.CollectT) { + result, err := GetClients().GatewayClient.GatewayV1().HTTPRoutes(namespace.Name).Create(GetCtx(), httpRoute, metav1.CreateOptions{}) + if err != nil { + t.Logf("failed to deploy httproute: %v", err) + c.Errorf("failed to deploy httproute: %v", err) + return + } + cleaner.Add(result) + }, + testutils.DefaultIngressWait, testutils.WaitIngressTick, + ) + + t.Log("verifying connectivity to the HTTPRoute") + const ( + httpRouteAccessTimeout = 3 * time.Minute + waitTick = time.Second + ) + + require.Eventually( + t, testutils.GetResponseBodyContains( + t, GetCtx(), clients, httpsClient, "https://"+gatewayIPAddress+"/prefix-test-http-route", http.MethodGet, "httpbin.org", + ), + httpRouteAccessTimeout, time.Second, + ) + // will route to path /1234 of service httpbin, but httpbin will return a 404 page on this path. + require.Eventually( + t, testutils.GetResponseBodyContains( + t, GetCtx(), clients, httpsClient, "https://"+gatewayIPAddress+"/prefix-test-http-route/1234", http.MethodGet, "

Not Found

", + ), + httpRouteAccessTimeout, time.Second, + ) +} diff --git a/test/integration/zz_generated_registered_testcases.go b/test/integration/zz_generated_registered_testcases.go index e4879b251..bb8913f2b 100644 --- a/test/integration/zz_generated_registered_testcases.go +++ b/test/integration/zz_generated_registered_testcases.go @@ -23,7 +23,8 @@ func init() { TestGatewayMultiple, TestGatewayProvisionDataPlaneFail, TestGatewayWithMultipleListeners, - TestHTTPRouteV1Beta1, + TestHTTPRoute, + TestHTTPRouteWithTLS, TestIngressEssentials, TestManualGatewayUpgradesAndDowngrades, TestScalingDataPlaneThroughGatewayConfiguration,