diff --git a/test/integration/consul-container/test/gateways/terminating_gateway_test.go b/test/integration/consul-container/test/gateways/terminating_gateway_test.go new file mode 100644 index 0000000000..ef8f95ab82 --- /dev/null +++ b/test/integration/consul-container/test/gateways/terminating_gateway_test.go @@ -0,0 +1,187 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package gateways + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/sdk/testutil/retry" + "github.com/stretchr/testify/require" + + libassert "github.com/hashicorp/consul/test/integration/consul-container/libs/assert" + libcluster "github.com/hashicorp/consul/test/integration/consul-container/libs/cluster" + libservice "github.com/hashicorp/consul/test/integration/consul-container/libs/service" + libtopology "github.com/hashicorp/consul/test/integration/consul-container/libs/topology" + "github.com/hashicorp/consul/test/integration/consul-container/libs/utils" +) + +const externalServerName = libservice.StaticServerServiceName + +var requestRetryTimer = &retry.Timer{Timeout: 120 * time.Second, Wait: 500 * time.Millisecond} + +// TestTerminatingGateway Summary +// This test makes sure an external service can be reached via and terminating gateway. External server +// refers to being outside of the consul service mesh but it runs under the same docker network. +// +// Steps: +// - Create a cluster (1 server and 1 client). +// - Create the external service static-server (a single container, no proxy). +// - Register an external node and the external service on that node in Consul. +// - Create a terminating gateway config entry that includes an entry for the "external" static-server. +// - Create the terminating gateway and register it with Consul. +// - Create a static-client proxy (no need for a service container). +// - Verify that the static-client can communicate with the external static-server through the terminating gateway +func TestTerminatingGatewayBasic(t *testing.T) { + t.Parallel() + var deferClean utils.ResettableDefer + defer deferClean.Execute() + + t.Logf("creating consul cluster") + cluster, _, client := libtopology.NewCluster(t, &libtopology.ClusterConfig{ + NumServers: 1, + NumClients: 1, + BuildOpts: &libcluster.BuildOptions{ + Datacenter: "dc1", + }, + ApplyDefaultProxySettings: true, + }) + node := cluster.Clients()[0] + + // Creates an external server that is not part of consul (no proxy) + t.Logf("creating external server: %s", externalServerName) + externalServerPort := 8083 + externalServerGRPCPort := 8079 + externalServer, err := libservice.NewExampleService(context.Background(), externalServerName, externalServerPort, externalServerGRPCPort, node) + require.NoError(t, err) + deferClean.Add(func() { + _ = externalServer.Terminate() + }) + + // Register the external service in the default namespace. Tell consul it is located on an 'external node' and + // not part of the service mesh. Because of the way that containers are created, the terminating gateway can + // make a call to the address `localhost::8080/debug?env=dump` and checks for `FORTIO_NAME=` +// in the response. +func assertHTTPRequestToServiceAddress(t *testing.T, client *libservice.ConnectContainer, serviceName string, port int, expSuccess bool) { + upstreamURL := fmt.Sprintf("http://localhost:%d/debug?env=dump", port) + retry.RunWith(requestRetryTimer, t, func(r *retry.R) { + out, err := client.Exec(context.Background(), []string{"curl", "-s", upstreamURL}) + t.Logf("curl request to upstream service address: url=%s\nerr = %v\nout = %s", upstreamURL, err, out) + + if expSuccess { + require.NoError(r, err) + require.Contains(r, out, fmt.Sprintf("FORTIO_NAME=%s", serviceName)) + t.Logf("successfuly messaged %s", serviceName) + } else { + require.Error(r, err) + require.Contains(r, err.Error(), "exit code 52") + } + }) +}