Skip to content

Commit

Permalink
Retry and timeout test acceptance test (#18791)
Browse files Browse the repository at this point in the history
* retry and timeout test

* add docker mirrior

* checkpoint

* add in error

* add in delay

* up error rate

* fix status code
  • Loading branch information
sarahalsmiller authored Sep 15, 2023
1 parent 6838441 commit 753c8f1
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 4 deletions.
2 changes: 1 addition & 1 deletion test/integration/consul-container/libs/service/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ import (

const (
envoyLogLevel = "debug"
hashicorpDockerProxy = "docker.mirror.hashicorp.services"
HashicorpDockerProxy = "docker.mirror.hashicorp.services"
)
2 changes: 1 addition & 1 deletion test/integration/consul-container/libs/service/examples.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ func NewExampleService(ctx context.Context, name string, httpPort int, grpcPort
command = append(command, containerArgs...)

req := testcontainers.ContainerRequest{
Image: hashicorpDockerProxy + "/fortio/fortio",
Image: HashicorpDockerProxy + "/fortio/fortio",
WaitingFor: wait.ForLog("").WithStartupTimeout(60 * time.Second),
AutoRemove: false,
Name: containerName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ type checkOptions struct {
responseHeaders map[string]string
statusCode int
testName string
expectedBody string
}

// checkRoute, customized version of libassert.RouteEchos to allow for headers/distinguishing between the server instances
Expand Down Expand Up @@ -289,8 +290,13 @@ func checkRoute(t *testing.T, port int, path string, headers map[string]string,
return false
}
}
if !strings.Contains(string(body), "hello") {
t.Log("body does not contain 'hello'")
expectedBody := expected.expectedBody
if expectedBody == "" {
expectedBody = "hello"
}
if !strings.Contains(string(body), expectedBody) {
t.Log(string(body))
t.Log("body does not contain " + expectedBody)
return false
}

Expand Down
232 changes: 232 additions & 0 deletions test/integration/consul-container/test/gateways/http_route_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"crypto/rand"
"encoding/hex"
"fmt"
"github.com/testcontainers/testcontainers-go"
"k8s.io/utils/pointer"
"testing"
"time"

Expand Down Expand Up @@ -737,3 +739,233 @@ func TestHTTPRouteParentRefChange(t *testing.T) {
"Host": "test.foo",
}, "")
}

func TestHTTPRouteRetryAndTimeout(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}

t.Parallel()

// infrastructure set up
listenerPort := 6018
retryServiceHTTPPort := 6019
retryServiceGRPCPort := 6020
timeoutServiceHTTPPort := 6021
timeoutServiceGRPCPort := 6022

retryServiceName := randomName("service", 16)
gatewayName := randomName("gw", 16)
retryRouteName := randomName("route", 16)
timeoutServiceName := randomName("service", 16)
timeoutRouteName := randomName("route", 16)
retryPath := "/retry"
timeoutPath := "/timeout"

clusterConfig := &libtopology.ClusterConfig{
NumServers: 1,
NumClients: 1,
BuildOpts: &libcluster.BuildOptions{
Datacenter: "dc1",
InjectAutoEncryption: true,
InjectGossipEncryption: true,
AllowHTTPAnyway: true,
},
ExposedPorts: []int{
listenerPort,
retryServiceGRPCPort,
retryServiceHTTPPort,
timeoutServiceGRPCPort,
timeoutServiceHTTPPort,
},
ApplyDefaultProxySettings: true,
}

cluster, _, _ := libtopology.NewCluster(t, clusterConfig)
client := cluster.Agents[0].GetClient()

namespace := getOrCreateNamespace(t, client)

_, _, err := libservice.CreateAndRegisterCustomServiceAndSidecar(
cluster.Agents[0], &libservice.ServiceOpts{
ID: retryServiceName,
Name: retryServiceName,
Namespace: namespace,
HTTPPort: retryServiceHTTPPort,
GRPCPort: retryServiceGRPCPort,
},
testcontainers.ContainerRequest{
Image: libservice.HashicorpDockerProxy + "/nicholasjackson/fake-service:v0.26.0",
Env: map[string]string{
"LISTEN_ADDR": fmt.Sprintf("0.0.0.0:%d", retryServiceHTTPPort),
"ERROR_RATE": "0.5",
},
},
nil,
)
require.NoError(t, err)

_, _, err = libservice.CreateAndRegisterCustomServiceAndSidecar(
cluster.Agents[0], &libservice.ServiceOpts{
ID: timeoutServiceName,
Name: timeoutServiceName,
Namespace: namespace,
HTTPPort: timeoutServiceHTTPPort,
GRPCPort: timeoutServiceGRPCPort,
},
testcontainers.ContainerRequest{
Image: libservice.HashicorpDockerProxy + "/nicholasjackson/fake-service:v0.26.0",
Env: map[string]string{
"LISTEN_ADDR": fmt.Sprintf("0.0.0.0:%d", timeoutServiceHTTPPort),
"ERROR_RATE": "1.0",
"ERROR_DELAY": "1m",
},
},
nil,
)

require.NoError(t, err)

// write config entries
proxyDefaults := &api.ProxyConfigEntry{
Kind: api.ProxyDefaults,
Name: api.ProxyConfigGlobal,
Config: map[string]interface{}{
"protocol": "http",
},
}

require.NoError(t, cluster.ConfigEntryWrite(proxyDefaults))

apiGateway := &api.APIGatewayConfigEntry{
Kind: "api-gateway",
Name: gatewayName,
Listeners: []api.APIGatewayListener{
{
Name: "listener",
Port: listenerPort,
Protocol: "http",
},
},
Namespace: namespace,
}

retryRoute := &api.HTTPRouteConfigEntry{
Kind: api.HTTPRoute,
Name: retryRouteName,
Namespace: namespace,
Parents: []api.ResourceReference{
{
Kind: api.APIGateway,
Name: gatewayName,
Namespace: namespace,
},
},
Hostnames: []string{
"test.foo",
"test.example",
},
Rules: []api.HTTPRouteRule{
{
Filters: api.HTTPFilters{
RetryFilter: &api.RetryFilter{
NumRetries: pointer.Uint32(10),
RetryOnStatusCodes: []uint32{500},
},
},
Services: []api.HTTPService{
{
Name: retryServiceName,
Namespace: namespace,
},
},
Matches: []api.HTTPMatch{
{
Path: api.HTTPPathMatch{
Match: api.HTTPPathMatchPrefix,
Value: retryPath,
},
},
},
},
},
}

timeoutRoute := &api.HTTPRouteConfigEntry{
Kind: api.HTTPRoute,
Name: timeoutRouteName,
Namespace: namespace,
Parents: []api.ResourceReference{
{
Kind: api.APIGateway,
Name: gatewayName,
Namespace: namespace,
},
},
Hostnames: []string{
"test.foo",
"test.example",
},
Rules: []api.HTTPRouteRule{
{
Filters: api.HTTPFilters{
TimeoutFilter: &api.TimeoutFilter{
RequestTimeout: 1,
IdleTimeout: 1,
},
},
Services: []api.HTTPService{
{
Name: timeoutServiceName,
Namespace: namespace,
},
},
Matches: []api.HTTPMatch{
{
Path: api.HTTPPathMatch{
Match: api.HTTPPathMatchPrefix,
Value: timeoutPath,
},
},
},
},
},
}

require.NoError(t, cluster.ConfigEntryWrite(apiGateway))
require.NoError(t, cluster.ConfigEntryWrite(timeoutRoute))
require.NoError(t, cluster.ConfigEntryWrite(retryRoute))

// create gateway service
gwCfg := libservice.GatewayConfig{
Name: gatewayName,
Kind: "api",
Namespace: namespace,
}
gatewayService, err := libservice.NewGatewayService(context.Background(), gwCfg, cluster.Agents[0], listenerPort)
require.NoError(t, err)
libassert.CatalogServiceExists(t, client, gatewayName, &api.QueryOptions{Namespace: namespace})

// make sure config entries have been properly created
checkGatewayConfigEntry(t, client, gatewayName, &api.QueryOptions{Namespace: namespace})
t.Log("checking retry route")
checkHTTPRouteConfigEntry(t, client, retryRouteName, &api.QueryOptions{Namespace: namespace})

t.Log("checking timeout route")
checkHTTPRouteConfigEntry(t, client, timeoutRouteName, &api.QueryOptions{Namespace: namespace})

// gateway resolves routes
gatewayPort, err := gatewayService.GetPort(listenerPort)
require.NoError(t, err)
fmt.Println("Gateway Port: ", gatewayPort)

// hit service 1 by hitting root path
checkRoute(t, gatewayPort, retryPath, map[string]string{
"Host": "test.foo",
}, checkOptions{debug: false, statusCode: 200, testName: "retry should succeed cleanly", expectedBody: "Hello World"})

checkRoute(t, gatewayPort, timeoutPath, map[string]string{
"Host": "test.foo",
}, checkOptions{debug: false, statusCode: 500, testName: "timeout should timeout", expectedBody: "timeout"})

}

0 comments on commit 753c8f1

Please sign in to comment.