mirror of https://github.com/hashicorp/consul
286 lines
6.8 KiB
Go
286 lines
6.8 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package tfgen
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/hcl/v2/hclwrite"
|
|
|
|
"github.com/hashicorp/consul/testing/deployer/sprawl/internal/secrets"
|
|
"github.com/hashicorp/consul/testing/deployer/topology"
|
|
)
|
|
|
|
func (g *Generator) generateAgentHCL(node *topology.Node) string {
|
|
if !node.IsAgent() {
|
|
panic("generateAgentHCL only applies to agents")
|
|
}
|
|
|
|
cluster, ok := g.topology.Clusters[node.Cluster]
|
|
if !ok {
|
|
panic(fmt.Sprintf("no such cluster: %s", node.Cluster))
|
|
}
|
|
|
|
var b HCLBuilder
|
|
|
|
// We first write ExtraConfig since it could be overwritten by specific
|
|
// configurations below
|
|
if node.ExtraConfig != "" {
|
|
b.format(node.ExtraConfig)
|
|
}
|
|
|
|
b.add("server", node.IsServer())
|
|
b.add("bind_addr", "0.0.0.0")
|
|
b.add("client_addr", "0.0.0.0")
|
|
b.add("advertise_addr", `{{ GetInterfaceIP "eth0" }}`)
|
|
b.add("datacenter", node.Datacenter)
|
|
b.add("disable_update_check", true)
|
|
b.add("log_level", "trace")
|
|
b.add("enable_debug", true)
|
|
b.add("use_streaming_backend", true)
|
|
|
|
// speed up leaves
|
|
b.addBlock("performance", func() {
|
|
b.add("leave_drain_time", "50ms")
|
|
b.add("raft_multiplier", 1)
|
|
})
|
|
|
|
b.add("primary_datacenter", node.Datacenter)
|
|
|
|
// Using retry_join here is bad because changing server membership will
|
|
// destroy and recreate all of the servers
|
|
// if !node.IsServer() {
|
|
b.addSlice("retry_join", []string{"server." + node.Cluster + "-consulcluster.lan"})
|
|
b.add("retry_interval", "1s")
|
|
// }
|
|
|
|
if node.Segment != nil {
|
|
if node.Kind != topology.NodeKindClient {
|
|
panic("segment only applies to client agent")
|
|
}
|
|
b.add("segment", node.Segment.Name)
|
|
b.addSlice("retry_join", []string{
|
|
fmt.Sprintf("server.%s-consulcluster.lan:%d", node.Cluster, node.Segment.Port),
|
|
})
|
|
}
|
|
|
|
if node.Images.GreaterThanVersion(topology.MinVersionPeering) {
|
|
if node.IsServer() {
|
|
b.addBlock("peering", func() {
|
|
b.add("enabled", true)
|
|
})
|
|
}
|
|
}
|
|
|
|
b.addBlock("ui_config", func() {
|
|
b.add("enabled", true)
|
|
})
|
|
|
|
b.addBlock("telemetry", func() {
|
|
b.add("disable_hostname", true)
|
|
b.add("prometheus_retention_time", "168h")
|
|
})
|
|
|
|
if !cluster.DisableGossipEncryption {
|
|
b.add("encrypt", g.sec.ReadGeneric(node.Cluster, secrets.GossipKey))
|
|
}
|
|
|
|
{
|
|
var (
|
|
root = "/consul/config/certs"
|
|
caFile = root + "/consul-agent-ca.pem"
|
|
certFile = root + "/" + node.TLSCertPrefix + ".pem"
|
|
certKey = root + "/" + node.TLSCertPrefix + "-key.pem"
|
|
)
|
|
|
|
if node.Images.GreaterThanVersion(topology.MinVersionTLS) {
|
|
b.addBlock("tls", func() {
|
|
b.addBlock("internal_rpc", func() {
|
|
b.add("ca_file", caFile)
|
|
b.add("cert_file", certFile)
|
|
b.add("key_file", certKey)
|
|
b.add("verify_incoming", true)
|
|
b.add("verify_server_hostname", true)
|
|
b.add("verify_outgoing", true)
|
|
})
|
|
// if cfg.EncryptionTLSAPI {
|
|
// b.addBlock("https", func() {
|
|
// b.add("ca_file", caFile)
|
|
// b.add("cert_file", certFile)
|
|
// b.add("key_file", certKey)
|
|
// // b.add("verify_incoming", true)
|
|
// })
|
|
// }
|
|
if node.IsServer() {
|
|
b.addBlock("grpc", func() {
|
|
b.add("ca_file", caFile)
|
|
b.add("cert_file", certFile)
|
|
b.add("key_file", certKey)
|
|
// b.add("verify_incoming", true)
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
b.addBlock("ports", func() {
|
|
if node.Images.GreaterThanVersion(topology.MinVersionPeering) {
|
|
if node.IsServer() {
|
|
b.add("grpc_tls", 8503)
|
|
b.add("grpc", -1)
|
|
} else {
|
|
b.add("grpc", 8502)
|
|
b.add("grpc_tls", -1)
|
|
}
|
|
}
|
|
b.add("http", 8500)
|
|
b.add("dns", 8600)
|
|
})
|
|
|
|
b.addSlice("recursors", []string{"8.8.8.8"})
|
|
|
|
b.addBlock("acl", func() {
|
|
b.add("enabled", true)
|
|
b.add("default_policy", "deny")
|
|
b.add("down_policy", "extend-cache")
|
|
b.add("enable_token_persistence", true)
|
|
|
|
if node.Images.GreaterThanVersion(topology.MinVersionAgentTokenPartition) {
|
|
b.addBlock("tokens", func() {
|
|
if node.IsServer() {
|
|
b.add("initial_management", g.sec.ReadGeneric(node.Cluster, secrets.BootstrapToken))
|
|
}
|
|
b.add("agent_recovery", g.sec.ReadGeneric(node.Cluster, secrets.AgentRecovery))
|
|
b.add("agent", g.sec.ReadAgentToken(node.Cluster, node.ID()))
|
|
})
|
|
} else {
|
|
b.addBlock("tokens", func() {
|
|
if node.IsServer() {
|
|
b.add("master", g.sec.ReadGeneric(node.Cluster, secrets.BootstrapToken))
|
|
}
|
|
})
|
|
}
|
|
})
|
|
|
|
if node.IsServer() {
|
|
// bootstrap_expect is omitted if this node is a new server
|
|
if !node.IsNewServer {
|
|
b.add("bootstrap_expect", len(cluster.ServerNodes()))
|
|
}
|
|
// b.add("translate_wan_addrs", true)
|
|
b.addBlock("rpc", func() {
|
|
b.add("enable_streaming", true)
|
|
})
|
|
if node.HasPublicAddress() {
|
|
b.add("advertise_addr_wan", `{{ GetInterfaceIP "eth1" }}`) // note: can't use 'node.PublicAddress()' b/c we don't know that yet
|
|
}
|
|
|
|
// Exercise config entry bootstrap
|
|
// b.addBlock("config_entries", func() {
|
|
// b.addBlock("bootstrap", func() {
|
|
// b.add("kind", "service-defaults")
|
|
// b.add("name", "placeholder")
|
|
// b.add("protocol", "grpc")
|
|
// })
|
|
// b.addBlock("bootstrap", func() {
|
|
// b.add("kind", "service-intentions")
|
|
// b.add("name", "placeholder")
|
|
// b.addBlock("sources", func() {
|
|
// b.add("name", "placeholder-client")
|
|
// b.add("action", "allow")
|
|
// })
|
|
// })
|
|
// })
|
|
|
|
b.addBlock("connect", func() {
|
|
b.add("enabled", true)
|
|
})
|
|
|
|
// b.addBlock("autopilot", func() {
|
|
// b.add("upgrade_version_tag", "build")
|
|
// })
|
|
|
|
if node.AutopilotConfig != nil {
|
|
b.addBlock("autopilot", func() {
|
|
for k, v := range node.AutopilotConfig {
|
|
b.add(k, v)
|
|
}
|
|
})
|
|
}
|
|
|
|
if node.Meta != nil {
|
|
b.addBlock("node_meta", func() {
|
|
for k, v := range node.Meta {
|
|
b.add(k, v)
|
|
}
|
|
})
|
|
}
|
|
|
|
if cluster.Segments != nil {
|
|
b.format("segments = [")
|
|
for name, port := range cluster.Segments {
|
|
b.format("{")
|
|
b.add("name", name)
|
|
b.add("port", port)
|
|
b.format("},")
|
|
}
|
|
b.format("]")
|
|
}
|
|
} else {
|
|
if cluster.Enterprise && node.Images.GreaterThanVersion(topology.MinVersionAgentTokenPartition) {
|
|
b.add("partition", node.Partition)
|
|
}
|
|
}
|
|
|
|
return b.String()
|
|
}
|
|
|
|
type HCLBuilder struct {
|
|
parts []string
|
|
}
|
|
|
|
func (b *HCLBuilder) format(s string, a ...any) {
|
|
if len(a) == 0 {
|
|
b.parts = append(b.parts, s)
|
|
} else {
|
|
b.parts = append(b.parts, fmt.Sprintf(s, a...))
|
|
}
|
|
}
|
|
|
|
func (b *HCLBuilder) add(k string, v any) {
|
|
switch x := v.(type) {
|
|
case string:
|
|
if x != "" {
|
|
b.format("%s = %q", k, x)
|
|
}
|
|
case int:
|
|
b.format("%s = %d", k, x)
|
|
case bool:
|
|
b.format("%s = %v", k, x)
|
|
default:
|
|
panic(fmt.Sprintf("unexpected type %T", v))
|
|
}
|
|
}
|
|
|
|
func (b *HCLBuilder) addBlock(block string, fn func()) {
|
|
b.format(block + "{")
|
|
fn()
|
|
b.format("}")
|
|
}
|
|
|
|
func (b *HCLBuilder) addSlice(name string, vals []string) {
|
|
b.format(name + " = [")
|
|
for _, v := range vals {
|
|
b.format("%q,", v)
|
|
}
|
|
b.format("]")
|
|
}
|
|
|
|
func (b *HCLBuilder) String() string {
|
|
joined := strings.Join(b.parts, "\n")
|
|
// Ensure it looks tidy
|
|
return string(hclwrite.Format([]byte(joined)))
|
|
}
|