test: for upgrade compatibility tests retain assigned container ip addresses on upgrade (#13615)

Use a synthetic pod construct to hold onto the IP address in the
interim.
This commit is contained in:
R.B. Boyer 2022-06-28 09:50:13 -05:00 committed by GitHub
parent 883ccc2a98
commit f3f941f1a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 59 additions and 65 deletions

View File

@ -7,6 +7,7 @@ import (
"strconv" "strconv"
"time" "time"
dockercontainer "github.com/docker/docker/api/types/container"
"github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/ioutils"
"github.com/hashicorp/consul/api" "github.com/hashicorp/consul/api"
"github.com/hashicorp/hcl" "github.com/hashicorp/hcl"
@ -24,11 +25,13 @@ const disableRYUKEnv = "TESTCONTAINERS_RYUK_DISABLED"
type consulContainerNode struct { type consulContainerNode struct {
ctx context.Context ctx context.Context
client *api.Client client *api.Client
pod testcontainers.Container
container testcontainers.Container container testcontainers.Container
ip string ip string
port int port int
config Config config Config
req testcontainers.ContainerRequest podReq testcontainers.ContainerRequest
consulReq testcontainers.ContainerRequest
dataDir string dataDir string
} }
@ -36,15 +39,11 @@ func (c *consulContainerNode) GetConfig() Config {
return c.config return c.config
} }
func newConsulContainerWithReq(ctx context.Context, req testcontainers.ContainerRequest) (testcontainers.Container, error) { func startContainer(ctx context.Context, req testcontainers.ContainerRequest) (testcontainers.Container, error) {
container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ return testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: req, ContainerRequest: req,
Started: true, Started: true,
}) })
if err != nil {
return nil, err
}
return container, nil
} }
// NewConsulContainer starts a Consul node in a container with the given config. // NewConsulContainer starts a Consul node in a container with the given config.
@ -74,34 +73,40 @@ func NewConsulContainer(ctx context.Context, config Config) (Node, error) {
return nil, err return nil, err
} }
req := newContainerRequest(config, name, configFile, tmpDirData, license) podReq, consulReq := newContainerRequest(config, name, configFile, tmpDirData, license)
container, err := newConsulContainerWithReq(ctx, req)
podContainer, err := startContainer(ctx, podReq)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := container.StartLogProducer(ctx); err != nil { localIP, err := podContainer.Host(ctx)
if err != nil {
return nil, err return nil, err
} }
container.FollowOutput(&NodeLogConsumer{
mappedPort, err := podContainer.MappedPort(ctx, "8500")
if err != nil {
return nil, err
}
ip, err := podContainer.ContainerIP(ctx)
if err != nil {
return nil, err
}
consulContainer, err := startContainer(ctx, consulReq)
if err != nil {
return nil, err
}
if err := consulContainer.StartLogProducer(ctx); err != nil {
return nil, err
}
consulContainer.FollowOutput(&NodeLogConsumer{
Prefix: pc.NodeName, Prefix: pc.NodeName,
}) })
localIP, err := container.Host(ctx)
if err != nil {
return nil, err
}
mappedPort, err := container.MappedPort(ctx, "8500")
if err != nil {
return nil, err
}
ip, err := container.ContainerIP(ctx)
if err != nil {
return nil, err
}
uri := fmt.Sprintf("http://%s:%s", localIP, mappedPort.Port()) uri := fmt.Sprintf("http://%s:%s", localIP, mappedPort.Port())
apiConfig := api.DefaultConfig() apiConfig := api.DefaultConfig()
apiConfig.Address = uri apiConfig.Address = uri
@ -112,25 +117,37 @@ func NewConsulContainer(ctx context.Context, config Config) (Node, error) {
return &consulContainerNode{ return &consulContainerNode{
config: config, config: config,
container: container, pod: podContainer,
container: consulContainer,
ip: ip, ip: ip,
port: mappedPort.Int(), port: mappedPort.Int(),
client: apiClient, client: apiClient,
ctx: ctx, ctx: ctx,
req: req, podReq: podReq,
consulReq: consulReq,
dataDir: tmpDirData, dataDir: tmpDirData,
}, nil }, nil
} }
func newContainerRequest(config Config, name, configFile, dataDir, license string) testcontainers.ContainerRequest { const pauseImage = "k8s.gcr.io/pause:3.3"
func newContainerRequest(config Config, name, configFile, dataDir, license string) (podRequest, consulRequest testcontainers.ContainerRequest) {
skipReaper := isRYUKDisabled() skipReaper := isRYUKDisabled()
return testcontainers.ContainerRequest{ pod := testcontainers.ContainerRequest{
Image: consulImage + ":" + config.Version, Image: pauseImage,
ExposedPorts: []string{"8500/tcp"},
WaitingFor: wait.ForLog(bootLogLine).WithStartupTimeout(10 * time.Second),
AutoRemove: false, AutoRemove: false,
Name: name, Name: name + "-pod",
SkipReaper: skipReaper,
ExposedPorts: []string{"8500/tcp"},
}
app := testcontainers.ContainerRequest{
NetworkMode: dockercontainer.NetworkMode("container:" + name + "-pod"),
Image: consulImage + ":" + config.Version,
WaitingFor: wait.ForLog(bootLogLine).WithStartupTimeout(10 * time.Second),
AutoRemove: false,
Name: name,
Mounts: []testcontainers.ContainerMount{ Mounts: []testcontainers.ContainerMount{
{Source: testcontainers.DockerBindMountSource{HostPath: configFile}, Target: "/consul/config/config.hcl"}, {Source: testcontainers.DockerBindMountSource{HostPath: configFile}, Target: "/consul/config/config.hcl"},
{Source: testcontainers.DockerBindMountSource{HostPath: dataDir}, Target: "/consul/data"}, {Source: testcontainers.DockerBindMountSource{HostPath: dataDir}, Target: "/consul/data"},
@ -139,6 +156,8 @@ func newContainerRequest(config Config, name, configFile, dataDir, license strin
SkipReaper: skipReaper, SkipReaper: skipReaper,
Env: map[string]string{"CONSUL_LICENSE": license}, Env: map[string]string{"CONSUL_LICENSE": license},
} }
return pod, app
} }
// GetClient returns an API client that can be used to communicate with the Node. // GetClient returns an API client that can be used to communicate with the Node.
@ -162,23 +181,24 @@ func (c *consulContainerNode) Upgrade(ctx context.Context, config Config) error
return err return err
} }
req2 := newContainerRequest( // We'll keep the same pod.
_, consulReq2 := newContainerRequest(
config, config,
c.req.Name, c.consulReq.Name,
file, file,
c.dataDir, c.dataDir,
"", "",
) )
req2.Env = c.req.Env // copy license consulReq2.Env = c.consulReq.Env // copy license
_ = c.container.StopLogProducer() _ = c.container.StopLogProducer()
if err := c.container.Terminate(ctx); err != nil { if err := c.container.Terminate(ctx); err != nil {
return err return err
} }
c.req = req2 c.consulReq = consulReq2
container, err := newConsulContainerWithReq(ctx, c.req) container, err := startContainer(ctx, c.consulReq)
if err != nil { if err != nil {
return err return err
} }
@ -192,32 +212,6 @@ func (c *consulContainerNode) Upgrade(ctx context.Context, config Config) error
c.container = container c.container = container
localIP, err := container.Host(ctx)
if err != nil {
return err
}
mappedPort, err := container.MappedPort(ctx, "8500")
if err != nil {
return err
}
ip, err := container.ContainerIP(ctx)
if err != nil {
return err
}
uri := fmt.Sprintf("http://%s:%s", localIP, mappedPort.Port())
c.ip = ip
c.port = mappedPort.Int()
apiConfig := api.DefaultConfig()
apiConfig.Address = uri
c.client, err = api.NewClient(apiConfig)
c.ctx = ctx
c.config = config
if err != nil {
return err
}
return nil return nil
} }