Skip to content

Commit

Permalink
add peer locality to discovery chains
Browse files Browse the repository at this point in the history
  • Loading branch information
erichaberkorn committed Mar 9, 2023
1 parent e2c4a78 commit e58a8da
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 6 deletions.
3 changes: 3 additions & 0 deletions agent/configentry/discoverychain.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package configentry

import (
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/proto/private/pbpeering"
)

// DiscoveryChainSet is a wrapped set of raw cross-referenced config entries
Expand All @@ -13,6 +14,7 @@ type DiscoveryChainSet struct {
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
}

Expand All @@ -22,6 +24,7 @@ func NewDiscoveryChainSet() *DiscoveryChainSet {
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),
}
}
Expand Down
5 changes: 5 additions & 0 deletions agent/consul/discoverychain/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/hashicorp/consul/agent/configentry"
"github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/proto/private/pbpeering"
)

type CompileRequest struct {
Expand Down Expand Up @@ -736,6 +737,10 @@ func (c *compiler) newTarget(opts structs.DiscoveryTargetOpts) *structs.Discover
// Use the same representation for the name. This will NOT be overridden
// later.
t.Name = t.SNI
peer := c.entries.Peers[opts.Peer]
if peer != nil && peer.Remote != nil && peer.Remote.Locality != nil {
t.Locality = pbpeering.LocalityToStruct(peer.Remote.Locality)
}
}

