Step 2: extract the grpc address logic and a new type

The new grpcAddress function contains all of the logic to translate the
command line options into the values used in the template.

The new type has two advantages.

1. It introduces a logical grouping of values in the BootstrapTplArgs
   struct which is exceptionally large. This grouping makes the struct
   easier to understand because each set of nested values can be seen
   as a single entity.
2. It gives us a reasonable return value for this new function.
This commit is contained in:
Daniel Nephin 2020-04-07 16:33:22 -04:00
parent 5092aaf9b8
commit ae42dea2d5
3 changed files with 196 additions and 149 deletions

View File

@ -3,6 +3,8 @@ package envoy
// BootstrapTplArgs is the set of arguments that may be interpolated into the
// Envoy bootstrap template.
type BootstrapTplArgs struct {
GRPC
// ProxyCluster is the cluster name for the the Envoy `node` specification and
// is typically the same as the ProxyID.
ProxyCluster string
@ -12,26 +14,10 @@ type BootstrapTplArgs struct {
// the agent to deliver the correct configuration.
ProxyID string
// AgentAddress is the IP address of the local agent where the proxy instance
// is registered.
AgentAddress string
// AgentPort is the gRPC port exposed on the local agent.
AgentPort string
// AgentTLS is true if the local agent gRPC service should be accessed over
// TLS.
AgentTLS bool
// AgentCAPEM is the CA to use to verify the local agent gRPC service if
// TLS is enabled.
AgentCAPEM string
// AgentSocket is the path to a Unix Socket for communicating with the
// local agent's gRPC endpoint. Disabled if the empty (the default),
// but overrides AgentAddress and AgentPort if set.
AgentSocket string
// AdminAccessLogPath The path to write the access log for the
// administration server. If no access log is desired specify
// "/dev/null". By default it will use "/dev/null".
@ -102,6 +88,25 @@ type BootstrapTplArgs struct {
EnvoyVersion string
}
// GRPC settings used in the bootstrap template.
type GRPC struct {
// AgentAddress is the IP address of the local agent where the proxy instance
// is registered.
AgentAddress string
// AgentPort is the gRPC port exposed on the local agent.
AgentPort string
// AgentTLS is true if the local agent gRPC service should be accessed over
// TLS.
AgentTLS bool
// AgentSocket is the path to a Unix Socket for communicating with the
// local agent's gRPC endpoint. Disabled if the empty (the default),
// but overrides AgentAddress and AgentPort if set.
AgentSocket string
}
const bootstrapTemplate = `{
"admin": {
"access_log_path": "{{ .AdminAccessLogPath }}",

View File

@ -396,68 +396,9 @@ func (c *cmd) templateArgs() (*BootstrapTplArgs, error) {
return nil, err
}
// See if we need to lookup grpcAddr
if c.grpcAddr == "" {
port, err := c.lookupGRPCPort()
if err != nil {
c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
}
if port <= 0 {
// This is the dev mode default and recommended production setting if
// enabled.
port = 8502
c.UI.Info(fmt.Sprintf("Defaulting to grpc port = %d", port))
}
c.grpcAddr = fmt.Sprintf("localhost:%v", port)
}
// Decide on TLS if the scheme is provided and indicates it, if the HTTP env
// suggests TLS is supported explicitly (CONSUL_HTTP_SSL) or implicitly
// (CONSUL_HTTP_ADDR) is https://
useTLS := false
if strings.HasPrefix(strings.ToLower(c.grpcAddr), "https://") {
useTLS = true
} else if useSSLEnv := os.Getenv(api.HTTPSSLEnvName); useSSLEnv != "" {
if enabled, err := strconv.ParseBool(useSSLEnv); err == nil {
useTLS = enabled
}
} else if strings.HasPrefix(strings.ToLower(httpCfg.Address), "https://") {
useTLS = true
}
// We want to allow grpcAddr set as host:port with no scheme but if the host
// is an IP this will fail to parse as a URL with "parse 127.0.0.1:8500: first
// path segment in URL cannot contain colon". On the other hand we also
// support both http(s)://host:port and unix:///path/to/file.
var agentAddr, agentPort, agentSock string
if grpcAddr := strings.TrimPrefix(c.grpcAddr, "unix://"); grpcAddr != c.grpcAddr {
// Path to unix socket
agentSock = grpcAddr
} else {
// Parse as host:port with option http prefix
grpcAddr = strings.TrimPrefix(c.grpcAddr, "http://")
grpcAddr = strings.TrimPrefix(c.grpcAddr, "https://")
var err error
agentAddr, agentPort, err = net.SplitHostPort(grpcAddr)
if err != nil {
return nil, fmt.Errorf("Invalid Consul HTTP address: %s", err)
}
if agentAddr == "" {
agentAddr = "127.0.0.1"
}
// We use STATIC for agent which means we need to resolve DNS names like
// `localhost` ourselves. We could use STRICT_DNS or LOGICAL_DNS with envoy
// but Envoy resolves `localhost` differently to go on macOS at least which
// causes paper cuts like default dev agent (which binds specifically to
// 127.0.0.1) isn't reachable since Envoy resolves localhost to `[::]` and
// can't connect.
agentIP, err := net.ResolveIPAddr("ip", agentAddr)
if err != nil {
return nil, fmt.Errorf("Failed to resolve agent address: %s", err)
}
agentAddr = agentIP.String()
grpcAddr, err := c.grpcAddress(httpCfg)
if err != nil {
return nil, err
}
adminAddr, adminPort, err := net.SplitHostPort(c.adminBind)
@ -499,12 +440,9 @@ func (c *cmd) templateArgs() (*BootstrapTplArgs, error) {
}
return &BootstrapTplArgs{
GRPC: grpcAddr,
ProxyCluster: cluster,
ProxyID: c.proxyID,
AgentAddress: agentAddr,
AgentPort: agentPort,
AgentSocket: agentSock,
AgentTLS: useTLS,
AgentCAPEM: caPEM,
AdminAccessLogPath: adminAccessLogPath,
AdminBindAddress: adminBindIP.String(),
@ -549,6 +487,76 @@ func (c *cmd) generateConfig() ([]byte, error) {
return bsCfg.GenerateJSON(args)
}
// TODO: make method a function
func (c *cmd) grpcAddress(httpCfg *api.Config) (GRPC, error) {
g := GRPC{}
addr := c.grpcAddr
// See if we need to lookup grpcAddr
if addr == "" {
port, err := c.lookupGRPCPort()
if err != nil {
c.UI.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
}
if port <= 0 {
// This is the dev mode default and recommended production setting if
// enabled.
port = 8502
c.UI.Info(fmt.Sprintf("Defaulting to grpc port = %d", port))
}
addr = fmt.Sprintf("localhost:%v", port)
}
// Decide on TLS if the scheme is provided and indicates it, if the HTTP env
// suggests TLS is supported explicitly (CONSUL_HTTP_SSL) or implicitly
// (CONSUL_HTTP_ADDR) is https://
if strings.HasPrefix(strings.ToLower(addr), "https://") {
g.AgentTLS = true
} else if useSSLEnv := os.Getenv(api.HTTPSSLEnvName); useSSLEnv != "" {
if enabled, err := strconv.ParseBool(useSSLEnv); err == nil {
g.AgentTLS = enabled
}
} else if strings.HasPrefix(strings.ToLower(httpCfg.Address), "https://") {
g.AgentTLS = true
}
// We want to allow grpcAddr set as host:port with no scheme but if the host
// is an IP this will fail to parse as a URL with "parse 127.0.0.1:8500: first
// path segment in URL cannot contain colon". On the other hand we also
// support both http(s)://host:port and unix:///path/to/file.
if grpcAddr := strings.TrimPrefix(addr, "unix://"); grpcAddr != addr {
// Path to unix socket
g.AgentSocket = grpcAddr
} else {
// Parse as host:port with option http prefix
grpcAddr = strings.TrimPrefix(addr, "http://")
grpcAddr = strings.TrimPrefix(addr, "https://")
var err error
g.AgentAddress, g.AgentPort, err = net.SplitHostPort(grpcAddr)
if err != nil {
return g, fmt.Errorf("Invalid Consul HTTP address: %s", err)
}
// TODO: isn't this case impossible because we have already set a default value
if g.AgentAddress == "" {
g.AgentAddress = "127.0.0.1"
}
// We use STATIC for agent which means we need to resolve DNS names like
// `localhost` ourselves. We could use STRICT_DNS or LOGICAL_DNS with envoy
// but Envoy resolves `localhost` differently to go on macOS at least which
// causes paper cuts like default dev agent (which binds specifically to
// 127.0.0.1) isn't reachable since Envoy resolves localhost to `[::]` and
// can't connect.
agentIP, err := net.ResolveIPAddr("ip", g.AgentAddress)
if err != nil {
return g, fmt.Errorf("Failed to resolve agent address: %s", err)
}
g.AgentAddress = agentIP.String()
}
return g, nil
}
func (c *cmd) lookupGRPCPort() (int, error) {
self, err := c.client.Agent().Self()
if err != nil {

View File

@ -125,11 +125,13 @@ func TestGenerateConfig(t *testing.T) {
Name: "defaults",
Flags: []string{"-proxy-id", "test-proxy"},
WantArgs: BootstrapTplArgs{
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
@ -141,11 +143,13 @@ func TestGenerateConfig(t *testing.T) {
Flags: []string{"-proxy-id", "test-proxy",
"-token", "c9a52720-bf6c-4aa6-b8bc-66881a5ade95"},
WantArgs: BootstrapTplArgs{
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
@ -160,11 +164,13 @@ func TestGenerateConfig(t *testing.T) {
"CONSUL_HTTP_TOKEN=c9a52720-bf6c-4aa6-b8bc-66881a5ade95",
},
WantArgs: BootstrapTplArgs{
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
@ -181,11 +187,13 @@ func TestGenerateConfig(t *testing.T) {
"token.txt": "c9a52720-bf6c-4aa6-b8bc-66881a5ade95",
},
WantArgs: BootstrapTplArgs{
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
@ -203,11 +211,13 @@ func TestGenerateConfig(t *testing.T) {
"token.txt": "c9a52720-bf6c-4aa6-b8bc-66881a5ade95",
},
WantArgs: BootstrapTplArgs{
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502", // Note this is the gRPC port
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
@ -226,8 +236,10 @@ func TestGenerateConfig(t *testing.T) {
// Should resolve IP, note this might not resolve the same way
// everywhere which might make this test brittle but not sure what else
// to do.
AgentAddress: "127.0.0.1",
AgentPort: "9999",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "9999",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
@ -247,8 +259,10 @@ func TestGenerateConfig(t *testing.T) {
// Should resolve IP, note this might not resolve the same way
// everywhere which might make this test brittle but not sure what else
// to do.
AgentAddress: "127.0.0.1",
AgentPort: "9999",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "9999",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
@ -260,10 +274,12 @@ func TestGenerateConfig(t *testing.T) {
Flags: []string{"-proxy-id", "test-proxy",
"-grpc-addr", "unix:///var/run/consul.sock"},
WantArgs: BootstrapTplArgs{
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
AgentSocket: "/var/run/consul.sock",
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
GRPC: GRPC{
AgentSocket: "/var/run/consul.sock",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
@ -281,8 +297,10 @@ func TestGenerateConfig(t *testing.T) {
// Should resolve IP, note this might not resolve the same way
// everywhere which might make this test brittle but not sure what else
// to do.
AgentAddress: "127.0.0.1",
AgentPort: "9999",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "9999",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
@ -299,8 +317,10 @@ func TestGenerateConfig(t *testing.T) {
// Should resolve IP, note this might not resolve the same way
// everywhere which might make this test brittle but not sure what else
// to do.
AgentAddress: "127.0.0.1",
AgentPort: "8502",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/some/path/access.log",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
@ -317,8 +337,10 @@ func TestGenerateConfig(t *testing.T) {
// Should resolve IP, note this might not resolve the same way
// everywhere which might make this test brittle but not sure what else
// to do.
AgentAddress: "127.0.0.1",
AgentPort: "8502",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
},
WantErr: "Error loading CA File: open some/path: no such file or directory",
},
@ -333,9 +355,11 @@ func TestGenerateConfig(t *testing.T) {
// Should resolve IP, note this might not resolve the same way
// everywhere which might make this test brittle but not sure what else
// to do.
AgentAddress: "127.0.0.1",
AgentPort: "8502",
AgentTLS: true,
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
AgentTLS: true,
},
AgentCAPEM: `-----BEGIN CERTIFICATE-----\nMIIEtzCCA5+gAwIBAgIJAIewRMI8OnvTMA0GCSqGSIb3DQEBBQUAMIGYMQswCQYD\nVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xHDAa\nBgNVBAoTE0hhc2hpQ29ycCBUZXN0IENlcnQxDDAKBgNVBAsTA0RldjEWMBQGA1UE\nAxMNdGVzdC5pbnRlcm5hbDEgMB4GCSqGSIb3DQEJARYRdGVzdEBpbnRlcm5hbC5j\nb20wHhcNMTQwNDA3MTkwMTA4WhcNMjQwNDA0MTkwMTA4WjCBmDELMAkGA1UEBhMC\nVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRwwGgYDVQQK\nExNIYXNoaUNvcnAgVGVzdCBDZXJ0MQwwCgYDVQQLEwNEZXYxFjAUBgNVBAMTDXRl\nc3QuaW50ZXJuYWwxIDAeBgkqhkiG9w0BCQEWEXRlc3RAaW50ZXJuYWwuY29tMIIB\nIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxrs6JK4NpiOItxrpNR/1ppUU\nmH7p2BgLCBZ6eHdclle9J56i68adt8J85zaqphCfz6VDP58DsFx+N50PZyjQaDsU\nd0HejRqfHRMtg2O+UQkv4Z66+Vo+gc6uGuANi2xMtSYDVTAqqzF48OOPQDgYkzcG\nxcFZzTRFFZt2vPnyHj8cHcaFo/NMNVh7C3yTXevRGNm9u2mrbxCEeiHzFC2WUnvg\nU2jQuC7Fhnl33Zd3B6d3mQH6O23ncmwxTcPUJe6xZaIRrDuzwUcyhLj5Z3faag/f\npFIIcHSiHRfoqHLGsGg+3swId/zVJSSDHr7pJUu7Cre+vZa63FqDaooqvnisrQID\nAQABo4IBADCB/TAdBgNVHQ4EFgQUo/nrOfqvbee2VklVKIFlyQEbuJUwgc0GA1Ud\nIwSBxTCBwoAUo/nrOfqvbee2VklVKIFlyQEbuJWhgZ6kgZswgZgxCzAJBgNVBAYT\nAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEcMBoGA1UE\nChMTSGFzaGlDb3JwIFRlc3QgQ2VydDEMMAoGA1UECxMDRGV2MRYwFAYDVQQDEw10\nZXN0LmludGVybmFsMSAwHgYJKoZIhvcNAQkBFhF0ZXN0QGludGVybmFsLmNvbYIJ\nAIewRMI8OnvTMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADa9fV9h\ngjapBlkNmu64WX0Ufub5dsJrdHS8672P30S7ILB7Mk0W8sL65IezRsZnG898yHf9\n2uzmz5OvNTM9K380g7xFlyobSVq+6yqmmSAlA/ptAcIIZT727P5jig/DB7fzJM3g\njctDlEGOmEe50GQXc25VKpcpjAsNQi5ER5gowQ0v3IXNZs+yU+LvxLHc0rUJ/XSp\nlFCAMOqd5uRoMOejnT51G6krvLNzPaQ3N9jQfNVY4Q0zfs0M+6dRWvqfqB9Vyq8/\nPOLMld+HyAZEBk9zK3ZVIXx6XS4dkDnSNR91njLq7eouf6M7+7s/oMQZZRtAfQ6r\nwlW975rYa1ZqEdA=\n-----END CERTIFICATE-----\n`,
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
@ -369,11 +393,13 @@ func TestGenerateConfig(t *testing.T) {
}`,
},
WantArgs: BootstrapTplArgs{
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
AgentAddress: "127.0.0.1",
AgentPort: "8502",
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
@ -401,11 +427,13 @@ func TestGenerateConfig(t *testing.T) {
}`,
},
WantArgs: BootstrapTplArgs{
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
AgentAddress: "127.0.0.1",
AgentPort: "8502",
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
@ -438,11 +466,13 @@ func TestGenerateConfig(t *testing.T) {
} , { "name": "fake_sink_2" }`,
},
WantArgs: BootstrapTplArgs{
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
AgentAddress: "127.0.0.1",
AgentPort: "8502",
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
@ -462,11 +492,13 @@ func TestGenerateConfig(t *testing.T) {
}`,
},
WantArgs: BootstrapTplArgs{
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
AgentAddress: "127.0.0.1",
AgentPort: "8502",
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",
@ -516,11 +548,13 @@ func TestGenerateConfig(t *testing.T) {
}`,
},
WantArgs: BootstrapTplArgs{
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
AgentAddress: "127.0.0.1",
AgentPort: "8502",
EnvoyVersion: defaultEnvoyVersion,
ProxyCluster: "test-proxy",
ProxyID: "test-proxy",
GRPC: GRPC{
AgentAddress: "127.0.0.1",
AgentPort: "8502",
},
AdminAccessLogPath: "/dev/null",
AdminBindAddress: "127.0.0.1",
AdminBindPort: "19000",