add sameness groups to discovery chains (#16671)
This commit is contained in:
parent
5a9948fab7
commit
2bf2e81a6b
|
@ -59,6 +59,13 @@ func (e *DiscoveryChainSet) GetService(sid structs.ServiceID) *structs.ServiceCo
|
|||
return nil
|
||||
}
|
||||
|
||||
func (e *DiscoveryChainSet) GetSamenessGroup(name string) *structs.SamenessGroupConfigEntry {
|
||||
if e.SamenessGroups != nil {
|
||||
return e.SamenessGroups[name]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *DiscoveryChainSet) GetProxyDefaults(partition string) *structs.ProxyConfigEntry {
|
||||
if e.ProxyDefaults != nil {
|
||||
return e.ProxyDefaults[partition]
|
||||
|
@ -106,6 +113,16 @@ func (e *DiscoveryChainSet) AddServices(entries ...*structs.ServiceConfigEntry)
|
|||
}
|
||||
}
|
||||
|
||||
// AddSamenessGroup adds service configs. Convenience function for testing.
|
||||
func (e *DiscoveryChainSet) AddSamenessGroup(entries ...*structs.SamenessGroupConfigEntry) {
|
||||
if e.Services == nil {
|
||||
e.SamenessGroups = make(map[string]*structs.SamenessGroupConfigEntry)
|
||||
}
|
||||
for _, entry := range entries {
|
||||
e.SamenessGroups[entry.Name] = entry
|
||||
}
|
||||
}
|
||||
|
||||
// AddProxyDefaults adds proxy-defaults configs. Convenience function for testing.
|
||||
func (e *DiscoveryChainSet) AddProxyDefaults(entries ...*structs.ProxyConfigEntry) {
|
||||
if e.ProxyDefaults == nil {
|
||||
|
@ -139,6 +156,8 @@ func (e *DiscoveryChainSet) AddEntries(entries ...structs.ConfigEntry) {
|
|||
e.AddResolvers(entry.(*structs.ServiceResolverConfigEntry))
|
||||
case structs.ServiceDefaults:
|
||||
e.AddServices(entry.(*structs.ServiceConfigEntry))
|
||||
case structs.SamenessGroup:
|
||||
e.AddSamenessGroup(entry.(*structs.SamenessGroupConfigEntry))
|
||||
case structs.ProxyDefaults:
|
||||
if entry.GetName() != structs.ProxyConfigGlobal {
|
||||
panic("the only supported proxy-defaults name is '" + structs.ProxyConfigGlobal + "'")
|
||||
|
|
|
@ -930,7 +930,8 @@ RESOLVE_AGAIN:
|
|||
//
|
||||
// TODO(rb): What about a redirected subset reference? (web/v2, but web redirects to alt/"")
|
||||
|
||||
if resolver.Redirect != nil {
|
||||
// Redirects to sameness groups are technically failovers.
|
||||
if resolver.Redirect != nil && resolver.Redirect.SamenessGroup == "" {
|
||||
redirect := resolver.Redirect
|
||||
|
||||
redirectedTarget := c.rewriteTarget(
|
||||
|
@ -1070,6 +1071,23 @@ RESOLVE_AGAIN:
|
|||
// reasonably if there is some sort of graph loop below.
|
||||
c.recordNode(node)
|
||||
|
||||
var err error
|
||||
// Determine which failover definitions apply.
|
||||
var failoverTargets []*structs.DiscoveryTarget
|
||||
var failoverPolicy *structs.ServiceResolverFailoverPolicy
|
||||
proxyDefault := c.entries.GetProxyDefaults(targetID.PartitionOrDefault())
|
||||
if proxyDefault != nil {
|
||||
failoverPolicy = proxyDefault.FailoverPolicy
|
||||
}
|
||||
|
||||
if resolver.Redirect != nil && resolver.Redirect.SamenessGroup != "" {
|
||||
opts := resolver.Redirect.ToDiscoveryTargetOpts()
|
||||
failoverTargets, err = c.makeSamenessGroupFailover(target, opts, resolver.Redirect.SamenessGroup)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(resolver.Failover) > 0 {
|
||||
f := resolver.Failover
|
||||
|
||||
|
@ -1083,8 +1101,10 @@ RESOLVE_AGAIN:
|
|||
return node, nil
|
||||
}
|
||||
|
||||
// Determine which failover definitions apply.
|
||||
var failoverTargets []*structs.DiscoveryTarget
|
||||
if failover.Policy != nil {
|
||||
failoverPolicy = failover.Policy
|
||||
}
|
||||
|
||||
if len(failover.Datacenters) > 0 {
|
||||
opts := failover.ToDiscoveryTargetOpts()
|
||||
for _, dc := range failover.Datacenters {
|
||||
|
@ -1103,6 +1123,11 @@ RESOLVE_AGAIN:
|
|||
failoverTargets = append(failoverTargets, failoverTarget)
|
||||
}
|
||||
}
|
||||
} else if failover.SamenessGroup != "" {
|
||||
failoverTargets, err = c.makeSamenessGroupFailover(target, failover.ToDiscoveryTargetOpts(), failover.SamenessGroup)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Rewrite the target as per the failover policy.
|
||||
failoverTarget := c.rewriteTarget(target, failover.ToDiscoveryTargetOpts())
|
||||
|
@ -1111,37 +1136,55 @@ RESOLVE_AGAIN:
|
|||
}
|
||||
}
|
||||
|
||||
// If we filtered everything out then no point in having a failover.
|
||||
if len(failoverTargets) > 0 {
|
||||
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
|
||||
}
|
||||
// If we filtered everything out then no point in having a failover.
|
||||
if len(failoverTargets) > 0 {
|
||||
df := &structs.DiscoveryFailover{}
|
||||
node.Resolver.Failover = df
|
||||
|
||||
// Take care of doing any redirects or configuration loading
|
||||
// related to targets by cheating a bit and recursing into
|
||||
// ourselves.
|
||||
for _, target := range failoverTargets {
|
||||
failoverResolveNode, err := c.getResolverNode(target, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
failoverTarget := failoverResolveNode.Resolver.Target
|
||||
df.Targets = append(df.Targets, failoverTarget)
|
||||
df.Policy = failoverPolicy
|
||||
|
||||
// Take care of doing any redirects or configuration loading
|
||||
// related to targets by cheating a bit and recursing into
|
||||
// ourselves.
|
||||
for _, target := range failoverTargets {
|
||||
failoverResolveNode, err := c.getResolverNode(target, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
failoverTarget := failoverResolveNode.Resolver.Target
|
||||
df.Targets = append(df.Targets, failoverTarget)
|
||||
}
|
||||
}
|
||||
|
||||
return node, nil
|
||||
}
|
||||
|
||||
func (c *compiler) makeSamenessGroupFailover(target *structs.DiscoveryTarget, opts structs.DiscoveryTargetOpts, samenessGroupName string) ([]*structs.DiscoveryTarget, error) {
|
||||
samenessGroup := c.entries.GetSamenessGroup(samenessGroupName)
|
||||
if samenessGroup == nil {
|
||||
return nil, &structs.ConfigEntryGraphError{
|
||||
Message: fmt.Sprintf(
|
||||
"sameness group missing for service %q",
|
||||
target.Service,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
var failoverTargets []*structs.DiscoveryTarget
|
||||
for _, t := range samenessGroup.ToFailoverTargets() {
|
||||
// Rewrite the target as per the failover policy.
|
||||
targetOpts := structs.MergeDiscoveryTargetOpts(opts, t.ToDiscoveryTargetOpts())
|
||||
failoverTarget := c.rewriteTarget(target, targetOpts)
|
||||
if failoverTarget.ID != target.ID { // don't failover to yourself
|
||||
failoverTargets = append(failoverTargets, failoverTarget)
|
||||
}
|
||||
}
|
||||
|
||||
return failoverTargets, nil
|
||||
}
|
||||
|
||||
func newDefaultServiceResolver(sid structs.ServiceID) *structs.ServiceResolverConfigEntry {
|
||||
return &structs.ServiceResolverConfigEntry{
|
||||
Kind: structs.ServiceResolver,
|
||||
|
|
|
@ -725,6 +725,16 @@ func validateProposedConfigEntryInServiceGraph(
|
|||
// Exported services and mesh config do not influence discovery chains.
|
||||
return nil
|
||||
|
||||
case structs.SamenessGroup:
|
||||
// Any service resolver could reference a sameness group.
|
||||
_, entries, err := configEntriesByKindTxn(tx, nil, structs.ServiceResolver, wildcardEntMeta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, entry := range entries {
|
||||
checkChains[structs.NewServiceID(entry.GetName(), entry.GetEnterpriseMeta())] = struct{}{}
|
||||
}
|
||||
|
||||
case structs.ProxyDefaults:
|
||||
// Check anything that has a discovery chain entry. In the future we could
|
||||
// somehow omit the ones that have a default protocol configured.
|
||||
|
@ -1408,8 +1418,8 @@ func readDiscoveryChainConfigEntriesTxn(
|
|||
todoPeers[peer] = struct{}{}
|
||||
}
|
||||
|
||||
for _, peer := range resolver.RelatedSamenessGroups() {
|
||||
todoSamenessGroups[peer] = struct{}{}
|
||||
for _, sg := range resolver.RelatedSamenessGroups() {
|
||||
todoSamenessGroups[sg] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1103,7 +1103,7 @@ func (e *ServiceResolverConfigEntry) Validate() error {
|
|||
}
|
||||
|
||||
if f.isEmpty() {
|
||||
return fmt.Errorf(errorPrefix + "one of Service, ServiceSubset, Namespace, Targets, or Datacenters is required")
|
||||
return fmt.Errorf(errorPrefix + "one of Service, ServiceSubset, Namespace, Targets, SamenessGroup, or Datacenters is required")
|
||||
}
|
||||
|
||||
if err := f.Policy.ValidateEnterprise(); err != nil {
|
||||
|
@ -1447,7 +1447,12 @@ func (f *ServiceResolverFailover) ToDiscoveryTargetOpts() DiscoveryTargetOpts {
|
|||
}
|
||||
|
||||
func (f *ServiceResolverFailover) isEmpty() bool {
|
||||
return f.Service == "" && f.ServiceSubset == "" && f.Namespace == "" && len(f.Datacenters) == 0 && len(f.Targets) == 0
|
||||
return f.Service == "" &&
|
||||
f.ServiceSubset == "" &&
|
||||
f.Namespace == "" &&
|
||||
len(f.Datacenters) == 0 &&
|
||||
len(f.Targets) == 0 &&
|
||||
f.SamenessGroup == ""
|
||||
}
|
||||
|
||||
func (fp *ServiceResolverFailoverPolicy) isValid() bool {
|
||||
|
|
|
@ -1397,7 +1397,7 @@ func TestServiceResolverConfigEntry(t *testing.T) {
|
|||
"v1": {},
|
||||
},
|
||||
},
|
||||
validateErr: `Bad Failover["v1"]: one of Service, ServiceSubset, Namespace, Targets, or Datacenters is required`,
|
||||
validateErr: `Bad Failover["v1"]: one of Service, ServiceSubset, Namespace, Targets, SamenessGroup, or Datacenters is required`,
|
||||
},
|
||||
{
|
||||
name: "failover to self using invalid subset",
|
||||
|
|
|
@ -70,3 +70,20 @@ type SamenessGroupMember struct {
|
|||
Partition string
|
||||
Peer string
|
||||
}
|
||||
|
||||
func (s *SamenessGroupConfigEntry) ToFailoverTargets() []ServiceResolverFailoverTarget {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var targets []ServiceResolverFailoverTarget
|
||||
|
||||
for _, m := range s.Members {
|
||||
targets = append(targets, ServiceResolverFailoverTarget{
|
||||
Peer: m.Peer,
|
||||
Partition: m.Partition,
|
||||
})
|
||||
}
|
||||
|
||||
return targets
|
||||
}
|
||||
|
|
|
@ -262,6 +262,34 @@ type DiscoveryTargetOpts struct {
|
|||
Peer string
|
||||
}
|
||||
|
||||
func MergeDiscoveryTargetOpts(o1 DiscoveryTargetOpts, o2 DiscoveryTargetOpts) DiscoveryTargetOpts {
|
||||
if o2.Service != "" {
|
||||
o1.Service = o2.Service
|
||||
}
|
||||
|
||||
if o2.ServiceSubset != "" {
|
||||
o1.ServiceSubset = o2.ServiceSubset
|
||||
}
|
||||
|
||||
if o2.Namespace != "" {
|
||||
o1.Namespace = o2.Namespace
|
||||
}
|
||||
|
||||
if o2.Partition != "" {
|
||||
o1.Partition = o2.Partition
|
||||
}
|
||||
|
||||
if o2.Datacenter != "" {
|
||||
o1.Datacenter = o2.Datacenter
|
||||
}
|
||||
|
||||
if o2.Peer != "" {
|
||||
o1.Peer = o2.Peer
|
||||
}
|
||||
|
||||
return o1
|
||||
}
|
||||
|
||||
func NewDiscoveryTarget(opts DiscoveryTargetOpts) *DiscoveryTarget {
|
||||
t := &DiscoveryTarget{
|
||||
Service: opts.Service,
|
||||
|
|
Loading…
Reference in New Issue