Add support for failover policies (#16505)
This commit is contained in:
parent
6ca1c9f15c
commit
5c8414e772
|
@ -1110,6 +1110,15 @@ RESOLVE_AGAIN:
|
|||
df := &structs.DiscoveryFailover{}
|
||||
node.Resolver.Failover = df
|
||||
|
||||
if failover.Policy == nil || failover.Policy.Mode == "" {
|
||||
proxyDefault := c.entries.GetProxyDefaults(targetID.PartitionOrDefault())
|
||||
if proxyDefault != nil {
|
||||
df.Policy = proxyDefault.FailoverPolicy
|
||||
}
|
||||
} else {
|
||||
df.Policy = failover.Policy
|
||||
}
|
||||
|
||||
// Take care of doing any redirects or configuration loading
|
||||
// related to targets by cheating a bit and recursing into
|
||||
// ourselves.
|
||||
|
|
|
@ -368,6 +368,7 @@ type ProxyConfigEntry struct {
|
|||
Expose ExposeConfig `json:",omitempty"`
|
||||
AccessLogs AccessLogsConfig `json:",omitempty" alias:"access_logs"`
|
||||
EnvoyExtensions EnvoyExtensions `json:",omitempty" alias:"envoy_extensions"`
|
||||
FailoverPolicy *ServiceResolverFailoverPolicy `json:",omitempty" alias:"failover_policy"`
|
||||
|
||||
Meta map[string]string `json:",omitempty"`
|
||||
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||
|
@ -434,6 +435,10 @@ func (e *ProxyConfigEntry) Validate() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if !e.FailoverPolicy.isValid() {
|
||||
return fmt.Errorf("Failover policy must be one of '', 'default', or 'order-by-locality'")
|
||||
}
|
||||
|
||||
return e.validateEnterpriseMeta()
|
||||
}
|
||||
|
||||
|
|
|
@ -1079,6 +1079,14 @@ func (e *ServiceResolverConfigEntry) Validate() error {
|
|||
return fmt.Errorf(errorPrefix + "one of Service, ServiceSubset, Namespace, Targets, or Datacenters is required")
|
||||
}
|
||||
|
||||
if err := f.Policy.ValidateEnterprise(); err != nil {
|
||||
return fmt.Errorf("Bad Failover[%q]: %s", subset, err)
|
||||
}
|
||||
|
||||
if !f.Policy.isValid() {
|
||||
return fmt.Errorf("Bad Failover[%q]: Policy must be one of '', 'default', or 'order-by-locality'", subset)
|
||||
}
|
||||
|
||||
if f.ServiceSubset != "" {
|
||||
if f.Service == "" || f.Service == e.Name {
|
||||
if !isSubset(f.ServiceSubset) {
|
||||
|
@ -1368,13 +1376,22 @@ type ServiceResolverFailover struct {
|
|||
//
|
||||
// This is a DESTINATION during failover.
|
||||
Targets []ServiceResolverFailoverTarget `json:",omitempty"`
|
||||
|
||||
// Policy specifies the exact mechanism used for failover.
|
||||
Policy *ServiceResolverFailoverPolicy `json:",omitempty"`
|
||||
}
|
||||
|
||||
func (t *ServiceResolverFailover) ToDiscoveryTargetOpts() DiscoveryTargetOpts {
|
||||
type ServiceResolverFailoverPolicy struct {
|
||||
// Mode specifies the type of failover that will be performed. Valid values are
|
||||
// "default", "" (equivalent to "default") and "order-by-locality".
|
||||
Mode string `json:",omitempty"`
|
||||
}
|
||||
|
||||
func (f *ServiceResolverFailover) ToDiscoveryTargetOpts() DiscoveryTargetOpts {
|
||||
return DiscoveryTargetOpts{
|
||||
Service: t.Service,
|
||||
ServiceSubset: t.ServiceSubset,
|
||||
Namespace: t.Namespace,
|
||||
Service: f.Service,
|
||||
ServiceSubset: f.ServiceSubset,
|
||||
Namespace: f.Namespace,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1382,6 +1399,22 @@ func (f *ServiceResolverFailover) isEmpty() bool {
|
|||
return f.Service == "" && f.ServiceSubset == "" && f.Namespace == "" && len(f.Datacenters) == 0 && len(f.Targets) == 0
|
||||
}
|
||||
|
||||
func (fp *ServiceResolverFailoverPolicy) isValid() bool {
|
||||
if fp == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
switch fp.Mode {
|
||||
case "":
|
||||
case "default":
|
||||
case "order-by-locality":
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type ServiceResolverFailoverTarget struct {
|
||||
// Service specifies the name of the service to try during failover.
|
||||
Service string `json:",omitempty"`
|
||||
|
|
|
@ -88,3 +88,17 @@ func (req *DiscoveryChainRequest) GetEnterpriseMeta() *acl.EnterpriseMeta {
|
|||
func (req *DiscoveryChainRequest) WithEnterpriseMeta(_ *acl.EnterpriseMeta) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
// ValidateEnterprise validates that enterprise fields are only set
|
||||
// with enterprise binaries.
|
||||
func (f *ServiceResolverFailoverPolicy) ValidateEnterprise() error {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if f.Mode != "" {
|
||||
return fmt.Errorf("Setting failover policies requires Consul Enterprise")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -72,6 +72,17 @@ func TestServiceResolverConfigEntry_OSS(t *testing.T) {
|
|||
},
|
||||
validateErr: `Bad Failover["*"]: Setting Namespace requires Consul Enterprise`,
|
||||
},
|
||||
{
|
||||
name: "setting failover Namespace on OSS",
|
||||
entry: &ServiceResolverConfigEntry{
|
||||
Kind: ServiceResolver,
|
||||
Name: "test",
|
||||
Failover: map[string]ServiceResolverFailover{
|
||||
"*": {Service: "s1", Policy: &ServiceResolverFailoverPolicy{Mode: "something"}},
|
||||
},
|
||||
},
|
||||
validateErr: `Bad Failover["*"]: Setting failover policies requires Consul Enterprise`,
|
||||
},
|
||||
{
|
||||
name: "setting redirect Namespace on OSS",
|
||||
entry: &ServiceResolverConfigEntry{
|
||||
|
|
|
@ -3195,6 +3195,25 @@ func TestProxyConfigEntry(t *testing.T) {
|
|||
EnterpriseMeta: *acl.DefaultEnterpriseMeta(),
|
||||
},
|
||||
},
|
||||
"proxy config has invalid failover policy": {
|
||||
entry: &ProxyConfigEntry{
|
||||
Name: "global",
|
||||
FailoverPolicy: &ServiceResolverFailoverPolicy{Mode: "bad"},
|
||||
},
|
||||
validateErr: `Failover policy must be one of '', 'default', or 'order-by-locality'`,
|
||||
},
|
||||
"proxy config with valid failover policy": {
|
||||
entry: &ProxyConfigEntry{
|
||||
Name: "global",
|
||||
FailoverPolicy: &ServiceResolverFailoverPolicy{Mode: "order-by-locality"},
|
||||
},
|
||||
expected: &ProxyConfigEntry{
|
||||
Name: ProxyConfigGlobal,
|
||||
Kind: ProxyDefaults,
|
||||
FailoverPolicy: &ServiceResolverFailoverPolicy{Mode: "order-by-locality"},
|
||||
EnterpriseMeta: *acl.DefaultEnterpriseMeta(),
|
||||
},
|
||||
},
|
||||
"proxy config has invalid access log type": {
|
||||
entry: &ProxyConfigEntry{
|
||||
Name: "global",
|
||||
|
|
|
@ -179,6 +179,7 @@ type DiscoverySplit struct {
|
|||
// compiled form of ServiceResolverFailover
|
||||
type DiscoveryFailover struct {
|
||||
Targets []string `json:",omitempty"`
|
||||
Policy *ServiceResolverFailoverPolicy `json:",omitempty"`
|
||||
}
|
||||
|
||||
// DiscoveryTarget represents all of the inputs necessary to use a resolver
|
||||
|
|
|
@ -177,6 +177,10 @@ func (o *DiscoveryFailover) DeepCopy() *DiscoveryFailover {
|
|||
cp.Targets = make([]string, len(o.Targets))
|
||||
copy(cp.Targets, o.Targets)
|
||||
}
|
||||
if o.Policy != nil {
|
||||
cp.Policy = new(ServiceResolverFailoverPolicy)
|
||||
*cp.Policy = *o.Policy
|
||||
}
|
||||
return &cp
|
||||
}
|
||||
|
||||
|
@ -894,6 +898,10 @@ func (o *ServiceResolverFailover) DeepCopy() *ServiceResolverFailover {
|
|||
cp.Targets = make([]ServiceResolverFailoverTarget, len(o.Targets))
|
||||
copy(cp.Targets, o.Targets)
|
||||
}
|
||||
if o.Policy != nil {
|
||||
cp.Policy = new(ServiceResolverFailoverPolicy)
|
||||
*cp.Policy = *o.Policy
|
||||
}
|
||||
return &cp
|
||||
}
|
||||
|
||||
|
|
|
@ -243,6 +243,7 @@ type ServiceResolverFailover struct {
|
|||
Namespace string `json:",omitempty"`
|
||||
Datacenters []string `json:",omitempty"`
|
||||
Targets []ServiceResolverFailoverTarget `json:",omitempty"`
|
||||
Policy *ServiceResolverFailoverPolicy `json:",omitempty"`
|
||||
}
|
||||
|
||||
type ServiceResolverFailoverTarget struct {
|
||||
|
@ -254,6 +255,10 @@ type ServiceResolverFailoverTarget struct {
|
|||
Peer string `json:",omitempty"`
|
||||
}
|
||||
|
||||
type ServiceResolverFailoverPolicy struct {
|
||||
Mode string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// LoadBalancer determines the load balancing policy and configuration for services
|
||||
// issuing requests to this upstream service.
|
||||
type LoadBalancer struct {
|
||||
|
|
|
@ -221,6 +221,7 @@ func (r *DiscoveryResolver) UnmarshalJSON(data []byte) error {
|
|||
// compiled form of ServiceResolverFailover
|
||||
type DiscoveryFailover struct {
|
||||
Targets []string
|
||||
Policy ServiceResolverFailoverPolicy `json:",omitempty"`
|
||||
}
|
||||
|
||||
// DiscoveryTarget represents all of the inputs necessary to use a resolver
|
||||
|
|
|
@ -1410,6 +1410,11 @@ func ServiceResolverFailoverToStructs(s *ServiceResolverFailover, t *structs.Ser
|
|||
}
|
||||
}
|
||||
}
|
||||
if s.Policy != nil {
|
||||
var x structs.ServiceResolverFailoverPolicy
|
||||
ServiceResolverFailoverPolicyToStructs(s.Policy, &x)
|
||||
t.Policy = &x
|
||||
}
|
||||
}
|
||||
func ServiceResolverFailoverFromStructs(t *structs.ServiceResolverFailover, s *ServiceResolverFailover) {
|
||||
if s == nil {
|
||||
|
@ -1429,6 +1434,23 @@ func ServiceResolverFailoverFromStructs(t *structs.ServiceResolverFailover, s *S
|
|||
}
|
||||
}
|
||||
}
|
||||
if t.Policy != nil {
|
||||
var x ServiceResolverFailoverPolicy
|
||||
ServiceResolverFailoverPolicyFromStructs(t.Policy, &x)
|
||||
s.Policy = &x
|
||||
}
|
||||
}
|
||||
func ServiceResolverFailoverPolicyToStructs(s *ServiceResolverFailoverPolicy, t *structs.ServiceResolverFailoverPolicy) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
t.Mode = s.Mode
|
||||
}
|
||||
func ServiceResolverFailoverPolicyFromStructs(t *structs.ServiceResolverFailoverPolicy, s *ServiceResolverFailoverPolicy) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.Mode = t.Mode
|
||||
}
|
||||
func ServiceResolverFailoverTargetToStructs(s *ServiceResolverFailoverTarget, t *structs.ServiceResolverFailoverTarget) {
|
||||
if s == nil {
|
||||
|
|
|
@ -117,6 +117,16 @@ func (msg *ServiceResolverFailover) UnmarshalBinary(b []byte) error {
|
|||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *ServiceResolverFailoverPolicy) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||
func (msg *ServiceResolverFailoverPolicy) UnmarshalBinary(b []byte) error {
|
||||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *ServiceResolverFailoverTarget) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -160,6 +160,16 @@ message ServiceResolverFailover {
|
|||
string Namespace = 3;
|
||||
repeated string Datacenters = 4;
|
||||
repeated ServiceResolverFailoverTarget Targets = 5;
|
||||
ServiceResolverFailoverPolicy Policy = 6;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
//
|
||||
// target=github.com/hashicorp/consul/agent/structs.ServiceResolverFailoverPolicy
|
||||
// output=config_entry.gen.go
|
||||
// name=Structs
|
||||
message ServiceResolverFailoverPolicy {
|
||||
string Mode = 1;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
|
|
Loading…
Reference in New Issue