Retry and timeout test acceptance test (#18791)

* retry and timeout test

* add docker mirrior

* checkpoint

* add in error

* add in delay

* up error rate

* fix status code
This commit is contained in:
sarahalsmiller 2023-09-15 11:54:51 -05:00 committed by GitHub
parent 6838441c54
commit 753c8f1774
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 242 additions and 4 deletions

View File

@ -9,5 +9,5 @@ import (
const ( const (
envoyLogLevel = "debug" envoyLogLevel = "debug"
hashicorpDockerProxy = "docker.mirror.hashicorp.services" HashicorpDockerProxy = "docker.mirror.hashicorp.services"
) )

View File

@ -187,7 +187,7 @@ func NewExampleService(ctx context.Context, name string, httpPort int, grpcPort
command = append(command, containerArgs...) command = append(command, containerArgs...)
req := testcontainers.ContainerRequest{ req := testcontainers.ContainerRequest{
Image: hashicorpDockerProxy + "/fortio/fortio", Image: HashicorpDockerProxy + "/fortio/fortio",
WaitingFor: wait.ForLog("").WithStartupTimeout(60 * time.Second), WaitingFor: wait.ForLog("").WithStartupTimeout(60 * time.Second),
AutoRemove: false, AutoRemove: false,
Name: containerName, Name: containerName,

View File

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

View File

@ -8,6 +8,8 @@ import (
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"github.com/testcontainers/testcontainers-go"
"k8s.io/utils/pointer"
"testing" "testing"
"time" "time"
@ -737,3 +739,233 @@ func TestHTTPRouteParentRefChange(t *testing.T) {
"Host": "test.foo", "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"})
}