2017-05-21 07:10:52 +00:00
|
|
|
package agent
|
|
|
|
|
|
|
|
import (
|
2019-09-27 21:06:43 +00:00
|
|
|
"bytes"
|
2020-06-19 19:16:00 +00:00
|
|
|
"context"
|
2020-06-10 20:47:35 +00:00
|
|
|
"crypto/x509"
|
2017-05-21 07:10:52 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"math/rand"
|
2020-09-04 18:53:02 +00:00
|
|
|
"net"
|
2017-05-21 07:10:52 +00:00
|
|
|
"net/http/httptest"
|
|
|
|
"path/filepath"
|
2017-05-23 14:03:52 +00:00
|
|
|
"strconv"
|
2019-02-14 15:59:14 +00:00
|
|
|
"testing"
|
2019-09-27 21:06:43 +00:00
|
|
|
"text/template"
|
2017-05-21 07:10:52 +00:00
|
|
|
"time"
|
|
|
|
|
2017-09-26 06:11:19 +00:00
|
|
|
metrics "github.com/armon/go-metrics"
|
2020-01-28 23:50:41 +00:00
|
|
|
"github.com/hashicorp/go-hclog"
|
2018-05-09 16:15:29 +00:00
|
|
|
uuid "github.com/hashicorp/go-uuid"
|
|
|
|
|
2020-06-10 20:47:35 +00:00
|
|
|
"github.com/hashicorp/consul/acl"
|
2017-09-25 18:40:42 +00:00
|
|
|
"github.com/hashicorp/consul/agent/config"
|
2018-05-10 16:04:33 +00:00
|
|
|
"github.com/hashicorp/consul/agent/connect"
|
pkg refactor
command/agent/* -> agent/*
command/consul/* -> agent/consul/*
command/agent/command{,_test}.go -> command/agent{,_test}.go
command/base/command.go -> command/base.go
command/base/* -> command/*
commands.go -> command/commands.go
The script which did the refactor is:
(
cd $GOPATH/src/github.com/hashicorp/consul
git mv command/agent/command.go command/agent.go
git mv command/agent/command_test.go command/agent_test.go
git mv command/agent/flag_slice_value{,_test}.go command/
git mv command/agent .
git mv command/base/command.go command/base.go
git mv command/base/config_util{,_test}.go command/
git mv commands.go command/
git mv consul agent
rmdir command/base/
gsed -i -e 's|package agent|package command|' command/agent{,_test}.go
gsed -i -e 's|package agent|package command|' command/flag_slice_value{,_test}.go
gsed -i -e 's|package base|package command|' command/base.go command/config_util{,_test}.go
gsed -i -e 's|package main|package command|' command/commands.go
gsed -i -e 's|base.Command|BaseCommand|' command/commands.go
gsed -i -e 's|agent.Command|AgentCommand|' command/commands.go
gsed -i -e 's|\tCommand:|\tBaseCommand:|' command/commands.go
gsed -i -e 's|base\.||' command/commands.go
gsed -i -e 's|command\.||' command/commands.go
gsed -i -e 's|command|c|' main.go
gsed -i -e 's|range Commands|range command.Commands|' main.go
gsed -i -e 's|Commands: Commands|Commands: command.Commands|' main.go
gsed -i -e 's|base\.BoolValue|BoolValue|' command/operator_autopilot_set.go
gsed -i -e 's|base\.DurationValue|DurationValue|' command/operator_autopilot_set.go
gsed -i -e 's|base\.StringValue|StringValue|' command/operator_autopilot_set.go
gsed -i -e 's|base\.UintValue|UintValue|' command/operator_autopilot_set.go
gsed -i -e 's|\bCommand\b|BaseCommand|' command/base.go
gsed -i -e 's|BaseCommand Options|Command Options|' command/base.go
gsed -i -e 's|base.Command|BaseCommand|' command/*.go
gsed -i -e 's|c\.Command|c.BaseCommand|g' command/*.go
gsed -i -e 's|\tCommand:|\tBaseCommand:|' command/*_test.go
gsed -i -e 's|base\.||' command/*_test.go
gsed -i -e 's|\bCommand\b|AgentCommand|' command/agent{,_test}.go
gsed -i -e 's|cmd.AgentCommand|cmd.BaseCommand|' command/agent.go
gsed -i -e 's|cli.AgentCommand = new(Command)|cli.Command = new(AgentCommand)|' command/agent_test.go
gsed -i -e 's|exec.AgentCommand|exec.Command|' command/agent_test.go
gsed -i -e 's|exec.BaseCommand|exec.Command|' command/agent_test.go
gsed -i -e 's|NewTestAgent|agent.NewTestAgent|' command/agent_test.go
gsed -i -e 's|= TestConfig|= agent.TestConfig|' command/agent_test.go
gsed -i -e 's|: RetryJoin|: agent.RetryJoin|' command/agent_test.go
gsed -i -e 's|\.\./\.\./|../|' command/config_util_test.go
gsed -i -e 's|\bverifyUniqueListeners|VerifyUniqueListeners|' agent/config{,_test}.go command/agent.go
gsed -i -e 's|\bserfLANKeyring\b|SerfLANKeyring|g' agent/{agent,keyring,testagent}.go command/agent.go
gsed -i -e 's|\bserfWANKeyring\b|SerfWANKeyring|g' agent/{agent,keyring,testagent}.go command/agent.go
gsed -i -e 's|\bNewAgent\b|agent.New|g' command/agent{,_test}.go
gsed -i -e 's|\bNewAgent|New|' agent/{acl_test,agent,testagent}.go
gsed -i -e 's|\bAgent\b|agent.&|g' command/agent{,_test}.go
gsed -i -e 's|\bBool\b|agent.&|g' command/agent{,_test}.go
gsed -i -e 's|\bConfig\b|agent.&|g' command/agent{,_test}.go
gsed -i -e 's|\bDefaultConfig\b|agent.&|g' command/agent{,_test}.go
gsed -i -e 's|\bDevConfig\b|agent.&|g' command/agent{,_test}.go
gsed -i -e 's|\bMergeConfig\b|agent.&|g' command/agent{,_test}.go
gsed -i -e 's|\bReadConfigPaths\b|agent.&|g' command/agent{,_test}.go
gsed -i -e 's|\bParseMetaPair\b|agent.&|g' command/agent{,_test}.go
gsed -i -e 's|\bSerfLANKeyring\b|agent.&|g' command/agent{,_test}.go
gsed -i -e 's|\bSerfWANKeyring\b|agent.&|g' command/agent{,_test}.go
gsed -i -e 's|circonus\.agent|circonus|g' command/agent{,_test}.go
gsed -i -e 's|logger\.agent|logger|g' command/agent{,_test}.go
gsed -i -e 's|metrics\.agent|metrics|g' command/agent{,_test}.go
gsed -i -e 's|// agent.Agent|// agent|' command/agent{,_test}.go
gsed -i -e 's|a\.agent\.Config|a.Config|' command/agent{,_test}.go
gsed -i -e 's|agent\.AppendSliceValue|AppendSliceValue|' command/{configtest,validate}.go
gsed -i -e 's|consul/consul|agent/consul|' GNUmakefile
gsed -i -e 's|\.\./test|../../test|' agent/consul/server_test.go
# fix imports
f=$(grep -rl 'github.com/hashicorp/consul/command/agent' * | grep '\.go')
gsed -i -e 's|github.com/hashicorp/consul/command/agent|github.com/hashicorp/consul/agent|' $f
goimports -w $f
f=$(grep -rl 'github.com/hashicorp/consul/consul' * | grep '\.go')
gsed -i -e 's|github.com/hashicorp/consul/consul|github.com/hashicorp/consul/agent/consul|' $f
goimports -w $f
goimports -w command/*.go main.go
)
2017-06-09 22:28:28 +00:00
|
|
|
"github.com/hashicorp/consul/agent/consul"
|
2017-07-06 10:34:00 +00:00
|
|
|
"github.com/hashicorp/consul/agent/structs"
|
2017-05-22 11:59:36 +00:00
|
|
|
"github.com/hashicorp/consul/api"
|
2022-05-19 20:03:46 +00:00
|
|
|
"github.com/hashicorp/consul/lib"
|
2019-04-24 13:11:08 +00:00
|
|
|
"github.com/hashicorp/consul/sdk/freeport"
|
2020-11-13 20:05:16 +00:00
|
|
|
"github.com/hashicorp/consul/sdk/testutil"
|
2019-03-27 12:54:56 +00:00
|
|
|
"github.com/hashicorp/consul/sdk/testutil/retry"
|
2020-06-10 20:47:35 +00:00
|
|
|
"github.com/hashicorp/consul/tlsutil"
|
2017-05-21 07:10:52 +00:00
|
|
|
)
|
|
|
|
|
2017-05-21 07:54:40 +00:00
|
|
|
func init() {
|
|
|
|
rand.Seed(time.Now().UnixNano()) // seed random number generator
|
|
|
|
}
|
|
|
|
|
2017-05-21 07:10:52 +00:00
|
|
|
// TestAgent encapsulates an Agent with a default configuration and
|
|
|
|
// startup procedure suitable for testing. It panics if there are errors
|
|
|
|
// during creation or startup instead of returning errors. It manages a
|
|
|
|
// temporary data directory which is removed after shutdown.
|
|
|
|
type TestAgent struct {
|
|
|
|
// Name is an optional name of the agent.
|
|
|
|
Name string
|
|
|
|
|
2022-03-31 19:11:49 +00:00
|
|
|
configFiles []string
|
|
|
|
HCL string
|
2017-09-25 18:40:42 +00:00
|
|
|
|
2017-05-21 07:10:52 +00:00
|
|
|
// Config is the agent configuration. If Config is nil then
|
|
|
|
// TestConfig() is used. If Config.DataDir is set then it is
|
|
|
|
// the callers responsibility to clean up the data directory.
|
|
|
|
// Otherwise, a temporary data directory is created and removed
|
|
|
|
// when Shutdown() is called.
|
2017-09-25 18:40:42 +00:00
|
|
|
Config *config.RuntimeConfig
|
2017-05-21 07:10:52 +00:00
|
|
|
|
2020-11-13 20:05:16 +00:00
|
|
|
// LogOutput is the sink for the logs. If nil, logs are written to os.Stderr.
|
2021-06-08 23:11:34 +00:00
|
|
|
// The io.Writer must allow concurrent reads and writes. Note that
|
|
|
|
// bytes.Buffer is not safe for concurrent reads and writes.
|
2017-05-21 07:10:52 +00:00
|
|
|
LogOutput io.Writer
|
2022-02-03 22:07:39 +00:00
|
|
|
LogLevel hclog.Level
|
2017-05-21 07:10:52 +00:00
|
|
|
|
2020-08-13 21:17:21 +00:00
|
|
|
// DataDir may be set to a directory which exists. If is it not set,
|
|
|
|
// TestAgent.Start will create one and set DataDir to the directory path.
|
|
|
|
// In all cases the agent will be configured to use this path as the data directory,
|
|
|
|
// and the directory will be removed once the test ends.
|
2017-05-21 07:10:52 +00:00
|
|
|
DataDir string
|
|
|
|
|
2022-08-19 17:07:22 +00:00
|
|
|
// UseHTTPS, if true, will disable the HTTP port and enable the HTTPS
|
2017-11-07 23:06:59 +00:00
|
|
|
// one.
|
2022-08-19 17:07:22 +00:00
|
|
|
UseHTTPS bool
|
|
|
|
|
|
|
|
// UseGRPCTLS, if true, will disable the GRPC port and enable the GRPC+TLS
|
|
|
|
// one.
|
|
|
|
UseGRPCTLS bool
|
2017-11-07 23:06:59 +00:00
|
|
|
|
2017-05-21 07:10:52 +00:00
|
|
|
// dns is a reference to the first started DNS endpoint.
|
|
|
|
// It is valid after Start().
|
|
|
|
dns *DNSServer
|
|
|
|
|
2020-09-04 18:42:15 +00:00
|
|
|
// srv is an HTTPHandlers that may be used to test http endpoints.
|
|
|
|
srv *HTTPHandlers
|
2017-05-21 07:10:52 +00:00
|
|
|
|
2020-06-10 20:47:35 +00:00
|
|
|
// overrides is an hcl config source to use to override otherwise
|
|
|
|
// non-user settable configurations
|
|
|
|
Overrides string
|
|
|
|
|
2022-09-26 18:58:15 +00:00
|
|
|
// allows the BaseDeps to be modified before starting the embedded agent
|
|
|
|
OverrideDeps func(deps *BaseDeps)
|
|
|
|
|
2017-05-21 07:10:52 +00:00
|
|
|
// Agent is the embedded consul agent.
|
|
|
|
// It is valid after Start().
|
|
|
|
*Agent
|
|
|
|
}
|
|
|
|
|
2020-03-31 19:59:56 +00:00
|
|
|
// NewTestAgent returns a started agent with the given configuration. It fails
|
|
|
|
// the test if the Agent could not be started.
|
|
|
|
func NewTestAgent(t *testing.T, hcl string) *TestAgent {
|
2021-05-20 14:07:23 +00:00
|
|
|
a := StartTestAgent(t, TestAgent{HCL: hcl})
|
|
|
|
t.Cleanup(func() { a.Shutdown() })
|
|
|
|
return a
|
2019-09-05 17:24:36 +00:00
|
|
|
}
|
|
|
|
|
2022-03-31 19:11:49 +00:00
|
|
|
// NewTestAgent returns a started agent with the given configuration. It fails
|
|
|
|
// the test if the Agent could not be started.
|
|
|
|
// The caller is responsible for calling Shutdown() to stop the agent and remove
|
|
|
|
// temporary directories.
|
|
|
|
func NewTestAgentWithConfigFile(t *testing.T, hcl string, configFiles []string) *TestAgent {
|
|
|
|
a := StartTestAgent(t, TestAgent{configFiles: configFiles, HCL: hcl})
|
|
|
|
t.Cleanup(func() { a.Shutdown() })
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
|
2020-03-31 20:24:39 +00:00
|
|
|
// StartTestAgent and wait for it to become available. If the agent fails to
|
|
|
|
// start the test will be marked failed and execution will stop.
|
|
|
|
//
|
|
|
|
// The caller is responsible for calling Shutdown() to stop the agent and remove
|
|
|
|
// temporary directories.
|
|
|
|
func StartTestAgent(t *testing.T, a TestAgent) *TestAgent {
|
2020-06-10 20:47:35 +00:00
|
|
|
t.Helper()
|
2019-09-03 22:05:51 +00:00
|
|
|
retry.RunWith(retry.ThreeTimes(), t, func(r *retry.R) {
|
2021-05-10 17:20:45 +00:00
|
|
|
t.Helper()
|
2020-03-30 20:05:27 +00:00
|
|
|
if err := a.Start(t); err != nil {
|
2019-09-03 22:05:51 +00:00
|
|
|
r.Fatal(err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2019-09-05 17:24:36 +00:00
|
|
|
return &a
|
2019-03-13 09:29:06 +00:00
|
|
|
}
|
|
|
|
|
2020-06-10 20:47:35 +00:00
|
|
|
func TestConfigHCL(nodeID string) string {
|
|
|
|
return fmt.Sprintf(`
|
|
|
|
bind_addr = "127.0.0.1"
|
|
|
|
advertise_addr = "127.0.0.1"
|
|
|
|
datacenter = "dc1"
|
|
|
|
bootstrap = true
|
|
|
|
server = true
|
|
|
|
node_id = "%[1]s"
|
|
|
|
node_name = "Node-%[1]s"
|
|
|
|
connect {
|
|
|
|
enabled = true
|
|
|
|
ca_config {
|
|
|
|
cluster_id = "%[2]s"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
performance {
|
|
|
|
raft_multiplier = 1
|
2022-08-01 19:22:36 +00:00
|
|
|
}
|
|
|
|
peering {
|
|
|
|
enabled = true
|
2020-06-10 20:47:35 +00:00
|
|
|
}`, nodeID, connect.TestClusterID,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-09-03 22:05:51 +00:00
|
|
|
// Start starts a test agent. It returns an error if the agent could not be started.
|
|
|
|
// If no error is returned, the caller must call Shutdown() when finished.
|
2021-05-10 17:20:45 +00:00
|
|
|
func (a *TestAgent) Start(t *testing.T) error {
|
2020-06-10 20:47:35 +00:00
|
|
|
t.Helper()
|
2019-09-03 22:05:51 +00:00
|
|
|
if a.Agent != nil {
|
|
|
|
return fmt.Errorf("TestAgent already started")
|
|
|
|
}
|
|
|
|
|
2020-03-30 20:05:27 +00:00
|
|
|
name := a.Name
|
2020-03-31 20:12:33 +00:00
|
|
|
if name == "" {
|
2020-03-30 20:05:27 +00:00
|
|
|
name = "TestAgent"
|
|
|
|
}
|
|
|
|
|
2017-09-25 18:40:42 +00:00
|
|
|
if a.DataDir == "" {
|
2020-08-13 21:17:21 +00:00
|
|
|
dirname := name + "-agent"
|
|
|
|
a.DataDir = testutil.TempDir(t, dirname)
|
2017-05-21 07:10:52 +00:00
|
|
|
}
|
2020-08-13 21:17:21 +00:00
|
|
|
// Convert windows style path to posix style path to avoid illegal char escape
|
|
|
|
// error when hcl parsing.
|
|
|
|
d := filepath.ToSlash(a.DataDir)
|
|
|
|
hclDataDir := fmt.Sprintf(`data_dir = "%s"`, d)
|
2017-05-23 14:03:52 +00:00
|
|
|
|
2019-12-06 19:01:34 +00:00
|
|
|
logOutput := a.LogOutput
|
|
|
|
if logOutput == nil {
|
2020-05-06 20:40:16 +00:00
|
|
|
logOutput = testutil.NewLogBuffer(t)
|
2019-12-06 19:01:34 +00:00
|
|
|
}
|
2020-01-28 23:50:41 +00:00
|
|
|
|
2022-02-03 22:07:39 +00:00
|
|
|
if a.LogLevel == 0 {
|
|
|
|
a.LogLevel = testutil.TestLogLevel
|
|
|
|
}
|
|
|
|
|
2020-01-28 23:50:41 +00:00
|
|
|
logger := hclog.NewInterceptLogger(&hclog.LoggerOptions{
|
2022-02-03 22:07:39 +00:00
|
|
|
Level: a.LogLevel,
|
2020-03-30 17:23:13 +00:00
|
|
|
Output: logOutput,
|
|
|
|
TimeFormat: "04:05.000",
|
2020-03-30 20:05:27 +00:00
|
|
|
Name: name,
|
2020-01-28 23:50:41 +00:00
|
|
|
})
|
2019-12-06 19:01:34 +00:00
|
|
|
|
2022-09-29 03:27:11 +00:00
|
|
|
portsConfig := randomPortsSource(t, a.UseHTTPS)
|
2020-06-10 20:47:35 +00:00
|
|
|
|
2020-08-08 01:08:43 +00:00
|
|
|
// Create NodeID outside the closure, so that it does not change
|
|
|
|
testHCLConfig := TestConfigHCL(NodeID())
|
2020-12-21 18:25:32 +00:00
|
|
|
loader := func(source config.Source) (config.LoadResult, error) {
|
|
|
|
opts := config.LoadOpts{
|
|
|
|
DefaultConfig: source,
|
|
|
|
HCL: []string{testHCLConfig, portsConfig, a.HCL, hclDataDir},
|
|
|
|
Overrides: []config.Source{
|
|
|
|
config.FileSource{
|
|
|
|
Name: "test-overrides",
|
|
|
|
Format: "hcl",
|
|
|
|
Data: a.Overrides},
|
|
|
|
config.DefaultConsulSource(),
|
|
|
|
config.DevConsulSource(),
|
|
|
|
},
|
2022-03-31 19:11:49 +00:00
|
|
|
ConfigFiles: a.configFiles,
|
2020-08-08 01:08:43 +00:00
|
|
|
}
|
2020-12-21 18:25:32 +00:00
|
|
|
result, err := config.Load(opts)
|
|
|
|
if result.RuntimeConfig != nil {
|
2021-10-08 17:31:50 +00:00
|
|
|
// If prom metrics need to be enabled, do not disable telemetry
|
|
|
|
if result.RuntimeConfig.Telemetry.PrometheusOpts.Expiration > 0 {
|
|
|
|
result.RuntimeConfig.Telemetry.Disable = false
|
|
|
|
} else {
|
|
|
|
result.RuntimeConfig.Telemetry.Disable = true
|
|
|
|
}
|
2022-10-25 15:27:26 +00:00
|
|
|
// Lower the maximum backoff period of a cache refresh just for
|
|
|
|
// tests see #14956 for more.
|
|
|
|
result.RuntimeConfig.Cache.CacheRefreshMaxWait = 1 * time.Second
|
2020-08-17 18:12:04 +00:00
|
|
|
}
|
2020-12-21 18:25:32 +00:00
|
|
|
return result, err
|
2020-06-10 20:47:35 +00:00
|
|
|
}
|
2022-10-24 22:02:38 +00:00
|
|
|
bd, err := NewBaseDeps(loader, logOutput, logger)
|
2021-05-10 17:20:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to create base deps: %w", err)
|
|
|
|
}
|
2020-08-08 01:08:43 +00:00
|
|
|
|
|
|
|
bd.Logger = logger
|
2021-10-08 17:31:50 +00:00
|
|
|
// if we are not testing telemetry things, let's use a "mock" sink for metrics
|
|
|
|
if bd.RuntimeConfig.Telemetry.Disable {
|
2022-05-19 20:03:46 +00:00
|
|
|
bd.MetricsConfig = &lib.MetricsConfig{
|
|
|
|
Handler: metrics.NewInmemSink(1*time.Second, time.Minute),
|
|
|
|
}
|
2021-10-08 17:31:50 +00:00
|
|
|
}
|
|
|
|
|
2022-04-04 15:31:39 +00:00
|
|
|
if a.Config != nil && bd.RuntimeConfig.AutoReloadConfigCoalesceInterval == 0 {
|
|
|
|
bd.RuntimeConfig.AutoReloadConfigCoalesceInterval = a.Config.AutoReloadConfigCoalesceInterval
|
|
|
|
}
|
2020-08-08 01:08:43 +00:00
|
|
|
a.Config = bd.RuntimeConfig
|
2019-09-03 22:05:51 +00:00
|
|
|
|
2022-09-26 18:58:15 +00:00
|
|
|
if a.OverrideDeps != nil {
|
|
|
|
a.OverrideDeps(&bd)
|
|
|
|
}
|
|
|
|
|
2020-08-08 01:08:43 +00:00
|
|
|
agent, err := New(bd)
|
2019-09-03 22:05:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error creating agent: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
id := string(a.Config.NodeID)
|
2020-06-19 19:16:00 +00:00
|
|
|
if err := agent.Start(context.Background()); err != nil {
|
2019-09-03 22:05:51 +00:00
|
|
|
agent.ShutdownAgent()
|
|
|
|
agent.ShutdownEndpoints()
|
|
|
|
|
2020-03-30 20:05:27 +00:00
|
|
|
return fmt.Errorf("%s %s Error starting agent: %s", id, name, err)
|
2017-05-23 14:03:52 +00:00
|
|
|
}
|
2017-08-28 12:17:16 +00:00
|
|
|
|
2019-09-03 22:05:51 +00:00
|
|
|
a.Agent = agent
|
|
|
|
|
2017-08-28 12:17:16 +00:00
|
|
|
// Start the anti-entropy syncer
|
|
|
|
a.Agent.StartSync()
|
2017-05-23 14:03:52 +00:00
|
|
|
|
2020-09-23 11:37:33 +00:00
|
|
|
a.srv = a.Agent.httpHandlers
|
2020-07-02 17:31:47 +00:00
|
|
|
|
2019-09-03 22:05:51 +00:00
|
|
|
if err := a.waitForUp(); err != nil {
|
|
|
|
a.Shutdown()
|
2021-05-10 17:20:45 +00:00
|
|
|
a.Agent = nil
|
|
|
|
return fmt.Errorf("error waiting for test agent to start: %w", err)
|
2019-09-03 22:05:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
a.dns = a.dnsServers[0]
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// waitForUp waits for leader election, or waits for the agent HTTP
|
|
|
|
// endpoint to start responding, depending on the agent config.
|
|
|
|
func (a *TestAgent) waitForUp() error {
|
|
|
|
timer := retry.TwoSeconds()
|
|
|
|
deadline := time.Now().Add(timer.Timeout)
|
|
|
|
|
|
|
|
var retErr error
|
2017-05-21 07:10:52 +00:00
|
|
|
var out structs.IndexedNodes
|
2019-09-03 22:05:51 +00:00
|
|
|
for ; !time.Now().After(deadline); time.Sleep(timer.Wait) {
|
2020-07-02 17:31:47 +00:00
|
|
|
if len(a.apiServers.servers) == 0 {
|
2020-03-30 20:05:27 +00:00
|
|
|
retErr = fmt.Errorf("waiting for server")
|
2019-09-03 22:05:51 +00:00
|
|
|
continue // fail, try again
|
2017-05-21 07:10:52 +00:00
|
|
|
}
|
2017-09-25 18:40:42 +00:00
|
|
|
if a.Config.Bootstrap && a.Config.ServerMode {
|
2017-05-21 07:10:52 +00:00
|
|
|
// Ensure we have a leader and a node registration.
|
2017-05-23 00:43:07 +00:00
|
|
|
args := &structs.DCSpecificRequest{
|
|
|
|
Datacenter: a.Config.Datacenter,
|
|
|
|
QueryOptions: structs.QueryOptions{
|
|
|
|
MinQueryIndex: out.Index,
|
|
|
|
MaxQueryTime: 25 * time.Millisecond,
|
|
|
|
},
|
|
|
|
}
|
2017-05-21 07:10:52 +00:00
|
|
|
if err := a.RPC("Catalog.ListNodes", args, &out); err != nil {
|
2019-09-03 22:05:51 +00:00
|
|
|
retErr = fmt.Errorf("Catalog.ListNodes failed: %v", err)
|
|
|
|
continue // fail, try again
|
2017-05-21 07:10:52 +00:00
|
|
|
}
|
|
|
|
if !out.QueryMeta.KnownLeader {
|
2020-03-30 20:05:27 +00:00
|
|
|
retErr = fmt.Errorf("No leader")
|
2019-09-03 22:05:51 +00:00
|
|
|
continue // fail, try again
|
2017-05-21 07:10:52 +00:00
|
|
|
}
|
|
|
|
if out.Index == 0 {
|
2020-03-30 20:05:27 +00:00
|
|
|
retErr = fmt.Errorf("Consul index is 0")
|
2019-09-03 22:05:51 +00:00
|
|
|
continue // fail, try again
|
2017-05-21 07:10:52 +00:00
|
|
|
}
|
2019-09-03 22:05:51 +00:00
|
|
|
return nil // success
|
2017-05-21 07:10:52 +00:00
|
|
|
} else {
|
2019-09-03 22:05:51 +00:00
|
|
|
req := httptest.NewRequest("GET", "/v1/agent/self", nil)
|
2017-05-21 07:10:52 +00:00
|
|
|
resp := httptest.NewRecorder()
|
2020-07-02 17:31:47 +00:00
|
|
|
_, err := a.srv.AgentSelf(resp, req)
|
2020-06-10 20:47:35 +00:00
|
|
|
if acl.IsErrPermissionDenied(err) || resp.Code == 403 {
|
|
|
|
// permission denied is enough to show that the client is
|
|
|
|
// connected to the servers as it would get a 503 if
|
|
|
|
// it couldn't connect to them.
|
|
|
|
} else if err != nil && resp.Code != 200 {
|
2020-03-30 20:05:27 +00:00
|
|
|
retErr = fmt.Errorf("failed OK response: %v", err)
|
2019-09-03 22:05:51 +00:00
|
|
|
continue
|
2017-05-21 07:10:52 +00:00
|
|
|
}
|
2019-09-03 22:05:51 +00:00
|
|
|
return nil // success
|
2017-05-21 07:10:52 +00:00
|
|
|
}
|
2019-09-03 22:05:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return fmt.Errorf("unavailable. last error: %v", retErr)
|
2017-05-21 07:10:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Shutdown stops the agent and removes the data directory if it is
|
|
|
|
// managed by the test agent.
|
|
|
|
func (a *TestAgent) Shutdown() error {
|
2019-09-04 20:59:11 +00:00
|
|
|
if a.Agent == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-06-20 07:29:20 +00:00
|
|
|
// shutdown agent before endpoints
|
|
|
|
defer a.Agent.ShutdownEndpoints()
|
2019-09-05 18:36:26 +00:00
|
|
|
if err := a.Agent.ShutdownAgent(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
<-a.Agent.ShutdownCh()
|
|
|
|
return nil
|
2017-05-21 07:10:52 +00:00
|
|
|
}
|
|
|
|
|
2017-09-25 18:40:42 +00:00
|
|
|
func (a *TestAgent) DNSAddr() string {
|
|
|
|
if a.dns == nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return a.dns.Addr
|
|
|
|
}
|
|
|
|
|
2017-05-22 11:59:36 +00:00
|
|
|
func (a *TestAgent) HTTPAddr() string {
|
2020-09-04 18:53:02 +00:00
|
|
|
addr, err := firstAddr(a.Agent.apiServers, "http")
|
|
|
|
if err != nil {
|
|
|
|
// TODO: t.Fatal instead of panic
|
|
|
|
panic("no http server registered")
|
|
|
|
}
|
|
|
|
return addr.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// firstAddr is used by tests to look up the address for the first server which
|
|
|
|
// matches the protocol
|
|
|
|
func firstAddr(s *apiServers, protocol string) (net.Addr, error) {
|
|
|
|
for _, srv := range s.servers {
|
|
|
|
if srv.Protocol == protocol {
|
|
|
|
return srv.Addr, nil
|
2020-07-02 17:31:47 +00:00
|
|
|
}
|
2017-05-22 11:59:36 +00:00
|
|
|
}
|
2020-09-04 18:53:02 +00:00
|
|
|
return nil, fmt.Errorf("no server registered with protocol %v", protocol)
|
2017-05-22 11:59:36 +00:00
|
|
|
}
|
|
|
|
|
2017-08-14 14:36:07 +00:00
|
|
|
func (a *TestAgent) SegmentAddr(name string) string {
|
|
|
|
if server, ok := a.Agent.delegate.(*consul.Server); ok {
|
|
|
|
return server.LANSegmentAddr(name)
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2017-05-22 11:59:36 +00:00
|
|
|
func (a *TestAgent) Client() *api.Client {
|
|
|
|
conf := api.DefaultConfig()
|
|
|
|
conf.Address = a.HTTPAddr()
|
|
|
|
c, err := api.NewClient(conf)
|
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Sprintf("Error creating consul API client: %s", err))
|
|
|
|
}
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
2017-06-29 14:42:17 +00:00
|
|
|
// DNSDisableCompression disables compression for all started DNS servers.
|
|
|
|
func (a *TestAgent) DNSDisableCompression(b bool) {
|
|
|
|
for _, srv := range a.dnsServers {
|
2021-06-08 23:25:55 +00:00
|
|
|
a.config.DNSDisableCompression = b
|
|
|
|
srv.ReloadConfig(a.config)
|
2017-06-29 14:42:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-29 17:49:52 +00:00
|
|
|
// FIXME: this should t.Fatal on error, not panic.
|
|
|
|
// TODO: rename to newConsulConfig
|
|
|
|
// TODO: remove TestAgent receiver, accept a.Agent.config as an arg
|
2017-05-21 07:10:52 +00:00
|
|
|
func (a *TestAgent) consulConfig() *consul.Config {
|
2020-07-29 17:49:52 +00:00
|
|
|
c, err := newConsulConfig(a.Agent.config, a.Agent.logger)
|
2017-05-21 07:10:52 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
2017-05-21 07:54:40 +00:00
|
|
|
// pickRandomPorts selects random ports from fixed size random blocks of
|
|
|
|
// ports. This does not eliminate the chance for port conflict but
|
2018-03-19 16:56:00 +00:00
|
|
|
// reduces it significantly with little overhead. Furthermore, asking
|
2017-05-21 07:54:40 +00:00
|
|
|
// the kernel for a random port by binding to port 0 prolongs the test
|
|
|
|
// execution (in our case +20sec) while also not fully eliminating the
|
|
|
|
// chance of port conflicts for concurrently executed test binaries.
|
|
|
|
// Instead of relying on one set of ports to be sufficient we retry
|
|
|
|
// starting the agent with different ports on port conflict.
|
2022-09-29 03:27:11 +00:00
|
|
|
func randomPortsSource(t *testing.T, useHTTPS bool) string {
|
2022-08-19 17:07:22 +00:00
|
|
|
ports := freeport.GetN(t, 8)
|
2019-08-27 21:16:41 +00:00
|
|
|
|
|
|
|
var http, https int
|
2022-08-19 17:07:22 +00:00
|
|
|
if useHTTPS {
|
2019-08-27 21:16:41 +00:00
|
|
|
http = -1
|
|
|
|
https = ports[2]
|
2017-11-07 23:06:59 +00:00
|
|
|
} else {
|
2019-08-27 21:16:41 +00:00
|
|
|
http = ports[1]
|
|
|
|
https = -1
|
2017-11-07 23:06:59 +00:00
|
|
|
}
|
2019-08-27 21:16:41 +00:00
|
|
|
|
2020-06-10 20:47:35 +00:00
|
|
|
return `
|
|
|
|
ports = {
|
|
|
|
dns = ` + strconv.Itoa(ports[0]) + `
|
|
|
|
http = ` + strconv.Itoa(http) + `
|
|
|
|
https = ` + strconv.Itoa(https) + `
|
|
|
|
serf_lan = ` + strconv.Itoa(ports[3]) + `
|
|
|
|
serf_wan = ` + strconv.Itoa(ports[4]) + `
|
|
|
|
server = ` + strconv.Itoa(ports[5]) + `
|
2022-09-29 03:27:11 +00:00
|
|
|
grpc = ` + strconv.Itoa(ports[6]) + `
|
|
|
|
grpc_tls = ` + strconv.Itoa(ports[7]) + `
|
2020-06-10 20:47:35 +00:00
|
|
|
}
|
2021-11-27 20:27:59 +00:00
|
|
|
`
|
2017-05-21 07:54:40 +00:00
|
|
|
}
|
2017-05-21 07:10:52 +00:00
|
|
|
|
2017-09-25 18:40:42 +00:00
|
|
|
func NodeID() string {
|
|
|
|
id, err := uuid.GenerateUUID()
|
2017-05-21 07:10:52 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2017-09-25 18:40:42 +00:00
|
|
|
return id
|
|
|
|
}
|
2017-05-21 07:10:52 +00:00
|
|
|
|
2020-12-21 18:46:41 +00:00
|
|
|
// TestConfig returns a unique default configuration for testing an agent.
|
2020-01-28 23:50:41 +00:00
|
|
|
func TestConfig(logger hclog.Logger, sources ...config.Source) *config.RuntimeConfig {
|
2017-09-25 18:40:42 +00:00
|
|
|
nodeID := NodeID()
|
2020-08-10 16:46:28 +00:00
|
|
|
testsrc := config.FileSource{
|
2017-09-25 18:40:42 +00:00
|
|
|
Name: "test",
|
|
|
|
Format: "hcl",
|
|
|
|
Data: `
|
|
|
|
bind_addr = "127.0.0.1"
|
|
|
|
advertise_addr = "127.0.0.1"
|
|
|
|
datacenter = "dc1"
|
|
|
|
bootstrap = true
|
|
|
|
server = true
|
|
|
|
node_id = "` + nodeID + `"
|
2019-08-29 21:52:13 +00:00
|
|
|
node_name = "Node-` + nodeID + `"
|
2018-04-30 03:44:40 +00:00
|
|
|
connect {
|
|
|
|
enabled = true
|
2018-05-10 16:04:33 +00:00
|
|
|
ca_config {
|
|
|
|
cluster_id = "` + connect.TestClusterID + `"
|
|
|
|
}
|
2018-04-30 03:44:40 +00:00
|
|
|
}
|
2017-09-25 18:40:42 +00:00
|
|
|
performance {
|
|
|
|
raft_multiplier = 1
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
}
|
2017-05-22 11:03:59 +00:00
|
|
|
|
2020-12-21 18:46:41 +00:00
|
|
|
opts := config.LoadOpts{
|
|
|
|
DefaultConfig: testsrc,
|
|
|
|
Overrides: sources,
|
2017-09-25 18:40:42 +00:00
|
|
|
}
|
2020-12-21 18:46:41 +00:00
|
|
|
r, err := config.Load(opts)
|
2017-09-25 18:40:42 +00:00
|
|
|
if err != nil {
|
2020-12-21 18:46:41 +00:00
|
|
|
panic("config.Load failed: " + err.Error())
|
2017-09-25 18:40:42 +00:00
|
|
|
}
|
2020-12-21 18:46:41 +00:00
|
|
|
for _, w := range r.Warnings {
|
2020-01-28 23:50:41 +00:00
|
|
|
logger.Warn(w)
|
2017-09-25 18:40:42 +00:00
|
|
|
}
|
2017-05-22 11:03:59 +00:00
|
|
|
|
2020-12-21 18:46:41 +00:00
|
|
|
cfg := r.RuntimeConfig
|
2019-01-10 12:46:11 +00:00
|
|
|
// Effectively disables the delay after root rotation before requesting CSRs
|
|
|
|
// to make test deterministic. 0 results in default jitter being applied but a
|
|
|
|
// tiny delay is effectively thre same.
|
|
|
|
cfg.ConnectTestCALeafRootChangeSpread = 1 * time.Nanosecond
|
2018-06-15 22:35:15 +00:00
|
|
|
|
2022-07-29 21:36:22 +00:00
|
|
|
// allows registering objects with the PeerName
|
|
|
|
cfg.PeeringTestAllowPeerRegistrations = true
|
|
|
|
|
2020-12-21 18:46:41 +00:00
|
|
|
return cfg
|
2017-05-21 07:10:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TestACLConfig returns a default configuration for testing an agent
|
|
|
|
// with ACLs.
|
2017-09-25 18:40:42 +00:00
|
|
|
func TestACLConfig() string {
|
|
|
|
return `
|
2021-12-07 12:48:50 +00:00
|
|
|
primary_datacenter = "dc1"
|
|
|
|
|
|
|
|
acl {
|
|
|
|
enabled = true
|
|
|
|
default_policy = "deny"
|
|
|
|
|
|
|
|
tokens {
|
|
|
|
initial_management = "root"
|
|
|
|
agent = "root"
|
|
|
|
agent_recovery = "towel"
|
|
|
|
}
|
|
|
|
}
|
2017-09-25 18:40:42 +00:00
|
|
|
`
|
2017-05-21 07:10:52 +00:00
|
|
|
}
|
2019-04-30 23:00:57 +00:00
|
|
|
|
2019-09-27 21:06:43 +00:00
|
|
|
const (
|
2021-12-07 12:48:50 +00:00
|
|
|
TestDefaultInitialManagementToken = "d9f05e83-a7ae-47ce-839e-c0d53a68c00a"
|
|
|
|
TestDefaultAgentRecoveryToken = "bca580d4-db07-4074-b766-48acc9676955'"
|
2019-09-27 21:06:43 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type TestACLConfigParams struct {
|
2019-12-06 14:25:26 +00:00
|
|
|
PrimaryDatacenter string
|
|
|
|
DefaultPolicy string
|
2021-12-07 12:48:50 +00:00
|
|
|
InitialManagementToken string
|
2019-12-06 14:25:26 +00:00
|
|
|
AgentToken string
|
|
|
|
DefaultToken string
|
2021-12-07 12:48:50 +00:00
|
|
|
AgentRecoveryToken string
|
2019-12-06 14:25:26 +00:00
|
|
|
ReplicationToken string
|
|
|
|
EnableTokenReplication bool
|
2019-09-27 21:06:43 +00:00
|
|
|
}
|
|
|
|
|
2021-12-07 12:48:50 +00:00
|
|
|
func DefaultTestACLConfigParams() *TestACLConfigParams {
|
2019-09-27 21:06:43 +00:00
|
|
|
return &TestACLConfigParams{
|
2021-12-07 12:48:50 +00:00
|
|
|
PrimaryDatacenter: "dc1",
|
|
|
|
DefaultPolicy: "deny",
|
|
|
|
InitialManagementToken: TestDefaultInitialManagementToken,
|
|
|
|
AgentToken: TestDefaultInitialManagementToken,
|
|
|
|
AgentRecoveryToken: TestDefaultAgentRecoveryToken,
|
2019-09-27 21:06:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *TestACLConfigParams) HasConfiguredTokens() bool {
|
2021-12-07 12:48:50 +00:00
|
|
|
return p.InitialManagementToken != "" ||
|
2019-09-27 21:06:43 +00:00
|
|
|
p.AgentToken != "" ||
|
|
|
|
p.DefaultToken != "" ||
|
2021-12-07 12:48:50 +00:00
|
|
|
p.AgentRecoveryToken != "" ||
|
2019-09-27 21:06:43 +00:00
|
|
|
p.ReplicationToken != ""
|
|
|
|
}
|
|
|
|
|
2019-04-30 23:00:57 +00:00
|
|
|
func TestACLConfigNew() string {
|
2019-09-27 21:06:43 +00:00
|
|
|
return TestACLConfigWithParams(&TestACLConfigParams{
|
2021-12-07 12:48:50 +00:00
|
|
|
PrimaryDatacenter: "dc1",
|
|
|
|
DefaultPolicy: "deny",
|
|
|
|
InitialManagementToken: "root",
|
|
|
|
AgentToken: "root",
|
|
|
|
AgentRecoveryToken: "towel",
|
2019-09-27 21:06:43 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
var aclConfigTpl = template.Must(template.New("ACL Config").Parse(`
|
2020-06-10 20:47:35 +00:00
|
|
|
{{- if ne .PrimaryDatacenter "" -}}
|
2019-09-27 21:06:43 +00:00
|
|
|
primary_datacenter = "{{ .PrimaryDatacenter }}"
|
2020-06-10 20:47:35 +00:00
|
|
|
{{end -}}
|
2019-09-27 21:06:43 +00:00
|
|
|
acl {
|
|
|
|
enabled = true
|
2020-06-10 20:47:35 +00:00
|
|
|
{{- if ne .DefaultPolicy ""}}
|
2019-09-27 21:06:43 +00:00
|
|
|
default_policy = "{{ .DefaultPolicy }}"
|
2020-06-10 20:47:35 +00:00
|
|
|
{{- end}}
|
2019-12-06 14:25:26 +00:00
|
|
|
enable_token_replication = {{printf "%t" .EnableTokenReplication }}
|
2020-06-10 20:47:35 +00:00
|
|
|
{{- if .HasConfiguredTokens}}
|
2019-09-27 21:06:43 +00:00
|
|
|
tokens {
|
2021-12-07 12:48:50 +00:00
|
|
|
{{- if ne .InitialManagementToken ""}}
|
|
|
|
initial_management = "{{ .InitialManagementToken }}"
|
2020-06-10 20:47:35 +00:00
|
|
|
{{- end}}
|
|
|
|
{{- if ne .AgentToken ""}}
|
2019-09-27 21:06:43 +00:00
|
|
|
agent = "{{ .AgentToken }}"
|
2020-06-10 20:47:35 +00:00
|
|
|
{{- end}}
|
2021-12-07 12:48:50 +00:00
|
|
|
{{- if ne .AgentRecoveryToken "" }}
|
|
|
|
agent_recovery = "{{ .AgentRecoveryToken }}"
|
2020-06-10 20:47:35 +00:00
|
|
|
{{- end}}
|
|
|
|
{{- if ne .DefaultToken "" }}
|
2019-09-27 21:06:43 +00:00
|
|
|
default = "{{ .DefaultToken }}"
|
2020-06-10 20:47:35 +00:00
|
|
|
{{- end}}
|
|
|
|
{{- if ne .ReplicationToken "" }}
|
2019-09-27 21:06:43 +00:00
|
|
|
replication = "{{ .ReplicationToken }}"
|
2020-06-10 20:47:35 +00:00
|
|
|
{{- end}}
|
2019-04-30 23:00:57 +00:00
|
|
|
}
|
2020-06-10 20:47:35 +00:00
|
|
|
{{- end}}
|
2019-09-27 21:06:43 +00:00
|
|
|
}
|
|
|
|
`))
|
|
|
|
|
|
|
|
func TestACLConfigWithParams(params *TestACLConfigParams) string {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
|
|
|
|
cfg := params
|
|
|
|
if params == nil {
|
2021-12-07 12:48:50 +00:00
|
|
|
cfg = DefaultTestACLConfigParams()
|
2019-09-27 21:06:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
err := aclConfigTpl.Execute(&buf, &cfg)
|
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Sprintf("Failed to generate test ACL config: %v", err))
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf.String()
|
2019-04-30 23:00:57 +00:00
|
|
|
}
|
2020-06-10 20:47:35 +00:00
|
|
|
|
|
|
|
// testTLSCertificates Generates a TLS CA and server key/cert and returns them
|
|
|
|
// in PEM encoded form.
|
|
|
|
func testTLSCertificates(serverName string) (cert string, key string, cacert string, err error) {
|
2021-01-27 07:52:15 +00:00
|
|
|
signer, _, err := tlsutil.GeneratePrivateKey()
|
2020-06-10 20:47:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", "", "", err
|
|
|
|
}
|
2021-01-27 07:52:15 +00:00
|
|
|
|
|
|
|
ca, _, err := tlsutil.GenerateCA(tlsutil.CAOpts{Signer: signer})
|
2020-06-10 20:47:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", "", "", err
|
|
|
|
}
|
|
|
|
|
2021-03-22 09:16:41 +00:00
|
|
|
cert, privateKey, err := tlsutil.GenerateCert(tlsutil.CertOpts{
|
|
|
|
Signer: signer,
|
|
|
|
CA: ca,
|
|
|
|
Name: "Test Cert Name",
|
|
|
|
Days: 365,
|
|
|
|
DNSNames: []string{serverName},
|
|
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
|
|
|
|
})
|
2020-06-10 20:47:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", "", "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return cert, privateKey, ca, nil
|
|
|
|
}
|