mirror of https://github.com/status-im/consul.git
tests: ensure integration tests show logs from the containers to help debugging (#13593)
This commit is contained in:
parent
a3a4495e78
commit
a1e911a70c
|
@ -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
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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"},
|
||||
|
|
Loading…
Reference in New Issue