2022-11-01 19:03:23 +00:00
|
|
|
package service
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/docker/go-connections/nat"
|
2023-01-04 20:28:15 +00:00
|
|
|
"github.com/hashicorp/consul/api"
|
2022-11-01 19:03:23 +00:00
|
|
|
"github.com/testcontainers/testcontainers-go"
|
|
|
|
"github.com/testcontainers/testcontainers-go/wait"
|
|
|
|
|
|
|
|
libnode "github.com/hashicorp/consul/test/integration/consul-container/libs/agent"
|
|
|
|
"github.com/hashicorp/consul/test/integration/consul-container/libs/utils"
|
|
|
|
)
|
|
|
|
|
|
|
|
// ConnectContainer
|
|
|
|
type ConnectContainer struct {
|
2023-01-04 20:28:15 +00:00
|
|
|
ctx context.Context
|
|
|
|
container testcontainers.Container
|
|
|
|
ip string
|
|
|
|
appPort int
|
|
|
|
adminPort int
|
|
|
|
serviceName string
|
2022-11-01 19:03:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g ConnectContainer) GetName() string {
|
|
|
|
name, err := g.container.Name(g.ctx)
|
|
|
|
if err != nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g ConnectContainer) GetAddr() (string, int) {
|
|
|
|
return g.ip, g.appPort
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g ConnectContainer) Start() error {
|
|
|
|
if g.container == nil {
|
|
|
|
return fmt.Errorf("container has not been initialized")
|
|
|
|
}
|
|
|
|
return g.container.Start(context.Background())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g ConnectContainer) GetAdminAddr() (string, int) {
|
|
|
|
return "localhost", g.adminPort
|
|
|
|
}
|
|
|
|
|
|
|
|
// Terminate attempts to terminate the container. On failure, an error will be
|
|
|
|
// returned and the reaper process (RYUK) will handle cleanup.
|
|
|
|
func (c ConnectContainer) Terminate() error {
|
|
|
|
if c.container == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-12-15 21:31:12 +00:00
|
|
|
var err error
|
|
|
|
if *utils.FollowLog {
|
|
|
|
err := c.container.StopLogProducer()
|
|
|
|
if err1 := c.container.Terminate(c.ctx); err == nil {
|
|
|
|
err = err1
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
err = c.container.Terminate(c.ctx)
|
2022-11-01 19:03:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
c.container = nil
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-01-04 20:28:15 +00:00
|
|
|
func (g ConnectContainer) Export(partition, peer string, client *api.Client) error {
|
|
|
|
return fmt.Errorf("ConnectContainer export unimplemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g ConnectContainer) GetServiceName() string {
|
|
|
|
return g.serviceName
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewConnectService returns a container that runs envoy sidecar, launched by
|
|
|
|
// "consul connect envoy", for service name (serviceName) on the specified
|
|
|
|
// node. The container exposes port serviceBindPort and envoy admin port (19000)
|
|
|
|
// by mapping them onto host ports. The container's name has a prefix
|
|
|
|
// combining datacenter and name.
|
2022-11-08 01:10:42 +00:00
|
|
|
func NewConnectService(ctx context.Context, name string, serviceName string, serviceBindPort int, node libnode.Agent) (*ConnectContainer, error) {
|
2022-11-01 19:03:23 +00:00
|
|
|
namePrefix := fmt.Sprintf("%s-service-connect-%s", node.GetDatacenter(), name)
|
|
|
|
containerName := utils.RandName(namePrefix)
|
|
|
|
|
|
|
|
envoyVersion := getEnvoyVersion()
|
|
|
|
buildargs := map[string]*string{
|
|
|
|
"ENVOY_VERSION": utils.StringToPointer(envoyVersion),
|
|
|
|
}
|
|
|
|
|
|
|
|
dockerfileCtx, err := getDevContainerDockerfile()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
dockerfileCtx.BuildArgs = buildargs
|
|
|
|
|
|
|
|
nodeIP, _ := node.GetAddr()
|
|
|
|
|
|
|
|
req := testcontainers.ContainerRequest{
|
|
|
|
FromDockerfile: dockerfileCtx,
|
|
|
|
WaitingFor: wait.ForLog("").WithStartupTimeout(10 * time.Second),
|
|
|
|
AutoRemove: false,
|
|
|
|
Name: containerName,
|
|
|
|
Cmd: []string{
|
|
|
|
"consul", "connect", "envoy",
|
|
|
|
"-sidecar-for", serviceName,
|
|
|
|
"-admin-bind", "0.0.0.0:19000",
|
|
|
|
"-grpc-addr", fmt.Sprintf("%s:8502", nodeIP),
|
|
|
|
"-http-addr", fmt.Sprintf("%s:8500", nodeIP),
|
|
|
|
"--",
|
2022-11-04 16:53:20 +00:00
|
|
|
"--log-level", envoyLogLevel},
|
2022-11-01 19:03:23 +00:00
|
|
|
ExposedPorts: []string{
|
|
|
|
fmt.Sprintf("%d/tcp", serviceBindPort), // Envoy Listener
|
|
|
|
"19000/tcp", // Envoy Admin Port
|
|
|
|
},
|
|
|
|
}
|
|
|
|
container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
|
|
|
ContainerRequest: req,
|
|
|
|
Started: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
ip, err := container.ContainerIP(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
mappedAppPort, err := container.MappedPort(ctx, nat.Port(fmt.Sprintf("%d", serviceBindPort)))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
mappedAdminPort, err := container.MappedPort(ctx, nat.Port(fmt.Sprintf("%d", 19000)))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-12-15 21:31:12 +00:00
|
|
|
if *utils.FollowLog {
|
|
|
|
if err := container.StartLogProducer(ctx); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
container.FollowOutput(&LogConsumer{
|
|
|
|
Prefix: containerName,
|
|
|
|
})
|
2022-11-01 19:03:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Register the termination function the agent so the containers can stop together
|
|
|
|
terminate := func() error {
|
|
|
|
return container.Terminate(context.Background())
|
|
|
|
}
|
|
|
|
node.RegisterTermination(terminate)
|
|
|
|
|
2023-01-04 20:28:15 +00:00
|
|
|
fmt.Printf("NewConnectService: name %s, mappedAppPort %d, bind port %d\n",
|
|
|
|
serviceName, mappedAppPort.Int(), serviceBindPort)
|
|
|
|
|
2022-11-01 19:03:23 +00:00
|
|
|
return &ConnectContainer{
|
2023-01-04 20:28:15 +00:00
|
|
|
container: container,
|
|
|
|
ip: ip,
|
|
|
|
appPort: mappedAppPort.Int(),
|
|
|
|
adminPort: mappedAdminPort.Int(),
|
|
|
|
serviceName: name,
|
2022-11-01 19:03:23 +00:00
|
|
|
}, nil
|
|
|
|
}
|