From 00a9e03f447c670b145bb08f819987caeb0c1e21 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" <4903+rboyer@users.noreply.github.com> Date: Tue, 16 May 2023 14:57:24 -0500 Subject: [PATCH] test: slight refactoring ahead of peering testing improvements (#17387) --- .../consul-container/libs/assert/envoy.go | 70 +++++++++++++++---- .../consul-container/libs/assert/peering.go | 22 +++++- .../consul-container/libs/assert/service.go | 30 ++++++-- .../consul-container/libs/utils/tenancy.go | 32 +++++++++ .../consul-container/libs/utils/version.go | 2 + .../libs/utils/version_oss.go | 1 + 6 files changed, 138 insertions(+), 19 deletions(-) create mode 100644 test/integration/consul-container/libs/utils/tenancy.go diff --git a/test/integration/consul-container/libs/assert/envoy.go b/test/integration/consul-container/libs/assert/envoy.go index ce6dd9454..eef407c25 100644 --- a/test/integration/consul-container/libs/assert/envoy.go +++ b/test/integration/consul-container/libs/assert/envoy.go @@ -6,6 +6,7 @@ package assert import ( "fmt" "io" + "net/http" "net/url" "regexp" "strconv" @@ -14,15 +15,29 @@ import ( "time" "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/stretchr/testify/assert" "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 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 ( dump string err error @@ -32,7 +47,7 @@ func GetEnvoyListenerTCPFilters(t *testing.T, adminPort int) { } 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 { 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") } +// 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 func AssertUpstreamEndpointStatus(t *testing.T, adminPort int, clusterName, healthStatus string, count int) { - var ( - clusters string - err error + require.True(t, adminPort > 0) + AssertUpstreamEndpointStatusWithClient( + 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 { return &retry.Timer{Timeout: 30 * time.Second, Wait: 500 * time.Millisecond} } 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 { 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) - 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(resultToString) + result, err := strconv.Atoi(results[0]) 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) { 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 - u.Host = fmt.Sprintf("localhost:%d", port) + u.Host = addr u.Scheme = "http" if path != "" { u.Path = path diff --git a/test/integration/consul-container/libs/assert/peering.go b/test/integration/consul-container/libs/assert/peering.go index 5432b62f8..2cf842a4a 100644 --- a/test/integration/consul-container/libs/assert/peering.go +++ b/test/integration/consul-container/libs/assert/peering.go @@ -14,15 +14,24 @@ import ( // 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) { + 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 { return &retry.Timer{Timeout: 180 * time.Second, Wait: defaultWait} } 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 { r.Fatal("error reading peering data") } + if peering == nil { + r.Fatal("peering not found") + } if status != peering.State { 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. 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 { return &retry.Timer{Timeout: defaultTimeout, Wait: defaultWait} } 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 { r.Fatal("error reading peering data") } + if peering == nil { + r.Fatal("peering not found") + } if exports != len(peering.StreamStatus.ExportedServices) { r.Fatal("peering exported services did not match: got ", len(peering.StreamStatus.ExportedServices), " want ", exports) } diff --git a/test/integration/consul-container/libs/assert/service.go b/test/integration/consul-container/libs/assert/service.go index b20f9d202..57e853e1e 100644 --- a/test/integration/consul-container/libs/assert/service.go +++ b/test/integration/consul-container/libs/assert/service.go @@ -17,8 +17,9 @@ import ( "github.com/hashicorp/consul/api" "github.com/hashicorp/consul/sdk/testutil/retry" - libservice "github.com/hashicorp/consul/test/integration/consul-container/libs/service" "github.com/stretchr/testify/assert" + + libservice "github.com/hashicorp/consul/test/integration/consul-container/libs/service" ) 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) { 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) { 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 // 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) { + 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" failer := func() *retry.Timer { return &retry.Timer{Timeout: defaultHTTPTimeout, Wait: defaultHTTPWait} } - client := cleanhttp.DefaultClient() - url := fmt.Sprintf("http://%s:%d", ip, port) + url := "http://" + addr if path != "" { url += "/" + path @@ -85,6 +103,10 @@ func doHTTPServiceEchoes(t *testing.T, ip string, port int, path string, expecte } 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) if err != nil { 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)) 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]) }) } diff --git a/test/integration/consul-container/libs/utils/tenancy.go b/test/integration/consul-container/libs/utils/tenancy.go new file mode 100644 index 000000000..c116a55a4 --- /dev/null +++ b/test/integration/consul-container/libs/utils/tenancy.go @@ -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), + } +} diff --git a/test/integration/consul-container/libs/utils/version.go b/test/integration/consul-container/libs/utils/version.go index 04f872e24..394cae35b 100644 --- a/test/integration/consul-container/libs/utils/version.go +++ b/test/integration/consul-container/libs/utils/version.go @@ -55,6 +55,8 @@ func GetLatestImageName() string { return LatestImageName } +func IsEnterprise() bool { return isInEnterpriseRepo } + func DockerImage(image, version string) string { v := image + ":" + version if strings.Contains(image, DefaultImageNameENT) && isSemVer(version) { diff --git a/test/integration/consul-container/libs/utils/version_oss.go b/test/integration/consul-container/libs/utils/version_oss.go index 709221cc4..9fefeebf4 100644 --- a/test/integration/consul-container/libs/utils/version_oss.go +++ b/test/integration/consul-container/libs/utils/version_oss.go @@ -9,4 +9,5 @@ package utils const ( defaultImageName = DefaultImageNameOSS ImageVersionSuffix = "" + isInEnterpriseRepo = false )