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. // 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. // 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. // Therefore, we attach the first hash-based load balancer config we encounter.
if !hasLB && node.LoadBalancer.IsHashBased() { if !hasLB {
splitNode.LoadBalancer = node.LoadBalancer if lb := node.LoadBalancer; lb != nil && lb.EnvoyLBConfig != nil && lb.EnvoyLBConfig.IsHashBased() {
hasLB = true splitNode.LoadBalancer = node.LoadBalancer
hasLB = true
}
} }
} }

View File

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

View File

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

View File

@ -3,6 +3,7 @@ package structs
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/golang/protobuf/ptypes"
"math" "math"
"regexp" "regexp"
"sort" "sort"
@ -10,12 +11,29 @@ import (
"strings" "strings"
"time" "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/acl"
"github.com/hashicorp/consul/agent/cache" "github.com/hashicorp/consul/agent/cache"
"github.com/hashicorp/consul/lib" "github.com/hashicorp/consul/lib"
"github.com/mitchellh/hashstructure" "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 // ServiceRouterConfigEntry defines L7 (e.g. http) routing rules for a named
// service exposed in Connect. // service exposed in Connect.
// //
@ -641,7 +659,7 @@ type ServiceResolverConfigEntry struct {
// LoadBalancer determines the load balancing policy and configuration for services // LoadBalancer determines the load balancing policy and configuration for services
// issuing requests to this upstream service. // 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"` EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
RaftIndex RaftIndex
@ -811,53 +829,60 @@ func (e *ServiceResolverConfigEntry) Validate() error {
return fmt.Errorf("Bad ConnectTimeout '%s', must be >= 0", e.ConnectTimeout) return fmt.Errorf("Bad ConnectTimeout '%s', must be >= 0", e.ConnectTimeout)
} }
validPolicies := map[string]bool{ if e.LoadBalancer != nil && e.LoadBalancer.EnvoyLBConfig != nil {
"": true, ec := e.LoadBalancer.EnvoyLBConfig
"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.Policy != "ring_hash" && e.LoadBalancer.RingHashConfig != (RingHashConfig{}) { validPolicies := map[string]bool{
return fmt.Errorf("Bad LoadBalancer configuration. "+ "": true,
"RingHashConfig specified for incompatible load balancing policy %q", e.LoadBalancer.Policy) LBPolicyRandom: true,
} LBPolicyRoundRobin: true,
if e.LoadBalancer.Policy != "least_request" && e.LoadBalancer.LeastRequestConfig != (LeastRequestConfig{}) { LBPolicyLeastRequest: true,
return fmt.Errorf("Bad LoadBalancer configuration. "+ LBPolicyRingHash: true,
"LeastRequestConfig specified for incompatible load balancing policy %q", e.LoadBalancer.Policy) LBPolicyMaglev: true,
} }
if !e.LoadBalancer.IsHashBased() && len(e.LoadBalancer.HashPolicies) > 0 { if ok := validPolicies[ec.Policy]; !ok {
return fmt.Errorf("Bad LoadBalancer configuration: "+ return fmt.Errorf("Bad LoadBalancer policy: %q is not supported", ec.Policy)
"HashPolicies specified for non-hash-based Policy: %q", e.LoadBalancer.Policy) }
}
validFields := map[string]bool{ if ec.Policy != LBPolicyRingHash && ec.RingHashConfig != nil {
"header": true, return fmt.Errorf("Bad LoadBalancer configuration. "+
"cookie": true, "RingHashConfig specified for incompatible load balancing policy %q", ec.Policy)
"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 hp.SourceAddress && hp.Field != "" { if ec.Policy != LBPolicyLeastRequest && ec.LeastRequestConfig != nil {
return fmt.Errorf("Bad LoadBalancer HashPolicy[%d]: "+ return fmt.Errorf("Bad LoadBalancer configuration. "+
"A single hash policy cannot hash both a source address and a %q", i, hp.Field) "LeastRequestConfig specified for incompatible load balancing policy %q", ec.Policy)
} }
if hp.SourceAddress && hp.FieldMatchValue != "" { if !ec.IsHashBased() && len(ec.HashPolicies) > 0 {
return fmt.Errorf("Bad LoadBalancer HashPolicy[%d]: "+ return fmt.Errorf("Bad LoadBalancer configuration: "+
"A FieldMatchValue cannot be specified when hashing SourceAddress", i) "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 == "" { for i, hp := range ec.HashPolicies {
return fmt.Errorf("Bad LoadBalancer HashPolicy[%d]: FieldMatchValue requires a Field to apply to", i) 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 // LoadBalancer determines the load balancing policy and configuration for services
// issuing requests to this upstream service. // issuing requests to this upstream service.
type LoadBalancer struct { 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 is the load balancing policy used to select a host
Policy string `json:",omitempty"` Policy string `json:",omitempty"`
// RingHashConfig contains configuration for the "ring_hash" policy type // 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 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. // 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 // 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"` 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 // RingHashConfig contains configuration for the "ring_hash" policy type
type RingHashConfig struct { type RingHashConfig struct {
// MinimumRingSize determines the minimum number of entries in the hash ring // 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"` ChoiceCount uint32 `json:",omitempty" alias:"choice_count"`
} }
// HashPolicy is a list of hash policies to use for hashing load balancing algorithms. // HashPolicy defines which attributes will be hashed by hash-based LB 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.
type HashPolicy struct { type HashPolicy struct {
// Field is the attribute type to hash on. // Field is the attribute type to hash on.
// Must be one of "header","cookie", or "query_parameter". // Must be one of "header","cookie", or "query_parameter".
// Cannot be specified along with SourceIP. // Cannot be specified along with SourceIP.
Field string `json:",omitempty"` 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 // ie. header name, cookie name, URL query parameter name
// Cannot be specified along with SourceIP. // 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. // CookieConfig contains configuration for the "cookie" hash policy type.
// Cannot be specified along with Field or FieldMatchValue. CookieConfig *CookieConfig `json:",omitempty" alias:"cookie_config"`
SourceAddress bool `json:",omitempty" alias:"source_address"`
// 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. // 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, // If a hash is computed when a Terminal policy is evaluated,
@ -1067,6 +1090,134 @@ type HashPolicy struct {
Terminal bool `json:",omitempty"` 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 { type discoveryChainConfigEntry interface {
ConfigEntry ConfigEntry
// ListRelatedServices returns a list of other names of services referenced // ListRelatedServices returns a list of other names of services referenced

View File

@ -3,10 +3,14 @@ package structs
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"github.com/golang/protobuf/ptypes"
"strings" "strings"
"testing" "testing"
"time" "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/acl"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -552,25 +556,31 @@ func TestServiceResolverConfigEntry_LoadBalancer(t *testing.T) {
{ {
name: "empty policy is valid", name: "empty policy is valid",
entry: &ServiceResolverConfigEntry{ entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver, Kind: ServiceResolver,
Name: "test", Name: "test",
LoadBalancer: LoadBalancer{Policy: ""}, LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{Policy: ""},
},
}, },
}, },
{ {
name: "supported policy", name: "supported policy",
entry: &ServiceResolverConfigEntry{ entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver, Kind: ServiceResolver,
Name: "test", Name: "test",
LoadBalancer: LoadBalancer{Policy: "random"}, LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{Policy: LBPolicyRandom},
},
}, },
}, },
{ {
name: "unsupported policy", name: "unsupported policy",
entry: &ServiceResolverConfigEntry{ entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver, Kind: ServiceResolver,
Name: "test", Name: "test",
LoadBalancer: LoadBalancer{Policy: "fake-policy"}, LoadBalancer: &LoadBalancer{
EnvoyLBConfig: &EnvoyLBConfig{Policy: "fake-policy"},
},
}, },
validateErr: `"fake-policy" is not supported`, validateErr: `"fake-policy" is not supported`,
}, },
@ -579,9 +589,11 @@ func TestServiceResolverConfigEntry_LoadBalancer(t *testing.T) {
entry: &ServiceResolverConfigEntry{ entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver, Kind: ServiceResolver,
Name: "test", Name: "test",
LoadBalancer: LoadBalancer{ LoadBalancer: &LoadBalancer{
Policy: "ring_hash", EnvoyLBConfig: &EnvoyLBConfig{
LeastRequestConfig: LeastRequestConfig{ChoiceCount: 2}, Policy: LBPolicyRingHash,
LeastRequestConfig: &LeastRequestConfig{ChoiceCount: 10},
},
}, },
}, },
validateErr: `LeastRequestConfig specified for incompatible load balancing policy`, validateErr: `LeastRequestConfig specified for incompatible load balancing policy`,
@ -591,9 +603,11 @@ func TestServiceResolverConfigEntry_LoadBalancer(t *testing.T) {
entry: &ServiceResolverConfigEntry{ entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver, Kind: ServiceResolver,
Name: "test", Name: "test",
LoadBalancer: LoadBalancer{ LoadBalancer: &LoadBalancer{
Policy: "least_request", EnvoyLBConfig: &EnvoyLBConfig{
RingHashConfig: RingHashConfig{MinimumRingSize: 1024}, Policy: LBPolicyLeastRequest,
RingHashConfig: &RingHashConfig{MinimumRingSize: 1024},
},
}, },
}, },
validateErr: `RingHashConfig specified for incompatible load balancing policy`, validateErr: `RingHashConfig specified for incompatible load balancing policy`,
@ -603,9 +617,11 @@ func TestServiceResolverConfigEntry_LoadBalancer(t *testing.T) {
entry: &ServiceResolverConfigEntry{ entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver, Kind: ServiceResolver,
Name: "test", Name: "test",
LoadBalancer: LoadBalancer{ LoadBalancer: &LoadBalancer{
Policy: "ring_hash", EnvoyLBConfig: &EnvoyLBConfig{
RingHashConfig: RingHashConfig{MinimumRingSize: 1024}, Policy: LBPolicyRingHash,
RingHashConfig: &RingHashConfig{MinimumRingSize: 1024},
},
}, },
}, },
}, },
@ -614,9 +630,11 @@ func TestServiceResolverConfigEntry_LoadBalancer(t *testing.T) {
entry: &ServiceResolverConfigEntry{ entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver, Kind: ServiceResolver,
Name: "test", Name: "test",
LoadBalancer: LoadBalancer{ LoadBalancer: &LoadBalancer{
Policy: "least_request", EnvoyLBConfig: &EnvoyLBConfig{
LeastRequestConfig: LeastRequestConfig{ChoiceCount: 2}, Policy: LBPolicyLeastRequest,
LeastRequestConfig: &LeastRequestConfig{ChoiceCount: 2},
},
}, },
}, },
}, },
@ -625,12 +643,12 @@ func TestServiceResolverConfigEntry_LoadBalancer(t *testing.T) {
entry: &ServiceResolverConfigEntry{ entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver, Kind: ServiceResolver,
Name: "test", Name: "test",
LoadBalancer: LoadBalancer{ LoadBalancer: &LoadBalancer{
Policy: "", EnvoyLBConfig: &EnvoyLBConfig{Policy: ""},
}, },
}, },
check: func(t *testing.T, entry *ServiceResolverConfigEntry) { 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{ entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver, Kind: ServiceResolver,
Name: "test", Name: "test",
LoadBalancer: LoadBalancer{ LoadBalancer: &LoadBalancer{
Policy: "", EnvoyLBConfig: &EnvoyLBConfig{
HashPolicies: []HashPolicy{ Policy: "",
{ HashPolicies: []HashPolicy{
SourceAddress: true, {
SourceIP: true,
},
}, },
}, },
}, },
}, },
validateErr: `HashPolicies specified for non-hash-based Policy`, 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", name: "supported match field",
entry: &ServiceResolverConfigEntry{ entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver, Kind: ServiceResolver,
Name: "test", Name: "test",
LoadBalancer: LoadBalancer{ LoadBalancer: &LoadBalancer{
Policy: "maglev", EnvoyLBConfig: &EnvoyLBConfig{
HashPolicies: []HashPolicy{ Policy: LBPolicyMaglev,
{ HashPolicies: []HashPolicy{
Field: "header", {
FieldMatchValue: "X-Consul-Token", Field: "header",
FieldValue: "X-Consul-Token",
},
}, },
}, },
}, },
@ -670,28 +737,32 @@ func TestServiceResolverConfigEntry_LoadBalancer(t *testing.T) {
entry: &ServiceResolverConfigEntry{ entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver, Kind: ServiceResolver,
Name: "test", Name: "test",
LoadBalancer: LoadBalancer{ LoadBalancer: &LoadBalancer{
Policy: "maglev", EnvoyLBConfig: &EnvoyLBConfig{
HashPolicies: []HashPolicy{ Policy: LBPolicyMaglev,
{ HashPolicies: []HashPolicy{
Field: "not-header", {
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", name: "cannot match on source address and custom field",
entry: &ServiceResolverConfigEntry{ entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver, Kind: ServiceResolver,
Name: "test", Name: "test",
LoadBalancer: LoadBalancer{ LoadBalancer: &LoadBalancer{
Policy: "maglev", EnvoyLBConfig: &EnvoyLBConfig{
HashPolicies: []HashPolicy{ Policy: LBPolicyMaglev,
{ HashPolicies: []HashPolicy{
Field: "header", {
SourceAddress: true, Field: "header",
SourceIP: true,
},
}, },
}, },
}, },
@ -703,67 +774,75 @@ func TestServiceResolverConfigEntry_LoadBalancer(t *testing.T) {
entry: &ServiceResolverConfigEntry{ entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver, Kind: ServiceResolver,
Name: "test", Name: "test",
LoadBalancer: LoadBalancer{ LoadBalancer: &LoadBalancer{
Policy: "maglev", EnvoyLBConfig: &EnvoyLBConfig{
HashPolicies: []HashPolicy{ Policy: LBPolicyMaglev,
{ HashPolicies: []HashPolicy{
FieldMatchValue: "X-Consul-Token", {
SourceAddress: true, 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", name: "field without match value",
entry: &ServiceResolverConfigEntry{ entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver, Kind: ServiceResolver,
Name: "test", Name: "test",
LoadBalancer: LoadBalancer{ LoadBalancer: &LoadBalancer{
Policy: "maglev", EnvoyLBConfig: &EnvoyLBConfig{
HashPolicies: []HashPolicy{ Policy: LBPolicyMaglev,
{ HashPolicies: []HashPolicy{
Field: "header", {
Field: "header",
},
}, },
}, },
}, },
}, },
validateErr: `Field "header" was specified without a FieldMatchValue`, validateErr: `Field "header" was specified without a FieldValue`,
}, },
{ {
name: "field without match value", name: "field without match value",
entry: &ServiceResolverConfigEntry{ entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver, Kind: ServiceResolver,
Name: "test", Name: "test",
LoadBalancer: LoadBalancer{ LoadBalancer: &LoadBalancer{
Policy: "maglev", EnvoyLBConfig: &EnvoyLBConfig{
HashPolicies: []HashPolicy{ Policy: LBPolicyMaglev,
{ HashPolicies: []HashPolicy{
FieldMatchValue: "my-cookie", {
FieldValue: "my-cookie",
},
}, },
}, },
}, },
}, },
validateErr: `FieldMatchValue requires a Field to apply to`, validateErr: `FieldValue requires a Field to apply to`,
}, },
{ {
name: "ring hash kitchen sink", name: "ring hash kitchen sink",
entry: &ServiceResolverConfigEntry{ entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver, Kind: ServiceResolver,
Name: "test", Name: "test",
LoadBalancer: LoadBalancer{ LoadBalancer: &LoadBalancer{
Policy: "ring_hash", EnvoyLBConfig: &EnvoyLBConfig{
RingHashConfig: RingHashConfig{MaximumRingSize: 10, MinimumRingSize: 2}, Policy: LBPolicyRingHash,
HashPolicies: []HashPolicy{ RingHashConfig: &RingHashConfig{MaximumRingSize: 10, MinimumRingSize: 2},
{ HashPolicies: []HashPolicy{
Field: "cookie", {
FieldMatchValue: "my-cookie", Field: "cookie",
}, FieldValue: "my-cookie",
{ },
Field: "header", {
FieldMatchValue: "alt-header", Field: "header",
Terminal: true, FieldValue: "alt-header",
Terminal: true,
},
}, },
}, },
}, },
@ -774,9 +853,11 @@ func TestServiceResolverConfigEntry_LoadBalancer(t *testing.T) {
entry: &ServiceResolverConfigEntry{ entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver, Kind: ServiceResolver,
Name: "test", Name: "test",
LoadBalancer: LoadBalancer{ LoadBalancer: &LoadBalancer{
Policy: "least_request", EnvoyLBConfig: &EnvoyLBConfig{
LeastRequestConfig: LeastRequestConfig{ChoiceCount: 20}, Policy: LBPolicyLeastRequest,
LeastRequestConfig: &LeastRequestConfig{ChoiceCount: 20},
},
}, },
}, },
}, },
@ -1444,3 +1525,267 @@ func TestIsProtocolHTTPLike(t *testing.T) {
assert.True(t, IsProtocolHTTPLike("http2")) assert.True(t, IsProtocolHTTPLike("http2"))
assert.True(t, IsProtocolHTTPLike("grpc")) 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"` Resolver *DiscoveryResolver `json:",omitempty"`
// shared by Type==resolver || Type==splitter // shared by Type==resolver || Type==splitter
LoadBalancer LoadBalancer `json:",omitempty"` LoadBalancer *LoadBalancer `json:",omitempty"`
} }
func (s *DiscoveryGraphNode) IsRouter() bool { func (s *DiscoveryGraphNode) IsRouter() bool {

View File

@ -16,7 +16,6 @@ import (
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes" "github.com/golang/protobuf/ptypes"
"github.com/golang/protobuf/ptypes/any" "github.com/golang/protobuf/ptypes/any"
"github.com/golang/protobuf/ptypes/wrappers"
"github.com/hashicorp/consul/agent/connect" "github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/proxycfg"
"github.com/hashicorp/consul/agent/structs" "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) clusterName := connect.ServiceSNI(svc.Name, "", svc.NamespaceOrDefault(), cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain)
resolver, hasResolver := resolvers[svc] 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 // Use a zero value resolver with no timeout and no subsets
resolver = &structs.ServiceResolverConfigEntry{} resolver = &structs.ServiceResolverConfigEntry{}
} else {
lb = resolver.LoadBalancer
} }
// When making service clusters we only pass endpoints with hostnames if the kind is a terminating gateway // 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: case structs.ServiceKindTerminatingGateway:
injectTerminatingGatewayTLSContext(cfgSnap, cluster, svc) injectTerminatingGatewayTLSContext(cfgSnap, cluster, svc)
err := injectLBToCluster(lb, cluster) if err := loadBalancer.InjectToCluster(cluster); err != nil {
if err != nil {
return nil, fmt.Errorf("failed to apply load balancer configuration to cluster %q: %v", clusterName, err) return nil, fmt.Errorf("failed to apply load balancer configuration to cluster %q: %v", clusterName, err)
} }
case structs.ServiceKindMeshGateway: case structs.ServiceKindMeshGateway:
// We can't apply hash based LB config to mesh gateways because they rely on inspecting HTTP attributes // 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 // and mesh gateways do not decrypt traffic
if !lb.IsHashBased() { if !loadBalancer.IsHashBased() {
err := injectLBToCluster(lb, cluster) if err := loadBalancer.InjectToCluster(cluster); err != nil {
if err != nil {
return nil, fmt.Errorf("failed to apply load balancer configuration to cluster %q: %v", clusterName, err) 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: case structs.ServiceKindTerminatingGateway:
injectTerminatingGatewayTLSContext(cfgSnap, cluster, svc) injectTerminatingGatewayTLSContext(cfgSnap, cluster, svc)
err := injectLBToCluster(lb, cluster) if err := loadBalancer.InjectToCluster(cluster); err != nil {
if err != nil {
return nil, fmt.Errorf("failed to apply load balancer configuration to cluster %q: %v", clusterName, err) return nil, fmt.Errorf("failed to apply load balancer configuration to cluster %q: %v", clusterName, err)
} }
case structs.ServiceKindMeshGateway: case structs.ServiceKindMeshGateway:
// We can't apply hash based LB config to mesh gateways because they rely on inspecting HTTP attributes // 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 // and mesh gateways do not decrypt traffic
if !lb.IsHashBased() { if !loadBalancer.IsHashBased() {
err := injectLBToCluster(lb, cluster) if err := loadBalancer.InjectToCluster(cluster); err != nil {
if err != nil {
return nil, fmt.Errorf("failed to apply load balancer configuration to cluster %q: %v", clusterName, err) 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(), OutlierDetection: cfg.PassiveHealthCheck.AsOutlierDetection(),
} }
if err := injectLBToCluster(node.LoadBalancer, c); err != nil { var lb *structs.EnvoyLBConfig
return nil, fmt.Errorf("failed to apply load balancer configuration to cluster %q: %v", sni, err) 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 proto := cfg.Protocol
@ -557,37 +558,6 @@ func (s *Server) makeUpstreamClustersForDiscoveryChain(
return out, nil 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 // makeClusterFromUserConfig returns the listener config decoded from an
// arbitrary proto3 json format string or an error if it's invalid. // arbitrary proto3 json format string or an error if it's invalid.
// //

View File

@ -2,7 +2,6 @@ package xds
import ( import (
"bytes" "bytes"
"github.com/golang/protobuf/ptypes/wrappers"
"path/filepath" "path/filepath"
"sort" "sort"
"testing" "testing"
@ -367,10 +366,12 @@ func TestClustersFromSnapshot(t *testing.T) {
OnlyPassing: true, OnlyPassing: true,
}, },
}, },
LoadBalancer: structs.LoadBalancer{ LoadBalancer: &structs.LoadBalancer{
Policy: "least_request", EnvoyLBConfig: &structs.EnvoyLBConfig{
LeastRequestConfig: structs.LeastRequestConfig{ Policy: "least_request",
ChoiceCount: 5, LeastRequestConfig: &structs.LeastRequestConfig{
ChoiceCount: 5,
},
}, },
}, },
}, },
@ -394,11 +395,13 @@ func TestClustersFromSnapshot(t *testing.T) {
OnlyPassing: true, OnlyPassing: true,
}, },
}, },
LoadBalancer: structs.LoadBalancer{ LoadBalancer: &structs.LoadBalancer{
Policy: "ring_hash", EnvoyLBConfig: &structs.EnvoyLBConfig{
RingHashConfig: structs.RingHashConfig{ Policy: "ring_hash",
MinimumRingSize: 20, RingHashConfig: &structs.RingHashConfig{
MaximumRingSize: 50, MinimumRingSize: 20,
MaximumRingSize: 50,
},
}, },
}, },
}, },
@ -605,11 +608,13 @@ func TestClustersFromSnapshot(t *testing.T) {
OnlyPassing: true, OnlyPassing: true,
}, },
}, },
LoadBalancer: structs.LoadBalancer{ LoadBalancer: &structs.LoadBalancer{
Policy: "ring_hash", EnvoyLBConfig: &structs.EnvoyLBConfig{
RingHashConfig: structs.RingHashConfig{ Policy: "ring_hash",
MinimumRingSize: 20, RingHashConfig: &structs.RingHashConfig{
MaximumRingSize: 50, 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", "", "") 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 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) clusterName := connect.ServiceSNI(svc.Name, "", svc.NamespaceOrDefault(), cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain)
resolver, hasResolver := cfgSnap.TerminatingGateway.ServiceResolvers[svc] resolver, hasResolver := cfgSnap.TerminatingGateway.ServiceResolvers[svc]
svcConfig := cfgSnap.TerminatingGateway.ServiceConfigs[svc] svcConfig := cfgSnap.TerminatingGateway.ServiceConfigs[svc]
cfg, err := ParseProxyConfig(svcConfig.ProxyConfig) 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 // Routes can only be defined for HTTP services
continue continue
} }
@ -59,7 +59,12 @@ func routesFromSnapshotTerminatingGateway(_ connectionInfo, cfgSnap *proxycfg.Co
// Use a zero value resolver with no timeout and no subsets // Use a zero value resolver with no timeout and no subsets
resolver = &structs.ServiceResolverConfigEntry{} 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 { if err != nil {
continue 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 // If there is a service-resolver for this service then also setup routes for each subset
for name := range resolver.Subsets { for name := range resolver.Subsets {
clusterName = connect.ServiceSNI(svc.Name, name, svc.NamespaceOrDefault(), cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain) 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 { if err != nil {
continue 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 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) 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) 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) return nil, fmt.Errorf("missing first node in compiled discovery chain for: %s", chain.ServiceName)
} }
var lb *structs.EnvoyLBConfig
switch startNode.Type { switch startNode.Type {
case structs.DiscoveryGraphNodeTypeRouter: case structs.DiscoveryGraphNodeTypeRouter:
routes = make([]*envoyroute.Route, 0, len(startNode.Routes)) routes = make([]*envoyroute.Route, 0, len(startNode.Routes))
@ -263,6 +270,10 @@ func makeUpstreamRouteForDiscoveryChain(
) )
nextNode := chain.Nodes[discoveryRoute.NextNode] nextNode := chain.Nodes[discoveryRoute.NextNode]
if nextNode.LoadBalancer != nil {
lb = nextNode.LoadBalancer.EnvoyLBConfig
}
switch nextNode.Type { switch nextNode.Type {
case structs.DiscoveryGraphNodeTypeSplitter: case structs.DiscoveryGraphNodeTypeSplitter:
routeAction, err = makeRouteActionForSplitter(nextNode.Splits, chain) routeAction, err = makeRouteActionForSplitter(nextNode.Splits, chain)
@ -270,14 +281,14 @@ func makeUpstreamRouteForDiscoveryChain(
return nil, err 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) return nil, fmt.Errorf("failed to apply load balancer configuration to route action: %v", err)
} }
case structs.DiscoveryGraphNodeTypeResolver: case structs.DiscoveryGraphNodeTypeResolver:
routeAction = makeRouteActionForChainCluster(nextNode.Resolver.Target, chain) 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) return nil, fmt.Errorf("failed to apply load balancer configuration to route action: %v", err)
} }
@ -332,7 +343,10 @@ func makeUpstreamRouteForDiscoveryChain(
return nil, err 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) return nil, fmt.Errorf("failed to apply load balancer configuration to route action: %v", err)
} }
@ -346,7 +360,10 @@ func makeUpstreamRouteForDiscoveryChain(
case structs.DiscoveryGraphNodeTypeResolver: case structs.DiscoveryGraphNodeTypeResolver:
routeAction := makeRouteActionForChainCluster(startNode.Resolver.Target, chain) 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) return nil, fmt.Errorf("failed to apply load balancer configuration to route action: %v", err)
} }
@ -370,62 +387,6 @@ func makeUpstreamRouteForDiscoveryChain(
return host, nil 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 { func makeRouteMatchForDiscoveryRoute(_ connectionInfo, discoveryRoute *structs.DiscoveryRoute) *envoyroute.RouteMatch {
match := discoveryRoute.Definition.Match match := discoveryRoute.Definition.Match
if match == nil || match.IsEmpty() { if match == nil || match.IsEmpty() {

View File

@ -7,7 +7,6 @@ import (
"time" "time"
envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2" 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/connect"
"github.com/hashicorp/consul/agent/consul/discoverychain" "github.com/hashicorp/consul/agent/consul/discoverychain"
"github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/proxycfg"
@ -203,25 +202,27 @@ func TestRoutesFromSnapshot(t *testing.T) {
OnlyPassing: true, OnlyPassing: true,
}, },
}, },
LoadBalancer: structs.LoadBalancer{ LoadBalancer: &structs.LoadBalancer{
Policy: "ring_hash", EnvoyLBConfig: &structs.EnvoyLBConfig{
RingHashConfig: structs.RingHashConfig{ Policy: "ring_hash",
MinimumRingSize: 20, RingHashConfig: &structs.RingHashConfig{
MaximumRingSize: 50, MinimumRingSize: 20,
}, MaximumRingSize: 50,
HashPolicies: []structs.HashPolicy{
{
Field: "cookie",
FieldMatchValue: "chocolate-chip",
Terminal: true,
}, },
{ HashPolicies: []structs.HashPolicy{
Field: "header", {
FieldMatchValue: "x-user-id", Field: structs.HashPolicyCookie,
}, FieldValue: "chocolate-chip",
{ Terminal: true,
SourceAddress: true, },
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", "connectTimeout": "5s",
"outlierDetection": { "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", "connectTimeout": "5s",
"outlierDetection": { "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", "connectTimeout": "5s",
"outlierDetection": { "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", "connectTimeout": "5s",
"outlierDetection": { "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", "connectTimeout": "5s",
"outlierDetection": { "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", "connectTimeout": "5s",
"outlierDetection": { "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", "connectTimeout": "5s",
"outlierDetection": { "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", "connectTimeout": "5s",
"outlierDetection": { "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": { "outlierDetection": {
} }
@ -111,38 +111,6 @@
"connectTimeout": "5s", "connectTimeout": "5s",
"outlierDetection": { "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": { "outlierDetection": {
} }
@ -111,38 +111,6 @@
"connectTimeout": "5s", "connectTimeout": "5s",
"outlierDetection": { "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": { "outlierDetection": {
} }
@ -111,38 +111,6 @@
"connectTimeout": "5s", "connectTimeout": "5s",
"outlierDetection": { "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": { "outlierDetection": {
} }
@ -111,38 +111,6 @@
"connectTimeout": "5s", "connectTimeout": "5s",
"outlierDetection": { "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", "versionInfo": "00000001",
"resources": [ "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", "@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "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", "@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",

View File

@ -1,60 +1,6 @@
{ {
"versionInfo": "00000001", "versionInfo": "00000001",
"resources": [ "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", "@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "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", "@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",

View File

@ -1,60 +1,6 @@
{ {
"versionInfo": "00000001", "versionInfo": "00000001",
"resources": [ "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", "@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "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", "@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",

View File

@ -1,60 +1,6 @@
{ {
"versionInfo": "00000001", "versionInfo": "00000001",
"resources": [ "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", "@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "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", "@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "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", "@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "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", "@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "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", "@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "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", "@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "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", "@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "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", "@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "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", "@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "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", "@type": "type.googleapis.com/envoy.api.v2.Cluster",
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",

View File

@ -3,10 +3,10 @@
"resources": [ "resources": [
{ {
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration", "@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": [ "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": [ "domains": [
"*" "*"
], ],
@ -16,26 +16,7 @@
"prefix": "/" "prefix": "/"
}, },
"route": { "route": {
"cluster": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "cluster": "api.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
}
]
} }
} }
] ]
@ -45,10 +26,10 @@
}, },
{ {
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration", "@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": [ "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": [ "domains": [
"*" "*"
], ],
@ -58,26 +39,7 @@
"prefix": "/" "prefix": "/"
}, },
"route": { "route": {
"cluster": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "cluster": "cache.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
}
]
} }
} }
] ]
@ -87,10 +49,10 @@
}, },
{ {
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration", "@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": [ "virtualHosts": [
{ {
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"domains": [ "domains": [
"*" "*"
], ],
@ -100,26 +62,7 @@
"prefix": "/" "prefix": "/"
}, },
"route": { "route": {
"cluster": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "cluster": "db.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
}
]
} }
} }
] ]

View File

@ -3,10 +3,10 @@
"resources": [ "resources": [
{ {
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration", "@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": [ "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": [ "domains": [
"*" "*"
], ],
@ -16,26 +16,7 @@
"prefix": "/" "prefix": "/"
}, },
"route": { "route": {
"cluster": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "cluster": "api.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
}
]
} }
} }
] ]
@ -45,10 +26,10 @@
}, },
{ {
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration", "@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": [ "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": [ "domains": [
"*" "*"
], ],
@ -58,26 +39,7 @@
"prefix": "/" "prefix": "/"
}, },
"route": { "route": {
"cluster": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "cluster": "cache.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
}
]
} }
} }
] ]
@ -87,10 +49,10 @@
}, },
{ {
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration", "@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": [ "virtualHosts": [
{ {
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"domains": [ "domains": [
"*" "*"
], ],
@ -100,26 +62,7 @@
"prefix": "/" "prefix": "/"
}, },
"route": { "route": {
"cluster": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "cluster": "db.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
}
]
} }
} }
] ]

View File

@ -3,10 +3,10 @@
"resources": [ "resources": [
{ {
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration", "@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": [ "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": [ "domains": [
"*" "*"
], ],
@ -16,26 +16,7 @@
"prefix": "/" "prefix": "/"
}, },
"route": { "route": {
"cluster": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "cluster": "api.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
}
]
} }
} }
] ]
@ -45,10 +26,10 @@
}, },
{ {
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration", "@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": [ "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": [ "domains": [
"*" "*"
], ],
@ -58,26 +39,7 @@
"prefix": "/" "prefix": "/"
}, },
"route": { "route": {
"cluster": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "cluster": "cache.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
}
]
} }
} }
] ]
@ -87,10 +49,10 @@
}, },
{ {
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration", "@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": [ "virtualHosts": [
{ {
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"domains": [ "domains": [
"*" "*"
], ],
@ -100,26 +62,7 @@
"prefix": "/" "prefix": "/"
}, },
"route": { "route": {
"cluster": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "cluster": "db.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
}
]
} }
} }
] ]

