switch all client nodes in dc2 to dataplane [NET-4299] (#18608)

This commit is contained in:
Nick Irvine 2023-09-06 16:46:34 -07:00 committed by GitHub
parent 4eb2197e82
commit 373c7dc144
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 294 additions and 240 deletions

View File

@ -86,7 +86,10 @@ func (s *ac5_2PQFailoverSuite) setupDC(ct *commonTopo, clu, peerClu *topology.Cl
Service: NewFortioServiceWithDefaults( Service: NewFortioServiceWithDefaults(
clu.Datacenter, clu.Datacenter,
serverSID, serverSID,
nil, func(s *topology.Service) {
s.EnvoyAdminPort = 0
s.DisableServiceMesh = true
},
), ),
Exports: []api.ServiceConsumer{{Peer: peer}}, Exports: []api.ServiceConsumer{{Peer: peer}},
} }
@ -149,7 +152,10 @@ func (s *ac5_2PQFailoverSuite) setupDC3(ct *commonTopo, clu, peer1, peer2 *topol
Service: NewFortioServiceWithDefaults( Service: NewFortioServiceWithDefaults(
clu.Datacenter, clu.Datacenter,
serverSID, serverSID,
nil, func(s *topology.Service) {
s.EnvoyAdminPort = 0
s.DisableServiceMesh = true
},
), ),
Exports: func() []api.ServiceConsumer { Exports: func() []api.ServiceConsumer {
var consumers []api.ServiceConsumer var consumers []api.ServiceConsumer

View File

@ -141,8 +141,12 @@ func (s *suiteRotateGW) setup(t *testing.T, ct *commonTopo) {
// add a second mesh gateway "new" // add a second mesh gateway "new"
s.newMGWNodeName = fmt.Sprintf("new-%s-default-mgw", clu.Name) s.newMGWNodeName = fmt.Sprintf("new-%s-default-mgw", clu.Name)
nodeKind := topology.NodeKindClient
if clu.Datacenter == agentlessDC {
nodeKind = topology.NodeKindDataplane
}
clu.Nodes = append(clu.Nodes, newTopologyMeshGatewaySet( clu.Nodes = append(clu.Nodes, newTopologyMeshGatewaySet(
topology.NodeKindClient, nodeKind,
"default", "default",
s.newMGWNodeName, s.newMGWNodeName,
1, 1,

View File

@ -41,6 +41,7 @@ type asserter struct {
type sprawlLite interface { type sprawlLite interface {
HTTPClientForCluster(clusterName string) (*http.Client, error) HTTPClientForCluster(clusterName string) (*http.Client, error)
APIClientForNode(clusterName string, nid topology.NodeID, token string) (*api.Client, error) APIClientForNode(clusterName string, nid topology.NodeID, token string) (*api.Client, error)
APIClientForCluster(clusterName string, token string) (*api.Client, error)
Topology() *topology.Topology Topology() *topology.Topology
} }
@ -58,18 +59,12 @@ func (a *asserter) mustGetHTTPClient(t *testing.T, cluster string) *http.Client
} }
func (a *asserter) mustGetAPIClient(t *testing.T, cluster string) *api.Client { func (a *asserter) mustGetAPIClient(t *testing.T, cluster string) *api.Client {
cl, err := a.apiClientFor(cluster) clu := a.sp.Topology().Clusters[cluster]
cl, err := a.sp.APIClientForCluster(clu.Name, "")
require.NoError(t, err) require.NoError(t, err)
return cl return cl
} }
func (a *asserter) apiClientFor(cluster string) (*api.Client, error) {
clu := a.sp.Topology().Clusters[cluster]
// TODO: this always goes to the first client, but we might want to balance this
cl, err := a.sp.APIClientForNode(cluster, clu.FirstClient().ID(), "")
return cl, err
}
// httpClientFor returns a pre-configured http.Client that proxies requests // httpClientFor returns a pre-configured http.Client that proxies requests
// through the embedded squid instance in each LAN. // through the embedded squid instance in each LAN.
// //

View File

@ -52,6 +52,8 @@ type commonTopo struct {
services map[string]map[topology.ServiceID]struct{} services map[string]map[topology.ServiceID]struct{}
} }
const agentlessDC = "dc2"
func NewCommonTopo(t *testing.T) *commonTopo { func NewCommonTopo(t *testing.T) *commonTopo {
t.Helper() t.Helper()
@ -84,11 +86,9 @@ func NewCommonTopo(t *testing.T) *commonTopo {
peerings = append(peerings, addPeerings(dc1, dc3)...) peerings = append(peerings, addPeerings(dc1, dc3)...)
peerings = append(peerings, addPeerings(dc2, dc3)...) peerings = append(peerings, addPeerings(dc2, dc3)...)
addMeshGateways(dc1, topology.NodeKindClient) addMeshGateways(dc1)
addMeshGateways(dc2, topology.NodeKindClient) addMeshGateways(dc2)
addMeshGateways(dc3, topology.NodeKindClient) addMeshGateways(dc3)
// TODO: consul-topology doesn't support this yet
// addMeshGateways(dc2, topology.NodeKindDataplane)
setupGlobals(dc1) setupGlobals(dc1)
setupGlobals(dc2) setupGlobals(dc2)
@ -131,7 +131,7 @@ func (ct *commonTopo) postLaunchChecks(t *testing.T) {
) )
// check that exports line up as expected // check that exports line up as expected
for _, clu := range ct.Sprawl.Config().Clusters { for _, clu := range ct.Sprawl.Topology().Clusters {
// expected exports per peer // expected exports per peer
type key struct { type key struct {
peer string peer string
@ -191,9 +191,6 @@ func LocalPeerName(clu *topology.Cluster, partition string) string {
type serviceExt struct { type serviceExt struct {
*topology.Service *topology.Service
// default NodeKindClient
NodeKind topology.NodeKind
Exports []api.ServiceConsumer Exports []api.ServiceConsumer
Config *api.ServiceConfigEntry Config *api.ServiceConfigEntry
Intentions *api.ServiceIntentionsConfigEntry Intentions *api.ServiceIntentionsConfigEntry
@ -227,8 +224,15 @@ func (ct *commonTopo) AddServiceNode(clu *topology.Cluster, svc serviceExt) *top
return n return n
} }
nodeKind := topology.NodeKindClient
// TODO: bug in deployer somewhere; it should guard against a KindDataplane node with
// DisableServiceMesh services on it; dataplane is only for service-mesh
if !svc.DisableServiceMesh && clu.Datacenter == agentlessDC {
nodeKind = topology.NodeKindDataplane
}
node := &topology.Node{ node := &topology.Node{
Kind: topology.NodeKindClient, Kind: nodeKind,
Name: serviceHostnameString(clu.Datacenter, svc.ID), Name: serviceHostnameString(clu.Datacenter, svc.ID),
Partition: svc.ID.Partition, Partition: svc.ID.Partition,
Addresses: []*topology.Address{ Addresses: []*topology.Address{
@ -239,9 +243,6 @@ func (ct *commonTopo) AddServiceNode(clu *topology.Cluster, svc serviceExt) *top
}, },
Cluster: clusterName, Cluster: clusterName,
} }
if svc.NodeKind != "" {
node.Kind = svc.NodeKind
}
clu.Nodes = append(clu.Nodes, node) clu.Nodes = append(clu.Nodes, node)
// Export if necessary // Export if necessary
@ -265,7 +266,7 @@ func (ct *commonTopo) AddServiceNode(clu *topology.Cluster, svc serviceExt) *top
} }
func (ct *commonTopo) APIClientForCluster(t *testing.T, clu *topology.Cluster) *api.Client { func (ct *commonTopo) APIClientForCluster(t *testing.T, clu *topology.Cluster) *api.Client {
cl, err := ct.Sprawl.APIClientForNode(clu.Name, clu.FirstClient().ID(), "") cl, err := ct.Sprawl.APIClientForCluster(clu.Name, "")
require.NoError(t, err) require.NoError(t, err)
return cl return cl
} }
@ -372,10 +373,14 @@ func setupGlobals(clu *topology.Cluster) {
// addMeshGateways adds a mesh gateway for every partition in the cluster. // addMeshGateways adds a mesh gateway for every partition in the cluster.
// Assumes that the LAN network name is equal to datacenter name. // Assumes that the LAN network name is equal to datacenter name.
func addMeshGateways(c *topology.Cluster, kind topology.NodeKind) { func addMeshGateways(c *topology.Cluster) {
nodeKind := topology.NodeKindClient
if c.Datacenter == agentlessDC {
nodeKind = topology.NodeKindDataplane
}
for _, p := range c.Partitions { for _, p := range c.Partitions {
c.Nodes = topology.MergeSlices(c.Nodes, newTopologyMeshGatewaySet( c.Nodes = topology.MergeSlices(c.Nodes, newTopologyMeshGatewaySet(
kind, nodeKind,
p.Name, p.Name,
fmt.Sprintf("%s-%s-mgw", c.Name, p.Name), fmt.Sprintf("%s-%s-mgw", c.Name, p.Name),
1, 1,

View File

@ -318,6 +318,18 @@ func serviceToCatalogRegistration(
Address: node.LocalAddress(), Address: node.LocalAddress(),
}, },
} }
if svc.IsMeshGateway {
reg.Service.Kind = api.ServiceKindMeshGateway
reg.Service.Proxy = &api.AgentServiceConnectProxyConfig{
Config: map[string]interface{}{
"envoy_gateway_no_default_bind": true,
"envoy_gateway_bind_tagged_addresses": true,
},
MeshGateway: api.MeshGatewayConfig{
Mode: api.MeshGatewayModeLocal,
},
}
}
if node.HasPublicAddress() { if node.HasPublicAddress() {
reg.TaggedAddresses = map[string]string{ reg.TaggedAddresses = map[string]string{
"lan": node.LocalAddress(), "lan": node.LocalAddress(),
@ -325,6 +337,26 @@ func serviceToCatalogRegistration(
"wan": node.PublicAddress(), "wan": node.PublicAddress(),
"wan_ipv4": node.PublicAddress(), "wan_ipv4": node.PublicAddress(),
} }
// TODO: not sure what the difference is between these, but with just the
// top-level set, it appeared to not get set in either :/
reg.Service.TaggedAddresses = map[string]api.ServiceAddress{
"lan": {
Address: node.LocalAddress(),
Port: svc.Port,
},
"lan_ipv4": {
Address: node.LocalAddress(),
Port: svc.Port,
},
"wan": {
Address: node.PublicAddress(),
Port: svc.Port,
},
"wan_ipv4": {
Address: node.PublicAddress(),
Port: svc.Port,
},
}
} }
if cluster.Enterprise { if cluster.Enterprise {
reg.Partition = svc.ID.Partition reg.Partition = svc.ID.Partition

View File

@ -23,9 +23,10 @@ COPY --from=0 /bin/consul /bin/consul
// FROM hashicorp/consul-dataplane:latest // FROM hashicorp/consul-dataplane:latest
// COPY --from=busybox:uclibc /bin/sh /bin/sh // COPY --from=busybox:uclibc /bin/sh /bin/sh
// TODO: busybox:latest doesn't work, see https://hashicorp.slack.com/archives/C03EUN3QF1C/p1691784078972959
const dockerfileDataplane = ` const dockerfileDataplane = `
ARG DATAPLANE_IMAGE ARG DATAPLANE_IMAGE
FROM busybox:latest FROM busybox:1.34
FROM ${DATAPLANE_IMAGE} FROM ${DATAPLANE_IMAGE}
COPY --from=0 /bin/busybox /bin/busybox COPY --from=0 /bin/busybox /bin/busybox
USER 0:0 USER 0:0

View File

@ -13,14 +13,14 @@ import (
"github.com/hashicorp/consul/testing/deployer/topology" "github.com/hashicorp/consul/testing/deployer/topology"
) )
func (g *Generator) generateAgentHCL(node *topology.Node) (string, error) { func (g *Generator) generateAgentHCL(node *topology.Node) string {
if !node.IsAgent() { if !node.IsAgent() {
return "", fmt.Errorf("not an agent") panic("generateAgentHCL only applies to agents")
} }
cluster, ok := g.topology.Clusters[node.Cluster] cluster, ok := g.topology.Clusters[node.Cluster]
if !ok { if !ok {
return "", fmt.Errorf("no such cluster: %s", node.Cluster) panic(fmt.Sprintf("no such cluster: %s", node.Cluster))
} }
var b HCLBuilder var b HCLBuilder
@ -167,7 +167,7 @@ func (g *Generator) generateAgentHCL(node *topology.Node) (string, error) {
} }
} }
return b.String(), nil return b.String()
} }
type HCLBuilder struct { type HCLBuilder struct {

View File

@ -9,7 +9,6 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"text/template"
"github.com/hashicorp/consul/testing/deployer/topology" "github.com/hashicorp/consul/testing/deployer/topology"
"github.com/hashicorp/consul/testing/deployer/util" "github.com/hashicorp/consul/testing/deployer/util"
@ -179,5 +178,3 @@ server IN A %s ; Consul server
return buf.Bytes() return buf.Bytes()
} }
var tfCorednsT = template.Must(template.ParseFS(content, "templates/container-coredns.tf.tmpl"))

View File

@ -11,6 +11,9 @@ import (
var invalidResourceName = regexp.MustCompile(`[^a-z0-9-]+`) var invalidResourceName = regexp.MustCompile(`[^a-z0-9-]+`)
func DockerImageResourceName(image string) string { func DockerImageResourceName(image string) string {
if image == "" {
panic(`image must not be ""`)
}
return invalidResourceName.ReplaceAllLiteralString(image, "-") return invalidResourceName.ReplaceAllLiteralString(image, "-")
} }

View File

@ -5,9 +5,6 @@ package tfgen
import ( import (
"fmt" "fmt"
"sort"
"strconv"
"text/template"
"github.com/hashicorp/consul/testing/deployer/topology" "github.com/hashicorp/consul/testing/deployer/topology"
) )
@ -22,32 +19,6 @@ type terraformPod struct {
DockerNetworkName string DockerNetworkName string
} }
type terraformConsulAgent struct {
terraformPod
ImageResource string
HCL string
EnterpriseLicense string
Env []string
}
type terraformMeshGatewayService struct {
terraformPod
EnvoyImageResource string
Service *topology.Service
Command []string
}
type terraformService struct {
terraformPod
AppImageResource string
EnvoyImageResource string // agentful
DataplaneImageResource string // agentless
Service *topology.Service
Env []string
Command []string
EnvoyCommand []string // agentful
}
func (g *Generator) generateNodeContainers( func (g *Generator) generateNodeContainers(
step Step, step Step,
cluster *topology.Cluster, cluster *topology.Cluster,
@ -82,156 +53,99 @@ func (g *Generator) generateNodeContainers(
} }
pod.DockerNetworkName = net.DockerName pod.DockerNetworkName = net.DockerName
var ( containers := []Resource{}
containers []Resource
)
if node.IsAgent() { if node.IsAgent() {
agentHCL, err := g.generateAgentHCL(node)
if err != nil {
return nil, err
}
agent := terraformConsulAgent{
terraformPod: pod,
ImageResource: DockerImageResourceName(node.Images.Consul),
HCL: agentHCL,
EnterpriseLicense: g.license,
Env: node.AgentEnv,
}
switch { switch {
case node.IsServer() && step.StartServers(), case node.IsServer() && step.StartServers(),
!node.IsServer() && step.StartAgents(): !node.IsServer() && step.StartAgents():
containers = append(containers, Eval(tfConsulT, &agent)) containers = append(containers, Eval(tfConsulT, struct {
terraformPod
ImageResource string
HCL string
EnterpriseLicense string
}{
terraformPod: pod,
ImageResource: DockerImageResourceName(node.Images.Consul),
HCL: g.generateAgentHCL(node),
EnterpriseLicense: g.license,
}))
} }
} }
svcContainers := []Resource{}
for _, svc := range node.SortedServices() { for _, svc := range node.SortedServices() {
if svc.IsMeshGateway { token := g.sec.ReadServiceToken(node.Cluster, svc.ID)
if node.Kind == topology.NodeKindDataplane { switch {
panic("NOT READY YET") case svc.IsMeshGateway && !node.IsDataplane():
} svcContainers = append(svcContainers, Eval(tfMeshGatewayT, struct {
gw := terraformMeshGatewayService{ terraformPod
ImageResource string
Enterprise bool
Service *topology.Service
Token string
}{
terraformPod: pod, terraformPod: pod,
EnvoyImageResource: DockerImageResourceName(node.Images.EnvoyConsulImage()), ImageResource: DockerImageResourceName(node.Images.EnvoyConsulImage()),
Enterprise: cluster.Enterprise,
Service: svc, Service: svc,
Command: []string{ Token: token,
"consul", "connect", "envoy", }))
"-register", case svc.IsMeshGateway && node.IsDataplane():
"-mesh-gateway", svcContainers = append(svcContainers, Eval(tfMeshGatewayDataplaneT, &struct {
}, terraformPod
} ImageResource string
if token := g.sec.ReadServiceToken(node.Cluster, svc.ID); token != "" { Enterprise bool
gw.Command = append(gw.Command, "-token", token) Service *topology.Service
} Token string
if cluster.Enterprise { }{
gw.Command = append(gw.Command,
"-partition",
svc.ID.Partition,
)
}
gw.Command = append(gw.Command,
"-address",
`{{ GetInterfaceIP \"eth0\" }}:`+strconv.Itoa(svc.Port),
"-wan-address",
`{{ GetInterfaceIP \"eth1\" }}:`+strconv.Itoa(svc.Port),
)
gw.Command = append(gw.Command,
"-grpc-addr", "http://127.0.0.1:8502",
"-admin-bind",
// for demo purposes
"0.0.0.0:"+strconv.Itoa(svc.EnvoyAdminPort),
"--",
"-l",
"trace",
)
if step.StartServices() {
containers = append(containers, Eval(tfMeshGatewayT, &gw))
}
} else {
tfsvc := terraformService{
terraformPod: pod, terraformPod: pod,
AppImageResource: DockerImageResourceName(svc.Image), ImageResource: DockerImageResourceName(node.Images.LocalDataplaneImage()),
Enterprise: cluster.Enterprise,
Service: svc, Service: svc,
Command: svc.Command, Token: token,
}))
case !svc.IsMeshGateway:
svcContainers = append(svcContainers, Eval(tfAppT, struct {
terraformPod
ImageResource string
Service *topology.Service
}{
terraformPod: pod,
ImageResource: DockerImageResourceName(svc.Image),
Service: svc,
}))
if svc.DisableServiceMesh {
break
} }
tfsvc.Env = append(tfsvc.Env, svc.Env...)
tmpl := tfAppSidecarT
var img string
if node.IsDataplane() {
tmpl = tfAppDataplaneT
img = DockerImageResourceName(node.Images.LocalDataplaneImage())
} else {
img = DockerImageResourceName(node.Images.EnvoyConsulImage())
}
svcContainers = append(svcContainers, Eval(tmpl, struct {
terraformPod
ImageResource string
Service *topology.Service
Token string
Enterprise bool
}{
terraformPod: pod,
ImageResource: img,
Service: svc,
Token: token,
Enterprise: cluster.Enterprise,
}))
}
if step.StartServices() { if step.StartServices() {
containers = append(containers, Eval(tfAppT, &tfsvc)) containers = append(containers, svcContainers...)
}
setenv := func(k, v string) {
tfsvc.Env = append(tfsvc.Env, k+"="+v)
}
if !svc.DisableServiceMesh {
if node.IsDataplane() {
tfsvc.DataplaneImageResource = DockerImageResourceName(node.Images.LocalDataplaneImage())
tfsvc.EnvoyImageResource = ""
tfsvc.EnvoyCommand = nil
// --- REQUIRED ---
setenv("DP_CONSUL_ADDRESSES", "server."+node.Cluster+"-consulcluster.lan")
setenv("DP_SERVICE_NODE_NAME", node.PodName())
setenv("DP_PROXY_SERVICE_ID", svc.ID.Name+"-sidecar-proxy")
} else {
tfsvc.DataplaneImageResource = ""
tfsvc.EnvoyImageResource = DockerImageResourceName(node.Images.EnvoyConsulImage())
tfsvc.EnvoyCommand = []string{
"consul", "connect", "envoy",
"-sidecar-for", svc.ID.Name,
}
}
if cluster.Enterprise {
if node.IsDataplane() {
setenv("DP_SERVICE_NAMESPACE", svc.ID.Namespace)
setenv("DP_SERVICE_PARTITION", svc.ID.Partition)
} else {
tfsvc.EnvoyCommand = append(tfsvc.EnvoyCommand,
"-partition",
svc.ID.Partition,
"-namespace",
svc.ID.Namespace,
)
}
}
if token := g.sec.ReadServiceToken(node.Cluster, svc.ID); token != "" {
if node.IsDataplane() {
setenv("DP_CREDENTIAL_TYPE", "static")
setenv("DP_CREDENTIAL_STATIC_TOKEN", token)
} else {
tfsvc.EnvoyCommand = append(tfsvc.EnvoyCommand, "-token", token)
}
}
if node.IsDataplane() {
setenv("DP_ENVOY_ADMIN_BIND_ADDRESS", "0.0.0.0") // for demo purposes
setenv("DP_ENVOY_ADMIN_BIND_PORT", "19000")
setenv("DP_LOG_LEVEL", "trace")
setenv("DP_CA_CERTS", "/consul/config/certs/consul-agent-ca.pem")
setenv("DP_CONSUL_GRPC_PORT", "8503")
setenv("DP_TLS_SERVER_NAME", "server."+node.Datacenter+".consul")
} else {
tfsvc.EnvoyCommand = append(tfsvc.EnvoyCommand,
"-grpc-addr", "http://127.0.0.1:8502",
"-admin-bind",
// for demo purposes
"0.0.0.0:"+strconv.Itoa(svc.EnvoyAdminPort),
"--",
"-l",
"trace",
)
}
if step.StartServices() {
sort.Strings(tfsvc.Env)
if node.IsDataplane() {
containers = append(containers, Eval(tfAppDataplaneT, &tfsvc))
} else {
containers = append(containers, Eval(tfAppSidecarT, &tfsvc))
}
}
}
} }
} }
@ -243,10 +157,3 @@ func (g *Generator) generateNodeContainers(
return containers, nil return containers, nil
} }
var tfPauseT = template.Must(template.ParseFS(content, "templates/container-pause.tf.tmpl"))
var tfConsulT = template.Must(template.ParseFS(content, "templates/container-consul.tf.tmpl"))
var tfMeshGatewayT = template.Must(template.ParseFS(content, "templates/container-mgw.tf.tmpl"))
var tfAppT = template.Must(template.ParseFS(content, "templates/container-app.tf.tmpl"))
var tfAppSidecarT = template.Must(template.ParseFS(content, "templates/container-app-sidecar.tf.tmpl"))
var tfAppDataplaneT = template.Must(template.ParseFS(content, "templates/container-app-dataplane.tf.tmpl"))

View File

@ -7,7 +7,6 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"text/template"
"github.com/hashicorp/consul/testing/deployer/topology" "github.com/hashicorp/consul/testing/deployer/topology"
"github.com/hashicorp/consul/testing/deployer/util" "github.com/hashicorp/consul/testing/deployer/util"
@ -86,5 +85,3 @@ func (g *Generator) getForwardProxyContainer(
return Eval(tfForwardProxyT, &proxy) return Eval(tfForwardProxyT, &proxy)
} }
var tfForwardProxyT = template.Must(template.ParseFS(content, "templates/container-proxy.tf.tmpl"))

View File

@ -1,7 +1,7 @@
resource "docker_container" "{{.Node.DockerName}}-{{.Service.ID.TFString}}-sidecar" { resource "docker_container" "{{.Node.DockerName}}-{{.Service.ID.TFString}}-sidecar" {
name = "{{.Node.DockerName}}-{{.Service.ID.TFString}}-sidecar" name = "{{.Node.DockerName}}-{{.Service.ID.TFString}}-sidecar"
network_mode = "container:${docker_container.{{.PodName}}.id}" network_mode = "container:${docker_container.{{.PodName}}.id}"
image = docker_image.{{.DataplaneImageResource}}.latest image = docker_image.{{.ImageResource}}.latest
restart = "on-failure" restart = "on-failure"
{{- range $k, $v := .Labels }} {{- range $k, $v := .Labels }}
@ -18,9 +18,24 @@ resource "docker_container" "{{.Node.DockerName}}-{{.Service.ID.TFString}}-sidec
} }
env = [ env = [
{{- range .Env }} "DP_CONSUL_ADDRESSES=server.{{.Node.Cluster}}-consulcluster.lan",
"{{.}}", "DP_SERVICE_NODE_NAME={{.Node.PodName}}",
{{- end}} "DP_PROXY_SERVICE_ID={{.Service.ID.Name}}-sidecar-proxy",
{{ if .Enterprise }}
"DP_SERVICE_NAMESPACE={{.Service.ID.Namespace}}",
"DP_SERVICE_PARTITION={{.Service.ID.Partition}}",
{{ end }}
{{ if .Token }}
"DP_CREDENTIAL_TYPE=static",
"DP_CREDENTIAL_STATIC_TOKEN={{.Token}}",
{{ end }}
// for demo purposes
"DP_ENVOY_ADMIN_BIND_ADDRESS=0.0.0.0",
"DP_ENVOY_ADMIN_BIND_PORT=19000",
"DP_LOG_LEVEL=trace",
"DP_CA_CERTS=/consul/config/certs/consul-agent-ca.pem",
"DP_CONSUL_GRPC_PORT=8503",
"DP_TLS_SERVER_NAME=server.{{.Node.Datacenter}}.consul",
] ]
command = [ command = [

View File

@ -1,7 +1,7 @@
resource "docker_container" "{{.Node.DockerName}}-{{.Service.ID.TFString}}-sidecar" { resource "docker_container" "{{.Node.DockerName}}-{{.Service.ID.TFString}}-sidecar" {
name = "{{.Node.DockerName}}-{{.Service.ID.TFString}}-sidecar" name = "{{.Node.DockerName}}-{{.Service.ID.TFString}}-sidecar"
network_mode = "container:${docker_container.{{.PodName}}.id}" network_mode = "container:${docker_container.{{.PodName}}.id}"
image = docker_image.{{.EnvoyImageResource}}.latest image = docker_image.{{.ImageResource}}.latest
restart = "on-failure" restart = "on-failure"
{{- range $k, $v := .Labels }} {{- range $k, $v := .Labels }}
@ -17,15 +17,21 @@ resource "docker_container" "{{.Node.DockerName}}-{{.Service.ID.TFString}}-sidec
read_only = true read_only = true
} }
env = [
{{- range .Env }}
"{{.}}",
{{- end}}
]
command = [ command = [
{{- range .EnvoyCommand }} "consul", "connect", "envoy",
"{{.}}", "-sidecar-for={{.Service.ID.Name}}",
{{- end }} "-grpc-addr=http://127.0.0.1:8502",
// for demo purposes (TODO: huh?)
"-admin-bind=0.0.0.0:{{.Service.EnvoyAdminPort}}",
{{if .Enterprise}}
"-partition={{.Service.ID.Partition}}",
"-namespace={{.Service.ID.Namespace}}",
{{end}}
{{if .Token }}
"-token={{.Token}}",
{{end}}
"--",
"-l",
"trace",
] ]
} }

View File

@ -1,7 +1,7 @@
resource "docker_container" "{{.Node.DockerName}}-{{.Service.ID.TFString}}" { resource "docker_container" "{{.Node.DockerName}}-{{.Service.ID.TFString}}" {
name = "{{.Node.DockerName}}-{{.Service.ID.TFString}}" name = "{{.Node.DockerName}}-{{.Service.ID.TFString}}"
network_mode = "container:${docker_container.{{.PodName}}.id}" network_mode = "container:${docker_container.{{.PodName}}.id}"
image = docker_image.{{.AppImageResource}}.latest image = docker_image.{{.ImageResource}}.latest
restart = "on-failure" restart = "on-failure"
{{- range $k, $v := .Labels }} {{- range $k, $v := .Labels }}
@ -12,13 +12,13 @@ resource "docker_container" "{{.Node.DockerName}}-{{.Service.ID.TFString}}" {
{{- end }} {{- end }}
env = [ env = [
{{- range .Env }} {{- range .Service.Env }}
"{{.}}", "{{.}}",
{{- end}} {{- end}}
] ]
command = [ command = [
{{- range .Command }} {{- range .Service.Command }}
"{{.}}", "{{.}}",
{{- end }} {{- end }}
] ]

View File

@ -8,9 +8,6 @@ resource "docker_container" "{{.Node.DockerName}}" {
"CONSUL_UID=0", "CONSUL_UID=0",
"CONSUL_GID=0", "CONSUL_GID=0",
"CONSUL_LICENSE={{.EnterpriseLicense}}", "CONSUL_LICENSE={{.EnterpriseLicense}}",
{{- range .Env }}
"{{.}}",
{{- end}}
] ]
{{- range $k, $v := .Labels }} {{- range $k, $v := .Labels }}

View File

@ -0,0 +1,45 @@
resource "docker_container" "{{.Node.DockerName}}-{{.Service.ID.TFString}}" {
name = "{{.Node.DockerName}}-{{.Service.ID.TFString}}"
network_mode = "container:${docker_container.{{.PodName}}.id}"
image = docker_image.{{.ImageResource}}.latest
restart = "on-failure"
{{- range $k, $v := .Labels }}
labels {
label = "{{ $k }}"
value = "{{ $v }}"
}
{{- end }}
volumes {
volume_name = "{{.TLSVolumeName}}"
container_path = "/consul/config/certs"
read_only = true
}
env = [
"DP_CONSUL_ADDRESSES=server.{{.Node.Cluster}}-consulcluster.lan",
"DP_SERVICE_NODE_NAME={{.Node.PodName}}",
"DP_PROXY_SERVICE_ID={{.Service.ID.Name}}",
{{ if .Enterprise }}
"DP_SERVICE_NAMESPACE={{.Service.ID.Namespace}}",
"DP_SERVICE_PARTITION={{.Service.ID.Partition}}",
{{ end }}
{{ if .Token }}
"DP_CREDENTIAL_TYPE=static",
"DP_CREDENTIAL_STATIC_TOKEN={{.Token}}",
{{ end }}
// for demo purposes
"DP_ENVOY_ADMIN_BIND_ADDRESS=0.0.0.0",
"DP_ENVOY_ADMIN_BIND_PORT=19000",
"DP_LOG_LEVEL=trace",
"DP_CA_CERTS=/consul/config/certs/consul-agent-ca.pem",
"DP_CONSUL_GRPC_PORT=8503",
"DP_TLS_SERVER_NAME=server.{{.Node.Datacenter}}.consul",
]
command = [
"/usr/local/bin/consul-dataplane",
]
}

View File

@ -1,7 +1,7 @@
resource "docker_container" "{{.Node.DockerName}}-{{.Service.ID.TFString}}" { resource "docker_container" "{{.Node.DockerName}}-{{.Service.ID.TFString}}" {
name = "{{.Node.DockerName}}-{{.Service.ID.TFString}}" name = "{{.Node.DockerName}}-{{.Service.ID.TFString}}"
network_mode = "container:${docker_container.{{.PodName}}.id}" network_mode = "container:${docker_container.{{.PodName}}.id}"
image = docker_image.{{.EnvoyImageResource}}.latest image = docker_image.{{.ImageResource}}.latest
restart = "on-failure" restart = "on-failure"
{{- range $k, $v := .Labels }} {{- range $k, $v := .Labels }}
@ -18,8 +18,22 @@ resource "docker_container" "{{.Node.DockerName}}-{{.Service.ID.TFString}}" {
} }
command = [ command = [
{{- range .Command }} "consul", "connect", "envoy",
"{{.}}", "-register",
{{- end }} "-mesh-gateway",
"-address={{`{{ GetInterfaceIP \"eth0\" }}`}}:{{.Service.Port}}",
"-wan-address={{`{{ GetInterfaceIP \"eth1\" }}`}}:{{.Service.Port}}",
"-grpc-addr=http://127.0.0.1:8502",
// for demo purposes (TODO: huh?)
"-admin-bind=0.0.0.0:{{.Service.EnvoyAdminPort}}",
{{ if .Enterprise }}
"-partition={{.Service.ID.Partition}}",
{{end}}
{{ if .Token }}
"-token={{.Token}}",
{{end}}
"--",
"-l",
"trace",
] ]
} }

View File

@ -5,6 +5,7 @@ package tfgen
import ( import (
"embed" "embed"
"text/template"
) )
//go:embed templates/container-app-dataplane.tf.tmpl //go:embed templates/container-app-dataplane.tf.tmpl
@ -12,7 +13,20 @@ import (
//go:embed templates/container-app.tf.tmpl //go:embed templates/container-app.tf.tmpl
//go:embed templates/container-consul.tf.tmpl //go:embed templates/container-consul.tf.tmpl
//go:embed templates/container-mgw.tf.tmpl //go:embed templates/container-mgw.tf.tmpl
//go:embed templates/container-mgw-dataplane.tf.tmpl
//go:embed templates/container-pause.tf.tmpl //go:embed templates/container-pause.tf.tmpl
//go:embed templates/container-proxy.tf.tmpl //go:embed templates/container-proxy.tf.tmpl
//go:embed templates/container-coredns.tf.tmpl //go:embed templates/container-coredns.tf.tmpl
var content embed.FS var content embed.FS
var (
tfAppDataplaneT = template.Must(template.ParseFS(content, "templates/container-app-dataplane.tf.tmpl"))
tfAppSidecarT = template.Must(template.ParseFS(content, "templates/container-app-sidecar.tf.tmpl"))
tfAppT = template.Must(template.ParseFS(content, "templates/container-app.tf.tmpl"))
tfConsulT = template.Must(template.ParseFS(content, "templates/container-consul.tf.tmpl"))
tfMeshGatewayT = template.Must(template.ParseFS(content, "templates/container-mgw.tf.tmpl"))
tfMeshGatewayDataplaneT = template.Must(template.ParseFS(content, "templates/container-mgw-dataplane.tf.tmpl"))
tfPauseT = template.Must(template.ParseFS(content, "templates/container-pause.tf.tmpl"))
tfForwardProxyT = template.Must(template.ParseFS(content, "templates/container-proxy.tf.tmpl"))
tfCorednsT = template.Must(template.ParseFS(content, "templates/container-coredns.tf.tmpl"))
)

View File

@ -113,6 +113,21 @@ func (s *Sprawl) APIClientForNode(clusterName string, nid topology.NodeID, token
) )
} }
// APIClientForCluster is a convenience wrapper for APIClientForNode that returns
// an API client for an agent node in the cluster, preferring clients, then servers
func (s *Sprawl) APIClientForCluster(clusterName, token string) (*api.Client, error) {
clu := s.topology.Clusters[clusterName]
// TODO: this always goes to the first client, but we might want to balance this
firstAgent := clu.FirstClient()
if firstAgent == nil {
firstAgent = clu.FirstServer()
}
if firstAgent == nil {
return nil, fmt.Errorf("failed to find agent in cluster %s", clusterName)
}
return s.APIClientForNode(clusterName, firstAgent.ID(), token)
}
func copyConfig(cfg *topology.Config) (*topology.Config, error) { func copyConfig(cfg *topology.Config) (*topology.Config, error) {
dup, err := copystructure.Copy(cfg) dup, err := copystructure.Copy(cfg)
if err != nil { if err != nil {

View File

@ -88,7 +88,9 @@ fi
"-i", "-i",
"--net=none", "--net=none",
"-v", cluster.TLSVolumeName + ":/data", "-v", cluster.TLSVolumeName + ":/data",
"busybox:latest", // TODO: latest busted?
// https://hashicorp.slack.com/archives/C03EUN3QF1C/p1691784078972959
"busybox:1.34",
"sh", "-c", "sh", "-c",
// Need this so the permissions stick; docker seems to treat unused volumes differently. // Need this so the permissions stick; docker seems to treat unused volumes differently.
`touch /data/VOLUME_PLACEHOLDER && chown -R ` + consulUserArg + ` /data`, `touch /data/VOLUME_PLACEHOLDER && chown -R ` + consulUserArg + ` /data`,

View File

@ -66,6 +66,7 @@ func (i Images) EnvoyConsulImage() string {
return "local/" + name1 + "-and-" + name2 + ":" + tag1 + "-with-" + tag2 return "local/" + name1 + "-and-" + name2 + ":" + tag1 + "-with-" + tag2
} }
// TODO: what is this for and why do we need to do this and why is it named this?
func (i Images) ChooseNode(kind NodeKind) Images { func (i Images) ChooseNode(kind NodeKind) Images {
switch kind { switch kind {
case NodeKindServer: case NodeKindServer:

View File

@ -290,6 +290,7 @@ func (c *Cluster) ServerByAddr(addr string) *Node {
func (c *Cluster) FirstServer() *Node { func (c *Cluster) FirstServer() *Node {
for _, node := range c.Nodes { for _, node := range c.Nodes {
// TODO: not sure why we check that it has 8500 exposed?
if node.IsServer() && !node.Disabled && node.ExposedPort(8500) > 0 { if node.IsServer() && !node.Disabled && node.ExposedPort(8500) > 0 {
return node return node
} }
@ -432,9 +433,6 @@ type Node struct {
// the enclosing Cluster. // the enclosing Cluster.
Images Images Images Images
// AgentEnv contains optional environment variables to attach to Consul agents.
AgentEnv []string
Disabled bool `json:",omitempty"` Disabled bool `json:",omitempty"`
Addresses []*Address Addresses []*Address