diff --git a/GNUmakefile b/GNUmakefile index 197a3186d8..7fa86d2347 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -333,12 +333,12 @@ ifeq ("$(GOTAGS)","") @docker tag consul-dev:latest consul:local @docker run --rm -t consul:local consul version @cd ./test/integration/consul-container && \ - go test -v -timeout=30m ./upgrade --target-version local --latest-version latest + go test -v -timeout=30m ./... --target-version local --latest-version latest else @docker tag consul-dev:latest hashicorp/consul-enterprise:local @docker run --rm -t hashicorp/consul-enterprise:local consul version @cd ./test/integration/consul-container && \ - go test -v -timeout=30m ./upgrade --tags $(GOTAGS) --target-version local --latest-version latest + go test -v -timeout=30m ./... --tags $(GOTAGS) --target-version local --latest-version latest endif .PHONY: test-metrics-integ diff --git a/test/integration/consul-container/go.mod b/test/integration/consul-container/go.mod index 4dea73bbab..182ff2e3aa 100644 --- a/test/integration/consul-container/go.mod +++ b/test/integration/consul-container/go.mod @@ -7,6 +7,7 @@ require ( github.com/hashicorp/consul/api v1.11.0 github.com/hashicorp/consul/sdk v0.8.0 github.com/hashicorp/go-uuid v1.0.2 + github.com/hashicorp/hcl v1.0.0 github.com/stretchr/testify v1.7.0 github.com/testcontainers/testcontainers-go v0.13.0 ) diff --git a/test/integration/consul-container/go.sum b/test/integration/consul-container/go.sum index 4bc58fd5c1..1ec317a43f 100644 --- a/test/integration/consul-container/go.sum +++ b/test/integration/consul-container/go.sum @@ -427,6 +427,7 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= diff --git a/test/integration/consul-container/libs/node/consul-container.go b/test/integration/consul-container/libs/node/consul-container.go index 5987241701..caa8a7409f 100644 --- a/test/integration/consul-container/libs/node/consul-container.go +++ b/test/integration/consul-container/libs/node/consul-container.go @@ -9,6 +9,7 @@ import ( "github.com/docker/docker/pkg/ioutils" "github.com/hashicorp/consul/api" + "github.com/hashicorp/hcl" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" @@ -48,7 +49,6 @@ func newConsulContainerWithReq(ctx context.Context, req testcontainers.Container // NewConsulContainer starts a Consul node in a container with the given config. func NewConsulContainer(ctx context.Context, config Config) (Node, error) { - license, err := readLicense() if err != nil { return nil, err @@ -64,30 +64,29 @@ func NewConsulContainer(ctx context.Context, config Config) (Node, error) { return nil, err } + pc, err := readSomeConfigFileFields(config.HCL) + if err != nil { + return nil, err + } + configFile, err := createConfigFile(config.HCL) if err != nil { return nil, err } - skipReaper := isRYUKDisabled() - req := testcontainers.ContainerRequest{ - Image: consulImage + ":" + config.Version, - ExposedPorts: []string{"8500/tcp"}, - WaitingFor: wait.ForLog(bootLogLine).WithStartupTimeout(10 * time.Second), - AutoRemove: false, - Name: name, - Mounts: testcontainers.ContainerMounts{ - testcontainers.ContainerMount{Source: testcontainers.DockerBindMountSource{HostPath: configFile}, Target: "/consul/config/config.hcl"}, - testcontainers.ContainerMount{Source: testcontainers.DockerBindMountSource{HostPath: tmpDirData}, Target: "/consul/data"}, - }, - Cmd: config.Cmd, - SkipReaper: skipReaper, - Env: map[string]string{"CONSUL_LICENSE": license}, - } + + req := newContainerRequest(config, name, configFile, tmpDirData, license) container, err := newConsulContainerWithReq(ctx, req) if err != nil { return nil, err } + if err := container.StartLogProducer(ctx); err != nil { + return nil, err + } + container.FollowOutput(&NodeLogConsumer{ + Prefix: pc.NodeName, + }) + localIP, err := container.Host(ctx) if err != nil { return nil, err @@ -104,21 +103,42 @@ func NewConsulContainer(ctx context.Context, config Config) (Node, error) { } uri := fmt.Sprintf("http://%s:%s", localIP, mappedPort.Port()) - c := new(consulContainerNode) - c.config = config - c.container = container - c.ip = ip - c.port = mappedPort.Int() apiConfig := api.DefaultConfig() apiConfig.Address = uri - c.client, err = api.NewClient(apiConfig) - c.ctx = ctx - c.req = req - c.dataDir = tmpDirData + apiClient, err := api.NewClient(apiConfig) if err != nil { return nil, err } - return c, nil + + return &consulContainerNode{ + config: config, + container: container, + ip: ip, + port: mappedPort.Int(), + client: apiClient, + ctx: ctx, + req: req, + dataDir: tmpDirData, + }, nil +} + +func newContainerRequest(config Config, name, configFile, dataDir, license string) testcontainers.ContainerRequest { + skipReaper := isRYUKDisabled() + + return testcontainers.ContainerRequest{ + Image: consulImage + ":" + config.Version, + ExposedPorts: []string{"8500/tcp"}, + WaitingFor: wait.ForLog(bootLogLine).WithStartupTimeout(10 * time.Second), + AutoRemove: false, + Name: name, + Mounts: []testcontainers.ContainerMount{ + {Source: testcontainers.DockerBindMountSource{HostPath: configFile}, Target: "/consul/config/config.hcl"}, + {Source: testcontainers.DockerBindMountSource{HostPath: dataDir}, Target: "/consul/data"}, + }, + Cmd: config.Cmd, + SkipReaper: skipReaper, + Env: map[string]string{"CONSUL_LICENSE": license}, + } } // GetClient returns an API client that can be used to communicate with the Node. @@ -132,25 +152,44 @@ func (c *consulContainerNode) GetAddr() (string, int) { } func (c *consulContainerNode) Upgrade(ctx context.Context, config Config) error { + pc, err := readSomeConfigFileFields(config.HCL) + if err != nil { + return err + } + file, err := createConfigFile(config.HCL) if err != nil { return err } - c.req.Cmd = config.Cmd - c.req.Mounts = testcontainers.ContainerMounts{ - testcontainers.ContainerMount{Source: testcontainers.DockerBindMountSource{HostPath: file}, Target: "/consul/config/config.hcl"}, - testcontainers.ContainerMount{Source: testcontainers.DockerBindMountSource{HostPath: c.dataDir}, Target: "/consul/data"}, - } - c.req.Image = consulImage + ":" + config.Version - err = c.container.Terminate(ctx) - if err != nil { + + req2 := newContainerRequest( + config, + c.req.Name, + file, + c.dataDir, + "", + ) + req2.Env = c.req.Env // copy license + + _ = c.container.StopLogProducer() + if err := c.container.Terminate(ctx); err != nil { return err } + + c.req = req2 + container, err := newConsulContainerWithReq(ctx, c.req) if err != nil { return err } + if err := container.StartLogProducer(ctx); err != nil { + return err + } + container.FollowOutput(&NodeLogConsumer{ + Prefix: pc.NodeName, + }) + c.container = container localIP, err := container.Host(ctx) @@ -185,7 +224,19 @@ func (c *consulContainerNode) Upgrade(ctx context.Context, config Config) error // Terminate attempts to terminate the container. On failure, an error will be // returned and the reaper process (RYUK) will handle cleanup. func (c *consulContainerNode) Terminate() error { - return c.container.Terminate(c.ctx) + if c.container == nil { + return nil + } + + err := c.container.StopLogProducer() + + if err1 := c.container.Terminate(c.ctx); err == nil { + err = err1 + } + + c.container = nil + + return err } // isRYUKDisabled returns whether the reaper process (RYUK) has been disabled @@ -236,3 +287,15 @@ func createConfigFile(HCL string) (string, error) { } return configFile, nil } + +type parsedConfig struct { + NodeName string `hcl:"node_name"` +} + +func readSomeConfigFileFields(HCL string) (parsedConfig, error) { + var pc parsedConfig + if err := hcl.Decode(&pc, HCL); err != nil { + return pc, fmt.Errorf("Failed to parse config file: %w", err) + } + return pc, nil +} diff --git a/test/integration/consul-container/libs/node/log.go b/test/integration/consul-container/libs/node/log.go new file mode 100644 index 0000000000..58b63f2ea0 --- /dev/null +++ b/test/integration/consul-container/libs/node/log.go @@ -0,0 +1,23 @@ +package node + +import ( + "fmt" + "os" + + "github.com/testcontainers/testcontainers-go" +) + +type NodeLogConsumer struct { + Prefix string +} + +var _ testcontainers.LogConsumer = (*NodeLogConsumer)(nil) + +func (c *NodeLogConsumer) Accept(log testcontainers.Log) { + switch log.LogType { + case "STDOUT": + fmt.Fprint(os.Stdout, c.Prefix+" ~~ "+string(log.Content)) + case "STDERR": + fmt.Fprint(os.Stderr, c.Prefix+" ~~ "+string(log.Content)) + } +} diff --git a/test/integration/consul-container/libs/node/node.go b/test/integration/consul-container/libs/node/node.go index 69e5c7e505..6f02a446c2 100644 --- a/test/integration/consul-container/libs/node/node.go +++ b/test/integration/consul-container/libs/node/node.go @@ -2,19 +2,18 @@ package node import ( "context" + "github.com/hashicorp/consul/api" ) -type ( - // Node represent a Consul node abstraction - Node interface { - Terminate() error - GetClient() *api.Client - GetAddr() (string, int) - GetConfig() Config - Upgrade(ctx context.Context, config Config) error - } -) +// Node represent a Consul node abstraction +type Node interface { + Terminate() error + GetClient() *api.Client + GetAddr() (string, int) + GetConfig() Config + Upgrade(ctx context.Context, config Config) error +} // Config is a set of configurations required to create a Node type Config struct { diff --git a/test/integration/consul-container/metrics/leader_test.go b/test/integration/consul-container/metrics/leader_test.go index 9bea12592d..08cf0e392d 100644 --- a/test/integration/consul-container/metrics/leader_test.go +++ b/test/integration/consul-container/metrics/leader_test.go @@ -23,7 +23,7 @@ func TestLeadershipMetrics(t *testing.T) { configs = append(configs, node.Config{ HCL: `node_name="` + utils.RandName("consul-server") + `" - log_level="TRACE" + log_level="DEBUG" server=true telemetry { statsite_address = "127.0.0.1:2180" @@ -37,7 +37,7 @@ func TestLeadershipMetrics(t *testing.T) { configs = append(configs, node.Config{ HCL: `node_name="` + utils.RandName("consul-server") + `" - log_level="TRACE" + log_level="DEBUG" bootstrap_expect=3 server=true`, Cmd: []string{"agent", "-client=0.0.0.0"}, diff --git a/test/integration/consul-container/upgrade/healthcheck_test.go b/test/integration/consul-container/upgrade/healthcheck_test.go index 73c8a807be..7cc88ad6e6 100644 --- a/test/integration/consul-container/upgrade/healthcheck_test.go +++ b/test/integration/consul-container/upgrade/healthcheck_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/require" "github.com/hashicorp/consul/api" + libcluster "github.com/hashicorp/consul/integration/consul-container/libs/cluster" "github.com/hashicorp/consul/integration/consul-container/libs/node" "github.com/hashicorp/consul/integration/consul-container/libs/utils" @@ -74,7 +75,7 @@ func TestMixedServersMajorityLatestGAClient(t *testing.T) { configs = append(configs, node.Config{ HCL: `node_name="` + utils.RandName("consul-server") + `" - log_level="TRACE" + log_level="DEBUG" server=true`, Cmd: []string{"agent", "-client=0.0.0.0"}, Version: *utils.TargetImage, @@ -84,7 +85,7 @@ func TestMixedServersMajorityLatestGAClient(t *testing.T) { configs = append(configs, node.Config{ HCL: `node_name="` + utils.RandName("consul-server") + `" - log_level="TRACE" + log_level="DEBUG" bootstrap_expect=3 server=true`, Cmd: []string{"agent", "-client=0.0.0.0"}, @@ -151,7 +152,7 @@ func TestMixedServersMajorityTargetGAClient(t *testing.T) { configs = append(configs, node.Config{ HCL: `node_name="` + utils.RandName("consul-server") + `" - log_level="TRACE" + log_level="DEBUG" bootstrap_expect=3 server=true`, Cmd: []string{"agent", "-client=0.0.0.0"}, @@ -162,7 +163,7 @@ func TestMixedServersMajorityTargetGAClient(t *testing.T) { configs = append(configs, node.Config{ HCL: `node_name="` + utils.RandName("consul-server") + `" - log_level="TRACE" + log_level="DEBUG" server=true`, Cmd: []string{"agent", "-client=0.0.0.0"}, Version: *utils.LatestImage, @@ -227,7 +228,7 @@ func clientsCreate(t *testing.T, numClients int, version string, serfKey string) node.Config{ HCL: fmt.Sprintf(` node_name = %q - log_level = "TRACE" + log_level = "DEBUG" encrypt = %q`, utils.RandName("consul-client"), serfKey), Cmd: []string{"agent", "-client=0.0.0.0"}, Version: version, @@ -255,7 +256,7 @@ func serversCluster(t *testing.T, numServers int, version string) *libcluster.Cl for i := 0; i < numServers; i++ { configs = append(configs, node.Config{ HCL: `node_name="` + utils.RandName("consul-server") + `" - log_level="TRACE" + log_level="DEBUG" bootstrap_expect=3 server=true`, Cmd: []string{"agent", "-client=0.0.0.0"},