2023-09-26 20:21:47 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
|
|
|
|
package cluster
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2023-10-06 16:06:12 +00:00
|
|
|
"io"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2023-09-26 20:21:47 +00:00
|
|
|
"github.com/hashicorp/consul/test/integration/consul-container/libs/utils"
|
|
|
|
"github.com/testcontainers/testcontainers-go"
|
|
|
|
"github.com/testcontainers/testcontainers-go/wait"
|
|
|
|
)
|
|
|
|
|
|
|
|
type ConsulDataplaneContainer struct {
|
|
|
|
ctx context.Context
|
|
|
|
container testcontainers.Container
|
|
|
|
ip string
|
|
|
|
appPort []int
|
|
|
|
serviceName string
|
|
|
|
externalAdminPort int
|
|
|
|
internalAdminPort int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g ConsulDataplaneContainer) GetAddr() (string, int) {
|
|
|
|
return g.ip, g.appPort[0]
|
|
|
|
}
|
|
|
|
|
2023-10-06 16:06:12 +00:00
|
|
|
func (g ConsulDataplaneContainer) GetServiceName() string {
|
|
|
|
return g.serviceName
|
|
|
|
}
|
|
|
|
|
2023-09-26 20:21:47 +00:00
|
|
|
// GetAdminAddr returns the external admin port
|
|
|
|
func (g ConsulDataplaneContainer) GetAdminAddr() (string, int) {
|
|
|
|
return "localhost", g.externalAdminPort
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c ConsulDataplaneContainer) Terminate() error {
|
|
|
|
return TerminateContainer(c.ctx, c.container, true)
|
|
|
|
}
|
|
|
|
|
2023-10-06 16:06:12 +00:00
|
|
|
func (g ConsulDataplaneContainer) Exec(ctx context.Context, cmd []string) (string, error) {
|
|
|
|
exitCode, reader, err := g.container.Exec(ctx, cmd)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("exec with error %s", err)
|
|
|
|
}
|
|
|
|
if exitCode != 0 {
|
|
|
|
return "", fmt.Errorf("exec with exit code %d", exitCode)
|
|
|
|
}
|
|
|
|
buf, err := io.ReadAll(reader)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("error reading from exec output: %w", err)
|
|
|
|
}
|
|
|
|
return string(buf), nil
|
|
|
|
}
|
|
|
|
|
2023-09-26 20:21:47 +00:00
|
|
|
func (g ConsulDataplaneContainer) GetStatus() (string, error) {
|
|
|
|
state, err := g.container.State(g.ctx)
|
|
|
|
return state.Status, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewConsulDataplane(ctx context.Context, proxyID string, serverAddresses string, grpcPort int, serviceBindPorts []int,
|
2023-10-06 16:06:12 +00:00
|
|
|
node Agent, tproxy bool, bootstrapToken string, containerArgs ...string) (*ConsulDataplaneContainer, error) {
|
2023-09-26 20:21:47 +00:00
|
|
|
namePrefix := fmt.Sprintf("%s-consul-dataplane-%s", node.GetDatacenter(), proxyID)
|
|
|
|
containerName := utils.RandName(namePrefix)
|
|
|
|
|
|
|
|
internalAdminPort, err := node.ClaimAdminPort()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
pod := node.GetPod()
|
|
|
|
if pod == nil {
|
|
|
|
return nil, fmt.Errorf("node Pod is required")
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
appPortStrs []string
|
|
|
|
adminPortStr = strconv.Itoa(internalAdminPort)
|
|
|
|
)
|
|
|
|
|
|
|
|
for _, port := range serviceBindPorts {
|
|
|
|
appPortStrs = append(appPortStrs, strconv.Itoa(port))
|
|
|
|
}
|
|
|
|
|
|
|
|
// expose the app ports and the envoy adminPortStr on the agent container
|
|
|
|
exposedPorts := make([]string, len(appPortStrs))
|
|
|
|
copy(exposedPorts, appPortStrs)
|
|
|
|
exposedPorts = append(exposedPorts, adminPortStr)
|
|
|
|
|
2023-10-06 16:06:12 +00:00
|
|
|
req := testcontainers.ContainerRequest{
|
|
|
|
Image: "consul-dataplane:local",
|
|
|
|
WaitingFor: wait.ForLog("").WithStartupTimeout(60 * time.Second),
|
|
|
|
AutoRemove: false,
|
|
|
|
Name: containerName,
|
|
|
|
Env: map[string]string{},
|
|
|
|
}
|
|
|
|
|
|
|
|
var command []string
|
|
|
|
|
|
|
|
if tproxy {
|
|
|
|
req.Entrypoint = []string{"sh", "/bin/tproxy-startup.sh"}
|
|
|
|
req.Env["REDIRECT_TRAFFIC_ARGS"] = strings.Join(
|
|
|
|
[]string{
|
|
|
|
// TODO once we run this on a different pod from Consul agents, we can eliminate most of this.
|
|
|
|
"-exclude-inbound-port", fmt.Sprint(internalAdminPort),
|
|
|
|
"-exclude-inbound-port", "8300",
|
|
|
|
"-exclude-inbound-port", "8301",
|
|
|
|
"-exclude-inbound-port", "8302",
|
|
|
|
"-exclude-inbound-port", "8500",
|
|
|
|
"-exclude-inbound-port", "8502",
|
|
|
|
"-exclude-inbound-port", "8600",
|
|
|
|
"-proxy-inbound-port", "20000",
|
|
|
|
"-consul-dns-ip", "127.0.0.1",
|
|
|
|
"-consul-dns-port", "8600",
|
|
|
|
},
|
|
|
|
" ",
|
|
|
|
)
|
|
|
|
req.CapAdd = append(req.CapAdd, "NET_ADMIN")
|
|
|
|
command = append(command, "consul-dataplane")
|
|
|
|
}
|
|
|
|
|
|
|
|
command = append(command,
|
2023-09-26 20:21:47 +00:00
|
|
|
"-addresses", serverAddresses,
|
|
|
|
fmt.Sprintf("-grpc-port=%d", grpcPort),
|
|
|
|
fmt.Sprintf("-proxy-id=%s", proxyID),
|
|
|
|
"-proxy-namespace=default",
|
|
|
|
"-proxy-partition=default",
|
|
|
|
"-log-level=info",
|
|
|
|
"-log-json=false",
|
|
|
|
"-envoy-concurrency=2",
|
|
|
|
"-tls-disabled",
|
|
|
|
fmt.Sprintf("-envoy-admin-bind-port=%d", internalAdminPort),
|
2023-10-06 16:06:12 +00:00
|
|
|
)
|
2023-09-26 20:21:47 +00:00
|
|
|
|
2023-10-06 16:06:12 +00:00
|
|
|
if bootstrapToken != "" {
|
|
|
|
command = append(command,
|
|
|
|
"-credential-type=static",
|
|
|
|
fmt.Sprintf("-static-token=%s", bootstrapToken))
|
2023-09-26 20:21:47 +00:00
|
|
|
}
|
|
|
|
|
2023-10-06 16:06:12 +00:00
|
|
|
req.Cmd = append(command, containerArgs...)
|
|
|
|
|
2023-09-26 20:21:47 +00:00
|
|
|
info, err := LaunchContainerOnNode(ctx, node, req, exposedPorts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
out := &ConsulDataplaneContainer{
|
|
|
|
ctx: ctx,
|
|
|
|
container: info.Container,
|
|
|
|
ip: info.IP,
|
|
|
|
serviceName: containerName,
|
|
|
|
externalAdminPort: info.MappedPorts[adminPortStr].Int(),
|
|
|
|
internalAdminPort: internalAdminPort,
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, port := range appPortStrs {
|
|
|
|
out.appPort = append(out.appPort, info.MappedPorts[port].Int())
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Printf("NewConsulDataplane: proxyID %s, mapped App Port %d, service bind port %v\n",
|
|
|
|
proxyID, out.appPort, serviceBindPorts)
|
|
|
|
fmt.Printf("NewConsulDataplane: proxyID %s, , mapped admin port %d, admin port %d\n",
|
|
|
|
proxyID, out.externalAdminPort, internalAdminPort)
|
|
|
|
|
|
|
|
return out, nil
|
|
|
|
}
|