View File

@ -3,10 +3,10 @@
"resources": [ "resources": [
{ {
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration", "@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": [ "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": [ "domains": [
"*" "*"
], ],
@ -16,26 +16,7 @@
"prefix": "/" "prefix": "/"
}, },
"route": { "route": {
"cluster": "v1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "cluster": "api.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
}
]
} }
} }
] ]
@ -45,10 +26,10 @@
}, },
{ {
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration", "@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": [ "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": [ "domains": [
"*" "*"
], ],
@ -58,26 +39,7 @@
"prefix": "/" "prefix": "/"
}, },
"route": { "route": {
"cluster": "v2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "cluster": "cache.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
}
]
} }
} }
] ]
@ -87,10 +49,10 @@
}, },
{ {
"@type": "type.googleapis.com/envoy.api.v2.RouteConfiguration", "@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": [ "virtualHosts": [
{ {
"name": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"domains": [ "domains": [
"*" "*"
], ],
@ -100,26 +62,7 @@
"prefix": "/" "prefix": "/"
}, },
"route": { "route": {
"cluster": "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", "cluster": "db.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
}
]
} }
} }
] ]

View File

@ -140,7 +140,7 @@ type ServiceResolverConfigEntry struct {
// LoadBalancer determines the load balancing policy and configuration for services // LoadBalancer determines the load balancing policy and configuration for services
// issuing requests to this upstream service. // issuing requests to this upstream service.
LoadBalancer LoadBalancer `json:",omitempty" alias:"load_balancer"` LoadBalancer *LoadBalancer `json:",omitempty" alias:"load_balancer"`
CreateIndex uint64 CreateIndex uint64
ModifyIndex uint64 ModifyIndex uint64
@ -209,14 +209,22 @@ type ServiceResolverFailover struct {
// LoadBalancer determines the load balancing policy and configuration for services // LoadBalancer determines the load balancing policy and configuration for services
// issuing requests to this upstream service. // issuing requests to this upstream service.
type LoadBalancer struct { 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 is the load balancing policy used to select a host
Policy string `json:",omitempty"` Policy string `json:",omitempty"`
// RingHashConfig contains configuration for the "ring_hash" policy type // 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 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. // 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 // 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"` ChoiceCount uint32 `json:",omitempty" alias:"choice_count"`
} }
// HashPolicy is a list of hash policies to use for hashing load balancing algorithms. // HashPolicy defines which attributes will be hashed by hash-based LB 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.
type HashPolicy struct { type HashPolicy struct {
// Field is the attribute type to hash on. // Field is the attribute type to hash on.
// Must be one of "header","cookie", or "query_parameter". // Must be one of "header","cookie", or "query_parameter".
// Cannot be specified along with SourceIP. // Cannot be specified along with SourceIP.
Field string `json:",omitempty"` 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 // ie. header name, cookie name, URL query parameter name
// Cannot be specified along with SourceIP. // 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. // CookieConfig contains configuration for the "cookie" hash policy type.
// Cannot be specified along with Field and FieldMatchValue. CookieConfig *CookieConfig `json:",omitempty" alias:"cookie_config"`
SourceAddress bool `json:",omitempty" alias:"source_address"`
// 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. // 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, // If a hash is computed when a Terminal policy is evaluated,
// then that hash will be used and subsequent hash policies will be ignored. // then that hash will be used and subsequent hash policies will be ignored.
Terminal bool `json:",omitempty"` 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, Kind: ServiceResolver,
Name: "test-least-req", Name: "test-least-req",
Namespace: defaultNamespace, Namespace: defaultNamespace,
LoadBalancer: LoadBalancer{ LoadBalancer: &LoadBalancer{
Policy: "least_request", EnvoyLBConfig: &EnvoyLBConfig{
LeastRequestConfig: LeastRequestConfig{ChoiceCount: 10}, Policy: "least_request",
LeastRequestConfig: &LeastRequestConfig{ChoiceCount: 10},
},
}, },
}, },
verify: verifyResolver, verify: verifyResolver,
@ -306,20 +308,30 @@ func TestAPI_ConfigEntry_ServiceResolver_LoadBalancer(t *testing.T) {
Kind: ServiceResolver, Kind: ServiceResolver,
Name: "test-ring-hash", Name: "test-ring-hash",
Namespace: defaultNamespace, Namespace: defaultNamespace,
LoadBalancer: LoadBalancer{ LoadBalancer: &LoadBalancer{
Policy: "ring_hash", EnvoyLBConfig: &EnvoyLBConfig{
RingHashConfig: RingHashConfig{ Policy: "ring_hash",
MinimumRingSize: 1024 * 2, RingHashConfig: &RingHashConfig{
MaximumRingSize: 1024 * 4, MinimumRingSize: 1024 * 2,
}, MaximumRingSize: 1024 * 4,
HashPolicies: []HashPolicy{
{
Field: "header",
FieldMatchValue: "my-session-header",
Terminal: true,
}, },
{ HashPolicies: []HashPolicy{
SourceAddress: true, {
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 package write
import ( import (
"github.com/hashicorp/consul/agent/structs"
"io" "io"
"strings" "strings"
"testing" "testing"
@ -1165,6 +1166,235 @@ func TestParseConfigEntry(t *testing.T) {
Name: "main", 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", name: "expose paths: kitchen sink proxy defaults",
snake: ` snake: `

View File

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

View File

@ -185,6 +185,12 @@ A single node in the compiled discovery chain.
- `Targets` `(array<string>)` - List of targets found in - `Targets` `(array<string>)` - List of targets found in
[`Targets`](#targets) to failover to in order of preference. [`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` #### `DiscoveryTarget`
- `ID` `(string)` - The unique name of this target. - `ID` `(string)` - The unique name of this target.