test: slight refactoring ahead of peering testing improvements (#17387)

This commit is contained in:
R.B. Boyer 2023-05-16 14:57:24 -05:00 committed by GitHub
parent 4859cfb47b
commit 00a9e03f44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 138 additions and 19 deletions

View File

@ -6,6 +6,7 @@ package assert
import ( import (
"fmt" "fmt"
"io" "io"
"net/http"
"net/url" "net/url"
"regexp" "regexp"
"strconv" "strconv"
@ -14,15 +15,29 @@ import (
"time" "time"
"github.com/hashicorp/consul/sdk/testutil/retry" "github.com/hashicorp/consul/sdk/testutil/retry"
libcluster "github.com/hashicorp/consul/test/integration/consul-container/libs/cluster"
"github.com/hashicorp/consul/test/integration/consul-container/libs/utils"
"github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-cleanhttp"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
libcluster "github.com/hashicorp/consul/test/integration/consul-container/libs/cluster"
"github.com/hashicorp/consul/test/integration/consul-container/libs/utils"
) )
// GetEnvoyListenerTCPFilters validates that proxy was configured with tcp protocol and one rbac listener filter // GetEnvoyListenerTCPFilters validates that proxy was configured with tcp protocol and one rbac listener filter
func GetEnvoyListenerTCPFilters(t *testing.T, adminPort int) { func GetEnvoyListenerTCPFilters(t *testing.T, adminPort int) {
require.True(t, adminPort > 0)
GetEnvoyListenerTCPFiltersWithClient(
t,
cleanhttp.DefaultClient(),
fmt.Sprintf("localhost:%d", adminPort),
)
}
func GetEnvoyListenerTCPFiltersWithClient(
t *testing.T,
client *http.Client,
addr string,
) {
var ( var (
dump string dump string
err error err error
@ -32,7 +47,7 @@ func GetEnvoyListenerTCPFilters(t *testing.T, adminPort int) {
} }
retry.RunWith(failer(), t, func(r *retry.R) { retry.RunWith(failer(), t, func(r *retry.R) {
dump, _, err = GetEnvoyOutput(adminPort, "config_dump", map[string]string{}) dump, _, err = GetEnvoyOutputWithClient(client, addr, "config_dump", map[string]string{})
if err != nil { if err != nil {
r.Fatal("could not fetch envoy configuration") r.Fatal("could not fetch envoy configuration")
} }
@ -55,30 +70,55 @@ func GetEnvoyListenerTCPFilters(t *testing.T, adminPort int) {
require.Contains(t, filteredResult, "envoy.filters.network.tcp_proxy") require.Contains(t, filteredResult, "envoy.filters.network.tcp_proxy")
} }
// func GetEnvoyOutputWithClient(client *http.Client, addr string, path string, query map[string]string) (string, int, error) {
// AssertUpstreamEndpointStatus validates that proxy was configured with provided clusterName in the healthStatus // AssertUpstreamEndpointStatus validates that proxy was configured with provided clusterName in the healthStatus
func AssertUpstreamEndpointStatus(t *testing.T, adminPort int, clusterName, healthStatus string, count int) { func AssertUpstreamEndpointStatus(t *testing.T, adminPort int, clusterName, healthStatus string, count int) {
var ( require.True(t, adminPort > 0)
clusters string AssertUpstreamEndpointStatusWithClient(
err error t,
cleanhttp.DefaultClient(),
fmt.Sprintf("localhost:%d", adminPort),
clusterName,
healthStatus,
count,
) )
}
func AssertUpstreamEndpointStatusWithClient(
t *testing.T,
client *http.Client,
addr string,
clusterName string,
healthStatus string,
count int,
) {
require.NotNil(t, client)
require.NotEmpty(t, addr)
failer := func() *retry.Timer { failer := func() *retry.Timer {
return &retry.Timer{Timeout: 30 * time.Second, Wait: 500 * time.Millisecond} return &retry.Timer{Timeout: 30 * time.Second, Wait: 500 * time.Millisecond}
} }
retry.RunWith(failer(), t, func(r *retry.R) { retry.RunWith(failer(), t, func(r *retry.R) {
clusters, _, err = GetEnvoyOutput(adminPort, "clusters", map[string]string{"format": "json"}) clusters, statusCode, err := GetEnvoyOutputWithClient(client, addr, "clusters", map[string]string{"format": "json"})
if err != nil { if err != nil {
r.Fatal("could not fetch envoy clusters") r.Fatal("could not fetch envoy clusters")
} }
require.Equal(r, 200, statusCode)
filter := fmt.Sprintf(`.cluster_statuses[] | select(.name|contains("%s")) | [.host_statuses[].health_status.eds_health_status] | [select(.[] == "%s")] | length`, clusterName, healthStatus) filter := fmt.Sprintf(
`.cluster_statuses[]
| select(.name|contains("%s"))
| [.host_statuses[].health_status.eds_health_status]
| [select(.[] == "%s")]
| length`,
clusterName, healthStatus)
results, err := utils.JQFilter(clusters, filter) results, err := utils.JQFilter(clusters, filter)
require.NoErrorf(r, err, "could not found cluster name %s", clusterName) require.NoErrorf(r, err, "could not found cluster name %s in \n%s", clusterName, clusters)
require.Len(r, results, 1) // the final part of the pipeline is "length" which only ever returns 1 result
resultToString := strings.Join(results, " ") result, err := strconv.Atoi(results[0])
result, err := strconv.Atoi(resultToString)
assert.NoError(r, err) assert.NoError(r, err)
require.Equal(r, count, result) require.Equal(r, count, result, "original results: %v", clusters)
}) })
} }
@ -240,8 +280,12 @@ func AssertEnvoyRunning(t *testing.T, port int) {
func GetEnvoyOutput(port int, path string, query map[string]string) (string, int, error) { func GetEnvoyOutput(port int, path string, query map[string]string) (string, int, error) {
client := cleanhttp.DefaultClient() client := cleanhttp.DefaultClient()
return GetEnvoyOutputWithClient(client, fmt.Sprintf("localhost:%d", port), path, query)
}
func GetEnvoyOutputWithClient(client *http.Client, addr string, path string, query map[string]string) (string, int, error) {
var u url.URL var u url.URL
u.Host = fmt.Sprintf("localhost:%d", port) u.Host = addr
u.Scheme = "http" u.Scheme = "http"
if path != "" { if path != "" {
u.Path = path u.Path = path

View File

@ -14,15 +14,24 @@ import (
// PeeringStatus verifies the peering connection is the specified state with a default retry. // PeeringStatus verifies the peering connection is the specified state with a default retry.
func PeeringStatus(t *testing.T, client *api.Client, peerName string, status api.PeeringState) { func PeeringStatus(t *testing.T, client *api.Client, peerName string, status api.PeeringState) {
PeeringStatusOpts(t, client, peerName, status, nil)
}
// PeeringStatusOpts verifies the peering connection is the specified
// state with a default retry with options.
func PeeringStatusOpts(t *testing.T, client *api.Client, peerName string, status api.PeeringState, opts *api.QueryOptions) {
failer := func() *retry.Timer { failer := func() *retry.Timer {
return &retry.Timer{Timeout: 180 * time.Second, Wait: defaultWait} return &retry.Timer{Timeout: 180 * time.Second, Wait: defaultWait}
} }
retry.RunWith(failer(), t, func(r *retry.R) { retry.RunWith(failer(), t, func(r *retry.R) {
peering, _, err := client.Peerings().Read(context.Background(), peerName, &api.QueryOptions{}) peering, _, err := client.Peerings().Read(context.Background(), peerName, opts)
if err != nil { if err != nil {
r.Fatal("error reading peering data") r.Fatal("error reading peering data")
} }
if peering == nil {
r.Fatal("peering not found")
}
if status != peering.State { if status != peering.State {
r.Fatal("peering state did not match: got ", peering.State, " want ", status) r.Fatal("peering state did not match: got ", peering.State, " want ", status)
} }
@ -31,15 +40,24 @@ func PeeringStatus(t *testing.T, client *api.Client, peerName string, status api
// PeeringExports verifies the correct number of exported services with a default retry. // PeeringExports verifies the correct number of exported services with a default retry.
func PeeringExports(t *testing.T, client *api.Client, peerName string, exports int) { func PeeringExports(t *testing.T, client *api.Client, peerName string, exports int) {
PeeringExportsOpts(t, client, peerName, exports, nil)
}
// PeeringExportsOpts verifies the correct number of exported services
// with a default retry with options.
func PeeringExportsOpts(t *testing.T, client *api.Client, peerName string, exports int, opts *api.QueryOptions) {
failer := func() *retry.Timer { failer := func() *retry.Timer {
return &retry.Timer{Timeout: defaultTimeout, Wait: defaultWait} return &retry.Timer{Timeout: defaultTimeout, Wait: defaultWait}
} }
retry.RunWith(failer(), t, func(r *retry.R) { retry.RunWith(failer(), t, func(r *retry.R) {
peering, _, err := client.Peerings().Read(context.Background(), peerName, &api.QueryOptions{}) peering, _, err := client.Peerings().Read(context.Background(), peerName, opts)
if err != nil { if err != nil {
r.Fatal("error reading peering data") r.Fatal("error reading peering data")
} }
if peering == nil {
r.Fatal("peering not found")
}
if exports != len(peering.StreamStatus.ExportedServices) { if exports != len(peering.StreamStatus.ExportedServices) {
r.Fatal("peering exported services did not match: got ", len(peering.StreamStatus.ExportedServices), " want ", exports) r.Fatal("peering exported services did not match: got ", len(peering.StreamStatus.ExportedServices), " want ", exports)
} }

View File

@ -17,8 +17,9 @@ import (
"github.com/hashicorp/consul/api" "github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/sdk/testutil/retry" "github.com/hashicorp/consul/sdk/testutil/retry"
libservice "github.com/hashicorp/consul/test/integration/consul-container/libs/service"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
libservice "github.com/hashicorp/consul/test/integration/consul-container/libs/service"
) )
const ( const (
@ -55,22 +56,39 @@ func CatalogNodeExists(t *testing.T, c *api.Client, nodeName string) {
func HTTPServiceEchoes(t *testing.T, ip string, port int, path string) { func HTTPServiceEchoes(t *testing.T, ip string, port int, path string) {
doHTTPServiceEchoes(t, ip, port, path, nil) doHTTPServiceEchoes(t, ip, port, path, nil)
} }
func HTTPServiceEchoesWithClient(t *testing.T, client *http.Client, addr string, path string) {
doHTTPServiceEchoesWithClient(t, client, addr, path, nil)
}
func HTTPServiceEchoesResHeader(t *testing.T, ip string, port int, path string, expectedResHeader map[string]string) { func HTTPServiceEchoesResHeader(t *testing.T, ip string, port int, path string, expectedResHeader map[string]string) {
doHTTPServiceEchoes(t, ip, port, path, expectedResHeader) doHTTPServiceEchoes(t, ip, port, path, expectedResHeader)
} }
func HTTPServiceEchoesResHeaderWithClient(t *testing.T, client *http.Client, addr string, path string, expectedResHeader map[string]string) {
doHTTPServiceEchoesWithClient(t, client, addr, path, expectedResHeader)
}
// HTTPServiceEchoes verifies that a post to the given ip/port combination returns the data // HTTPServiceEchoes verifies that a post to the given ip/port combination returns the data
// in the response body. Optional path can be provided to differentiate requests. // in the response body. Optional path can be provided to differentiate requests.
func doHTTPServiceEchoes(t *testing.T, ip string, port int, path string, expectedResHeader map[string]string) { func doHTTPServiceEchoes(t *testing.T, ip string, port int, path string, expectedResHeader map[string]string) {
client := cleanhttp.DefaultClient()
addr := fmt.Sprintf("%s:%d", ip, port)
doHTTPServiceEchoesWithClient(t, client, addr, path, expectedResHeader)
}
func doHTTPServiceEchoesWithClient(
t *testing.T,
client *http.Client,
addr string,
path string,
expectedResHeader map[string]string,
) {
const phrase = "hello" const phrase = "hello"
failer := func() *retry.Timer { failer := func() *retry.Timer {
return &retry.Timer{Timeout: defaultHTTPTimeout, Wait: defaultHTTPWait} return &retry.Timer{Timeout: defaultHTTPTimeout, Wait: defaultHTTPWait}
} }
client := cleanhttp.DefaultClient() url := "http://" + addr
url := fmt.Sprintf("http://%s:%d", ip, port)
if path != "" { if path != "" {
url += "/" + path url += "/" + path
@ -85,6 +103,10 @@ func doHTTPServiceEchoes(t *testing.T, ip string, port int, path string, expecte
} }
defer res.Body.Close() defer res.Body.Close()
statusCode := res.StatusCode
t.Logf("...got response code %d", statusCode)
require.Equal(r, 200, statusCode)
body, err := io.ReadAll(res.Body) body, err := io.ReadAll(res.Body)
if err != nil { if err != nil {
r.Fatal("could not read response body ", url) r.Fatal("could not read response body ", url)
@ -166,7 +188,7 @@ func AssertFortioNameWithClient(t *testing.T, urlbase string, name string, reqHo
m := fortioNameRE.FindStringSubmatch(string(body)) m := fortioNameRE.FindStringSubmatch(string(body))
require.GreaterOrEqual(r, len(m), 2) require.GreaterOrEqual(r, len(m), 2)
t.Logf("got response from server name %s", m[1]) t.Logf("got response from server name %q expect %q", m[1], name)
assert.Equal(r, name, m[1]) assert.Equal(r, name, m[1])
}) })
} }

View File

@ -0,0 +1,32 @@
package utils
import "github.com/hashicorp/consul/api"
func PartitionOrDefault(name string) string {
if name == "" {
return "default"
}
return name
}
func NamespaceOrDefault(name string) string {
if name == "" {
return "default"
}
return name
}
func DefaultToEmpty(name string) string {
if name == "default" {
return ""
}
return name
}
// PartitionQueryOptions returns an *api.QueryOptions with the given partition
// field set only if the partition is non-default. This helps when writing
// tests for joint use in OSS and ENT.
func PartitionQueryOptions(partition string) *api.QueryOptions {
return &api.QueryOptions{
Partition: DefaultToEmpty(partition),
}
}

View File

@ -55,6 +55,8 @@ func GetLatestImageName() string {
return LatestImageName return LatestImageName
} }
func IsEnterprise() bool { return isInEnterpriseRepo }
func DockerImage(image, version string) string { func DockerImage(image, version string) string {
v := image + ":" + version v := image + ":" + version
if strings.Contains(image, DefaultImageNameENT) && isSemVer(version) { if strings.Contains(image, DefaultImageNameENT) && isSemVer(version) {

View File

@ -9,4 +9,5 @@ package utils
const ( const (
defaultImageName = DefaultImageNameOSS defaultImageName = DefaultImageNameOSS
ImageVersionSuffix = "" ImageVersionSuffix = ""
isInEnterpriseRepo = false
) )