add sameness group support to service resolver failover and redirects (#16664)

This commit is contained in:
Eric Haberkorn 2023-03-17 10:48:06 -04:00 committed by GitHub
parent 64f5e20793
commit 68046060ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1066 additions and 925 deletions

View File

@ -10,22 +10,24 @@ import (
//
// None of these are defaulted.
type DiscoveryChainSet struct {
Routers map[structs.ServiceID]*structs.ServiceRouterConfigEntry
Splitters map[structs.ServiceID]*structs.ServiceSplitterConfigEntry
Resolvers map[structs.ServiceID]*structs.ServiceResolverConfigEntry
Services map[structs.ServiceID]*structs.ServiceConfigEntry
Peers map[string]*pbpeering.Peering
ProxyDefaults map[string]*structs.ProxyConfigEntry
Routers map[structs.ServiceID]*structs.ServiceRouterConfigEntry
Splitters map[structs.ServiceID]*structs.ServiceSplitterConfigEntry
Resolvers map[structs.ServiceID]*structs.ServiceResolverConfigEntry
Services map[structs.ServiceID]*structs.ServiceConfigEntry
Peers map[string]*pbpeering.Peering
SamenessGroups map[string]*structs.SamenessGroupConfigEntry
ProxyDefaults map[string]*structs.ProxyConfigEntry
}
func NewDiscoveryChainSet() *DiscoveryChainSet {
return &DiscoveryChainSet{
Routers: make(map[structs.ServiceID]*structs.ServiceRouterConfigEntry),
Splitters: make(map[structs.ServiceID]*structs.ServiceSplitterConfigEntry),
Resolvers: make(map[structs.ServiceID]*structs.ServiceResolverConfigEntry),
Services: make(map[structs.ServiceID]*structs.ServiceConfigEntry),
Peers: make(map[string]*pbpeering.Peering),
ProxyDefaults: make(map[string]*structs.ProxyConfigEntry),
Routers: make(map[structs.ServiceID]*structs.ServiceRouterConfigEntry),
Splitters: make(map[structs.ServiceID]*structs.ServiceSplitterConfigEntry),
Resolvers: make(map[structs.ServiceID]*structs.ServiceResolverConfigEntry),
Services: make(map[structs.ServiceID]*structs.ServiceConfigEntry),
Peers: make(map[string]*pbpeering.Peering),
ProxyDefaults: make(map[string]*structs.ProxyConfigEntry),
SamenessGroups: make(map[string]*structs.SamenessGroupConfigEntry),
}
}

View File

@ -1297,10 +1297,11 @@ func readDiscoveryChainConfigEntriesTxn(
// the end of this function to indicate "no such entry".
var (
todoSplitters = make(map[structs.ServiceID]struct{})
todoResolvers = make(map[structs.ServiceID]struct{})
todoDefaults = make(map[structs.ServiceID]struct{})
todoPeers = make(map[string]struct{})
todoSplitters = make(map[structs.ServiceID]struct{})
todoResolvers = make(map[structs.ServiceID]struct{})
todoDefaults = make(map[structs.ServiceID]struct{})
todoPeers = make(map[string]struct{})
todoSamenessGroups = make(map[string]struct{})
)
sid := structs.NewServiceID(serviceName, entMeta)
@ -1406,6 +1407,10 @@ func readDiscoveryChainConfigEntriesTxn(
for _, peer := range resolver.RelatedPeers() {
todoPeers[peer] = struct{}{}
}
for _, peer := range resolver.RelatedSamenessGroups() {
todoSamenessGroups[peer] = struct{}{}
}
}
for {
@ -1448,6 +1453,26 @@ func readDiscoveryChainConfigEntriesTxn(
}
peerEntMeta := structs.DefaultEnterpriseMetaInPartition(entMeta.PartitionOrDefault())
for sg := range todoSamenessGroups {
idx, entry, err := getSamenessGroupConfigEntryTxn(tx, ws, sg, overrides, peerEntMeta)
if err != nil {
return 0, nil, err
}
if idx > maxIdx {
maxIdx = idx
}
if entry == nil {
continue
}
for _, e := range entry.Members {
if e.Peer != "" {
todoPeers[e.Peer] = struct{}{}
}
}
res.SamenessGroups[sg] = entry
}
for peerName := range todoPeers {
q := Query{
Value: peerName,

View File

@ -6,6 +6,8 @@ package state
import (
"fmt"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/configentry"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/go-memdb"
)
@ -27,3 +29,19 @@ func (*SamenessGroupDefaultIndex) FromArgs(args ...interface{}) ([]byte, error)
func checkSamenessGroup(tx ReadTxn, newConfig structs.ConfigEntry) error {
return fmt.Errorf("sameness-groups are an enterprise-only feature")
}
// getExportedServicesConfigEntryTxn is a convenience method for fetching a
// sameness-group config entries.
//
// If an override KEY is present for the requested config entry, the index
// returned will be 0. Any override VALUE (nil or otherwise) will be returned
// if there is a KEY match.
func getSamenessGroupConfigEntryTxn(
tx ReadTxn,
ws memdb.WatchSet,
name string,
overrides map[configentry.KindName]structs.ConfigEntry,
entMeta *acl.EnterpriseMeta,
) (uint64, *structs.SamenessGroupConfigEntry, error) {
return 0, nil, nil
}

View File

@ -1053,6 +1053,12 @@ func (e *ServiceResolverConfigEntry) Validate() error {
}
switch {
case r.SamenessGroup != "" && r.ServiceSubset != "":
return fmt.Errorf("Redirect.SamenessGroup cannot be set with Redirect.ServiceSubset")
case r.SamenessGroup != "" && r.Partition != "":
return fmt.Errorf("Redirect.Partition cannot be set with Redirect.SamenessGroup")
case r.SamenessGroup != "" && r.Datacenter != "":
return fmt.Errorf("Redirect.SamenessGroup cannot be set with Redirect.Datacenter")
case r.Peer != "" && r.ServiceSubset != "":
return fmt.Errorf("Redirect.Peer cannot be set with Redirect.ServiceSubset")
case r.Peer != "" && r.Partition != "":
@ -1116,6 +1122,17 @@ func (e *ServiceResolverConfigEntry) Validate() error {
}
}
if f.SamenessGroup != "" {
switch {
case len(f.Datacenters) > 0:
return fmt.Errorf("Bad Failover[%q]: SamenessGroup cannot be set with Datacenters", subset)
case f.ServiceSubset != "":
return fmt.Errorf("Bad Failover[%q]: SamenessGroup cannot be set with ServiceSubset", subset)
case len(f.Targets) > 0:
return fmt.Errorf("Bad Failover[%q]: SamenessGroup cannot be set with Targets", subset)
}
}
if len(f.Datacenters) != 0 && len(f.Targets) != 0 {
return fmt.Errorf("Bad Failover[%q]: Targets cannot be set with Datacenters", subset)
}
@ -1341,6 +1358,10 @@ type ServiceResolverRedirect struct {
// Peer is the name of the cluster peer to resolve the service from instead
// of the current one (optional).
Peer string `json:",omitempty"`
// SamenessGroup is the name of the sameness group to resolve the service from instead
// of the local partition.
SamenessGroup string `json:",omitempty"`
}
func (r *ServiceResolverRedirect) ToDiscoveryTargetOpts() DiscoveryTargetOpts {
@ -1355,7 +1376,13 @@ func (r *ServiceResolverRedirect) ToDiscoveryTargetOpts() DiscoveryTargetOpts {
}
func (r *ServiceResolverRedirect) isEmpty() bool {
return r.Service == "" && r.ServiceSubset == "" && r.Namespace == "" && r.Partition == "" && r.Datacenter == "" && r.Peer == ""
return r.Service == "" &&
r.ServiceSubset == "" &&
r.Namespace == "" &&
r.Partition == "" &&
r.Datacenter == "" &&
r.Peer == "" &&
r.SamenessGroup == ""
}
// There are some restrictions on what is allowed in here:
@ -1400,6 +1427,9 @@ type ServiceResolverFailover struct {
// Policy specifies the exact mechanism used for failover.
Policy *ServiceResolverFailoverPolicy `json:",omitempty"`
// SamenessGroup specifies the sameness group to failover to.
SamenessGroup string `json:",omitempty"`
}
type ServiceResolverFailoverPolicy struct {

View File

@ -38,6 +38,10 @@ func (redir *ServiceResolverRedirect) ValidateEnterprise() error {
return fmt.Errorf("Setting Namespace requires Consul Enterprise")
}
if redir.SamenessGroup != "" {
return fmt.Errorf("Setting SamenessGroup requires Consul Enterprise")
}
return nil
}
@ -54,6 +58,10 @@ func (failover *ServiceResolverFailover) ValidateEnterprise() error {
return fmt.Errorf("Setting Namespace requires Consul Enterprise")
}
if failover.SamenessGroup != "" {
return fmt.Errorf("Setting SamenessGroup requires Consul Enterprise")
}
return nil
}
@ -102,3 +110,8 @@ func (f *ServiceResolverFailoverPolicy) ValidateEnterprise() error {
return nil
}
// RelatedSamenessGroups doesn't return anything on open source.
func (e *ServiceResolverConfigEntry) RelatedSamenessGroups() []string {
return nil
}

View File

@ -21,6 +21,19 @@ func TestServiceResolverConfigEntry_OSS(t *testing.T) {
}
cases := []testcase{
{
name: "failover with a sameness group on OSS",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Failover: map[string]ServiceResolverFailover{
"*": {
SamenessGroup: "ns1",
},
},
},
validateErr: `Bad Failover["*"]: Setting SamenessGroup requires Consul Enterprise`,
},
{
name: "failover with a namespace on OSS",
entry: &ServiceResolverConfigEntry{
@ -83,6 +96,17 @@ func TestServiceResolverConfigEntry_OSS(t *testing.T) {
},
validateErr: `Bad Failover["*"]: Setting failover policies requires Consul Enterprise`,
},
{
name: "setting redirect SamenessGroup on OSS",
entry: &ServiceResolverConfigEntry{
Kind: ServiceResolver,
Name: "test",
Redirect: &ServiceResolverRedirect{
SamenessGroup: "group",
},
},
validateErr: `Redirect: Setting SamenessGroup requires Consul Enterprise`,
},
{
name: "setting redirect Namespace on OSS",
entry: &ServiceResolverConfigEntry{

View File

@ -234,16 +234,18 @@ type ServiceResolverRedirect struct {
Partition string `json:",omitempty"`
Datacenter string `json:",omitempty"`
Peer string `json:",omitempty"`
SamenessGroup string `json:",omitempty"`
}
type ServiceResolverFailover struct {
Service string `json:",omitempty"`
ServiceSubset string `json:",omitempty" alias:"service_subset"`
// Referencing other partitions is not supported.
Namespace string `json:",omitempty"`
Datacenters []string `json:",omitempty"`
Targets []ServiceResolverFailoverTarget `json:",omitempty"`
Policy *ServiceResolverFailoverPolicy `json:",omitempty"`
Namespace string `json:",omitempty"`
Datacenters []string `json:",omitempty"`
Targets []ServiceResolverFailoverTarget `json:",omitempty"`
Policy *ServiceResolverFailoverPolicy `json:",omitempty"`
SamenessGroup string `json:",omitempty"`
}
type ServiceResolverFailoverTarget struct {

View File

@ -1415,6 +1415,7 @@ func ServiceResolverFailoverToStructs(s *ServiceResolverFailover, t *structs.Ser
ServiceResolverFailoverPolicyToStructs(s.Policy, &x)
t.Policy = &x
}
t.SamenessGroup = s.SamenessGroup
}
func ServiceResolverFailoverFromStructs(t *structs.ServiceResolverFailover, s *ServiceResolverFailover) {
if s == nil {
@ -1439,6 +1440,7 @@ func ServiceResolverFailoverFromStructs(t *structs.ServiceResolverFailover, s *S
ServiceResolverFailoverPolicyFromStructs(t.Policy, &x)
s.Policy = &x
}
s.SamenessGroup = t.SamenessGroup
}
func ServiceResolverFailoverPolicyToStructs(s *ServiceResolverFailoverPolicy, t *structs.ServiceResolverFailoverPolicy) {
if s == nil {
@ -1484,6 +1486,7 @@ func ServiceResolverRedirectToStructs(s *ServiceResolverRedirect, t *structs.Ser
t.Partition = s.Partition
t.Datacenter = s.Datacenter
t.Peer = s.Peer
t.SamenessGroup = s.SamenessGroup
}
func ServiceResolverRedirectFromStructs(t *structs.ServiceResolverRedirect, s *ServiceResolverRedirect) {
if s == nil {
@ -1495,6 +1498,7 @@ func ServiceResolverRedirectFromStructs(t *structs.ServiceResolverRedirect, s *S
s.Partition = t.Partition
s.Datacenter = t.Datacenter
s.Peer = t.Peer
s.SamenessGroup = t.SamenessGroup
}
func ServiceResolverSubsetToStructs(s *ServiceResolverSubset, t *structs.ServiceResolverSubset) {
if s == nil {

File diff suppressed because it is too large Load Diff

View File

@ -147,6 +147,7 @@ message ServiceResolverRedirect {
string Partition = 4;
string Datacenter = 5;
string Peer = 6;
string SamenessGroup = 7;
}
// mog annotation:
@ -161,6 +162,7 @@ message ServiceResolverFailover {
repeated string Datacenters = 4;
repeated ServiceResolverFailoverTarget Targets = 5;
ServiceResolverFailoverPolicy Policy = 6;
string SamenessGroup = 7;
}
// mog annotation: