2019-04-29 16:27:57 +00:00
|
|
|
package xds
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
2020-04-27 18:53:21 +00:00
|
|
|
"time"
|
2019-04-29 16:27:57 +00:00
|
|
|
|
2020-04-27 18:53:21 +00:00
|
|
|
envoycluster "github.com/envoyproxy/go-control-plane/envoy/api/v2/cluster"
|
2020-06-23 20:19:56 +00:00
|
|
|
"github.com/golang/protobuf/ptypes"
|
|
|
|
"github.com/golang/protobuf/ptypes/wrappers"
|
2019-06-18 00:52:01 +00:00
|
|
|
"github.com/hashicorp/consul/agent/structs"
|
2020-05-27 18:42:01 +00:00
|
|
|
"github.com/hashicorp/consul/lib/decode"
|
2019-04-29 16:27:57 +00:00
|
|
|
"github.com/mitchellh/mapstructure"
|
|
|
|
)
|
|
|
|
|
|
|
|
// ProxyConfig describes the keys we understand from Connect.Proxy.Config. Note
|
|
|
|
// that this only includes config keys that affects runtime config delivered by
|
|
|
|
// xDS. For Envoy config keys that affect bootstrap generation see
|
|
|
|
// command/connect/envoy/bootstrap_config.go.
|
|
|
|
type ProxyConfig struct {
|
|
|
|
// PublicListenerJSON is a complete override ("escape hatch") for the
|
|
|
|
// upstream's public listener. The Connect server TLS certificate and
|
|
|
|
// validation context will be injected overriding any TLS settings present. An
|
|
|
|
// AuthZ filter will also be prepended to each filterChain provided to enforce
|
|
|
|
// Connect's access control.
|
2019-08-21 20:10:12 +00:00
|
|
|
//
|
|
|
|
// Note: This escape hatch is compatible with the discovery chain.
|
2019-04-29 16:27:57 +00:00
|
|
|
PublicListenerJSON string `mapstructure:"envoy_public_listener_json"`
|
|
|
|
|
|
|
|
// LocalClusterJSON is a complete override ("escape hatch") for the
|
|
|
|
// local application cluster.
|
2019-08-21 20:10:12 +00:00
|
|
|
//
|
|
|
|
// Note: This escape hatch is compatible with the discovery chain.
|
2019-04-29 16:27:57 +00:00
|
|
|
LocalClusterJSON string `mapstructure:"envoy_local_cluster_json"`
|
|
|
|
|
|
|
|
// LocalConnectTimeoutMs is the number of milliseconds to timeout making a new
|
|
|
|
// connection to the local app instance. Defaults to 5000 (5 seconds) if not
|
|
|
|
// set.
|
|
|
|
LocalConnectTimeoutMs int `mapstructure:"local_connect_timeout_ms"`
|
|
|
|
|
|
|
|
// Protocol describes the service's protocol. Valid values are "tcp",
|
2019-07-05 15:06:47 +00:00
|
|
|
// "http" and "grpc". Anything else is treated as tcp. This enables
|
|
|
|
// protocol aware features like per-request metrics and connection
|
|
|
|
// pooling, tracing, routing etc.
|
2019-04-29 16:27:57 +00:00
|
|
|
Protocol string `mapstructure:"protocol"`
|
2019-07-05 15:06:47 +00:00
|
|
|
|
|
|
|
// BindAddress overrides the address the proxy's listener binds to. This
|
|
|
|
// enables proxies in network namespaces to bind to a different address
|
|
|
|
// than the host address.
|
|
|
|
BindAddress string `mapstructure:"bind_address"`
|
|
|
|
|
|
|
|
// BindPort overrides the port the proxy's listener binds to. This
|
|
|
|
// enable proxies in network namespaces to bind to a different port
|
|
|
|
// than the host port being advertised.
|
|
|
|
BindPort int `mapstructure:"bind_port"`
|
2019-04-29 16:27:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ParseProxyConfig returns the ProxyConfig parsed from the an opaque map. If an
|
|
|
|
// error occurs during parsing it is returned along with the default config this
|
|
|
|
// allows caller to choose whether and how to report the error.
|
|
|
|
func ParseProxyConfig(m map[string]interface{}) (ProxyConfig, error) {
|
|
|
|
var cfg ProxyConfig
|
2020-05-27 17:02:22 +00:00
|
|
|
decodeConf := &mapstructure.DecoderConfig{
|
|
|
|
DecodeHook: mapstructure.ComposeDecodeHookFunc(
|
|
|
|
decode.HookWeakDecodeFromSlice,
|
|
|
|
decode.HookTranslateKeys,
|
|
|
|
),
|
|
|
|
Result: &cfg,
|
|
|
|
WeaklyTypedInput: true,
|
|
|
|
}
|
|
|
|
decoder, err := mapstructure.NewDecoder(decodeConf)
|
|
|
|
if err != nil {
|
|
|
|
return cfg, err
|
|
|
|
}
|
|
|
|
if err := decoder.Decode(m); err != nil {
|
|
|
|
return cfg, err
|
|
|
|
}
|
|
|
|
|
2019-04-29 16:27:57 +00:00
|
|
|
// Set defaults (even if error is returned)
|
|
|
|
if cfg.Protocol == "" {
|
|
|
|
cfg.Protocol = "tcp"
|
|
|
|
} else {
|
|
|
|
cfg.Protocol = strings.ToLower(cfg.Protocol)
|
|
|
|
}
|
|
|
|
if cfg.LocalConnectTimeoutMs < 1 {
|
|
|
|
cfg.LocalConnectTimeoutMs = 5000
|
|
|
|
}
|
|
|
|
return cfg, err
|
|
|
|
}
|
|
|
|
|
2020-03-26 16:20:56 +00:00
|
|
|
type GatewayConfig struct {
|
2019-06-18 00:52:01 +00:00
|
|
|
// BindTaggedAddresses when set will cause all of the services tagged
|
|
|
|
// addresses to have listeners bound to them in addition to the main service
|
|
|
|
// address listener. This is only suitable when the tagged addresses are IP
|
|
|
|
// addresses of network interfaces Envoy can see. i.e. When using DNS names
|
|
|
|
// for those addresses or where an external entity maps that IP to the Envoy
|
|
|
|
// (like AWS EC2 mapping a public IP to the private interface) then this
|
|
|
|
// cannot be used. See the BindAddresses config instead
|
2020-05-27 18:28:28 +00:00
|
|
|
BindTaggedAddresses bool `mapstructure:"envoy_gateway_bind_tagged_addresses" alias:"envoy_mesh_gateway_bind_tagged_addresses"`
|
2019-06-18 00:52:01 +00:00
|
|
|
|
|
|
|
// BindAddresses additional bind addresses to configure listeners for
|
2020-05-27 18:28:28 +00:00
|
|
|
BindAddresses map[string]structs.ServiceAddress `mapstructure:"envoy_gateway_bind_addresses" alias:"envoy_mesh_gateway_bind_addresses"`
|
2019-06-18 00:52:01 +00:00
|
|
|
|
|
|
|
// NoDefaultBind indicates that we should not bind to the default address of the
|
|
|
|
// gateway service
|
2020-05-27 18:28:28 +00:00
|
|
|
NoDefaultBind bool `mapstructure:"envoy_gateway_no_default_bind" alias:"envoy_mesh_gateway_no_default_bind"`
|
2019-06-18 00:52:01 +00:00
|
|
|
|
2020-06-03 21:28:45 +00:00
|
|
|
// DNSDiscoveryType indicates the DNS service discovery type.
|
|
|
|
// See: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/service_discovery#arch-overview-service-discovery-types
|
|
|
|
DNSDiscoveryType string `mapstructure:"envoy_dns_discovery_type"`
|
|
|
|
|
2019-06-18 00:52:01 +00:00
|
|
|
// ConnectTimeoutMs is the number of milliseconds to timeout making a new
|
|
|
|
// connection to this upstream. Defaults to 5000 (5 seconds) if not set.
|
|
|
|
ConnectTimeoutMs int `mapstructure:"connect_timeout_ms"`
|
|
|
|
}
|
|
|
|
|
2020-03-26 16:20:56 +00:00
|
|
|
// ParseGatewayConfig returns the GatewayConfig parsed from an opaque map. If an
|
2019-06-18 00:52:01 +00:00
|
|
|
// error occurs during parsing, it is returned along with the default config. This
|
|
|
|
// allows the caller to choose whether and how to report the error
|
2020-03-26 16:20:56 +00:00
|
|
|
func ParseGatewayConfig(m map[string]interface{}) (GatewayConfig, error) {
|
|
|
|
var cfg GatewayConfig
|
2020-05-27 18:42:01 +00:00
|
|
|
d, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
2020-05-27 17:02:22 +00:00
|
|
|
DecodeHook: mapstructure.ComposeDecodeHookFunc(
|
|
|
|
decode.HookWeakDecodeFromSlice,
|
|
|
|
decode.HookTranslateKeys,
|
|
|
|
),
|
2020-05-27 18:42:01 +00:00
|
|
|
Result: &cfg,
|
|
|
|
WeaklyTypedInput: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return cfg, err
|
|
|
|
}
|
|
|
|
if err := d.Decode(m); err != nil {
|
|
|
|
return cfg, err
|
|
|
|
}
|
2019-06-18 00:52:01 +00:00
|
|
|
|
|
|
|
if cfg.ConnectTimeoutMs < 1 {
|
|
|
|
cfg.ConnectTimeoutMs = 5000
|
|
|
|
}
|
2020-06-03 21:28:45 +00:00
|
|
|
|
|
|
|
cfg.DNSDiscoveryType = strings.ToLower(cfg.DNSDiscoveryType)
|
|
|
|
|
2019-06-18 00:52:01 +00:00
|
|
|
return cfg, err
|
|
|
|
}
|
|
|
|
|
2019-12-03 20:13:33 +00:00
|
|
|
// UpstreamLimits describes the limits that are associated with a specific
|
|
|
|
// upstream of a service instance.
|
|
|
|
type UpstreamLimits struct {
|
|
|
|
// MaxConnections is the maximum number of connections the local proxy can
|
|
|
|
// make to the upstream service.
|
|
|
|
MaxConnections *int `mapstructure:"max_connections"`
|
|
|
|
|
|
|
|
// MaxPendingRequests is the maximum number of requests that will be queued
|
|
|
|
// waiting for an available connection. This is mostly applicable to HTTP/1.1
|
|
|
|
// clusters since all HTTP/2 requests are streamed over a single
|
|
|
|
// connection.
|
|
|
|
MaxPendingRequests *int `mapstructure:"max_pending_requests"`
|
|
|
|
|
|
|
|
// MaxConcurrentRequests is the maximum number of in-flight requests that will be allowed
|
|
|
|
// to the upstream cluster at a point in time. This is mostly applicable to HTTP/2
|
|
|
|
// clusters since all HTTP/1.1 requests are limited by MaxConnections.
|
|
|
|
MaxConcurrentRequests *int `mapstructure:"max_concurrent_requests"`
|
|
|
|
}
|
|
|
|
|
2019-04-29 16:27:57 +00:00
|
|
|
// UpstreamConfig describes the keys we understand from
|
|
|
|
// Connect.Proxy.Upstream[*].Config.
|
|
|
|
type UpstreamConfig struct {
|
|
|
|
// ListenerJSON is a complete override ("escape hatch") for the upstream's
|
|
|
|
// listener.
|
2019-08-21 20:10:12 +00:00
|
|
|
//
|
|
|
|
// Note: This escape hatch is NOT compatible with the discovery chain and
|
|
|
|
// will be ignored if a discovery chain is active.
|
2019-04-29 16:27:57 +00:00
|
|
|
ListenerJSON string `mapstructure:"envoy_listener_json"`
|
|
|
|
|
|
|
|
// ClusterJSON is a complete override ("escape hatch") for the upstream's
|
|
|
|
// cluster. The Connect client TLS certificate and context will be injected
|
|
|
|
// overriding any TLS settings present.
|
2019-08-21 20:10:12 +00:00
|
|
|
//
|
|
|
|
// Note: This escape hatch is NOT compatible with the discovery chain and
|
|
|
|
// will be ignored if a discovery chain is active.
|
2019-04-29 16:27:57 +00:00
|
|
|
ClusterJSON string `mapstructure:"envoy_cluster_json"`
|
|
|
|
|
|
|
|
// Protocol describes the upstream's service protocol. Valid values are "tcp",
|
|
|
|
// "http" and "grpc". Anything else is treated as tcp. The enables protocol
|
|
|
|
// aware features like per-request metrics and connection pooling, tracing,
|
|
|
|
// routing etc.
|
|
|
|
Protocol string `mapstructure:"protocol"`
|
|
|
|
|
|
|
|
// ConnectTimeoutMs is the number of milliseconds to timeout making a new
|
|
|
|
// connection to this upstream. Defaults to 5000 (5 seconds) if not set.
|
|
|
|
ConnectTimeoutMs int `mapstructure:"connect_timeout_ms"`
|
2019-12-03 20:13:33 +00:00
|
|
|
|
|
|
|
// Limits are the set of limits that are applied to the proxy for a specific upstream of a
|
|
|
|
// service instance.
|
|
|
|
Limits UpstreamLimits `mapstructure:"limits"`
|
2020-04-27 18:53:21 +00:00
|
|
|
|
|
|
|
// PassiveHealthCheck configuration
|
|
|
|
PassiveHealthCheck PassiveHealthCheck `mapstructure:"passive_health_check"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type PassiveHealthCheck struct {
|
|
|
|
// Interval between health check analysis sweeps. Each sweep may remove
|
|
|
|
// hosts or return hosts to the pool.
|
|
|
|
Interval time.Duration
|
|
|
|
// MaxFailures is the count of consecutive failures that results in a host
|
|
|
|
// being removed from the pool.
|
|
|
|
MaxFailures uint32 `mapstructure:"max_failures"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return an envoy.OutlierDetection populated by the values from this struct.
|
|
|
|
// If all values are zero a default empty OutlierDetection will be returned to
|
|
|
|
// enable outlier detection with default values.
|
|
|
|
func (p PassiveHealthCheck) AsOutlierDetection() *envoycluster.OutlierDetection {
|
|
|
|
od := &envoycluster.OutlierDetection{}
|
|
|
|
if p.Interval != 0 {
|
2020-06-23 20:19:56 +00:00
|
|
|
od.Interval = ptypes.DurationProto(p.Interval)
|
2020-04-27 18:53:21 +00:00
|
|
|
}
|
|
|
|
if p.MaxFailures != 0 {
|
2020-06-23 20:19:56 +00:00
|
|
|
od.Consecutive_5Xx = &wrappers.UInt32Value{Value: p.MaxFailures}
|
2020-04-27 18:53:21 +00:00
|
|
|
}
|
|
|
|
return od
|
2019-04-29 16:27:57 +00:00
|
|
|
}
|
|
|
|
|
2019-07-08 11:48:48 +00:00
|
|
|
func ParseUpstreamConfigNoDefaults(m map[string]interface{}) (UpstreamConfig, error) {
|
|
|
|
var cfg UpstreamConfig
|
2020-05-05 20:17:24 +00:00
|
|
|
config := &mapstructure.DecoderConfig{
|
2020-05-27 17:02:22 +00:00
|
|
|
DecodeHook: mapstructure.ComposeDecodeHookFunc(
|
|
|
|
decode.HookWeakDecodeFromSlice,
|
|
|
|
decode.HookTranslateKeys,
|
|
|
|
mapstructure.StringToTimeDurationHookFunc(),
|
|
|
|
),
|
2020-05-05 20:17:24 +00:00
|
|
|
Result: &cfg,
|
|
|
|
WeaklyTypedInput: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
decoder, err := mapstructure.NewDecoder(config)
|
|
|
|
if err != nil {
|
|
|
|
return cfg, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = decoder.Decode(m)
|
2019-07-08 11:48:48 +00:00
|
|
|
return cfg, err
|
|
|
|
}
|
|
|
|
|
2019-09-26 02:55:52 +00:00
|
|
|
// ParseUpstreamConfig returns the UpstreamConfig parsed from an opaque map.
|
2019-04-29 16:27:57 +00:00
|
|
|
// If an error occurs during parsing it is returned along with the default
|
|
|
|
// config this allows caller to choose whether and how to report the error.
|
|
|
|
func ParseUpstreamConfig(m map[string]interface{}) (UpstreamConfig, error) {
|
2019-07-08 11:48:48 +00:00
|
|
|
cfg, err := ParseUpstreamConfigNoDefaults(m)
|
2019-04-29 16:27:57 +00:00
|
|
|
// Set defaults (even if error is returned)
|
|
|
|
if cfg.Protocol == "" {
|
|
|
|
cfg.Protocol = "tcp"
|
|
|
|
} else {
|
|
|
|
cfg.Protocol = strings.ToLower(cfg.Protocol)
|
|
|
|
}
|
|
|
|
if cfg.ConnectTimeoutMs < 1 {
|
|
|
|
cfg.ConnectTimeoutMs = 5000
|
|
|
|
}
|
|
|
|
return cfg, err
|
|
|
|
}
|