Restructure structs and other PR comments

pull/8585/head
freddygv 2020-09-02 09:10:50 -06:00
parent 0236e169bb
commit 63f79e5f9b
43 changed files with 1228 additions and 2284 deletions

View File

@ -745,9 +745,11 @@ func (c *compiler) getSplitterNode(sid structs.ServiceID) (*structs.DiscoveryGra
// with distinct hash-based load balancer configs specified in their service resolvers.
// We cannot apply multiple hash policies to a splitter node's route action.
// Therefore, we attach the first hash-based load balancer config we encounter.
if !hasLB && node.LoadBalancer.IsHashBased() {
splitNode.LoadBalancer = node.LoadBalancer
hasLB = true
if !hasLB {
if lb := node.LoadBalancer; lb != nil && lb.EnvoyLBConfig != nil && lb.EnvoyLBConfig.IsHashBased() {
splitNode.LoadBalancer = node.LoadBalancer
hasLB = true
}
}
}

View File

@ -1761,14 +1761,16 @@ func testcase_AllBellsAndWhistles() compileTestCase {
"prod": {Filter: "ServiceMeta.env == prod"},
"qa": {Filter: "ServiceMeta.env == qa"},
},
LoadBalancer: structs.LoadBalancer{
Policy: "ring_hash",
RingHashConfig: structs.RingHashConfig{
MaximumRingSize: 100,
},
HashPolicies: []structs.HashPolicy{
{
SourceAddress: true,
LoadBalancer: &structs.LoadBalancer{
EnvoyLBConfig: &structs.EnvoyLBConfig{
Policy: "ring_hash",
RingHashConfig: &structs.RingHashConfig{
MaximumRingSize: 100,
},
HashPolicies: []structs.HashPolicy{
{
SourceIP: true,
},
},
},
},
@ -1833,14 +1835,16 @@ func testcase_AllBellsAndWhistles() compileTestCase {
NextNode: "resolver:v3.main.default.dc1",
},
},
LoadBalancer: structs.LoadBalancer{
Policy: "ring_hash",
RingHashConfig: structs.RingHashConfig{
MaximumRingSize: 100,
},
HashPolicies: []structs.HashPolicy{
{
SourceAddress: true,
LoadBalancer: &structs.LoadBalancer{
EnvoyLBConfig: &structs.EnvoyLBConfig{
Policy: "ring_hash",
RingHashConfig: &structs.RingHashConfig{
MaximumRingSize: 100,
},
HashPolicies: []structs.HashPolicy{
{
SourceIP: true,
},
},
},
},
@ -1852,14 +1856,16 @@ func testcase_AllBellsAndWhistles() compileTestCase {
ConnectTimeout: 5 * time.Second,
Target: "prod.redirected.default.dc1",
},
LoadBalancer: structs.LoadBalancer{
Policy: "ring_hash",
RingHashConfig: structs.RingHashConfig{
MaximumRingSize: 100,
},
HashPolicies: []structs.HashPolicy{
{
SourceAddress: true,
LoadBalancer: &structs.LoadBalancer{
EnvoyLBConfig: &structs.EnvoyLBConfig{
Policy: "ring_hash",
RingHashConfig: &structs.RingHashConfig{
MaximumRingSize: 100,
},
HashPolicies: []structs.HashPolicy{
{
SourceIP: true,
},
},
},
},
@ -2275,24 +2281,28 @@ func testcase_LBConfig() compileTestCase {
&structs.ServiceResolverConfigEntry{
Kind: "service-resolver",
Name: "foo",
LoadBalancer: structs.LoadBalancer{
Policy: "least_request",
LeastRequestConfig: structs.LeastRequestConfig{
ChoiceCount: 3,
LoadBalancer: &structs.LoadBalancer{
EnvoyLBConfig: &structs.EnvoyLBConfig{
Policy: "least_request",
LeastRequestConfig: &structs.LeastRequestConfig{
ChoiceCount: 3,
},
},
},
},
&structs.ServiceResolverConfigEntry{
Kind: "service-resolver",
Name: "bar",
LoadBalancer: structs.LoadBalancer{
Policy: "ring_hash",
RingHashConfig: structs.RingHashConfig{
MaximumRingSize: 101,
},
HashPolicies: []structs.HashPolicy{
{
SourceAddress: true,
LoadBalancer: &structs.LoadBalancer{
EnvoyLBConfig: &structs.EnvoyLBConfig{
Policy: "ring_hash",
RingHashConfig: &structs.RingHashConfig{
MaximumRingSize: 101,
},
HashPolicies: []structs.HashPolicy{
{
SourceIP: true,
},
},
},
},
@ -2300,13 +2310,19 @@ func testcase_LBConfig() compileTestCase {
&structs.ServiceResolverConfigEntry{
Kind: "service-resolver",
Name: "baz",
LoadBalancer: structs.LoadBalancer{
Policy: "maglev",
HashPolicies: []structs.HashPolicy{
{
Field: "cookie",
FieldMatchValue: "chocolate-chip",
Terminal: true,
LoadBalancer: &structs.LoadBalancer{
EnvoyLBConfig: &structs.EnvoyLBConfig{
Policy: "maglev",
HashPolicies: []structs.HashPolicy{
{
Field: "cookie",
FieldValue: "chocolate-chip",
CookieConfig: &structs.CookieConfig{
TTL: 2 * time.Minute,
Path: "/bowl",
},
Terminal: true,
},
},
},
},
@ -2336,14 +2352,16 @@ func testcase_LBConfig() compileTestCase {
},
// The LB config from bar is attached because splitters only care about hash-based policies,
// and it's the config from bar not baz because we pick the first one we encounter in the Splits.
LoadBalancer: structs.LoadBalancer{
Policy: "ring_hash",
RingHashConfig: structs.RingHashConfig{
MaximumRingSize: 101,
},
HashPolicies: []structs.HashPolicy{
{
SourceAddress: true,
LoadBalancer: &structs.LoadBalancer{
EnvoyLBConfig: &structs.EnvoyLBConfig{
Policy: "ring_hash",
RingHashConfig: &structs.RingHashConfig{
MaximumRingSize: 101,
},
HashPolicies: []structs.HashPolicy{
{
SourceIP: true,
},
},
},
},
@ -2357,10 +2375,12 @@ func testcase_LBConfig() compileTestCase {
ConnectTimeout: 5 * time.Second,
Target: "foo.default.dc1",
},
LoadBalancer: structs.LoadBalancer{
Policy: "least_request",
LeastRequestConfig: structs.LeastRequestConfig{
ChoiceCount: 3,
LoadBalancer: &structs.LoadBalancer{
EnvoyLBConfig: &structs.EnvoyLBConfig{
Policy: "least_request",
LeastRequestConfig: &structs.LeastRequestConfig{
ChoiceCount: 3,
},
},
},
},
@ -2372,14 +2392,16 @@ func testcase_LBConfig() compileTestCase {
ConnectTimeout: 5 * time.Second,
Target: "bar.default.dc1",
},
LoadBalancer: structs.LoadBalancer{
Policy: "ring_hash",
RingHashConfig: structs.RingHashConfig{
MaximumRingSize: 101,
},
HashPolicies: []structs.HashPolicy{
{
SourceAddress: true,
LoadBalancer: &structs.LoadBalancer{
EnvoyLBConfig: &structs.EnvoyLBConfig{
Policy: "ring_hash",
RingHashConfig: &structs.RingHashConfig{
MaximumRingSize: 101,
},
HashPolicies: []structs.HashPolicy{
{
SourceIP: true,
},
},
},
},
@ -2392,13 +2414,19 @@ func testcase_LBConfig() compileTestCase {
ConnectTimeout: 5 * time.Second,
Target: "baz.default.dc1",
},
LoadBalancer: structs.LoadBalancer{
Policy: "maglev",
HashPolicies: []structs.HashPolicy{
{
Field: "cookie",
FieldMatchValue: "chocolate-chip",
Terminal: true,
LoadBalancer: &structs.LoadBalancer{
EnvoyLBConfig: &structs.EnvoyLBConfig{
Policy: "maglev",
HashPolicies: []structs.HashPolicy{
{
Field: "cookie",
FieldValue: "chocolate-chip",
CookieConfig: &structs.CookieConfig{
TTL: 2 * time.Minute,
Path: "/bowl",
},
Terminal: true,
},
},
},
},

View File

@ -1274,25 +1274,27 @@ func setupTestVariationConfigEntriesAndSnapshot(
&structs.ServiceResolverConfigEntry{
Kind: structs.ServiceResolver,
Name: "db",
LoadBalancer: structs.LoadBalancer{
Policy: "ring_hash",
RingHashConfig: structs.RingHashConfig{
MinimumRingSize: 20,
MaximumRingSize: 30,
},
HashPolicies: []structs.HashPolicy{
{
Field: "cookie",
FieldMatchValue: "chocolate-chip",
Terminal: true,
LoadBalancer: &structs.LoadBalancer{
EnvoyLBConfig: &structs.EnvoyLBConfig{
Policy: "ring_hash",
RingHashConfig: &structs.RingHashConfig{
MinimumRingSize: 20,
MaximumRingSize: 30,
},
{
Field: "header",
FieldMatchValue: "x-user-id",
},
{
SourceAddress: true,
Terminal: true,
HashPolicies: []structs.HashPolicy{
{
Field: "cookie",
FieldValue: "chocolate-chip",
Terminal: true,
},
{
Field: "header",
FieldValue: "x-user-id",
},
{
SourceIP: true,
Terminal: true,
},
},
},
},

View File

@ -3,6 +3,7 @@ package structs
import (
"encoding/json"
"fmt"
"github.com/golang/protobuf/ptypes"
"math"
"regexp"
"sort"
@ -10,12 +11,29 @@ import (
"strings"
"time"
envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
envoyroute "github.com/envoyproxy/go-control-plane/envoy/api/v2/route"
"github.com/golang/protobuf/ptypes/wrappers"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/cache"
"github.com/hashicorp/consul/lib"
"github.com/mitchellh/hashstructure"
)
const (
// Names of Envoy's LB policies
LBPolicyMaglev = "maglev"
LBPolicyRingHash = "ring_hash"
LBPolicyRandom = "random"
LBPolicyLeastRequest = "least_request"
LBPolicyRoundRobin = "round_robin"
// Names of Envoy's LB policies
HashPolicyCookie = "cookie"
HashPolicyHeader = "header"
HashPolicyQueryParam = "query_parameter"
)
// ServiceRouterConfigEntry defines L7 (e.g. http) routing rules for a named
// service exposed in Connect.
//
@ -641,7 +659,7 @@ type ServiceResolverConfigEntry struct {
// LoadBalancer determines the load balancing policy and configuration for services
// issuing requests to this upstream service.
LoadBalancer LoadBalancer `json:",omitempty" alias:"load_balancer"`
LoadBalancer *LoadBalancer `json:",omitempty" alias:"load_balancer"`
EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
RaftIndex
@ -811,53 +829,60 @@ func (e *ServiceResolverConfigEntry) Validate() error {
return fmt.Errorf("Bad ConnectTimeout '%s', must be >= 0", e.ConnectTimeout)
}
validPolicies := map[string]bool{
"": true,
"random": true,
"round_robin": true,
"least_request": true,
"ring_hash": true,
"maglev": true,
}
if ok := validPolicies[e.LoadBalancer.Policy]; !ok {
return fmt.Errorf("Bad LoadBalancer policy: %q is not supported", e.LoadBalancer.Policy)
}
if e.LoadBalancer != nil && e.LoadBalancer.EnvoyLBConfig != nil {
ec := e.LoadBalancer.EnvoyLBConfig
if e.LoadBalancer.Policy != "ring_hash" && e.LoadBalancer.RingHashConfig != (RingHashConfig{}) {
return fmt.Errorf("Bad LoadBalancer configuration. "+
"RingHashConfig specified for incompatible load balancing policy %q", e.LoadBalancer.Policy)
}
if e.LoadBalancer.Policy != "least_request" && e.LoadBalancer.LeastRequestConfig != (LeastRequestConfig{}) {
return fmt.Errorf("Bad LoadBalancer configuration. "+
"LeastRequestConfig specified for incompatible load balancing policy %q", e.LoadBalancer.Policy)
}
if !e.LoadBalancer.IsHashBased() && len(e.LoadBalancer.HashPolicies) > 0 {
return fmt.Errorf("Bad LoadBalancer configuration: "+
"HashPolicies specified for non-hash-based Policy: %q", e.LoadBalancer.Policy)
}
validPolicies := map[string]bool{
"": true,
LBPolicyRandom: true,
LBPolicyRoundRobin: true,
LBPolicyLeastRequest: true,
LBPolicyRingHash: true,
LBPolicyMaglev: true,
}
if ok := validPolicies[ec.Policy]; !ok {
return fmt.Errorf("Bad LoadBalancer policy: %q is not supported", ec.Policy)
}
validFields := map[string]bool{
"header": true,
"cookie": true,
"query_parameter": true,
}
for i, hp := range e.LoadBalancer.HashPolicies {
if ok := validFields[hp.Field]; hp.Field != "" && !ok {
return fmt.Errorf("Bad LoadBalancer HashPolicy[%d]: %q is not a supported field", i, hp.Field)
if ec.Policy != LBPolicyRingHash && ec.RingHashConfig != nil {
return fmt.Errorf("Bad LoadBalancer configuration. "+
"RingHashConfig specified for incompatible load balancing policy %q", ec.Policy)
}
if hp.SourceAddress && hp.Field != "" {
return fmt.Errorf("Bad LoadBalancer HashPolicy[%d]: "+
"A single hash policy cannot hash both a source address and a %q", i, hp.Field)
if ec.Policy != LBPolicyLeastRequest && ec.LeastRequestConfig != nil {
return fmt.Errorf("Bad LoadBalancer configuration. "+
"LeastRequestConfig specified for incompatible load balancing policy %q", ec.Policy)
}
if hp.SourceAddress && hp.FieldMatchValue != "" {
return fmt.Errorf("Bad LoadBalancer HashPolicy[%d]: "+
"A FieldMatchValue cannot be specified when hashing SourceAddress", i)
if !ec.IsHashBased() && len(ec.HashPolicies) > 0 {
return fmt.Errorf("Bad LoadBalancer configuration: "+
"HashPolicies specified for non-hash-based Policy: %q", ec.Policy)
}
if hp.Field != "" && hp.FieldMatchValue == "" {
return fmt.Errorf("Bad LoadBalancer HashPolicy[%d]: Field %q was specified without a FieldMatchValue", i, hp.Field)
validFields := map[string]bool{
HashPolicyHeader: true,
HashPolicyCookie: true,
HashPolicyQueryParam: true,
}
if hp.FieldMatchValue != "" && hp.Field == "" {
return fmt.Errorf("Bad LoadBalancer HashPolicy[%d]: FieldMatchValue requires a Field to apply to", i)
for i, hp := range ec.HashPolicies {
if ok := validFields[hp.Field]; hp.Field != "" && !ok {
return fmt.Errorf("Bad LoadBalancer HashPolicy[%d]: %q is not a supported field", i, hp.Field)
}
if hp.SourceIP && hp.Field != "" {
return fmt.Errorf("Bad LoadBalancer HashPolicy[%d]: "+
"A single hash policy cannot hash both a source address and a %q", i, hp.Field)
}
if hp.SourceIP && hp.FieldValue != "" {
return fmt.Errorf("Bad LoadBalancer HashPolicy[%d]: "+
"A FieldValue cannot be specified when hashing SourceIP", i)
}
if hp.Field != "" && hp.FieldValue == "" {
return fmt.Errorf("Bad LoadBalancer HashPolicy[%d]: Field %q was specified without a FieldValue", i, hp.Field)
}
if hp.FieldValue != "" && hp.Field == "" {
return fmt.Errorf("Bad LoadBalancer HashPolicy[%d]: FieldValue requires a Field to apply to", i)
}
if hp.CookieConfig != nil && hp.Field != HashPolicyCookie {
return fmt.Errorf("Bad LoadBalancer HashPolicy[%d]: cookie_config provided for %q", i, hp.Field)
}
}
}
@ -1000,14 +1025,22 @@ type ServiceResolverFailover struct {
// LoadBalancer determines the load balancing policy and configuration for services
// issuing requests to this upstream service.
type LoadBalancer struct {
// EnvoyLBConfig contains Envoy-specific load balancing configuration for this upstream
EnvoyLBConfig *EnvoyLBConfig `json:",omitempty" alias:"envoy_lb_config"`
// OpaqueConfig contains load balancing configuration opaque to Consul for 3rd party proxies
OpaqueConfig string `json:",omitempty" alias:"opaque_config"`
}
type EnvoyLBConfig struct {
// Policy is the load balancing policy used to select a host
Policy string `json:",omitempty"`
// RingHashConfig contains configuration for the "ring_hash" policy type
RingHashConfig RingHashConfig `json:",omitempty" alias:"ring_hash_config"`
RingHashConfig *RingHashConfig `json:",omitempty" alias:"ring_hash_config"`
// LeastRequestConfig contains configuration for the "least_request" policy type
LeastRequestConfig LeastRequestConfig `json:",omitempty" alias:"least_request_config"`
LeastRequestConfig *LeastRequestConfig `json:",omitempty" alias:"least_request_config"`
// HashPolicies is a list of hash policies to use for hashing load balancing algorithms.
// Hash policies are evaluated individually and combined such that identical lists
@ -1017,15 +1050,6 @@ type LoadBalancer struct {
HashPolicies []HashPolicy `json:",omitempty" alias:"hash_policies"`
}
func (l LoadBalancer) IsHashBased() bool {
switch l.Policy {
case "maglev", "ring_hash":
return true
default:
return false
}
}
// RingHashConfig contains configuration for the "ring_hash" policy type
type RingHashConfig struct {
// MinimumRingSize determines the minimum number of entries in the hash ring
@ -1041,25 +1065,24 @@ type LeastRequestConfig struct {
ChoiceCount uint32 `json:",omitempty" alias:"choice_count"`
}
// HashPolicy is a list of hash policies to use for hashing load balancing algorithms.
// Hash policies are evaluated individually and combined such that identical lists
// result in the same hash.
// If no hash policies are present, or none are successfully evaluated,
// then a random backend host will be selected.
// HashPolicy defines which attributes will be hashed by hash-based LB algorithms
type HashPolicy struct {
// Field is the attribute type to hash on.
// Must be one of "header","cookie", or "query_parameter".
// Cannot be specified along with SourceIP.
Field string `json:",omitempty"`
// FieldMatchValue is the value to hash.
// FieldValue is the value to hash.
// ie. header name, cookie name, URL query parameter name
// Cannot be specified along with SourceIP.
FieldMatchValue string `json:",omitempty" alias:"field_value"`
FieldValue string `json:",omitempty" alias:"field_value"`
// SourceAddress determines whether the hash should be of the source IP rather than of a field and field value.
// Cannot be specified along with Field or FieldMatchValue.
SourceAddress bool `json:",omitempty" alias:"source_address"`
// CookieConfig contains configuration for the "cookie" hash policy type.
CookieConfig *CookieConfig `json:",omitempty" alias:"cookie_config"`
// SourceIP determines whether the hash should be of the source IP rather than of a field and field value.
// Cannot be specified along with Field or FieldValue.
SourceIP bool `json:",omitempty" alias:"source_ip"`
// Terminal will short circuit the computation of the hash when multiple hash policies are present.
// If a hash is computed when a Terminal policy is evaluated,
@ -1067,6 +1090,134 @@ type HashPolicy struct {
Terminal bool `json:",omitempty"`
}
// CookieConfig contains configuration for the "cookie" hash policy type.
// This is specified to have Envoy generate a cookie for a client on its first request.
type CookieConfig struct {
// TTL for generated cookies
TTL time.Duration `json:",omitempty"`
// The path to set for the cookie
Path string `json:",omitempty"`
}
func (ec *EnvoyLBConfig) IsHashBased() bool {
if ec == nil {
return false
}
switch ec.Policy {
case LBPolicyMaglev, LBPolicyRingHash:
return true
default:
return false
}
}
func (ec *EnvoyLBConfig) InjectToCluster(c *envoy.Cluster) error {
if ec == nil {
return nil
}
switch ec.Policy {
case "":
return nil
case LBPolicyLeastRequest:
c.LbPolicy = envoy.Cluster_LEAST_REQUEST
if ec.LeastRequestConfig != nil {
c.LbConfig = &envoy.Cluster_LeastRequestLbConfig_{
LeastRequestLbConfig: &envoy.Cluster_LeastRequestLbConfig{
ChoiceCount: &wrappers.UInt32Value{Value: ec.LeastRequestConfig.ChoiceCount},
},
}
}
case LBPolicyRoundRobin:
c.LbPolicy = envoy.Cluster_ROUND_ROBIN
case LBPolicyRandom:
c.LbPolicy = envoy.Cluster_RANDOM
case LBPolicyRingHash:
c.LbPolicy = envoy.Cluster_RING_HASH
if ec.RingHashConfig != nil {
c.LbConfig = &envoy.Cluster_RingHashLbConfig_{
RingHashLbConfig: &envoy.Cluster_RingHashLbConfig{
MinimumRingSize: &wrappers.UInt64Value{Value: ec.RingHashConfig.MinimumRingSize},
MaximumRingSize: &wrappers.UInt64Value{Value: ec.RingHashConfig.MaximumRingSize},
},
}
}
case LBPolicyMaglev:
c.LbPolicy = envoy.Cluster_MAGLEV
default:
return fmt.Errorf("unsupported load balancer policy %q for cluster %q", ec.Policy, c.Name)
}
return nil
}
func (ec *EnvoyLBConfig) InjectToRouteAction(action *envoyroute.RouteAction) error {
if ec == nil || !ec.IsHashBased() {
return nil
}
result := make([]*envoyroute.RouteAction_HashPolicy, 0, len(ec.HashPolicies))
for _, policy := range ec.HashPolicies {
if policy.SourceIP {
result = append(result, &envoyroute.RouteAction_HashPolicy{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_ConnectionProperties_{
ConnectionProperties: &envoyroute.RouteAction_HashPolicy_ConnectionProperties{
SourceIp: true,
},
},
Terminal: policy.Terminal,
})
continue
}
switch policy.Field {
case HashPolicyHeader:
result = append(result, &envoyroute.RouteAction_HashPolicy{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Header_{
Header: &envoyroute.RouteAction_HashPolicy_Header{
HeaderName: policy.FieldValue,
},
},
Terminal: policy.Terminal,
})
case HashPolicyCookie:
cookie := envoyroute.RouteAction_HashPolicy_Cookie{
Name: policy.FieldValue,
}
if policy.CookieConfig != nil {
cookie.Ttl = ptypes.DurationProto(policy.CookieConfig.TTL)
cookie.Path = policy.CookieConfig.Path
}
result = append(result, &envoyroute.RouteAction_HashPolicy{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{
Cookie: &cookie,
},
Terminal: policy.Terminal,
})
case HashPolicyQueryParam:
result = append(result, &envoyroute.RouteAction_HashPolicy{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_QueryParameter_{
QueryParameter: &envoyroute.RouteAction_HashPolicy_QueryParameter{
Name: policy.FieldValue,
},
},
Terminal: policy.Terminal,
})
default:
return fmt.Errorf("unsupported load balancer hash policy field: %v", policy.Field)
}
}
action.HashPolicy = result
return nil
}
type discoveryChainConfigEntry interface {
ConfigEntry
// ListRelatedServices returns a list of other names of services referenced

View File

@ -3,10 +3,14 @@ package structs
import (
"bytes"
"fmt"
"github.com/golang/protobuf/ptypes"
"strings"
"testing"
"time"
envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
envoyroute "github.com/envoyproxy/go-control-plane/envoy/api/v2/route"
"github.com/golang/protobuf/ptypes/wrappers"
"github.com/hashicorp/consul/acl"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -552,25 +556,31 @@ func TestServiceResolverConfigEntry_LoadBalancer(t *testing.T) {
{
name: "empty policy is valid",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: LoadBalancer{Policy: ""},
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{Policy: ""},
},
},
},
{
name: "supported policy",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: LoadBalancer{Policy: "random"},
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{Policy: LBPolicyRandom},
},
},
},
{
name: "unsupported policy",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: LoadBalancer{Policy: "fake-policy"},
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{Policy: "fake-policy"},
},
},
validateErr: `"fake-policy" is not supported`,
},
@ -579,9 +589,11 @@ func TestServiceResolverConfigEntry_LoadBalancer(t *testing.T) {
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: LoadBalancer{
Policy: "ring_hash",
LeastRequestConfig: LeastRequestConfig{ChoiceCount: 2},
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{
Policy: LBPolicyRingHash,
LeastRequestConfig: &LeastRequestConfig{ChoiceCount: 10},
},
},
},
validateErr: `LeastRequestConfig specified for incompatible load balancing policy`,
@ -591,9 +603,11 @@ func TestServiceResolverConfigEntry_LoadBalancer(t *testing.T) {
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: LoadBalancer{
Policy: "least_request",
RingHashConfig: RingHashConfig{MinimumRingSize: 1024},
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{
Policy: LBPolicyLeastRequest,
RingHashConfig: &RingHashConfig{MinimumRingSize: 1024},
},
},
},
validateErr: `RingHashConfig specified for incompatible load balancing policy`,
@ -603,9 +617,11 @@ func TestServiceResolverConfigEntry_LoadBalancer(t *testing.T) {
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: LoadBalancer{
Policy: "ring_hash",
RingHashConfig: RingHashConfig{MinimumRingSize: 1024},
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{
Policy: LBPolicyRingHash,
RingHashConfig: &RingHashConfig{MinimumRingSize: 1024},
},
},
},
},
@ -614,9 +630,11 @@ func TestServiceResolverConfigEntry_LoadBalancer(t *testing.T) {
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: LoadBalancer{
Policy: "least_request",
LeastRequestConfig: LeastRequestConfig{ChoiceCount: 2},
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{
Policy: LBPolicyLeastRequest,
LeastRequestConfig: &LeastRequestConfig{ChoiceCount: 2},
},
},
},
},
@ -625,12 +643,12 @@ func TestServiceResolverConfigEntry_LoadBalancer(t *testing.T) {
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: LoadBalancer{
Policy: "",
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{Policy: ""},
},
},
check: func(t *testing.T, entry *ServiceResolverConfigEntry) {
require.Equal(t, "", entry.LoadBalancer.Policy)
require.Equal(t, "", entry.LoadBalancer.EnvoyLBConfig.Policy)
},
},
{
@ -638,28 +656,77 @@ func TestServiceResolverConfigEntry_LoadBalancer(t *testing.T) {
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: LoadBalancer{
Policy: "",
HashPolicies: []HashPolicy{
{
SourceAddress: true,
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{
Policy: "",
HashPolicies: []HashPolicy{
{
SourceIP: true,
},
},
},
},
},
validateErr: `HashPolicies specified for non-hash-based Policy`,
},
{
name: "empty policy with hash policy",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{
Policy: LBPolicyMaglev,
HashPolicies: []HashPolicy{
{
Field: HashPolicyHeader,
FieldValue: "x-user-id",
CookieConfig: &CookieConfig{
TTL: 10 * time.Second,
Path: "/root",
},
},
},
},
},
},
validateErr: `cookie_config provided for "header"`,
},
{
name: "empty policy with hash policy",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{
Policy: LBPolicyMaglev,
HashPolicies: []HashPolicy{
{
Field: HashPolicyCookie,
FieldValue: "good-cookie",
CookieConfig: &CookieConfig{
TTL: 10 * time.Second,
Path: "/oven",
},
},
},
},
},
},
},
{
name: "supported match field",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: LoadBalancer{
Policy: "maglev",
HashPolicies: []HashPolicy{
{
Field: "header",
FieldMatchValue: "X-Consul-Token",
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{
Policy: LBPolicyMaglev,
HashPolicies: []HashPolicy{
{
Field: "header",
FieldValue: "X-Consul-Token",
},
},
},
},
@ -670,28 +737,32 @@ func TestServiceResolverConfigEntry_LoadBalancer(t *testing.T) {
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: LoadBalancer{
Policy: "maglev",
HashPolicies: []HashPolicy{
{
Field: "not-header",
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{
Policy: LBPolicyMaglev,
HashPolicies: []HashPolicy{
{
Field: "fake-field",
},
},
},
},
},
validateErr: `"not-header" is not a supported field`,
validateErr: `"fake-field" is not a supported field`,
},
{
name: "cannot match on source address and custom field",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: LoadBalancer{
Policy: "maglev",
HashPolicies: []HashPolicy{
{
Field: "header",
SourceAddress: true,
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{
Policy: LBPolicyMaglev,
HashPolicies: []HashPolicy{
{
Field: "header",
SourceIP: true,
},
},
},
},
@ -703,67 +774,75 @@ func TestServiceResolverConfigEntry_LoadBalancer(t *testing.T) {
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: LoadBalancer{
Policy: "maglev",
HashPolicies: []HashPolicy{
{
FieldMatchValue: "X-Consul-Token",
SourceAddress: true,
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{
Policy: LBPolicyMaglev,
HashPolicies: []HashPolicy{
{
FieldValue: "X-Consul-Token",
SourceIP: true,
},
},
},
},
},
validateErr: `A FieldMatchValue cannot be specified when hashing SourceAddress`,
validateErr: `A FieldValue cannot be specified when hashing SourceIP`,
},
{
name: "field without match value",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: LoadBalancer{
Policy: "maglev",
HashPolicies: []HashPolicy{
{
Field: "header",
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{
Policy: LBPolicyMaglev,
HashPolicies: []HashPolicy{
{
Field: "header",
},
},
},
},
},
validateErr: `Field "header" was specified without a FieldMatchValue`,
validateErr: `Field "header" was specified without a FieldValue`,
},
{
name: "field without match value",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: LoadBalancer{
Policy: "maglev",
HashPolicies: []HashPolicy{
{
FieldMatchValue: "my-cookie",
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{
Policy: LBPolicyMaglev,
HashPolicies: []HashPolicy{
{
FieldValue: "my-cookie",
},
},
},
},
},
validateErr: `FieldMatchValue requires a Field to apply to`,
validateErr: `FieldValue requires a Field to apply to`,
},
{
name: "ring hash kitchen sink",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: LoadBalancer{
Policy: "ring_hash",
RingHashConfig: RingHashConfig{MaximumRingSize: 10, MinimumRingSize: 2},
HashPolicies: []HashPolicy{
{
Field: "cookie",
FieldMatchValue: "my-cookie",
},
{
Field: "header",
FieldMatchValue: "alt-header",
Terminal: true,
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{
Policy: LBPolicyRingHash,
RingHashConfig: &RingHashConfig{MaximumRingSize: 10, MinimumRingSize: 2},
HashPolicies: []HashPolicy{
{
Field: "cookie",
FieldValue: "my-cookie",
},
{
Field: "header",
FieldValue: "alt-header",
Terminal: true,
},
},
},
},
@ -774,9 +853,11 @@ func TestServiceResolverConfigEntry_LoadBalancer(t *testing.T) {
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
LoadBalancer: LoadBalancer{
Policy: "least_request",
LeastRequestConfig: LeastRequestConfig{ChoiceCount: 20},
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{
Policy: LBPolicyLeastRequest,
LeastRequestConfig: &LeastRequestConfig{ChoiceCount: 20},
},
},
},
},
@ -1444,3 +1525,267 @@ func TestIsProtocolHTTPLike(t *testing.T) {
assert.True(t, IsProtocolHTTPLike("http2"))
assert.True(t, IsProtocolHTTPLike("grpc"))
}
func TestEnvoyLBConfig_InjectToRouteAction(t *testing.T) {
var tests = []struct {
name string
lb *EnvoyLBConfig
expected envoyroute.RouteAction
}{
{
name: "empty",
lb: &EnvoyLBConfig{
Policy: "",
},
// we only modify route actions for hash-based LB policies
expected: envoyroute.RouteAction{},
},
{
name: "least request",
lb: &EnvoyLBConfig{
Policy: LBPolicyLeastRequest,
LeastRequestConfig: &LeastRequestConfig{
ChoiceCount: 3,
},
},
// we only modify route actions for hash-based LB policies
expected: envoyroute.RouteAction{},
},
{
name: "headers",
lb: &EnvoyLBConfig{
Policy: "ring_hash",
RingHashConfig: &RingHashConfig{
MinimumRingSize: 3,
MaximumRingSize: 7,
},
HashPolicies: []HashPolicy{
{
Field: HashPolicyHeader,
FieldValue: "x-route-key",
Terminal: true,
},
},
},
expected: envoyroute.RouteAction{
HashPolicy: []*envoyroute.RouteAction_HashPolicy{
{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Header_{
Header: &envoyroute.RouteAction_HashPolicy_Header{
HeaderName: "x-route-key",
},
},
Terminal: true,
},
},
},
},
{
name: "cookies",
lb: &EnvoyLBConfig{
Policy: LBPolicyMaglev,
HashPolicies: []HashPolicy{
{
Field: HashPolicyCookie,
FieldValue: "red-velvet",
Terminal: true,
},
{
Field: HashPolicyCookie,
FieldValue: "oatmeal",
},
},
},
expected: envoyroute.RouteAction{
HashPolicy: []*envoyroute.RouteAction_HashPolicy{
{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{
Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{
Name: "red-velvet",
},
},
Terminal: true,
},
{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{
Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{
Name: "oatmeal",
},
},
},
},
},
},
{
name: "source addr",
lb: &EnvoyLBConfig{
Policy: LBPolicyMaglev,
HashPolicies: []HashPolicy{
{
SourceIP: true,
Terminal: true,
},
},
},
expected: envoyroute.RouteAction{
HashPolicy: []*envoyroute.RouteAction_HashPolicy{
{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_ConnectionProperties_{
ConnectionProperties: &envoyroute.RouteAction_HashPolicy_ConnectionProperties{
SourceIp: true,
},
},
Terminal: true,
},
},
},
},
{
name: "kitchen sink",
lb: &EnvoyLBConfig{
Policy: LBPolicyMaglev,
HashPolicies: []HashPolicy{
{
SourceIP: true,
Terminal: true,
},
{
Field: HashPolicyCookie,
FieldValue: "oatmeal",
CookieConfig: &CookieConfig{
TTL: 10 * time.Second,
Path: "/oven",
},
},
{
Field: HashPolicyHeader,
FieldValue: "special-header",
Terminal: true,
},
},
},
expected: envoyroute.RouteAction{
HashPolicy: []*envoyroute.RouteAction_HashPolicy{
{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_ConnectionProperties_{
ConnectionProperties: &envoyroute.RouteAction_HashPolicy_ConnectionProperties{
SourceIp: true,
},
},
Terminal: true,
},
{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{
Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{
Name: "oatmeal",
Ttl: ptypes.DurationProto(10 * time.Second),
Path: "/oven",
},
},
},
{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Header_{
Header: &envoyroute.RouteAction_HashPolicy_Header{
HeaderName: "special-header",
},
},
Terminal: true,
},
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var ra envoyroute.RouteAction
err := tc.lb.InjectToRouteAction(&ra)
require.NoError(t, err)
require.Equal(t, &tc.expected, &ra)
})
}
}
func TestEnvoyLBConfig_InjectToCluster(t *testing.T) {
var tests = []struct {
name string
lb *EnvoyLBConfig
expected envoy.Cluster
}{
{
name: "skip empty",
lb: &EnvoyLBConfig{
Policy: "",
},
expected: envoy.Cluster{},
},
{
name: "round robin",
lb: &EnvoyLBConfig{
Policy: LBPolicyRoundRobin,
},
expected: envoy.Cluster{LbPolicy: envoy.Cluster_ROUND_ROBIN},
},
{
name: "random",
lb: &EnvoyLBConfig{
Policy: LBPolicyRandom,
},
expected: envoy.Cluster{LbPolicy: envoy.Cluster_RANDOM},
},
{
name: "maglev",
lb: &EnvoyLBConfig{
Policy: LBPolicyMaglev,
},
expected: envoy.Cluster{LbPolicy: envoy.Cluster_MAGLEV},
},
{
name: "ring_hash",
lb: &EnvoyLBConfig{
Policy: LBPolicyRingHash,
RingHashConfig: &RingHashConfig{
MinimumRingSize: 3,
MaximumRingSize: 7,
},
},
expected: envoy.Cluster{
LbPolicy: envoy.Cluster_RING_HASH,
LbConfig: &envoy.Cluster_RingHashLbConfig_{
RingHashLbConfig: &envoy.Cluster_RingHashLbConfig{
MinimumRingSize: &wrappers.UInt64Value{Value: 3},
MaximumRingSize: &wrappers.UInt64Value{Value: 7},
},
},
},
},
{
name: "least_request",
lb: &EnvoyLBConfig{
Policy: "least_request",
LeastRequestConfig: &LeastRequestConfig{
ChoiceCount: 3,
},
},
expected: envoy.Cluster{
LbPolicy: envoy.Cluster_LEAST_REQUEST,
LbConfig: &envoy.Cluster_LeastRequestLbConfig_{
LeastRequestLbConfig: &envoy.Cluster_LeastRequestLbConfig{
ChoiceCount: &wrappers.UInt32Value{Value: 3},
},
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var c envoy.Cluster
err := tc.lb.InjectToCluster(&c)
require.NoError(t, err)
require.Equal(t, tc.expected, c)
})
}
}

View File

@ -109,7 +109,7 @@ type DiscoveryGraphNode struct {
Resolver *DiscoveryResolver `json:",omitempty"`
// shared by Type==resolver || Type==splitter
LoadBalancer LoadBalancer `json:",omitempty"`
LoadBalancer *LoadBalancer `json:",omitempty"`
}
func (s *DiscoveryGraphNode) IsRouter() bool {

View File

@ -16,7 +16,6 @@ import (
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
"github.com/golang/protobuf/ptypes/any"
"github.com/golang/protobuf/ptypes/wrappers"
"github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/agent/proxycfg"
"github.com/hashicorp/consul/agent/structs"
@ -206,13 +205,14 @@ func (s *Server) makeGatewayServiceClusters(cfgSnap *proxycfg.ConfigSnapshot) ([
clusterName := connect.ServiceSNI(svc.Name, "", svc.NamespaceOrDefault(), cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain)
resolver, hasResolver := resolvers[svc]
var lb structs.LoadBalancer
var loadBalancer *structs.EnvoyLBConfig
if !hasResolver {
if hasResolver && resolver.LoadBalancer != nil {
loadBalancer = resolver.LoadBalancer.EnvoyLBConfig
} else {
// Use a zero value resolver with no timeout and no subsets
resolver = &structs.ServiceResolverConfigEntry{}
} else {
lb = resolver.LoadBalancer
}
// When making service clusters we only pass endpoints with hostnames if the kind is a terminating gateway
@ -232,16 +232,14 @@ func (s *Server) makeGatewayServiceClusters(cfgSnap *proxycfg.ConfigSnapshot) ([
case structs.ServiceKindTerminatingGateway:
injectTerminatingGatewayTLSContext(cfgSnap, cluster, svc)
err := injectLBToCluster(lb, cluster)
if err != nil {
if err := loadBalancer.InjectToCluster(cluster); err != nil {
return nil, fmt.Errorf("failed to apply load balancer configuration to cluster %q: %v", clusterName, err)
}
case structs.ServiceKindMeshGateway:
// We can't apply hash based LB config to mesh gateways because they rely on inspecting HTTP attributes
// and mesh gateways do not decrypt traffic
if !lb.IsHashBased() {
err := injectLBToCluster(lb, cluster)
if err != nil {
if !loadBalancer.IsHashBased() {
if err := loadBalancer.InjectToCluster(cluster); err != nil {
return nil, fmt.Errorf("failed to apply load balancer configuration to cluster %q: %v", clusterName, err)
}
}
@ -267,16 +265,15 @@ func (s *Server) makeGatewayServiceClusters(cfgSnap *proxycfg.ConfigSnapshot) ([
case structs.ServiceKindTerminatingGateway:
injectTerminatingGatewayTLSContext(cfgSnap, cluster, svc)
err := injectLBToCluster(lb, cluster)
if err != nil {
if err := loadBalancer.InjectToCluster(cluster); err != nil {
return nil, fmt.Errorf("failed to apply load balancer configuration to cluster %q: %v", clusterName, err)
}
case structs.ServiceKindMeshGateway:
// We can't apply hash based LB config to mesh gateways because they rely on inspecting HTTP attributes
// and mesh gateways do not decrypt traffic
if !lb.IsHashBased() {
err := injectLBToCluster(lb, cluster)
if err != nil {
if !loadBalancer.IsHashBased() {
if err := loadBalancer.InjectToCluster(cluster); err != nil {
return nil, fmt.Errorf("failed to apply load balancer configuration to cluster %q: %v", clusterName, err)
}
}
@ -516,8 +513,12 @@ func (s *Server) makeUpstreamClustersForDiscoveryChain(
OutlierDetection: cfg.PassiveHealthCheck.AsOutlierDetection(),
}
if err := injectLBToCluster(node.LoadBalancer, c); err != nil {
return nil, fmt.Errorf("failed to apply load balancer configuration to cluster %q: %v", sni, err)
var lb *structs.EnvoyLBConfig
if node.LoadBalancer != nil {
lb = node.LoadBalancer.EnvoyLBConfig
}
if err := lb.InjectToCluster(c); err != nil {
return nil, fmt.Errorf("failed to apply load balancer configuration to cluster %q: %v", clusterName, err)
}
proto := cfg.Protocol
@ -557,37 +558,6 @@ func (s *Server) makeUpstreamClustersForDiscoveryChain(
return out, nil
}
func injectLBToCluster(l structs.LoadBalancer, c *envoy.Cluster) error {
switch l.Policy {
case "":
return nil
case "least_request":
c.LbPolicy = envoy.Cluster_LEAST_REQUEST
c.LbConfig = &envoy.Cluster_LeastRequestLbConfig_{
LeastRequestLbConfig: &envoy.Cluster_LeastRequestLbConfig{
ChoiceCount: &wrappers.UInt32Value{Value: l.LeastRequestConfig.ChoiceCount},
},
}
case "round_robin":
c.LbPolicy = envoy.Cluster_ROUND_ROBIN
case "random":
c.LbPolicy = envoy.Cluster_RANDOM
case "ring_hash":
c.LbPolicy = envoy.Cluster_RING_HASH
c.LbConfig = &envoy.Cluster_RingHashLbConfig_{
RingHashLbConfig: &envoy.Cluster_RingHashLbConfig{
MinimumRingSize: &wrappers.UInt64Value{Value: l.RingHashConfig.MinimumRingSize},
MaximumRingSize: &wrappers.UInt64Value{Value: l.RingHashConfig.MaximumRingSize},
},
}
case "maglev":
c.LbPolicy = envoy.Cluster_MAGLEV
default:
return fmt.Errorf("unsupported load balancer policy %q for cluster %q", l.Policy, c.Name)
}
return nil
}
// makeClusterFromUserConfig returns the listener config decoded from an
// arbitrary proto3 json format string or an error if it's invalid.
//

View File

@ -2,7 +2,6 @@ package xds
import (
"bytes"
"github.com/golang/protobuf/ptypes/wrappers"
"path/filepath"
"sort"
"testing"
@ -367,10 +366,12 @@ func TestClustersFromSnapshot(t *testing.T) {
OnlyPassing: true,
},
},
LoadBalancer: structs.LoadBalancer{
Policy: "least_request",
LeastRequestConfig: structs.LeastRequestConfig{
ChoiceCount: 5,
LoadBalancer: &structs.LoadBalancer{
EnvoyLBConfig: &structs.EnvoyLBConfig{
Policy: "least_request",
LeastRequestConfig: &structs.LeastRequestConfig{
ChoiceCount: 5,
},
},
},
},
@ -394,11 +395,13 @@ func TestClustersFromSnapshot(t *testing.T) {
OnlyPassing: true,
},
},
LoadBalancer: structs.LoadBalancer{
Policy: "ring_hash",
RingHashConfig: structs.RingHashConfig{
MinimumRingSize: 20,
MaximumRingSize: 50,
LoadBalancer: &structs.LoadBalancer{
EnvoyLBConfig: &structs.EnvoyLBConfig{
Policy: "ring_hash",
RingHashConfig: &structs.RingHashConfig{
MinimumRingSize: 20,
MaximumRingSize: 50,
},
},
},
},
@ -605,11 +608,13 @@ func TestClustersFromSnapshot(t *testing.T) {
OnlyPassing: true,
},
},
LoadBalancer: structs.LoadBalancer{
Policy: "ring_hash",
RingHashConfig: structs.RingHashConfig{
MinimumRingSize: 20,
MaximumRingSize: 50,
LoadBalancer: &structs.LoadBalancer{
EnvoyLBConfig: &structs.EnvoyLBConfig{
Policy: "ring_hash",
RingHashConfig: &structs.RingHashConfig{
MinimumRingSize: 20,
MaximumRingSize: 50,
},
},
},
},
@ -834,86 +839,3 @@ func setupTLSRootsAndLeaf(t *testing.T, snap *proxycfg.ConfigSnapshot) {
snap.Roots.Roots[0].RootCert = golden(t, "test-root-cert", "", "")
}
}
func TestLoadBalancer_injectLBToCluster(t *testing.T) {
var tests = []struct {
name string
lb structs.LoadBalancer
expected envoy.Cluster
}{
{
name: "skip empty",
lb: structs.LoadBalancer{
Policy: "",
},
expected: envoy.Cluster{},
},
{
name: "round_robin",
lb: structs.LoadBalancer{
Policy: "round_robin",
},
expected: envoy.Cluster{LbPolicy: envoy.Cluster_ROUND_ROBIN},
},
{
name: "random",
lb: structs.LoadBalancer{
Policy: "random",
},
expected: envoy.Cluster{LbPolicy: envoy.Cluster_RANDOM},
},
{
name: "maglev",
lb: structs.LoadBalancer{
Policy: "maglev",
},
expected: envoy.Cluster{LbPolicy: envoy.Cluster_MAGLEV},
},
{
name: "ring_hash",
lb: structs.LoadBalancer{
Policy: "ring_hash",
RingHashConfig: structs.RingHashConfig{
MinimumRingSize: 3,
MaximumRingSize: 7,
},
},
expected: envoy.Cluster{
LbPolicy: envoy.Cluster_RING_HASH,
LbConfig: &envoy.Cluster_RingHashLbConfig_{
RingHashLbConfig: &envoy.Cluster_RingHashLbConfig{
MinimumRingSize: &wrappers.UInt64Value{Value: 3},
MaximumRingSize: &wrappers.UInt64Value{Value: 7},
},
},
},
},
{
name: "least_request",
lb: structs.LoadBalancer{
Policy: "least_request",
LeastRequestConfig: structs.LeastRequestConfig{
ChoiceCount: 3,
},
},
expected: envoy.Cluster{
LbPolicy: envoy.Cluster_LEAST_REQUEST,
LbConfig: &envoy.Cluster_LeastRequestLbConfig_{
LeastRequestLbConfig: &envoy.Cluster_LeastRequestLbConfig{
ChoiceCount: &wrappers.UInt32Value{Value: 3},
},
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var c envoy.Cluster
err := injectLBToCluster(tc.lb, &c)
require.NoError(t, err)
require.Equal(t, tc.expected, c)
})
}
}

View File

@ -43,14 +43,14 @@ func routesFromSnapshotTerminatingGateway(_ connectionInfo, cfgSnap *proxycfg.Co
}
var resources []proto.Message
for svc := range cfgSnap.TerminatingGateway.ServiceGroups {
for _, svc := range cfgSnap.TerminatingGateway.ValidServices() {
clusterName := connect.ServiceSNI(svc.Name, "", svc.NamespaceOrDefault(), cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain)
resolver, hasResolver := cfgSnap.TerminatingGateway.ServiceResolvers[svc]
svcConfig := cfgSnap.TerminatingGateway.ServiceConfigs[svc]
cfg, err := ParseProxyConfig(svcConfig.ProxyConfig)
if err != nil || cfg.Protocol != "http" {
if err != nil || structs.IsProtocolHTTPLike(cfg.Protocol) {
// Routes can only be defined for HTTP services
continue
}
@ -59,7 +59,12 @@ func routesFromSnapshotTerminatingGateway(_ connectionInfo, cfgSnap *proxycfg.Co
// Use a zero value resolver with no timeout and no subsets
resolver = &structs.ServiceResolverConfigEntry{}
}
route, err := makeNamedDefaultRouteWithLB(clusterName, resolver.LoadBalancer)
var lb *structs.EnvoyLBConfig
if resolver.LoadBalancer != nil {
lb = resolver.LoadBalancer.EnvoyLBConfig
}
route, err := makeNamedDefaultRouteWithLB(clusterName, lb)
if err != nil {
continue
}
@ -68,7 +73,7 @@ func routesFromSnapshotTerminatingGateway(_ connectionInfo, cfgSnap *proxycfg.Co
// If there is a service-resolver for this service then also setup routes for each subset
for name := range resolver.Subsets {
clusterName = connect.ServiceSNI(svc.Name, name, svc.NamespaceOrDefault(), cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain)
route, err := makeNamedDefaultRouteWithLB(clusterName, resolver.LoadBalancer)
route, err := makeNamedDefaultRouteWithLB(clusterName, lb)
if err != nil {
continue
}
@ -76,13 +81,13 @@ func routesFromSnapshotTerminatingGateway(_ connectionInfo, cfgSnap *proxycfg.Co
}
}
// TODO(rb): make sure we don't generate an empty result
return resources, nil
}
func makeNamedDefaultRouteWithLB(clusterName string, lb structs.LoadBalancer) (*envoy.RouteConfiguration, error) {
func makeNamedDefaultRouteWithLB(clusterName string, lb *structs.EnvoyLBConfig) (*envoy.RouteConfiguration, error) {
action := makeRouteActionFromName(clusterName)
if err := injectLBToRouteAction(lb, action.Route); err != nil {
if err := lb.InjectToRouteAction(action.Route); err != nil {
return nil, fmt.Errorf("failed to apply load balancer configuration to route action: %v", err)
}
@ -250,6 +255,8 @@ func makeUpstreamRouteForDiscoveryChain(
return nil, fmt.Errorf("missing first node in compiled discovery chain for: %s", chain.ServiceName)
}
var lb *structs.EnvoyLBConfig
switch startNode.Type {
case structs.DiscoveryGraphNodeTypeRouter:
routes = make([]*envoyroute.Route, 0, len(startNode.Routes))
@ -263,6 +270,10 @@ func makeUpstreamRouteForDiscoveryChain(
)
nextNode := chain.Nodes[discoveryRoute.NextNode]
if nextNode.LoadBalancer != nil {
lb = nextNode.LoadBalancer.EnvoyLBConfig
}
switch nextNode.Type {
case structs.DiscoveryGraphNodeTypeSplitter:
routeAction, err = makeRouteActionForSplitter(nextNode.Splits, chain)
@ -270,14 +281,14 @@ func makeUpstreamRouteForDiscoveryChain(
return nil, err
}
if err := injectLBToRouteAction(nextNode.LoadBalancer, routeAction.Route); err != nil {
if err := lb.InjectToRouteAction(routeAction.Route); err != nil {
return nil, fmt.Errorf("failed to apply load balancer configuration to route action: %v", err)
}
case structs.DiscoveryGraphNodeTypeResolver:
routeAction = makeRouteActionForChainCluster(nextNode.Resolver.Target, chain)
if err := injectLBToRouteAction(nextNode.LoadBalancer, routeAction.Route); err != nil {
if err := lb.InjectToRouteAction(routeAction.Route); err != nil {
return nil, fmt.Errorf("failed to apply load balancer configuration to route action: %v", err)
}
@ -332,7 +343,10 @@ func makeUpstreamRouteForDiscoveryChain(
return nil, err
}
if err := injectLBToRouteAction(startNode.LoadBalancer, routeAction.Route); err != nil {
if startNode.LoadBalancer != nil {
lb = startNode.LoadBalancer.EnvoyLBConfig
}
if err := lb.InjectToRouteAction(routeAction.Route); err != nil {
return nil, fmt.Errorf("failed to apply load balancer configuration to route action: %v", err)
}
@ -346,7 +360,10 @@ func makeUpstreamRouteForDiscoveryChain(
case structs.DiscoveryGraphNodeTypeResolver:
routeAction := makeRouteActionForChainCluster(startNode.Resolver.Target, chain)
if err := injectLBToRouteAction(startNode.LoadBalancer, routeAction.Route); err != nil {
if startNode.LoadBalancer != nil {
lb = startNode.LoadBalancer.EnvoyLBConfig
}
if err := lb.InjectToRouteAction(routeAction.Route); err != nil {
return nil, fmt.Errorf("failed to apply load balancer configuration to route action: %v", err)
}
@ -370,62 +387,6 @@ func makeUpstreamRouteForDiscoveryChain(
return host, nil
}
func injectLBToRouteAction(lb structs.LoadBalancer, action *envoyroute.RouteAction) error {
if !lb.IsHashBased() {
return nil
}
result := make([]*envoyroute.RouteAction_HashPolicy, 0, len(lb.HashPolicies))
for _, policy := range lb.HashPolicies {
if policy.SourceAddress {
result = append(result, &envoyroute.RouteAction_HashPolicy{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_ConnectionProperties_{
ConnectionProperties: &envoyroute.RouteAction_HashPolicy_ConnectionProperties{
SourceIp: true,
},
},
Terminal: policy.Terminal,
})
continue
}
switch policy.Field {
case "header":
result = append(result, &envoyroute.RouteAction_HashPolicy{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Header_{
Header: &envoyroute.RouteAction_HashPolicy_Header{
HeaderName: policy.FieldMatchValue,
},
},
Terminal: policy.Terminal,
})
case "cookie":
result = append(result, &envoyroute.RouteAction_HashPolicy{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{
Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{
Name: policy.FieldMatchValue,
},
},
Terminal: policy.Terminal,
})
case "query_parameter":
result = append(result, &envoyroute.RouteAction_HashPolicy{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_QueryParameter_{
QueryParameter: &envoyroute.RouteAction_HashPolicy_QueryParameter{
Name: policy.FieldMatchValue,
},
},
Terminal: policy.Terminal,
})
default:
return fmt.Errorf("unsupported load balancer hash policy field: %v", policy.Field)
}
}
action.HashPolicy = result
return nil
}
func makeRouteMatchForDiscoveryRoute(_ connectionInfo, discoveryRoute *structs.DiscoveryRoute) *envoyroute.RouteMatch {
match := discoveryRoute.Definition.Match
if match == nil || match.IsEmpty() {

View File

@ -7,7 +7,6 @@ import (
"time"
envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
envoyroute "github.com/envoyproxy/go-control-plane/envoy/api/v2/route"
"github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/agent/consul/discoverychain"
"github.com/hashicorp/consul/agent/proxycfg"
@ -203,25 +202,27 @@ func TestRoutesFromSnapshot(t *testing.T) {
OnlyPassing: true,
},
},
LoadBalancer: structs.LoadBalancer{
Policy: "ring_hash",
RingHashConfig: structs.RingHashConfig{
MinimumRingSize: 20,
MaximumRingSize: 50,
},
HashPolicies: []structs.HashPolicy{
{
Field: "cookie",
FieldMatchValue: "chocolate-chip",
Terminal: true,
LoadBalancer: &structs.LoadBalancer{
EnvoyLBConfig: &structs.EnvoyLBConfig{
Policy: "ring_hash",
RingHashConfig: &structs.RingHashConfig{
MinimumRingSize: 20,
MaximumRingSize: 50,
},
{
Field: "header",
FieldMatchValue: "x-user-id",
},
{
SourceAddress: true,
Terminal: true,
HashPolicies: []structs.HashPolicy{
{
Field: structs.HashPolicyCookie,
FieldValue: "chocolate-chip",
Terminal: true,
},
{
Field: structs.HashPolicyHeader,
FieldValue: "x-user-id",
},
{
SourceIP: true,
Terminal: true,
},
},
},
},
@ -276,178 +277,3 @@ func TestRoutesFromSnapshot(t *testing.T) {
})
}
}
func TestLoadBalancer_injectLBToRouteAction(t *testing.T) {
var tests = []struct {
name string
lb structs.LoadBalancer
expected envoyroute.RouteAction
}{
{
name: "empty",
lb: structs.LoadBalancer{
Policy: "",
},
// we only modify route actions for hash-based LB policies
expected: envoyroute.RouteAction{},
},
{
name: "least_request",
lb: structs.LoadBalancer{
Policy: "least_request",
LeastRequestConfig: structs.LeastRequestConfig{
ChoiceCount: 3,
},
},
// we only modify route actions for hash-based LB policies
expected: envoyroute.RouteAction{},
},
{
name: "header",
lb: structs.LoadBalancer{
Policy: "ring_hash",
RingHashConfig: structs.RingHashConfig{
MinimumRingSize: 3,
MaximumRingSize: 7,
},
HashPolicies: []structs.HashPolicy{
{
Field: "header",
FieldMatchValue: "x-route-key",
Terminal: true,
},
},
},
expected: envoyroute.RouteAction{
HashPolicy: []*envoyroute.RouteAction_HashPolicy{
{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Header_{
Header: &envoyroute.RouteAction_HashPolicy_Header{
HeaderName: "x-route-key",
},
},
Terminal: true,
},
},
},
},
{
name: "cookies",
lb: structs.LoadBalancer{
Policy: "maglev",
HashPolicies: []structs.HashPolicy{
{
Field: "cookie",
FieldMatchValue: "red-velvet",
Terminal: true,
},
{
Field: "cookie",
FieldMatchValue: "oatmeal",
},
},
},
expected: envoyroute.RouteAction{
HashPolicy: []*envoyroute.RouteAction_HashPolicy{
{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{
Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{
Name: "red-velvet",
},
},
Terminal: true,
},
{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{
Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{
Name: "oatmeal",
},
},
},
},
},
},
{
name: "source addr",
lb: structs.LoadBalancer{
Policy: "maglev",
HashPolicies: []structs.HashPolicy{
{
SourceAddress: true,
Terminal: true,
},
},
},
expected: envoyroute.RouteAction{
HashPolicy: []*envoyroute.RouteAction_HashPolicy{
{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_ConnectionProperties_{
ConnectionProperties: &envoyroute.RouteAction_HashPolicy_ConnectionProperties{
SourceIp: true,
},
},
Terminal: true,
},
},
},
},
{
name: "kitchen sink",
lb: structs.LoadBalancer{
Policy: "maglev",
HashPolicies: []structs.HashPolicy{
{
SourceAddress: true,
Terminal: true,
},
{
Field: "cookie",
FieldMatchValue: "oatmeal",
},
{
Field: "header",
FieldMatchValue: "special-header",
Terminal: true,
},
},
},
expected: envoyroute.RouteAction{
HashPolicy: []*envoyroute.RouteAction_HashPolicy{
{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_ConnectionProperties_{
ConnectionProperties: &envoyroute.RouteAction_HashPolicy_ConnectionProperties{
SourceIp: true,
},
},
Terminal: true,
},
{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Cookie_{
Cookie: &envoyroute.RouteAction_HashPolicy_Cookie{
Name: "oatmeal",
},
},
},
{
PolicySpecifier: &envoyroute.RouteAction_HashPolicy_Header_{
Header: &envoyroute.RouteAction_HashPolicy_Header{
HeaderName: "special-header",
},
},
Terminal: true,
},
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
ra := &envoyroute.RouteAction{}
err := injectLBToRouteAction(tc.lb, ra)
require.NoError(t, err)
require.Equal(t, &tc.expected, ra)
})
}
}

View File

@ -111,38 +111,6 @@
"connectTimeout": "5s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v1.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v2.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"outlierDetection": {
}
}
],

View File

@ -111,38 +111,6 @@
"connectTimeout": "5s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v1.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v2.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"outlierDetection": {
}
}
],

View File

@ -111,38 +111,6 @@
"connectTimeout": "5s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v1.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v2.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"outlierDetection": {
}
}
],

View File

@ -111,38 +111,6 @@
"connectTimeout": "5s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v1.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v2.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"outlierDetection": {
}
}
],

View File

@ -111,38 +111,6 @@
"connectTimeout": "5s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v1.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v2.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"outlierDetection": {
}
}
],

View File

@ -111,38 +111,6 @@
"connectTimeout": "5s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v1.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v2.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"outlierDetection": {
}
}
],

View File

@ -111,38 +111,6 @@
"connectTimeout": "5s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v1.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v2.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"outlierDetection": {
}
}
],

View File

@ -111,38 +111,6 @@
"connectTimeout": "5s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v1.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v2.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"outlierDetection": {
}
}
],

View File

@ -12,7 +12,7 @@
}
}
},
"connectTimeout": "10s",
"connectTimeout": "5s",
"outlierDetection": {
}
@ -111,38 +111,6 @@
"connectTimeout": "5s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v1.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "10s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v2.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "10s",
"outlierDetection": {
}
}
],

View File

@ -12,7 +12,7 @@
}
}
},
"connectTimeout": "10s",
"connectTimeout": "5s",
"outlierDetection": {
}
@ -111,38 +111,6 @@
"connectTimeout": "5s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v1.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "10s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v2.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "10s",
"outlierDetection": {
}
}
],

View File

@ -12,7 +12,7 @@
}
}
},
"connectTimeout": "10s",
"connectTimeout": "5s",
"outlierDetection": {
}
@ -111,38 +111,6 @@
"connectTimeout": "5s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v1.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "10s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v2.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "10s",
"outlierDetection": {
}
}
],

View File

@ -12,7 +12,7 @@
}
}
},
"connectTimeout": "10s",
"connectTimeout": "5s",
"outlierDetection": {
}
@ -111,38 +111,6 @@
"connectTimeout": "5s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v1.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "10s",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v2.bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "10s",
"outlierDetection": {
}
}
],

View File

@ -1,60 +1,6 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "alt.api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "LOGICAL_DNS",
"connectTimeout": "5s",
"loadAssignment": {
"clusterName": "alt.api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"endpoints": [
{
"lbEndpoints": [
{
"endpoint": {
"address": {
"socketAddress": {
"address": "api.altdomain",
"portValue": 8081
}
}
},
"healthStatus": "HEALTHY",
"loadBalancingWeight": 1
}
]
}
]
},
"tlsContext": {
"commonTlsContext": {
"tlsParams": {
},
"tlsCertificates": [
{
"certificateChain": {
"filename": "api.cert.pem"
},
"privateKey": {
"filename": "api.key.pem"
}
}
],
"validationContext": {
"trustedCa": {
"filename": "ca.cert.pem"
}
}
}
},
"dnsRefreshRate": "10s",
"dnsLookupFamily": "V4_ONLY",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
@ -173,38 +119,6 @@
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "prod.cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "LOGICAL_DNS",
"connectTimeout": "5s",
"loadAssignment": {
"clusterName": "prod.cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"endpoints": [
{
"lbEndpoints": [
{
"endpoint": {
"address": {
"socketAddress": {
"address": "cache.mydomain",
"portValue": 8081
}
}
},
"healthStatus": "UNHEALTHY",
"loadBalancingWeight": 1
}
]
}
]
},
"dnsRefreshRate": "10s",
"dnsLookupFamily": "V4_ONLY",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",

View File

@ -1,60 +1,6 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "alt.api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "LOGICAL_DNS",
"connectTimeout": "5s",
"loadAssignment": {
"clusterName": "alt.api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"endpoints": [
{
"lbEndpoints": [
{
"endpoint": {
"address": {
"socketAddress": {
"address": "api.altdomain",
"portValue": 8081
}
}
},
"healthStatus": "HEALTHY",
"loadBalancingWeight": 1
}
]
}
]
},
"tlsContext": {
"commonTlsContext": {
"tlsParams": {
},
"tlsCertificates": [
{
"certificateChain": {
"filename": "api.cert.pem"
},
"privateKey": {
"filename": "api.key.pem"
}
}
],
"validationContext": {
"trustedCa": {
"filename": "ca.cert.pem"
}
}
}
},
"dnsRefreshRate": "10s",
"dnsLookupFamily": "V4_ONLY",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
@ -173,38 +119,6 @@
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "prod.cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "LOGICAL_DNS",
"connectTimeout": "5s",
"loadAssignment": {
"clusterName": "prod.cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"endpoints": [
{
"lbEndpoints": [
{
"endpoint": {
"address": {
"socketAddress": {
"address": "cache.mydomain",
"portValue": 8081
}
}
},
"healthStatus": "UNHEALTHY",
"loadBalancingWeight": 1
}
]
}
]
},
"dnsRefreshRate": "10s",
"dnsLookupFamily": "V4_ONLY",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",

View File

@ -1,60 +1,6 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "alt.api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "LOGICAL_DNS",
"connectTimeout": "5s",
"loadAssignment": {
"clusterName": "alt.api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"endpoints": [
{
"lbEndpoints": [
{
"endpoint": {
"address": {
"socketAddress": {
"address": "api.altdomain",
"portValue": 8081
}
}
},
"healthStatus": "HEALTHY",
"loadBalancingWeight": 1
}
]
}
]
},
"tlsContext": {
"commonTlsContext": {
"tlsParams": {
},
"tlsCertificates": [
{
"certificateChain": {
"filename": "api.cert.pem"
},
"privateKey": {
"filename": "api.key.pem"
}
}
],
"validationContext": {
"trustedCa": {
"filename": "ca.cert.pem"
}
}
}
},
"dnsRefreshRate": "10s",
"dnsLookupFamily": "V4_ONLY",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
@ -173,38 +119,6 @@
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "prod.cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "LOGICAL_DNS",
"connectTimeout": "5s",
"loadAssignment": {
"clusterName": "prod.cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"endpoints": [
{
"lbEndpoints": [
{
"endpoint": {
"address": {
"socketAddress": {
"address": "cache.mydomain",
"portValue": 8081
}
}
},
"healthStatus": "UNHEALTHY",
"loadBalancingWeight": 1
}
]
}
]
},
"dnsRefreshRate": "10s",
"dnsLookupFamily": "V4_ONLY",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",

View File

@ -1,60 +1,6 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "alt.api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "LOGICAL_DNS",
"connectTimeout": "5s",
"loadAssignment": {
"clusterName": "alt.api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"endpoints": [
{
"lbEndpoints": [
{
"endpoint": {
"address": {
"socketAddress": {
"address": "api.altdomain",
"portValue": 8081
}
}
},
"healthStatus": "HEALTHY",
"loadBalancingWeight": 1
}
]
}
]
},
"tlsContext": {
"commonTlsContext": {
"tlsParams": {
},
"tlsCertificates": [
{
"certificateChain": {
"filename": "api.cert.pem"
},
"privateKey": {
"filename": "api.key.pem"
}
}
],
"validationContext": {
"trustedCa": {
"filename": "ca.cert.pem"
}
}
}
},
"dnsRefreshRate": "10s",
"dnsLookupFamily": "V4_ONLY",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
@ -173,38 +119,6 @@
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "prod.cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "LOGICAL_DNS",
"connectTimeout": "5s",
"loadAssignment": {
"clusterName": "prod.cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"endpoints": [
{
"lbEndpoints": [
{
"endpoint": {
"address": {
"socketAddress": {
"address": "cache.mydomain",
"portValue": 8081
}
}
},
"healthStatus": "UNHEALTHY",
"loadBalancingWeight": 1
}
]
}
]
},
"dnsRefreshRate": "10s",
"dnsLookupFamily": "V4_ONLY",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",

View File

@ -119,62 +119,6 @@
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"tlsContext": {
"commonTlsContext": {
"tlsParams": {
},
"validationContext": {
"trustedCa": {
"filename": "ca.cert.pem"
}
}
}
},
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"tlsContext": {
"commonTlsContext": {
"tlsParams": {
},
"validationContext": {
"trustedCa": {
"filename": "ca.cert.pem"
}
}
}
},
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",

View File

@ -119,62 +119,6 @@
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"tlsContext": {
"commonTlsContext": {
"tlsParams": {
},
"validationContext": {
"trustedCa": {
"filename": "ca.cert.pem"
}
}
}
},
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"tlsContext": {
"commonTlsContext": {
"tlsParams": {
},
"validationContext": {
"trustedCa": {
"filename": "ca.cert.pem"
}
}
}
},
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",

View File

@ -119,62 +119,6 @@
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"tlsContext": {
"commonTlsContext": {
"tlsParams": {
},
"validationContext": {
"trustedCa": {
"filename": "ca.cert.pem"
}
}
}
},
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"tlsContext": {
"commonTlsContext": {
"tlsParams": {
},
"validationContext": {
"trustedCa": {
"filename": "ca.cert.pem"
}
}
}
},
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",

View File

@ -119,62 +119,6 @@
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"tlsContext": {
"commonTlsContext": {
"tlsParams": {
},
"validationContext": {
"trustedCa": {
"filename": "ca.cert.pem"
}
}
}
},
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"tlsContext": {
"commonTlsContext": {
"tlsParams": {
},
"validationContext": {
"trustedCa": {
"filename": "ca.cert.pem"
}
}
}
},
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",

View File

@ -119,94 +119,6 @@
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "prod.cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "LOGICAL_DNS",
"connectTimeout": "5s",
"loadAssignment": {
"clusterName": "prod.cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"endpoints": [
{
"lbEndpoints": [
{
"endpoint": {
"address": {
"socketAddress": {
"address": "cache.mydomain",
"portValue": 8081
}
}
},
"healthStatus": "UNHEALTHY",
"loadBalancingWeight": 1
}
]
}
]
},
"dnsRefreshRate": "10s",
"dnsLookupFamily": "V4_ONLY",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"tlsContext": {
"commonTlsContext": {
"tlsParams": {
},
"validationContext": {
"trustedCa": {
"filename": "ca.cert.pem"
}
}
}
},
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"tlsContext": {
"commonTlsContext": {
"tlsParams": {
},
"validationContext": {
"trustedCa": {
"filename": "ca.cert.pem"
}
}
}
},
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",

View File

@ -119,94 +119,6 @@
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "prod.cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "LOGICAL_DNS",
"connectTimeout": "5s",
"loadAssignment": {
"clusterName": "prod.cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"endpoints": [
{
"lbEndpoints": [
{
"endpoint": {
"address": {
"socketAddress": {
"address": "cache.mydomain",
"portValue": 8081
}
}
},
"healthStatus": "UNHEALTHY",
"loadBalancingWeight": 1
}
]
}
]
},
"dnsRefreshRate": "10s",
"dnsLookupFamily": "V4_ONLY",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"tlsContext": {
"commonTlsContext": {
"tlsParams": {
},
"validationContext": {
"trustedCa": {
"filename": "ca.cert.pem"
}
}
}
},
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"tlsContext": {
"commonTlsContext": {
"tlsParams": {
},
"validationContext": {
"trustedCa": {
"filename": "ca.cert.pem"
}
}
}
},
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",

View File

@ -119,94 +119,6 @@
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "prod.cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "LOGICAL_DNS",
"connectTimeout": "5s",
"loadAssignment": {
"clusterName": "prod.cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"endpoints": [
{
"lbEndpoints": [
{
"endpoint": {
"address": {
"socketAddress": {
"address": "cache.mydomain",
"portValue": 8081
}
}
},
"healthStatus": "UNHEALTHY",
"loadBalancingWeight": 1
}
]
}
]
},
"dnsRefreshRate": "10s",
"dnsLookupFamily": "V4_ONLY",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"tlsContext": {
"commonTlsContext": {
"tlsParams": {
},
"validationContext": {
"trustedCa": {
"filename": "ca.cert.pem"
}
}
}
},
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"tlsContext": {
"commonTlsContext": {
"tlsParams": {
},
"validationContext": {
"trustedCa": {
"filename": "ca.cert.pem"
}
}
}
},
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",

View File

@ -119,94 +119,6 @@
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "prod.cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "LOGICAL_DNS",
"connectTimeout": "5s",
"loadAssignment": {
"clusterName": "prod.cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"endpoints": [
{
"lbEndpoints": [
{
"endpoint": {
"address": {
"socketAddress": {
"address": "cache.mydomain",
"portValue": 8081
}
}
},
"healthStatus": "UNHEALTHY",
"loadBalancingWeight": 1
}
]
}
]
},
"dnsRefreshRate": "10s",
"dnsLookupFamily": "V4_ONLY",
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"tlsContext": {
"commonTlsContext": {
"tlsParams": {
},
"validationContext": {
"trustedCa": {
"filename": "ca.cert.pem"
}
}
}
},
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
}
}
},
"connectTimeout": "5s",
"tlsContext": {
"commonTlsContext": {
"tlsParams": {
},
"validationContext": {
"trustedCa": {
"filename": "ca.cert.pem"
}
}
}
},
"outlierDetection": {
}
},
{
"@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",

View File

@ -3,10 +3,10 @@
"resources": [
{
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration",
"name": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"virtualHosts": [
{
"name": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"domains": [
"*"
],
@ -16,26 +16,7 @@
"prefix": "/"
},
"route": {
"cluster": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"hashPolicy": [
{
"cookie": {
"name": "chocolate-chip"
},
"terminal": true
},
{
"header": {
"headerName": "x-user-id"
}
},
{
"connectionProperties": {
"sourceIp": true
},
"terminal": true
}
]
"cluster": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
]
@ -45,10 +26,10 @@
},
{
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration",
"name": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"virtualHosts": [
{
"name": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"domains": [
"*"
],
@ -58,26 +39,7 @@
"prefix": "/"
},
"route": {
"cluster": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"hashPolicy": [
{
"cookie": {
"name": "chocolate-chip"
},
"terminal": true
},
{
"header": {
"headerName": "x-user-id"
}
},
{
"connectionProperties": {
"sourceIp": true
},
"terminal": true
}
]
"cluster": "cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
]
@ -87,10 +49,10 @@
},
{
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"virtualHosts": [
{
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"domains": [
"*"
],
@ -100,26 +62,7 @@
"prefix": "/"
},
"route": {
"cluster": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"hashPolicy": [
{
"cookie": {
"name": "chocolate-chip"
},
"terminal": true
},
{
"header": {
"headerName": "x-user-id"
}
},
{
"connectionProperties": {
"sourceIp": true
},
"terminal": true
}
]
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
]

View File

@ -3,10 +3,10 @@
"resources": [
{
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration",
"name": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"virtualHosts": [
{
"name": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"domains": [
"*"
],
@ -16,26 +16,7 @@
"prefix": "/"
},
"route": {
"cluster": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"hashPolicy": [
{
"cookie": {
"name": "chocolate-chip"
},
"terminal": true
},
{
"header": {
"headerName": "x-user-id"
}
},
{
"connectionProperties": {
"sourceIp": true
},
"terminal": true
}
]
"cluster": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
]
@ -45,10 +26,10 @@
},
{
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration",
"name": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"virtualHosts": [
{
"name": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"domains": [
"*"
],
@ -58,26 +39,7 @@
"prefix": "/"
},
"route": {
"cluster": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"hashPolicy": [
{
"cookie": {
"name": "chocolate-chip"
},
"terminal": true
},
{
"header": {
"headerName": "x-user-id"
}
},
{
"connectionProperties": {
"sourceIp": true
},
"terminal": true
}
]
"cluster": "cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
]
@ -87,10 +49,10 @@
},
{
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"virtualHosts": [
{
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"domains": [
"*"
],
@ -100,26 +62,7 @@
"prefix": "/"
},
"route": {
"cluster": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"hashPolicy": [
{
"cookie": {
"name": "chocolate-chip"
},
"terminal": true
},
{
"header": {
"headerName": "x-user-id"
}
},
{
"connectionProperties": {
"sourceIp": true
},
"terminal": true
}
]
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
]

View File

@ -3,10 +3,10 @@
"resources": [
{
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration",
"name": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"virtualHosts": [
{
"name": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"domains": [
"*"
],
@ -16,26 +16,7 @@
"prefix": "/"
},
"route": {
"cluster": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"hashPolicy": [
{
"cookie": {
"name": "chocolate-chip"
},
"terminal": true
},
{
"header": {
"headerName": "x-user-id"
}
},
{
"connectionProperties": {
"sourceIp": true
},
"terminal": true
}
]
"cluster": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
]
@ -45,10 +26,10 @@
},
{
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration",
"name": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"virtualHosts": [
{
"name": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"domains": [
"*"
],
@ -58,26 +39,7 @@
"prefix": "/"
},
"route": {
"cluster": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"hashPolicy": [
{
"cookie": {
"name": "chocolate-chip"
},
"terminal": true
},
{
"header": {
"headerName": "x-user-id"
}
},
{
"connectionProperties": {
"sourceIp": true
},
"terminal": true
}
]
"cluster": "cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
]
@ -87,10 +49,10 @@
},
{
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"virtualHosts": [
{
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"domains": [
"*"
],
@ -100,26 +62,7 @@
"prefix": "/"
},
"route": {
"cluster": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"hashPolicy": [
{
"cookie": {
"name": "chocolate-chip"
},
"terminal": true
},
{
"header": {
"headerName": "x-user-id"
}
},
{
"connectionProperties": {
"sourceIp": true
},
"terminal": true
}
]
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
]

View File

@ -3,10 +3,10 @@
"resources": [
{
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration",
"name": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"virtualHosts": [
{
"name": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"domains": [
"*"
],
@ -16,26 +16,7 @@
"prefix": "/"
},
"route": {
"cluster": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"hashPolicy": [
{
"cookie": {
"name": "chocolate-chip"
},
"terminal": true
},
{
"header": {
"headerName": "x-user-id"
}
},
{
"connectionProperties": {
"sourceIp": true
},
"terminal": true
}
]
"cluster": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
]
@ -45,10 +26,10 @@
},
{
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration",
"name": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"virtualHosts": [
{
"name": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"domains": [
"*"
],
@ -58,26 +39,7 @@
"prefix": "/"
},
"route": {
"cluster": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"hashPolicy": [
{
"cookie": {
"name": "chocolate-chip"
},
"terminal": true
},
{
"header": {
"headerName": "x-user-id"
}
},
{
"connectionProperties": {
"sourceIp": true
},
"terminal": true
}
]
"cluster": "cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
]
@ -87,10 +49,10 @@
},
{
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"virtualHosts": [
{
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"domains": [
"*"
],
@ -100,26 +62,7 @@
"prefix": "/"
},
"route": {
"cluster": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"hashPolicy": [
{
"cookie": {
"name": "chocolate-chip"
},
"terminal": true
},
{
"header": {
"headerName": "x-user-id"
}
},
{
"connectionProperties": {
"sourceIp": true
},
"terminal": true
}
]
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
]

View File

@ -140,7 +140,7 @@ type ServiceResolverConfigEntry struct {
// LoadBalancer determines the load balancing policy and configuration for services
// issuing requests to this upstream service.
LoadBalancer LoadBalancer `json:",omitempty" alias:"load_balancer"`
LoadBalancer *LoadBalancer `json:",omitempty" alias:"load_balancer"`
CreateIndex uint64
ModifyIndex uint64
@ -209,14 +209,22 @@ type ServiceResolverFailover struct {
// LoadBalancer determines the load balancing policy and configuration for services
// issuing requests to this upstream service.
type LoadBalancer struct {
// EnvoyLBConfig contains Envoy-specific load balancing configuration for this upstream
EnvoyLBConfig *EnvoyLBConfig `json:",omitempty" alias:"envoy_lb_config"`
// OpaqueConfig contains load balancing configuration opaque to Consul for 3rd party proxies
OpaqueConfig string `json:",omitempty" alias:"opaque_config"`
}
type EnvoyLBConfig struct {
// Policy is the load balancing policy used to select a host
Policy string `json:",omitempty"`
// RingHashConfig contains configuration for the "ring_hash" policy type
RingHashConfig RingHashConfig `json:",omitempty" alias:"ring_hash_config"`
RingHashConfig *RingHashConfig `json:",omitempty" alias:"ring_hash_config"`
// LeastRequestConfig contains configuration for the "least_request" policy type
LeastRequestConfig LeastRequestConfig `json:",omitempty" alias:"least_request_config"`
LeastRequestConfig *LeastRequestConfig `json:",omitempty" alias:"least_request_config"`
// HashPolicies is a list of hash policies to use for hashing load balancing algorithms.
// Hash policies are evaluated individually and combined such that identical lists
@ -241,28 +249,37 @@ type LeastRequestConfig struct {
ChoiceCount uint32 `json:",omitempty" alias:"choice_count"`
}
// HashPolicy is a list of hash policies to use for hashing load balancing algorithms.
// Hash policies are evaluated individually and combined such that identical lists
// result in the same hash.
// If no hash policies are present, or none are successfully evaluated,
// then a random backend host will be selected.
// HashPolicy defines which attributes will be hashed by hash-based LB algorithms
type HashPolicy struct {
// Field is the attribute type to hash on.
// Must be one of "header","cookie", or "query_parameter".
// Cannot be specified along with SourceIP.
Field string `json:",omitempty"`
// FieldMatchValue is the value to hash.
// FieldValue is the value to hash.
// ie. header name, cookie name, URL query parameter name
// Cannot be specified along with SourceIP.
FieldMatchValue string `json:",omitempty" alias:"field_value"`
FieldValue string `json:",omitempty" alias:"field_value"`
// SourceAddress determines whether the hash should be of the source IP rather than of a field and field value.
// Cannot be specified along with Field and FieldMatchValue.
SourceAddress bool `json:",omitempty" alias:"source_address"`
// CookieConfig contains configuration for the "cookie" hash policy type.
CookieConfig *CookieConfig `json:",omitempty" alias:"cookie_config"`
// SourceIP determines whether the hash should be of the source IP rather than of a field and field value.
// Cannot be specified along with Field or FieldValue.
SourceIP bool `json:",omitempty" alias:"source_ip"`
// Terminal will short circuit the computation of the hash when multiple hash policies are present.
// If a hash is computed when a Terminal policy is evaluated,
// then that hash will be used and subsequent hash policies will be ignored.
Terminal bool `json:",omitempty"`
}
// CookieConfig contains configuration for the "cookie" hash policy type.
// This is specified to have Envoy generate a cookie for a client on its first request.
type CookieConfig struct {
// TTL for generated cookies
TTL time.Duration `json:",omitempty"`
// The path to set for the cookie
Path string `json:",omitempty"`
}

View File

@ -293,9 +293,11 @@ func TestAPI_ConfigEntry_ServiceResolver_LoadBalancer(t *testing.T) {
Kind: ServiceResolver,
Name: "test-least-req",
Namespace: defaultNamespace,
LoadBalancer: LoadBalancer{
Policy: "least_request",
LeastRequestConfig: LeastRequestConfig{ChoiceCount: 10},
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{
Policy: "least_request",
LeastRequestConfig: &LeastRequestConfig{ChoiceCount: 10},
},
},
},
verify: verifyResolver,
@ -306,20 +308,30 @@ func TestAPI_ConfigEntry_ServiceResolver_LoadBalancer(t *testing.T) {
Kind: ServiceResolver,
Name: "test-ring-hash",
Namespace: defaultNamespace,
LoadBalancer: LoadBalancer{
Policy: "ring_hash",
RingHashConfig: RingHashConfig{
MinimumRingSize: 1024 * 2,
MaximumRingSize: 1024 * 4,
},
HashPolicies: []HashPolicy{
{
Field: "header",
FieldMatchValue: "my-session-header",
Terminal: true,
LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{
Policy: "ring_hash",
RingHashConfig: &RingHashConfig{
MinimumRingSize: 1024 * 2,
MaximumRingSize: 1024 * 4,
},
{
SourceAddress: true,
HashPolicies: []HashPolicy{
{
Field: "header",
FieldValue: "my-session-header",
Terminal: true,
},
{
Field: "cookie",
FieldValue: "oreo",
CookieConfig: &CookieConfig{
Path: "/tray",
TTL: 20 * time.Millisecond,
},
},
{
SourceIP: true,
},
},
},
},

View File

@ -1,6 +1,7 @@
package write
import (
"github.com/hashicorp/consul/agent/structs"
"io"
"strings"
"testing"
@ -1165,6 +1166,235 @@ func TestParseConfigEntry(t *testing.T) {
Name: "main",
},
},
{
name: "service-resolver: envoy hash lb kitchen sink",
snake: `
kind = "service-resolver"
name = "main"
load_balancer = {
envoy_lb_config = {
policy = "ring_hash"
ring_hash_config = {
minimum_ring_size = 1
maximum_ring_size = 2
}
hash_policies = [
{
field = "cookie"
field_value = "good-cookie"
cookie_config = {
ttl = "1s"
path = "/oven"
}
terminal = true
},
{
field = "header"
field_value = "x-user-id"
},
{
source_ip = true
}
]
}
}
`,
camel: `
Kind = "service-resolver"
Name = "main"
LoadBalancer = {
EnvoyLBConfig = {
Policy = "ring_hash"
RingHashConfig = {
MinimumRingSize = 1
MaximumRingSize = 2
}
HashPolicies = [
{
Field = "cookie"
FieldValue = "good-cookie"
CookieConfig = {
TTL = "1s"
Path = "/oven"
}
Terminal = true
},
{
Field = "header"
FieldValue = "x-user-id"
},
{
SourceIP = true
}
]
}
}
`,
snakeJSON: `
{
"kind": "service-resolver",
"name": "main",
"load_balancer": {
"envoy_lb_config": {
"policy": "ring_hash",
"ring_hash_config": {
"minimum_ring_size": 1,
"maximum_ring_size": 2
},
"hash_policies": [
{
"field": "cookie",
"field_value": "good-cookie",
"cookie_config": {
"ttl": "1s",
"path": "/oven"
},
"terminal": true
},
{
"field": "header",
"field_value": "x-user-id"
},
{
"source_ip": true
}
]
}
}
}
`,
camelJSON: `
{
"Kind": "service-resolver",
"Name": "main",
"LoadBalancer": {
"EnvoyLBConfig": {
"Policy": "ring_hash",
"RingHashConfig": {
"MinimumRingSize": 1,
"MaximumRingSize": 2
},
"HashPolicies": [
{
"Field": "cookie",
"FieldValue": "good-cookie",
"CookieConfig": {
"TTL": "1s",
"Path": "/oven"
},
"Terminal": true
},
{
"Field": "header",
"FieldValue": "x-user-id"
},
{
"SourceIP": true
}
]
}
}
}
`,
expect: &api.ServiceResolverConfigEntry{
Kind: "service-resolver",
Name: "main",
LoadBalancer: &api.LoadBalancer{
EnvoyLBConfig: &api.EnvoyLBConfig{
Policy: structs.LBPolicyRingHash,
RingHashConfig: &api.RingHashConfig{
MinimumRingSize: 1,
MaximumRingSize: 2,
},
HashPolicies: []api.HashPolicy{
{
Field: structs.HashPolicyCookie,
FieldValue: "good-cookie",
CookieConfig: &api.CookieConfig{
TTL: 1 * time.Second,
Path: "/oven",
},
Terminal: true,
},
{
Field: structs.HashPolicyHeader,
FieldValue: "x-user-id",
},
{
SourceIP: true,
},
},
},
},
},
},
{
name: "service-resolver: envoy least request kitchen sink",
snake: `
kind = "service-resolver"
name = "main"
load_balancer = {
envoy_lb_config = {
policy = "least_request"
least_request_config = {
choice_count = 2
}
}
}
`,
camel: `
Kind = "service-resolver"
Name = "main"
LoadBalancer = {
EnvoyLBConfig = {
Policy = "least_request"
LeastRequestConfig = {
ChoiceCount = 2
}
}
}
`,
snakeJSON: `
{
"kind": "service-resolver",
"name": "main",
"load_balancer": {
"envoy_lb_config": {
"policy": "least_request",
"least_request_config": {
"choice_count": 2
}
}
}
}
`,
camelJSON: `
{
"Kind": "service-resolver",
"Name": "main",
"LoadBalancer": {
"EnvoyLBConfig": {
"Policy": "least_request",
"LeastRequestConfig": {
"ChoiceCount": 2
}
}
}
}
`,
expect: &api.ServiceResolverConfigEntry{
Kind: "service-resolver",
Name: "main",
LoadBalancer: &api.LoadBalancer{
EnvoyLBConfig: &api.EnvoyLBConfig{
Policy: structs.LBPolicyLeastRequest,
LeastRequestConfig: &api.LeastRequestConfig{
ChoiceCount: 2,
},
},
},
},
},
{
name: "expose paths: kitchen sink proxy defaults",
snake: `

View File

@ -83,7 +83,7 @@ LoadBalancer = {
HashPolicies = [
{
Field = "header"
FieldMatchValue = "x-user-id"
FieldValue = "x-user-id"
}
]
}
@ -171,48 +171,52 @@ LoadBalancer = {
configuration for services issuing requests to this upstream.
This option is available in Consul versions 1.8.4 and newer.
**Note:** The options below are specific to Envoy proxy.
- `Policy` `(string: "")` - The load balancing policy used to select a host.
One of: `random`, `round_robin`, `least_request`, `ring_hash`, `maglev`.
- `RingHashConfig` `(RingHashConfig)` - Configuration for the `ring_hash`
policy type.
- `MinimumRingRize` `(int: 1024)` - Determines the minimum number of entries
in the hash ring.
- `MaximumRingRize` `(int: 8192)` - Determines the maximum number of entries
in the hash ring.
- `LeastRequestConfig` `(LeastRequestConfig)` - Configuration for the `least_request`
policy type.
- `ChoiceCount` `(int: 2)` - Determines the number of random healthy hosts
from which to select the one with the least requests.
- `HashPolicies` `(array<HashPolicies>)` - a list of hash policies to use for
hashing load balancing algorithms. Hash policies are evaluated individually
and combined such that identical lists result in the same hash.
If no hash policies are present, or none are successfully evaluated,
then a random backend host will be selected.
- `Field` `(string: "")` - The attribute type to hash on.
Must be one of `header`,`cookie`, or `query_parameter`.
Cannot be specified along with `SourceAddress`.
- `EnvoyLBConfig` `(EnvoyLBConfig)` - Envoy proxy specific load balancing configuration
for this upstream.
- `Policy` `(string: "")` - The load balancing policy used to select a host.
One of: `random`, `round_robin`, `least_request`, `ring_hash`, `maglev`.
- `RingHashConfig` `(RingHashConfig)` - Configuration for the `ring_hash`
policy type.
- `FieldMatchValue` `(string: "")` - The value to hash.
ie. header name, cookie name, URL query parameter name.
Cannot be specified along with `SourceAddress`.
- `MinimumRingRize` `(int: 1024)` - Determines the minimum number of entries
in the hash ring.
- `SourceAddress` `(bool: false)` - Determines whether the hash should be of the source IP
address rather than of a field and field value.
Cannot be specified along with `Field` or `FieldMatchValue`.
- `MaximumRingRize` `(int: 8192)` - Determines the maximum number of entries
in the hash ring.
- `LeastRequestConfig` `(LeastRequestConfig)` - Configuration for the `least_request`
policy type.
- `Terminal` `(bool: false)` - Will short circuit the computation of the hash
when multiple hash policies are present. If a hash is computed when a
Terminal policy is evaluated, then that hash will be used and subsequent
hash policies will be ignored.
- `ChoiceCount` `(int: 2)` - Determines the number of random healthy hosts
from which to select the one with the least requests.
- `HashPolicies` `(array<HashPolicies>)` - a list of hash policies to use for
hashing load balancing algorithms. Hash policies are evaluated individually
and combined such that identical lists result in the same hash.
If no hash policies are present, or none are successfully evaluated,
then a random backend host will be selected.
- `Field` `(string: "")` - The attribute type to hash on.
Must be one of `header`,`cookie`, or `query_parameter`.
Cannot be specified along with `SourceAddress`.
- `FieldValue` `(string: "")` - The value to hash.
ie. header name, cookie name, URL query parameter name.
Cannot be specified along with `SourceAddress`.
- `CookieConfig` `(CookieConfig)` - Additional configuration for the "cookie" hash policy type.
This is specified to have Envoy generate a cookie for a client on its first request.
- `SourceIP` `(bool: false)` - Determines whether the hash should be of the source IP
address rather than of a field and field value.
Cannot be specified along with `Field` or `FieldValue`.
- `Terminal` `(bool: false)` - Will short circuit the computation of the hash
when multiple hash policies are present. If a hash is computed when a
Terminal policy is evaluated, then that hash will be used and subsequent
hash policies will be ignored.
## Service Subsets

View File

@ -184,6 +184,12 @@ A single node in the compiled discovery chain.
- `Targets` `(array<string>)` - List of targets found in
[`Targets`](#targets) to failover to in order of preference.
- `LoadBalancer` `(LoadBalancer: <optional>`) - Copy of the underlying `service-resolver`
[`LoadBalancer`](/docs/agent/config-entries/service-resolver#loadbalancer) field.
If a `service-splitter` splits between services with differing `LoadBalancer` configuration
the first hash-based load balancing policy is copied.
#### `DiscoveryTarget`