mirror of https://github.com/hashicorp/consul
Browse Source
* Implement the Catalog V2 controller integration container tests This now allows the container tests to import things from the root module. However for now we want to be very restrictive about which packages we allow importing. * Add an upgrade test for the new catalog Currently this should be dormant and not executed. However its put in place to detect breaking changes in the future and show an example of how to do an upgrade test with integration tests structured like catalog v2. * Make testutil.Retry capable of performing cleanup operations These cleanup operations are executed after each retry attempt. * Move TestContext to taking an interface instead of a concrete testing.T This allows this to be used on a retry.R or generally anything that meets the interface. * Move to using TestContext instead of background contexts Also this forces all test methods to implement the Cleanup method now instead of that being an optional interface. Co-authored-by: Daniel Upton <daniel@floppy.co>aahel
Matt Keeler
1 year ago
committed by
GitHub
18 changed files with 613 additions and 113 deletions
@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env bash |
||||
# Copyright (c) HashiCorp, Inc. |
||||
# SPDX-License-Identifier: MPL-2.0 |
||||
|
||||
|
||||
readonly SCRIPT_NAME="$(basename ${BASH_SOURCE[0]})" |
||||
readonly SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")" |
||||
readonly SOURCE_DIR="$(dirname "$(dirname "${SCRIPT_DIR}")")" |
||||
readonly FN_DIR="$(dirname "${SCRIPT_DIR}")/functions" |
||||
|
||||
source "${SCRIPT_DIR}/functions.sh" |
||||
|
||||
|
||||
set -uo pipefail |
||||
|
||||
usage() { |
||||
cat <<-EOF |
||||
Usage: ${SCRIPT_NAME} <module root> [<allowed relative package path>...] |
||||
|
||||
Description: |
||||
Verifies that only the specified packages may be imported from the given module |
||||
|
||||
Options: |
||||
-h | --help Print this help text. |
||||
EOF |
||||
} |
||||
|
||||
function err_usage { |
||||
err "$1" |
||||
err "" |
||||
err "$(usage)" |
||||
} |
||||
|
||||
function main { |
||||
local module_root="" |
||||
declare -a allowed_packages=() |
||||
while test $# -gt 0 |
||||
do |
||||
case "$1" in |
||||
-h | --help ) |
||||
usage |
||||
return 0 |
||||
;; |
||||
* ) |
||||
if test -z "$module_root" |
||||
then |
||||
module_root="$1" |
||||
else |
||||
allowed_packages+="$1" |
||||
fi |
||||
shift |
||||
esac |
||||
done |
||||
|
||||
# If we could guarantee this ran with bash 4.2+ then the final argument could |
||||
# be just ${allowed_packages[@]}. However that with older versions of bash |
||||
# in combination with set -u causes bash to emit errors about using unbound |
||||
# variables when no allowed packages have been specified (i.e. the module should |
||||
# generally be disallowed with no exceptions). This syntax is very strange |
||||
# but seems to be the prescribed workaround I found. |
||||
check_imports "$module_root" ${allowed_packages[@]+"${allowed_packages[@]}"} |
||||
return $? |
||||
} |
||||
|
||||
function check_imports { |
||||
local module_root="$1" |
||||
shift |
||||
local allowed_packages="$@" |
||||
|
||||
module_imports=$( go list -test -f '{{join .TestImports "\n"}}' ./... | grep "$module_root" | sort | uniq) |
||||
module_test_imports=$( go list -test -f '{{join .TestImports "\n"}}' ./... | grep "$module_root" | sort | uniq) |
||||
|
||||
any_error=0 |
||||
|
||||
for imp in $module_imports |
||||
do |
||||
is_import_allowed "$imp" "$module_root" $allowed_packages |
||||
allowed=$? |
||||
|
||||
if test $any_error -ne 1 |
||||
then |
||||
any_error=$allowed |
||||
fi |
||||
done |
||||
|
||||
if test $any_error -eq 1 |
||||
then |
||||
echo "Only the following direct imports are allowed from module $module_root:" |
||||
for pkg in $allowed_packages |
||||
do |
||||
echo " * $pkg" |
||||
done |
||||
fi |
||||
|
||||
return $any_error |
||||
} |
||||
|
||||
function is_import_allowed { |
||||
local pkg_import=$1 |
||||
shift |
||||
local module_root=$1 |
||||
shift |
||||
local allowed_packages="$@" |
||||
|
||||
# check if the import path is a part of the module we are restricting imports for |
||||
if test "$( go list -f '{{.Module.Path}}' $pkg_import)" != "$module_root" |
||||
then |
||||
return 0 |
||||
fi |
||||
|
||||
for pkg in $allowed_packages |
||||
do |
||||
if test "${module_root}/$pkg" == "$pkg_import" |
||||
then |
||||
return 0 |
||||
fi |
||||
done |
||||
|
||||
err "Import of package $pkg_import is not allowed" |
||||
return 1 |
||||
} |
||||
|
||||
main "$@" |
||||
exit $? |
@ -0,0 +1,23 @@
|
||||
package retry |
||||
|
||||
import "time" |
||||
|
||||
// Counter repeats an operation a given number of
|
||||
// times and waits between subsequent operations.
|
||||
type Counter struct { |
||||
Count int |
||||
Wait time.Duration |
||||
|
||||
count int |
||||
} |
||||
|
||||
func (r *Counter) Continue() bool { |
||||
if r.count == r.Count { |
||||
return false |
||||
} |
||||
if r.count > 0 { |
||||
time.Sleep(r.Wait) |
||||
} |
||||
r.count++ |
||||
return true |
||||
} |
@ -0,0 +1,37 @@
|
||||
package retry |
||||
|
||||
import "time" |
||||
|
||||
// TwoSeconds repeats an operation for two seconds and waits 25ms in between.
|
||||
func TwoSeconds() *Timer { |
||||
return &Timer{Timeout: 2 * time.Second, Wait: 25 * time.Millisecond} |
||||
} |
||||
|
||||
// ThreeTimes repeats an operation three times and waits 25ms in between.
|
||||
func ThreeTimes() *Counter { |
||||
return &Counter{Count: 3, Wait: 25 * time.Millisecond} |
||||
} |
||||
|
||||
// Timer repeats an operation for a given amount
|
||||
// of time and waits between subsequent operations.
|
||||
type Timer struct { |
||||
Timeout time.Duration |
||||
Wait time.Duration |
||||
|
||||
// stop is the timeout deadline.
|
||||
// TODO: Next()?
|
||||
// Set on the first invocation of Next().
|
||||
stop time.Time |
||||
} |
||||
|
||||
func (r *Timer) Continue() bool { |
||||
if r.stop.IsZero() { |
||||
r.stop = time.Now().Add(r.Timeout) |
||||
return true |
||||
} |
||||
if time.Now().After(r.stop) { |
||||
return false |
||||
} |
||||
time.Sleep(r.Wait) |
||||
return true |
||||
} |
@ -0,0 +1,35 @@
|
||||
package catalog |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
|
||||
libcluster "github.com/hashicorp/consul/test/integration/consul-container/libs/cluster" |
||||
libtopology "github.com/hashicorp/consul/test/integration/consul-container/libs/topology" |
||||
|
||||
"github.com/hashicorp/consul/internal/catalog/catalogtest" |
||||
pbresource "github.com/hashicorp/consul/proto-public/pbresource" |
||||
) |
||||
|
||||
func TestCatalog(t *testing.T) { |
||||
t.Parallel() |
||||
|
||||
cluster, _, _ := libtopology.NewCluster(t, &libtopology.ClusterConfig{ |
||||
NumServers: 3, |
||||
BuildOpts: &libcluster.BuildOptions{Datacenter: "dc1"}, |
||||
Cmd: `-hcl=experiments=["resource-apis"]`, |
||||
}) |
||||
|
||||
followers, err := cluster.Followers() |
||||
require.NoError(t, err) |
||||
client := pbresource.NewResourceServiceClient(followers[0].GetGRPCConn()) |
||||
|
||||
t.Run("one-shot", func(t *testing.T) { |
||||
catalogtest.RunCatalogV1Alpha1IntegrationTest(t, client) |
||||
}) |
||||
|
||||
t.Run("lifecycle", func(t *testing.T) { |
||||
catalogtest.RunCatalogV1Alpha1LifecycleIntegrationTest(t, client) |
||||
}) |
||||
} |
@ -0,0 +1,87 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package catalog |
||||
|
||||
import ( |
||||
"context" |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/require" |
||||
|
||||
"github.com/hashicorp/consul/internal/catalog/catalogtest" |
||||
"github.com/hashicorp/consul/proto-public/pbresource" |
||||
libcluster "github.com/hashicorp/consul/test/integration/consul-container/libs/cluster" |
||||
"github.com/hashicorp/consul/test/integration/consul-container/libs/topology" |
||||
"github.com/hashicorp/consul/test/integration/consul-container/libs/utils" |
||||
"github.com/hashicorp/go-version" |
||||
) |
||||
|
||||
var minCatalogResourceVersion = version.Must(version.NewVersion("v1.16.0")) |
||||
|
||||
const ( |
||||
versionUndetermined = ` |
||||
Cannot determine the actual version the starting image represents. |
||||
Scrutinze test failures to ensure that the starting version should |
||||
actually be able to be used for creating the initial data set. |
||||
` |
||||
) |
||||
|
||||
func maybeSkipUpgradeTest(t *testing.T, minVersion *version.Version) { |
||||
t.Helper() |
||||
|
||||
image := utils.DockerImage(utils.GetLatestImageName(), utils.LatestVersion) |
||||
latestVersion, err := utils.DockerImageVersion(image) |
||||
|
||||
if latestVersion != nil && latestVersion.LessThan(minVersion) { |
||||
t.Skipf("Upgrade test isn't applicable with version %q as the starting version", latestVersion.String()) |
||||
} |
||||
|
||||
if err != nil || latestVersion == nil { |
||||
t.Log(versionUndetermined) |
||||
} |
||||
} |
||||
|
||||
// Test upgrade a cluster of latest version to the target version and ensure that the catalog still
|
||||
// functions properly. Note
|
||||
func TestCatalogUpgrade(t *testing.T) { |
||||
maybeSkipUpgradeTest(t, minCatalogResourceVersion) |
||||
t.Parallel() |
||||
|
||||
const numServers = 1 |
||||
buildOpts := &libcluster.BuildOptions{ |
||||
ConsulImageName: utils.GetLatestImageName(), |
||||
ConsulVersion: utils.LatestVersion, |
||||
Datacenter: "dc1", |
||||
InjectAutoEncryption: true, |
||||
} |
||||
|
||||
cluster, _, _ := topology.NewCluster(t, &topology.ClusterConfig{ |
||||
NumServers: 1, |
||||
BuildOpts: buildOpts, |
||||
ApplyDefaultProxySettings: true, |
||||
Cmd: `-hcl=experiments=["resource-apis"]`, |
||||
}) |
||||
|
||||
client := cluster.APIClient(0) |
||||
|
||||
libcluster.WaitForLeader(t, cluster, client) |
||||
libcluster.WaitForMembers(t, client, numServers) |
||||
|
||||
leader, err := cluster.Leader() |
||||
require.NoError(t, err) |
||||
rscClient := pbresource.NewResourceServiceClient(leader.GetGRPCConn()) |
||||
|
||||
// Initialize some data
|
||||
catalogtest.PublishCatalogV1Alpha1IntegrationTestData(t, rscClient) |
||||
|
||||
// upgrade the cluster to the Target version
|
||||
t.Logf("initiating standard upgrade to version=%q", utils.TargetVersion) |
||||
err = cluster.StandardUpgrade(t, context.Background(), utils.GetTargetImageName(), utils.TargetVersion) |
||||
|
||||
require.NoError(t, err) |
||||
libcluster.WaitForLeader(t, cluster, client) |
||||
libcluster.WaitForMembers(t, client, numServers) |
||||
|
||||
catalogtest.VerifyCatalogV1Alpha1IntegrationTestResults(t, rscClient) |
||||
} |
Loading…
Reference in new issue