prev, ok := c.loadedTargets[t.ID]
Expand Down
28 changes: 28 additions & 0 deletions agent/consul/state/config_entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package state
import (
"errors"
"fmt"
"strings"

memdb "github.com/hashicorp/go-memdb"

Expand Down Expand Up @@ -1293,6 +1294,7 @@ func readDiscoveryChainConfigEntriesTxn(
todoSplitters = make(map[structs.ServiceID]struct{})
todoResolvers = make(map[structs.ServiceID]struct{})
todoDefaults = make(map[structs.ServiceID]struct{})
todoPeers = make(map[string]struct{})
)

sid := structs.NewServiceID(serviceName, entMeta)
Expand Down Expand Up @@ -1394,6 +1396,10 @@ func readDiscoveryChainConfigEntriesTxn(
for _, svc := range resolver.ListRelatedServices() {
todoResolvers[svc] = struct{}{}
}

for _, peer := range resolver.RelatedPeers() {
todoPeers[peer] = struct{}{}
}
}

for {
Expand Down Expand Up @@ -1435,6 +1441,28 @@ func readDiscoveryChainConfigEntriesTxn(
res.Services[svcID] = entry
}

peerEntMeta := structs.DefaultEnterpriseMetaInPartition(entMeta.PartitionOrDefault())
for peerName := range todoPeers {
q := Query{
Value: strings.ToLower(peerName),
EnterpriseMeta: *peerEntMeta,
}
idx, entry, err := peeringReadTxn(tx, ws, q)
if err != nil {
return 0, nil, err
}
if idx > maxIdx {
maxIdx = idx
}

if entry == nil {
res.Peers[peerName] = nil
continue
}

res.Peers[peerName] = entry
}

// Strip nils now that they are no longer necessary.
for sid, entry := range res.Routers {
if entry == nil {
Expand Down
49 changes: 49 additions & 0 deletions agent/consul/state/config_entry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (

"github.com/hashicorp/consul/agent/configentry"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/proto/private/pbpeering"
"github.com/hashicorp/consul/proto/private/prototest"
"github.com/hashicorp/consul/sdk/testutil"
)

Expand Down Expand Up @@ -2065,6 +2067,53 @@ func TestStore_ReadDiscoveryChainConfigEntries_SubsetSplit(t *testing.T) {
require.Len(t, entrySet.Services, 1)
}

func TestStore_ReadDiscoveryChainConfigEntries_PeerLocality(t *testing.T) {
s := testConfigStateStore(t)

entries := []structs.ConfigEntry{
&structs.ServiceConfigEntry{
Kind: structs.ServiceDefaults,
Name: "main",
Protocol: "http",
},
&structs.ServiceResolverConfigEntry{
Kind: structs.ServiceResolver,
Name: "main",
Failover: map[string]structs.ServiceResolverFailover{
"*": {
Targets: []structs.ServiceResolverFailoverTarget{
{Peer: "cluster-01"},
{Peer: "cluster-02"}, // Non-existant
},
},
},
},
}

for _, entry := range entries {
require.NoError(t, s.EnsureConfigEntry(0, entry))
}

cluster01Peering := &pbpeering.Peering{
ID: testFooPeerID,
Name: "cluster-01",
}
err := s.PeeringWrite(0, &pbpeering.PeeringWriteRequest{Peering: cluster01Peering})
require.NoError(t, err)

_, entrySet, err := s.readDiscoveryChainConfigEntries(nil, "main", nil, nil)
require.NoError(t, err)

require.Len(t, entrySet.Routers, 0)
require.Len(t, entrySet.Splitters, 0)
require.Len(t, entrySet.Resolvers, 1)
require.Len(t, entrySet.Services, 1)
prototest.AssertDeepEqual(t, entrySet.Peers, map[string]*pbpeering.Peering{
"cluster-01": cluster01Peering,
"cluster-02": nil,
})
}

// TODO(rb): add ServiceIntentions tests

func TestStore_ValidateGatewayNamesCannotBeShared(t *testing.T) {
Expand Down
30 changes: 30 additions & 0 deletions agent/structs/config_entry_discoverychain.go
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,36 @@ type ServiceResolverConfigEntry struct {
RaftIndex
}

func (e *ServiceResolverConfigEntry) RelatedPeers() []string {
peers := make(map[string]struct{})

if r := e.Redirect; r != nil && r.Peer != "" {
peers[r.Peer] = struct{}{}
}

if e.Failover != nil {
for _, f := range e.Failover {
for _, t := range f.Targets {
if t.Peer != "" {
peers[t.Peer] = struct{}{}
}
}
}
}

if len(peers) == 0 {
return nil
}

out := make([]string, 0, len(peers))
for svc := range peers {
out = append(out, svc)
}
sort.Strings(out)

return out
}

func (e *ServiceResolverConfigEntry) MarshalJSON() ([]byte, error) {
type Alias ServiceResolverConfigEntry
exported := &struct {
Expand Down
13 changes: 7 additions & 6 deletions agent/structs/discovery_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,12 +190,13 @@ type DiscoveryTarget struct {
// chain. It should be treated as a per-compile opaque string.
ID string `json:",omitempty"`

Service string `json:",omitempty"`
ServiceSubset string `json:",omitempty"`
Namespace string `json:",omitempty"`
Partition string `json:",omitempty"`
Datacenter string `json:",omitempty"`
Peer string `json:",omitempty"`
Service string `json:",omitempty"`
ServiceSubset string `json:",omitempty"`
Namespace string `json:",omitempty"`
Partition string `json:",omitempty"`
Datacenter string `json:",omitempty"`
Peer string `json:",omitempty"`
Locality *Locality `json:",omitempty"`

MeshGateway MeshGatewayConfig `json:",omitempty"`
Subset ServiceResolverSubset `json:",omitempty"`
Expand Down
4 changes: 4 additions & 0 deletions agent/structs/structs.deepcopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ func (o *CompiledDiscoveryChain) DeepCopy() *CompiledDiscoveryChain {
if v2 != nil {
cp_Targets_v2 = new(DiscoveryTarget)
*cp_Targets_v2 = *v2
if v2.Locality != nil {
cp_Targets_v2.Locality = new(Locality)
*cp_Targets_v2.Locality = *v2.Locality
}
}
cp.Targets[k2] = cp_Targets_v2
}
Expand Down
14 changes: 14 additions & 0 deletions proto/private/pbpeering/peering.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,9 +332,23 @@ func (o *PeeringTrustBundle) DeepCopy() *PeeringTrustBundle {
return cp
}

// TODO: handle this with mog
// LocalityFromStruct converts a struct Locality to a protobuf Locality.
func LocalityFromStruct(l structs.Locality) *Locality {
return &Locality{
Region: l.Region,
Zone: l.Zone,
}
}

// TODO: handle this with mog
// LocalityToStruct converts a protobuf Locality to a struct Locality.
func LocalityToStruct(l *Locality) *structs.Locality {
if l == nil {
return nil
}
return &structs.Locality{
Region: l.Region,
Zone: l.Zone,
}
}

0 comments on commit e58a8da

Please sign in to comment.