From 90025bee167e273be66bcf64e45c7655b2aaf57b Mon Sep 17 00:00:00 2001 From: Jakub Warczarek Date: Tue, 9 Jul 2024 22:32:30 +0200 Subject: [PATCH] chore(tests): do not use InsecureSkipVerify for HTTPS tests (#6307) --- .golangci.yaml | 2 +- go.mod | 2 +- test/e2e/all_in_one_test.go | 18 +++-- test/e2e/features_test.go | 50 ++++++------ test/e2e/konnect_test.go | 16 ++-- test/helpers/ports_test.go | 4 +- test/integration/consumer_group_test.go | 8 +- test/integration/consumer_test.go | 2 +- test/integration/gateway_test.go | 12 +-- test/integration/httproute_test.go | 38 +++++----- test/integration/ingress_https_test.go | 21 ++--- test/integration/ingress_regex_match_test.go | 3 +- test/integration/ingress_test.go | 24 +++--- .../isolated/custom_entity_test.go | 6 +- .../examples_httproute_rewrite_test.go | 4 +- .../isolated/examples_httproute_test.go | 7 ++ test/integration/isolated/ingress_test.go | 1 + test/integration/plugin_test.go | 8 +- test/internal/helpers/http.go | 76 +++++++++++++++---- .../kongintegration/expression_router_test.go | 4 +- 20 files changed, 183 insertions(+), 123 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 2ee2c6332c..7707e805a7 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -171,7 +171,7 @@ issues: - path: _test\.go linters: - gosec - text: "TLS InsecureSkipVerify set true|Potential hardcoded credentials" + text: "Potential hardcoded credentials" # Ignore prealloc in tests - path: _test\.go linters: diff --git a/go.mod b/go.mod index 60b7dc5770..ce2c02be08 100644 --- a/go.mod +++ b/go.mod @@ -128,7 +128,7 @@ require ( github.com/googleapis/gax-go/v2 v2.12.5 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-memdb v1.3.4 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 diff --git a/test/e2e/all_in_one_test.go b/test/e2e/all_in_one_test.go index 864f1ea774..fc7e4b197b 100644 --- a/test/e2e/all_in_one_test.go +++ b/test/e2e/all_in_one_test.go @@ -12,6 +12,7 @@ import ( "testing" "time" + "github.com/hashicorp/go-cleanhttp" "github.com/kong/kubernetes-testing-framework/pkg/clusters/addons/kong" "github.com/kong/kubernetes-testing-framework/pkg/environments" "github.com/stretchr/testify/require" @@ -334,20 +335,21 @@ func ensureAllProxyReplicasAreConfigured(ctx context.Context, t *testing.T, env require.NoError(t, err) t.Logf("ensuring all %d proxy replicas are configured", len(pods)) + client := cleanhttp.DefaultClient() + tr := cleanhttp.DefaultTransport() + // Anything related to TLS can be ignored, because only availability is being tested here. + // Testing communicating over TLS is done as part of actual E2E test. + tr.TLSClientConfig = &tls.Config{ + InsecureSkipVerify: true, //nolint:gosec + } + client.Transport = tr + wg := sync.WaitGroup{} for _, pod := range pods { pod := pod wg.Add(1) go func() { defer wg.Done() - client := &http.Client{ - Timeout: time.Second * 30, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - }, - }, - } forwardCtx, cancel := context.WithCancel(ctx) defer cancel() diff --git a/test/e2e/features_test.go b/test/e2e/features_test.go index 106d66eb66..f6699016bd 100644 --- a/test/e2e/features_test.go +++ b/test/e2e/features_test.go @@ -6,6 +6,7 @@ import ( "bytes" "context" "crypto/tls" + "crypto/x509" "fmt" "net/http" "strings" @@ -19,6 +20,7 @@ import ( "github.com/kong/kubernetes-testing-framework/pkg/clusters/types/kind" "github.com/kong/kubernetes-testing-framework/pkg/environments" "github.com/kong/kubernetes-testing-framework/pkg/utils/kubernetes/generators" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" @@ -118,10 +120,13 @@ func TestWebhookUpdate(t *testing.T) { t.Log("deploying kong components") ManifestDeploy{Path: dblessPath}.Run(ctx, t, env) - const firstCertificateCommonName = "first.example" + certPool := x509.NewCertPool() + const firstCertificateHostName = "first.example" firstCertificateCrt, firstCertificateKey := certificate.MustGenerateSelfSignedCertPEMFormat( - certificate.WithCommonName(firstCertificateCommonName), + certificate.WithCommonName(firstCertificateHostName), + certificate.WithDNSNames(firstCertificateHostName), ) + require.True(t, certPool.AppendCertsFromPEM(firstCertificateCrt)) firstCertificate := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "admission-cert", @@ -133,10 +138,12 @@ func TestWebhookUpdate(t *testing.T) { }, } - const secondCertificateCommonName = "second.example" + const secondCertificateHostName = "second.example" secondCertificateCrt, secondCertificateKey := certificate.MustGenerateSelfSignedCertPEMFormat( - certificate.WithCommonName(secondCertificateCommonName), + certificate.WithCommonName(secondCertificateHostName), + certificate.WithDNSNames(secondCertificateHostName), ) + require.True(t, certPool.AppendCertsFromPEM(secondCertificateCrt)) secondCertificate := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "admission-cert", @@ -201,35 +208,26 @@ func TestWebhookUpdate(t *testing.T) { deployment, metav1.UpdateOptions{}) require.NoError(t, err) + checkCertificate := func(hostname string) { + require.EventuallyWithT(t, func(c *assert.CollectT) { + _, err := tls.Dial("tcp", admissionAddress+":443", &tls.Config{ + MinVersion: tls.VersionTLS12, + RootCAs: certPool, + ServerName: hostname, + }) + assert.NoError(c, err) + }, 1*time.Minute, time.Second) + } + t.Log("checking initial certificate") - require.Eventually(t, func() bool { - conn, err := tls.Dial("tcp", admissionAddress+":443", - &tls.Config{MinVersion: tls.VersionTLS12, InsecureSkipVerify: true}) - if err != nil { - t.Logf("failed to dial %s:443, error %v", admissionAddress, err) - return false - } - certCommonName := conn.ConnectionState().PeerCertificates[0].Subject.CommonName - t.Logf("subject common name of certificate: %s", certCommonName) - return certCommonName == firstCertificateCommonName - }, time.Minute*2, time.Second) + checkCertificate(firstCertificateHostName) t.Log("changing certificate") _, err = env.Cluster().Client().CoreV1().Secrets(kongNamespace).Update(ctx, secondCertificate, metav1.UpdateOptions{}) require.NoError(t, err) t.Log("checking second certificate") - require.Eventually(t, func() bool { - conn, err := tls.Dial("tcp", admissionAddress+":443", - &tls.Config{MinVersion: tls.VersionTLS12, InsecureSkipVerify: true}) - if err != nil { - t.Logf("failed to dial %s:443, error %v", admissionAddress, err) - return false - } - certCommonName := conn.ConnectionState().PeerCertificates[0].Subject.CommonName - t.Logf("subject common name of certificate: %s", certCommonName) - return certCommonName == secondCertificateCommonName - }, time.Minute*10, time.Second) + checkCertificate(secondCertificateHostName) } // TestDeployAllInOneDBLESSGateway tests the Gateway feature flag and the admission controller with no user-provided diff --git a/test/e2e/konnect_test.go b/test/e2e/konnect_test.go index b3611ea7f6..456fc1cd4a 100644 --- a/test/e2e/konnect_test.go +++ b/test/e2e/konnect_test.go @@ -7,12 +7,12 @@ import ( "context" "crypto/tls" "fmt" - "net/http" "os/exec" "sync" "testing" "time" + "github.com/hashicorp/go-cleanhttp" environment "github.com/kong/kubernetes-testing-framework/pkg/environments" "github.com/samber/lo" "github.com/stretchr/testify/assert" @@ -299,14 +299,14 @@ func requireAllProxyReplicasIDsConsistentWithKonnect( nodeAPIClient := createKonnectNodeClient(t, rg, cert, key) getNodeIDFromAdminAPI := func(proxyPod corev1.Pod) string { - client := &http.Client{ - Timeout: time.Second * 30, - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - }, - }, + client := cleanhttp.DefaultClient() + tr := cleanhttp.DefaultTransport() + // Anything related to TLS can be ignored, because only availability is being tested here. + // Testing communicating over TLS is done as part of actual E2E test. + tr.TLSClientConfig = &tls.Config{ + InsecureSkipVerify: true, //nolint:gosec } + client.Transport = tr forwardCtx, cancel := context.WithCancel(ctx) defer cancel() diff --git a/test/helpers/ports_test.go b/test/helpers/ports_test.go index 2a7c0541f4..f6d72b65b1 100644 --- a/test/helpers/ports_test.go +++ b/test/helpers/ports_test.go @@ -8,6 +8,7 @@ import ( "strconv" "sync" "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -46,10 +47,11 @@ func TestGetFreePort(t *testing.T) { } s := httptest.Server{ Listener: l, - Config: &http.Server{ //nolint:gosec + Config: &http.Server{ Addr: fmt.Sprintf("localhost:%d", p), Handler: http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) { }), + ReadHeaderTimeout: 10 * time.Second, }, } s.Start() diff --git a/test/integration/consumer_group_test.go b/test/integration/consumer_group_test.go index aa0b1d4ca0..3c6359c4c0 100644 --- a/test/integration/consumer_group_test.go +++ b/test/integration/consumer_group_test.go @@ -149,7 +149,7 @@ func TestConsumerGroup(t *testing.T) { req := helpers.MustHTTPRequest(t, http.MethodGet, proxyHTTPURL.Host, path, map[string]string{ "apikey": consumer.Name, }) - resp, err := helpers.DefaultHTTPClientWithProxy(proxyHTTPURL).Do(req) + resp, err := helpers.DefaultHTTPClient().Do(req) if err != nil { t.Logf("WARNING: consumer %q failed to make a request: %v", consumer.Name, err) return false @@ -194,7 +194,7 @@ func TestConsumerGroup(t *testing.T) { req := helpers.MustHTTPRequest(t, http.MethodGet, proxyHTTPURL.Host, multiPath, map[string]string{ "apikey": four.Name, }) - resp, err := helpers.DefaultHTTPClientWithProxy(proxyHTTPURL).Do(req) + resp, err := helpers.DefaultHTTPClient().Do(req) if !assert.NoError(c, err) { return } @@ -211,7 +211,7 @@ func TestConsumerGroup(t *testing.T) { clear := helpers.MustHTTPRequest(t, http.MethodGet, proxyHTTPURL.Host, path, map[string]string{ "apikey": four.Name, }) - clearResp, err := helpers.DefaultHTTPClientWithProxy(proxyHTTPURL).Do(clear) + clearResp, err := helpers.DefaultHTTPClient(helpers.WithResolveHostTo(proxyHTTPURL.Host)).Do(clear) if !assert.NoError(c, err) { return } @@ -228,7 +228,7 @@ func TestConsumerGroup(t *testing.T) { empty := helpers.MustHTTPRequest(t, http.MethodGet, proxyHTTPURL.Host, multiPath, map[string]string{ "apikey": "test-consumer-3", }) - emptyResp, err := helpers.DefaultHTTPClientWithProxy(proxyHTTPURL).Do(empty) + emptyResp, err := helpers.DefaultHTTPClient(helpers.WithResolveHostTo(proxyHTTPURL.Host)).Do(empty) if !assert.NoError(c, err) { return } diff --git a/test/integration/consumer_test.go b/test/integration/consumer_test.go index 9ea319eb5e..93d71fb318 100644 --- a/test/integration/consumer_test.go +++ b/test/integration/consumer_test.go @@ -145,7 +145,7 @@ func TestConsumerCredential(t *testing.T) { assert.Eventually(t, func() bool { req := helpers.MustHTTPRequest(t, http.MethodGet, proxyHTTPURL.Host, "/test_consumer_credential", nil) req.SetBasicAuth("test_consumer_credential", "test_consumer_credential") - resp, err := helpers.DefaultHTTPClientWithProxy(proxyHTTPURL).Do(req) + resp, err := helpers.DefaultHTTPClient(helpers.WithResolveHostTo(proxyHTTPURL.Host)).Do(req) if err != nil { return false } diff --git a/test/integration/gateway_test.go b/test/integration/gateway_test.go index 9c69988330..de0faa3dea 100644 --- a/test/integration/gateway_test.go +++ b/test/integration/gateway_test.go @@ -427,9 +427,9 @@ func TestGatewayFilters(t *testing.T) { require.Eventually(t, callback, ingressWait, waitTick) t.Log("waiting for routes from HTTPRoute to become operational") - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "test_gateway_filters", http.StatusOK, "httpbin.org", emptyHeaderSet, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "test_gateway_filters", nil, http.StatusOK, "httpbin.org", emptyHeaderSet, ingressWait, waitTick) t.Log("waiting for routes from HTTPRoute in other namespace to become operational") - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "other_test_gateway_filters", http.StatusOK, "httpbin.org", emptyHeaderSet, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "other_test_gateway_filters", nil, http.StatusOK, "httpbin.org", emptyHeaderSet, ingressWait, waitTick) t.Log("changing to the same namespace filter") require.Eventually(t, func() bool { @@ -452,9 +452,9 @@ func TestGatewayFilters(t *testing.T) { }, ingressWait, waitTick) t.Log("confirming other namespace route becomes inaccessible") - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "other_test_gateway_filters", http.StatusNotFound, "no Route matched", emptyHeaderSet, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "other_test_gateway_filters", nil, http.StatusNotFound, "no Route matched", emptyHeaderSet, ingressWait, waitTick) t.Log("confirming same namespace route still operational") - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "test_gateway_filters", http.StatusOK, "httpbin.org", emptyHeaderSet, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "test_gateway_filters", nil, http.StatusOK, "httpbin.org", emptyHeaderSet, ingressWait, waitTick) t.Log("changing to a selector filter") require.Eventually(t, func() bool { @@ -483,7 +483,7 @@ func TestGatewayFilters(t *testing.T) { }, ingressWait, waitTick) t.Log("confirming wrong selector namespace route becomes inaccessible") - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "test_gateway_filters", http.StatusNotFound, "no Route matched", emptyHeaderSet, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "test_gateway_filters", nil, http.StatusNotFound, "no Route matched", emptyHeaderSet, ingressWait, waitTick) t.Log("confirming right selector namespace route becomes operational") - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "other_test_gateway_filters", http.StatusOK, "httpbin.org", emptyHeaderSet, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "other_test_gateway_filters", nil, http.StatusOK, "httpbin.org", emptyHeaderSet, ingressWait, waitTick) } diff --git a/test/integration/httproute_test.go b/test/integration/httproute_test.go index 69beb094e6..90692845fe 100644 --- a/test/integration/httproute_test.go +++ b/test/integration/httproute_test.go @@ -156,16 +156,16 @@ func TestHTTPRouteEssentials(t *testing.T) { ) t.Log("waiting for routes from HTTPRoute to become operational") - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-essentials", http.StatusOK, "httpbin.org", emptyHeaderSet, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-essentials", nil, http.StatusOK, "httpbin.org", emptyHeaderSet, ingressWait, waitTick) helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-essentials/base64/wqt5b8q7ccK7IGRhbiBib3NocWEgYmlyIGphdm9iaW1peiB5b8q7cWRpci4K", - http.StatusOK, "«yoʻq» dan boshqa bir javobimiz yoʻqdir.", emptyHeaderSet, ingressWait, waitTick) - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/2/test-http-route-essentials/regex/999", http.StatusOK, "httpbin.org", emptyHeaderSet, ingressWait, waitTick) - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/3/exact-test-http-route-essentials", http.StatusOK, "httpbin.org", emptyHeaderSet, ingressWait, waitTick) - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/3/exact-test-http-route-essentialsNO", http.StatusNotFound, "no Route matched", emptyHeaderSet, ingressWait, waitTick) + nil, http.StatusOK, "«yoʻq» dan boshqa bir javobimiz yoʻqdir.", emptyHeaderSet, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/2/test-http-route-essentials/regex/999", nil, http.StatusOK, "httpbin.org", emptyHeaderSet, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/3/exact-test-http-route-essentials", nil, http.StatusOK, "httpbin.org", emptyHeaderSet, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/3/exact-test-http-route-essentialsNO", nil, http.StatusNotFound, "no Route matched", emptyHeaderSet, ingressWait, waitTick) require.EventuallyWithT(t, func(c *assert.CollectT) { req := helpers.MustHTTPRequest(t, http.MethodGet, proxyHTTPURL.Host, "/test-http-route-essentials", nil) - resp, err := helpers.DefaultHTTPClientWithProxy(proxyHTTPURL).Do(req) + resp, err := helpers.DefaultHTTPClient(helpers.WithResolveHostTo(proxyHTTPURL.Host)).Do(req) if !assert.NoError(c, err) { return } @@ -191,7 +191,7 @@ func TestHTTPRouteEssentials(t *testing.T) { require.NoError(t, err) t.Log("verifying HTTPRoute header match") - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/", http.StatusOK, "httpbin.org", map[string]string{"Content-Type": "audio/mp3"}, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/", nil, http.StatusOK, "httpbin.org", map[string]string{"Content-Type": "audio/mp3"}, ingressWait, waitTick) }) t.Run("HTTPRoute query param match", func(t *testing.T) { @@ -213,7 +213,7 @@ func TestHTTPRouteEssentials(t *testing.T) { require.NoError(t, err) t.Log("verifying HTTPRoute query param match") - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/?foo=bar", http.StatusOK, "httpbin.org", nil, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/?foo=bar", nil, http.StatusOK, "httpbin.org", nil, ingressWait, waitTick) }) t.Log("verifying that the HTTPRoute has the Condition 'Accepted' set to 'True'") @@ -243,7 +243,7 @@ func TestHTTPRouteEssentials(t *testing.T) { require.Eventually(t, callback, ingressWait, waitTick) t.Log("verifying that the data-plane configuration from the HTTPRoute gets dropped with the parentRefs now removed") - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-essentials", http.StatusNotFound, "", emptyHeaderSet, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-essentials", nil, http.StatusNotFound, "", emptyHeaderSet, ingressWait, waitTick) t.Log("putting the parentRefs back") require.EventuallyWithT(t, func(c *assert.CollectT) { @@ -261,7 +261,7 @@ func TestHTTPRouteEssentials(t *testing.T) { require.Eventually(t, callback, ingressWait, waitTick) t.Log("verifying that putting the parentRefs back results in the routes becoming available again") - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-essentials", http.StatusOK, "httpbin.org", emptyHeaderSet, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-essentials", nil, http.StatusOK, "httpbin.org", emptyHeaderSet, ingressWait, waitTick) t.Log("deleting the GatewayClass") require.NoError(t, gatewayClient.GatewayV1().GatewayClasses().Delete(ctx, gatewayClassName, metav1.DeleteOptions{})) @@ -270,7 +270,7 @@ func TestHTTPRouteEssentials(t *testing.T) { callback = helpers.GetGatewayIsUnlinkedCallback(ctx, t, gatewayClient, gatewayapi.HTTPProtocolType, ns.Name, httpRoute.Name) require.Eventually(t, callback, ingressWait, waitTick) t.Log("verifying that the data-plane configuration from the HTTPRoute gets dropped with the GatewayClass now removed") - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-essentials", http.StatusNotFound, "", emptyHeaderSet, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-essentials", nil, http.StatusNotFound, "", emptyHeaderSet, ingressWait, waitTick) t.Log("putting the GatewayClass back") gwc, err = helpers.DeployGatewayClass(ctx, gatewayClient, gatewayClassName) @@ -282,7 +282,7 @@ func TestHTTPRouteEssentials(t *testing.T) { require.Eventually(t, callback, ingressWait, waitTick) t.Log("verifying that creating the GatewayClass again triggers reconciliation of HTTPRoutes and the route becomes available again") - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-essentials", http.StatusOK, "httpbin.org", emptyHeaderSet, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-essentials", nil, http.StatusOK, "httpbin.org", emptyHeaderSet, ingressWait, waitTick) t.Log("deleting the Gateway") require.NoError(t, gatewayClient.GatewayV1().Gateways(ns.Name).Delete(ctx, gatewayName, metav1.DeleteOptions{})) @@ -292,7 +292,7 @@ func TestHTTPRouteEssentials(t *testing.T) { require.Eventually(t, callback, ingressWait, waitTick) t.Log("verifying that the data-plane configuration from the HTTPRoute gets dropped with the Gateway now removed") - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-essentials", http.StatusNotFound, "", emptyHeaderSet, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-essentials", nil, http.StatusNotFound, "", emptyHeaderSet, ingressWait, waitTick) t.Log("putting the Gateway back") gateway, err = helpers.DeployGateway(ctx, gatewayClient, ns.Name, gatewayClassName, func(gw *gatewayapi.Gateway) { @@ -305,7 +305,7 @@ func TestHTTPRouteEssentials(t *testing.T) { require.Eventually(t, callback, ingressWait, waitTick) t.Log("verifying that creating the Gateway again triggers reconciliation of HTTPRoutes and the route becomes available again") - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-essentials", http.StatusOK, "httpbin.org", emptyHeaderSet, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-essentials", nil, http.StatusOK, "httpbin.org", emptyHeaderSet, ingressWait, waitTick) t.Log("deleting both GatewayClass and Gateway rapidly") require.NoError(t, gatewayClient.GatewayV1().GatewayClasses().Delete(ctx, gwc.Name, metav1.DeleteOptions{})) @@ -316,7 +316,7 @@ func TestHTTPRouteEssentials(t *testing.T) { require.Eventually(t, callback, ingressWait, waitTick) t.Log("verifying that the data-plane configuration from the HTTPRoute does not get orphaned with the GatewayClass and Gateway gone") - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-essentials", http.StatusNotFound, "", emptyHeaderSet, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-essentials", nil, http.StatusNotFound, "", emptyHeaderSet, ingressWait, waitTick) t.Log("testing port matching....") t.Log("putting the Gateway back") @@ -494,8 +494,8 @@ func TestHTTPRouteMultipleServices(t *testing.T) { t.Log("verifying that both backends are ready to receive traffic") httpbinRespContent := "httpbin.org" nginxRespContent := "Welcome to nginx!" - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-multiple-services", http.StatusOK, httpbinRespContent, emptyHeaderSet, ingressWait, waitTick) - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-multiple-services", http.StatusOK, nginxRespContent, emptyHeaderSet, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-multiple-services", nil, http.StatusOK, httpbinRespContent, emptyHeaderSet, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-multiple-services", nil, http.StatusOK, nginxRespContent, emptyHeaderSet, ingressWait, waitTick) t.Log("verifying that both backends receive requests according to weighted distribution") httpbinRespName := "httpbin-resp" @@ -529,7 +529,7 @@ func TestHTTPRouteMultipleServices(t *testing.T) { ) t.Log("verifying that misconfigured service rules are _not_ routed") - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-multiple-services-broken", http.StatusNotFound, "", emptyHeaderSet, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, "/test-http-route-multiple-services-broken", nil, http.StatusNotFound, "", emptyHeaderSet, ingressWait, waitTick) } func TestHTTPRouteFilterHosts(t *testing.T) { @@ -609,7 +609,7 @@ func TestHTTPRouteFilterHosts(t *testing.T) { testGetByHost := func(t *testing.T, host string) error { t.Helper() req := helpers.MustHTTPRequest(t, http.MethodGet, host, "/test-http-route-filter-hosts", nil) - resp, err := helpers.DefaultHTTPClientWithProxy(proxyHTTPURL).Do(req) + resp, err := helpers.DefaultHTTPClient(helpers.WithResolveHostTo(proxyHTTPURL.Host)).Do(req) if err != nil { return err } diff --git a/test/integration/ingress_https_test.go b/test/integration/ingress_https_test.go index 742e703b10..3855a0b541 100644 --- a/test/integration/ingress_https_test.go +++ b/test/integration/ingress_https_test.go @@ -6,6 +6,7 @@ import ( "bytes" "context" "crypto/tls" + "crypto/x509" "fmt" "net" "net/http" @@ -21,7 +22,6 @@ import ( corev1 "k8s.io/api/core/v1" netv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - k8stypes "k8s.io/apimachinery/pkg/types" "github.com/kong/kubernetes-ingress-controller/v3/test" "github.com/kong/kubernetes-ingress-controller/v3/test/consts" @@ -82,6 +82,7 @@ func TestHTTPSIngress(t *testing.T) { t.Parallel() ns, cleaner := helpers.Setup(ctx, t, env) + certPool := x509.NewCertPool() dialer := &net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, @@ -95,7 +96,10 @@ func TestHTTPSIngress(t *testing.T) { } return dialer.DialContext(ctx, network, addr) }, - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + TLSClientConfig: &tls.Config{ + MinVersion: tls.VersionTLS12, + RootCAs: certPool, + }, } httpcStatic := http.Client{ Timeout: httpcTimeout, @@ -128,21 +132,22 @@ func TestHTTPSIngress(t *testing.T) { t.Log("configuring ingress tls spec") ingress1.Spec.TLS = []netv1.IngressTLS{{SecretName: "secret1", Hosts: []string{"foo.example"}}} ingress1.ObjectMeta.Name = "ingress1" - ingress2.Spec.TLS = []netv1.IngressTLS{{SecretName: "secret2", Hosts: []string{"bar.example"}}} + ingress2.Spec.TLS = []netv1.IngressTLS{{SecretName: "secret2", Hosts: []string{"bar.example", "baz.example"}}} ingress2.ObjectMeta.Name = "ingress2" t.Log("configuring secrets") fooExampleTLSCert, fooExampleTLSKey := certificate.MustGenerateSelfSignedCertPEMFormat( certificate.WithCommonName("secure-foo-bar"), certificate.WithDNSNames("secure-foo-bar", "foo.example"), ) + require.True(t, certPool.AppendCertsFromPEM(fooExampleTLSCert)) barExampleTLSCert, barExampleTLSKey := certificate.MustGenerateSelfSignedCertPEMFormat( - certificate.WithCommonName("foo.com"), certificate.WithDNSNames("foo.com", "bar.example"), + certificate.WithCommonName("foo.com"), certificate.WithDNSNames("foo.com", "bar.example", "baz.example"), ) + require.True(t, certPool.AppendCertsFromPEM(barExampleTLSCert)) secrets := []*corev1.Secret{ { ObjectMeta: metav1.ObjectMeta{ - UID: k8stypes.UID("7428fb98-180b-4702-a91f-61351a33c6e4"), Name: "secret1", Namespace: ns.Name, }, @@ -153,7 +158,6 @@ func TestHTTPSIngress(t *testing.T) { }, { ObjectMeta: metav1.ObjectMeta{ - UID: k8stypes.UID("7428fb98-180b-4702-a91f-61351a33c6e5"), Name: "secret2", Namespace: ns.Name, }, @@ -249,14 +253,11 @@ func TestHTTPSIngress(t *testing.T) { return false }, ingressWait, waitTick, true) - // This should work currently. generators.NewIngressForService() only creates path rules by default, so while we don't - // do anything for baz.example other than add fake DNS for it, the /bar still routes it through ingress2's route. - // We're going to break it later, but need to confirm it does work first. t.Log("confirm Ingress path routes available on other hostnames") assert.Eventually(t, func() bool { resp, err := httpcStatic.Get("https://baz.example:443/bar") if err != nil { - t.Logf("WARNING: error while waiting for https://bar.example:443/baz: %v", err) + t.Logf("WARNING: error while waiting for https://baz.example:443/bar: %v", err) return false } defer resp.Body.Close() diff --git a/test/integration/ingress_regex_match_test.go b/test/integration/ingress_regex_match_test.go index 2945ae0368..7310717d62 100644 --- a/test/integration/ingress_regex_match_test.go +++ b/test/integration/ingress_regex_match_test.go @@ -131,7 +131,7 @@ func TestIngressRegexMatchPath(t *testing.T) { t.Log("testing paths expected to match") for _, path := range tc.matchPaths { - helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, path, http.StatusOK, "httpbin.org", nil, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, proxyHTTPURL.Host, path, nil, http.StatusOK, "httpbin.org", nil, ingressWait, waitTick) } t.Log("testing paths expected not to match") for _, path := range tc.notMatchPaths { @@ -230,6 +230,7 @@ func TestIngressRegexMatchHeader(t *testing.T) { proxyHTTPURL, proxyHTTPURL.Host, "/", + nil, http.StatusOK, "httpbin.org", map[string]string{matchHeaderKey: header}, diff --git a/test/integration/ingress_test.go b/test/integration/ingress_test.go index 754f5af246..37273bfd7b 100644 --- a/test/integration/ingress_test.go +++ b/test/integration/ingress_test.go @@ -186,11 +186,11 @@ func TestIngressDefaultBackend(t *testing.T) { cleaner.Add(ingress) t.Log("matching path") - helpers.EventuallyGETPath(t, nil, proxyHTTPURL.String(), "/foo", http.StatusOK, "httpbin.org", nil, ingressWait, waitTick) + helpers.EventuallyGETPath(t, nil, proxyHTTPURL.String(), "/foo", nil, http.StatusOK, "httpbin.org", nil, ingressWait, waitTick) t.Log("non matching path - use default backend") helpers.EventuallyGETPath( - t, nil, proxyHTTPURL.String(), fmt.Sprintf("/status/%d", http.StatusTeapot), http.StatusTeapot, "", nil, ingressWait, waitTick, + t, nil, proxyHTTPURL.String(), fmt.Sprintf("/status/%d", http.StatusTeapot), nil, http.StatusTeapot, "", nil, ingressWait, waitTick, ) } @@ -1001,7 +1001,7 @@ func TestIngressMatchByHost(t *testing.T) { t.Log("try to access the ingress by matching host") req := helpers.MustHTTPRequest(t, http.MethodGet, "test.example", "/", nil) require.Eventually(t, func() bool { - resp, err := helpers.DefaultHTTPClientWithProxy(proxyHTTPURL).Do(req) + resp, err := helpers.DefaultHTTPClient(helpers.WithResolveHostTo(proxyHTTPURL.Host)).Do(req) if err != nil { t.Logf("WARNING: error while waiting for %s: %v", proxyHTTPURL, err) return false @@ -1019,7 +1019,7 @@ func TestIngressMatchByHost(t *testing.T) { t.Log("try to access the ingress by unmatching host, should return 404") req = helpers.MustHTTPRequest(t, http.MethodGet, "foo.example", "/", nil) - resp, err := helpers.DefaultHTTPClientWithProxy(proxyHTTPURL).Do(req) + resp, err := helpers.DefaultHTTPClient(helpers.WithResolveHostTo(proxyHTTPURL.Host)).Do(req) require.NoError(t, err) defer resp.Body.Close() require.Equal(t, resp.StatusCode, http.StatusNotFound) @@ -1044,7 +1044,7 @@ func TestIngressMatchByHost(t *testing.T) { req = helpers.MustHTTPRequest(t, http.MethodGet, "test0.example", "/", nil) require.Eventually(t, func() bool { - resp, err := helpers.DefaultHTTPClientWithProxy(proxyHTTPURL).Do(req) + resp, err := helpers.DefaultHTTPClient(helpers.WithResolveHostTo(proxyHTTPURL.Host)).Do(req) if err != nil { t.Logf("WARNING: error while waiting for %s: %v", proxyHTTPURL, err) return false @@ -1062,7 +1062,7 @@ func TestIngressMatchByHost(t *testing.T) { t.Log("try to access the ingress by unmatching host, should return 404") req = helpers.MustHTTPRequest(t, http.MethodGet, "test.another", "/", nil) - resp, err = helpers.DefaultHTTPClientWithProxy(proxyHTTPURL).Do(req) + resp, err = helpers.DefaultHTTPClient(helpers.WithResolveHostTo(proxyHTTPURL.Host)).Do(req) require.NoError(t, err) defer resp.Body.Close() require.Equal(t, resp.StatusCode, http.StatusNotFound) @@ -1109,7 +1109,7 @@ func TestIngressRewriteURI(t *testing.T) { // wait for first successful response. After it all subsequent must be successful too. t.Log("wait for the Ingress direct to become available") const path = "image/jpeg" - helpers.EventuallyGETPath(t, proxyHTTPURL, serviceDomainDirect, path, http.StatusOK, consts.JPEGMagicNumber, nil, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, serviceDomainDirect, path, nil, http.StatusOK, consts.JPEGMagicNumber, nil, ingressWait, waitTick) waitForMainTestToFinish, cancelBackgroundTest := context.WithCancel(ctx) backgroundTestError := make(chan error) @@ -1136,7 +1136,7 @@ func TestIngressRewriteURI(t *testing.T) { case <-time.After(50 * time.Millisecond): } cntAttempts++ - resp, err := helpers.DefaultHTTPClientWithProxy(proxyHTTPURL).Do(helpers.MustHTTPRequest(t, http.MethodGet, serviceDomainDirect, path, nil)) + resp, err := helpers.DefaultHTTPClient(helpers.WithResolveHostTo(proxyHTTPURL.Host)).Do(helpers.MustHTTPRequest(t, http.MethodGet, serviceDomainDirect, path, nil)) if err != nil { t.Logf("WARNING: Ingress without rewrite - http request failed for GET %s/%s to %s: %v", serviceDomainDirect, path, proxyHTTPURL, err) continue @@ -1181,10 +1181,10 @@ func TestIngressRewriteURI(t *testing.T) { t.Log("rewrite uri feature is enabled") t.Log("try to access the ingress with valid capture group") - helpers.EventuallyGETPath(t, proxyHTTPURL, serviceDomainRewrite, "/foo/jpeg", http.StatusOK, consts.JPEGMagicNumber, nil, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, serviceDomainRewrite, "/foo/jpeg", nil, http.StatusOK, consts.JPEGMagicNumber, nil, ingressWait, waitTick) t.Log("try to access the ingress with invalid capture group, should return 404") - helpers.EventuallyGETPath(t, proxyHTTPURL, serviceDomainRewrite, "/", http.StatusNotFound, "", nil, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, serviceDomainRewrite, "/", nil, http.StatusNotFound, "", nil, ingressWait, waitTick) ingressRewrite, err = env.Cluster().Client().NetworkingV1().Ingresses(ns.Name).Get(ctx, ingressRewrite.Name, metav1.GetOptions{}) require.NoError(t, err) @@ -1197,7 +1197,7 @@ func TestIngressRewriteURI(t *testing.T) { require.NoError(t, err) t.Log("try to access the ingress with new valid capture group") - helpers.EventuallyGETPath(t, proxyHTTPURL, serviceDomainRewrite, "/foo/jpeg", http.StatusOK, consts.JPEGMagicNumber, nil, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, serviceDomainRewrite, "/foo/jpeg", nil, http.StatusOK, consts.JPEGMagicNumber, nil, ingressWait, waitTick) ingressRewrite, err = env.Cluster().Client().NetworkingV1().Ingresses(ns.Name).Get(ctx, ingressRewrite.Name, metav1.GetOptions{}) require.NoError(t, err) @@ -1208,7 +1208,7 @@ func TestIngressRewriteURI(t *testing.T) { require.NoError(t, err) t.Log("try to access the ingress with new rewrite annotation") - helpers.EventuallyGETPath(t, proxyHTTPURL, serviceDomainRewrite, "/foo/test/png", http.StatusOK, consts.PNGMagicNumber, nil, ingressWait, waitTick) + helpers.EventuallyGETPath(t, proxyHTTPURL, serviceDomainRewrite, "/foo/test/png", nil, http.StatusOK, consts.PNGMagicNumber, nil, ingressWait, waitTick) cancelBackgroundTest() require.NoError(t, <-backgroundTestError, "for Ingress without rewrite run in background") diff --git a/test/integration/isolated/custom_entity_test.go b/test/integration/isolated/custom_entity_test.go index b23bfd9557..5c1570b976 100644 --- a/test/integration/isolated/custom_entity_test.go +++ b/test/integration/isolated/custom_entity_test.go @@ -78,7 +78,7 @@ func TestCustomEntityExample(t *testing.T) { Assess("degraphql plugin works as expected", func(ctx context.Context, t *testing.T, _ *envconf.Config) context.Context { proxyURL := GetHTTPURLFromCtx(ctx) t.Log("Waiting for graphQL service to be available") - helpers.EventuallyGETPath(t, proxyURL, proxyURL.Host, "/healthz", http.StatusOK, "OK", nil, consts.IngressWait, consts.WaitTick) + helpers.EventuallyGETPath(t, proxyURL, proxyURL.Host, "/healthz", nil, http.StatusOK, "OK", nil, consts.IngressWait, consts.WaitTick) t.Log("injecting data for graphQL service") injectDataURL := proxyURL.String() + "/v2/query" @@ -131,7 +131,7 @@ func TestCustomEntityExample(t *testing.T) { t.Log("verifying degraphQL plugin and degraphql_routes entity works") // The ingress providing graphQL service has a different host, so we need to set the `Host` header. - helpers.EventuallyGETPath(t, proxyURL, "graphql.service.example", "/contacts", http.StatusOK, `"name":"Alice"`, map[string]string{"Host": "graphql.service.example"}, consts.IngressWait, consts.WaitTick) + helpers.EventuallyGETPath(t, proxyURL, "graphql.service.example", "/contacts", nil, http.StatusOK, `"name":"Alice"`, map[string]string{"Host": "graphql.service.example"}, consts.IngressWait, consts.WaitTick) return ctx }). @@ -190,7 +190,7 @@ func TestCustomEntityExample(t *testing.T) { t.Log("verifying degraphQL plugin and degraphql_routes entity works") proxyURL := GetHTTPURLFromCtx(ctx) - helpers.EventuallyGETPath(t, proxyURL, "alter-graphql.service.example", "/contacts", http.StatusOK, `"name":"Alice"`, map[string]string{"Host": "graphql.service.example"}, consts.IngressWait, consts.WaitTick) + helpers.EventuallyGETPath(t, proxyURL, "alter-graphql.service.example", "/contacts", nil, http.StatusOK, `"name":"Alice"`, map[string]string{"Host": "graphql.service.example"}, consts.IngressWait, consts.WaitTick) return ctx }). diff --git a/test/integration/isolated/examples_httproute_rewrite_test.go b/test/integration/isolated/examples_httproute_rewrite_test.go index 6566b91927..4754bdf07b 100644 --- a/test/integration/isolated/examples_httproute_rewrite_test.go +++ b/test/integration/isolated/examples_httproute_rewrite_test.go @@ -41,7 +41,7 @@ func TestHTTPRouteRewriteExample(t *testing.T) { assert.NoError(t, clusters.ApplyManifestByYAML(ctx, cluster, string(manifest))) cleaner.AddManifest(string(manifest)) - t.Logf("verifying that the UDPIngress routes traffic properly") + t.Logf("verifying that the HTTPRoute routes traffic properly") t.Logf("asserting /full-path-prefix path is redirected to /echo?msg=hello from the manifest") helpers.EventuallyGETPath( @@ -49,6 +49,7 @@ func TestHTTPRouteRewriteExample(t *testing.T) { proxyURL, proxyURL.Host, "/full-path-prefix", + nil, http.StatusOK, "hello", nil, @@ -62,6 +63,7 @@ func TestHTTPRouteRewriteExample(t *testing.T) { proxyURL, proxyURL.Host, "/old-prefix?msg=hello", + nil, http.StatusOK, "hello", nil, diff --git a/test/integration/isolated/examples_httproute_test.go b/test/integration/isolated/examples_httproute_test.go index 8940a498f4..176d258885 100644 --- a/test/integration/isolated/examples_httproute_test.go +++ b/test/integration/isolated/examples_httproute_test.go @@ -79,6 +79,7 @@ func TestHTTPRouteWithBrokenPluginFallback(t *testing.T) { proxyURL, proxyURL.Host, "/for-auth-users", + nil, http.StatusNotFound, "no Route matched with those values", nil, @@ -140,6 +141,7 @@ func TestHTTPRouteUseLastValidConfigWithBrokenPluginFallback(t *testing.T) { proxyURL, proxyURL.Host, additionalRoutePath, + nil, http.StatusOK, additionalRouteServiceTarget, nil, @@ -297,6 +299,7 @@ func TestHTTPRouteUseLastValidConfigWithBrokenPluginFallback(t *testing.T) { proxyURL, proxyURL.Host, "/httproute-testing", + nil, http.StatusNotFound, "no Route matched with those values", nil, @@ -310,6 +313,7 @@ func TestHTTPRouteUseLastValidConfigWithBrokenPluginFallback(t *testing.T) { proxyURL, proxyURL.Host, newRoute, + nil, http.StatusOK, "echo-1", nil, @@ -321,6 +325,7 @@ func TestHTTPRouteUseLastValidConfigWithBrokenPluginFallback(t *testing.T) { proxyURL, proxyURL.Host, newRoute, + nil, http.StatusOK, "echo-2", nil, @@ -355,6 +360,7 @@ func runHTTPRouteExampleTestScenario(manifestToUse string) func(ctx context.Cont proxyURL, proxyURL.Host, "/httproute-testing", + nil, http.StatusOK, "echo-1", nil, @@ -368,6 +374,7 @@ func runHTTPRouteExampleTestScenario(manifestToUse string) func(ctx context.Cont proxyURL, proxyURL.Host, "/httproute-testing", + nil, http.StatusOK, "echo-2", nil, diff --git a/test/integration/isolated/ingress_test.go b/test/integration/isolated/ingress_test.go index 31fd1a2f05..22cbcd892d 100644 --- a/test/integration/isolated/ingress_test.go +++ b/test/integration/isolated/ingress_test.go @@ -367,6 +367,7 @@ func TestIngress_KongServiceFacadeAsBackend(t *testing.T) { proxyURL, proxyURL.Host, path, + nil, http.StatusOK, expectedMagicNumber, nil, diff --git a/test/integration/plugin_test.go b/test/integration/plugin_test.go index dc21fd0bf3..da3c9626f2 100644 --- a/test/integration/plugin_test.go +++ b/test/integration/plugin_test.go @@ -622,8 +622,8 @@ func TestPluginCrossNamespaceReference(t *testing.T) { t.Logf("validating that plugin %s is not configured without a grant", kongplugin.Name) assert.Never(t, func() bool { - req := helpers.MustHTTPRequest(t, "GET", proxyHTTPURL.String(), "/test_plugin_reference?key=thirtytangas", nil) - resp, err := helpers.DefaultHTTPClientWithProxy(proxyHTTPURL).Do(req) + req := helpers.MustHTTPRequest(t, http.MethodGet, proxyHTTPURL.String(), "/test_plugin_reference?key=thirtytangas", nil) + resp, err := helpers.DefaultHTTPClient(helpers.WithResolveHostTo(proxyHTTPURL.Host)).Do(req) if err != nil { return false } @@ -657,8 +657,8 @@ func TestPluginCrossNamespaceReference(t *testing.T) { t.Logf("validating that plugin %s was successfully configured", kongplugin.Name) assert.EventuallyWithT(t, func(c *assert.CollectT) { - req := helpers.MustHTTPRequest(t, "GET", proxyHTTPURL.String(), "/test_plugin_reference?apikey=thirtytangas", nil) - resp, err := helpers.DefaultHTTPClientWithProxy(proxyHTTPURL).Do(req) + req := helpers.MustHTTPRequest(t, http.MethodGet, proxyHTTPURL.String(), "/test_plugin_reference?apikey=thirtytangas", nil) + resp, err := helpers.DefaultHTTPClient(helpers.WithResolveHostTo(proxyHTTPURL.Host)).Do(req) if !assert.NoError(c, err) { return } diff --git a/test/internal/helpers/http.go b/test/internal/helpers/http.go index c6dd9e918a..a7a2bb2f09 100644 --- a/test/internal/helpers/http.go +++ b/test/internal/helpers/http.go @@ -2,35 +2,76 @@ package helpers import ( "bytes" + "context" + "crypto/tls" + "crypto/x509" "fmt" "io" + "net" "net/http" "net/url" "strings" "testing" "time" + "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-retryablehttp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +type httpClientCfg struct { + resolveHostTo string + rootCAs *x509.CertPool +} + +// HTTPClientOption is a functional option for configuring the HTTP client. +type HTTPClientOption func(*httpClientCfg) + +// WithResolveHostTo sets the host to resolve to (equivalent of `curl --resolve`). +func WithResolveHostTo(host string) HTTPClientOption { + return func(opts *httpClientCfg) { + opts.resolveHostTo = host + } +} + +// WithRootCAs sets the root CAs for the client. +func WithRootCAs(rootCAs *x509.CertPool) HTTPClientOption { + return func(opts *httpClientCfg) { + opts.rootCAs = rootCAs + } +} + // DefaultHTTPClient returns a client that should be used by default in tests. // All defaults that should be propagated to tests for use should be changed in here. -func DefaultHTTPClient() *http.Client { - return &http.Client{ - Timeout: 10 * time.Second, +func DefaultHTTPClient(opts ...HTTPClientOption) *http.Client { + var cfg httpClientCfg + for _, opt := range opts { + opt(&cfg) } -} -func DefaultHTTPClientWithProxy(proxyURL *url.URL) *http.Client { - tr := &http.Transport{ - Proxy: http.ProxyURL(proxyURL), + tr := cleanhttp.DefaultPooledTransport() + if cfg.rootCAs != nil { + tr.TLSClientConfig = &tls.Config{ + RootCAs: cfg.rootCAs, + MinVersion: tls.VersionTLS12, + } } - return &http.Client{ - Transport: tr, - Timeout: 10 * time.Second, + // It provides the equivalent of `curl --resolve` for the client. + if cfg.resolveHostTo != "" { + tr.DialContext = func(ctx context.Context, network, _ string) (net.Conn, error) { + return (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).DialContext(ctx, network, cfg.resolveHostTo) + } } + + client := cleanhttp.DefaultClient() + client.Transport = tr + client.Timeout = 10 * time.Second + + return client } // RetryableHTTPClient wraps a client with retry logic. That should be used when calling external services that might @@ -77,12 +118,14 @@ func MustHTTPRequest(t *testing.T, method string, host, path string, headers map // doesn't eventually succeed the calling test will fail and stop. // Parameter proxyURL is the URL of Kong Gateway proxy (set nil when it's not different // from parameter host). Parameter host, path and headers are used to make the GET request. +// Parameter certPool is used to validate the server certificate (nil to use system one). // Response is expected to have the given statusCode and contain the passed bodyContent. func EventuallyGETPath( t *testing.T, proxyURL *url.URL, host string, path string, + certPool *x509.CertPool, statusCode int, bodyContent string, requestHeaders map[string]string, @@ -91,12 +134,14 @@ func EventuallyGETPath( responseMatchers ...ResponseMatcher, ) { t.Helper() - var client *http.Client + var clientOptions []HTTPClientOption if proxyURL != nil { - client = DefaultHTTPClientWithProxy(proxyURL) - } else { - client = DefaultHTTPClient() + clientOptions = append(clientOptions, WithResolveHostTo(proxyURL.Host)) + } + if certPool != nil { + clientOptions = append(clientOptions, WithRootCAs(certPool)) } + client := DefaultHTTPClient(clientOptions...) require.EventuallyWithT(t, func(c *assert.CollectT) { resp, err := client.Do(MustHTTPRequest(t, http.MethodGet, host, path, requestHeaders)) @@ -143,6 +188,7 @@ func EventuallyExpectHTTP404WithNoRoute( proxyURL, host, path, + nil, http.StatusNotFound, "no Route matched with those values", headers, @@ -205,7 +251,7 @@ func CountHTTPGetResponses( } func countHTTPGetResponse(t *testing.T, req *http.Request, proxyURL *url.URL, matchCounter map[string]int, matchers ...ResponseMatcher) { - resp, err := DefaultHTTPClientWithProxy(proxyURL).Do(req) + resp, err := DefaultHTTPClient(WithResolveHostTo(proxyURL.Host)).Do(req) if err != nil { return } diff --git a/test/kongintegration/expression_router_test.go b/test/kongintegration/expression_router_test.go index 626db61829..527a7b9fb0 100644 --- a/test/kongintegration/expression_router_test.go +++ b/test/kongintegration/expression_router_test.go @@ -109,12 +109,12 @@ func TestExpressionsRouterMatchers_GenerateValidExpressions(t *testing.T) { // Matched requests should access the upstream service. for _, req := range tc.matchRequests { - helpers.EventuallyGETPath(t, proxyParsedURL, req.host, req.path, http.StatusOK, "", nil, timeout, period) + helpers.EventuallyGETPath(t, proxyParsedURL, req.host, req.path, nil, http.StatusOK, "", nil, timeout, period) } // Unmatched requests should get a 404 from Kong. for _, req := range tc.unmatchRequests { - helpers.EventuallyGETPath(t, proxyParsedURL, req.host, req.path, http.StatusNotFound, "", nil, timeout, period) + helpers.EventuallyGETPath(t, proxyParsedURL, req.host, req.path, nil, http.StatusNotFound, "", nil, timeout, period) } }) }