Skip to content

Commit

Permalink
xds: deduplicate mesh gateway listeners in a stable way
Browse files Browse the repository at this point in the history
1.7.x backport of #9650

In a situation where the mesh gateway is configured to bind to multiple
network interfaces, we use a feature called 'tagged addresses'.
Sometimes an address is duplicated across multiple tags such as 'lan'
and 'lan_ipv4'.

There is code to deduplicate these things when creating envoy listeners,
but that code doesn't ensure that the same tag wins every time. If the
winning tag flaps between xDS discovery requests it will cause the
listener to be drained and replaced.
  • Loading branch information
rboyer committed Feb 5, 2021
1 parent ef0530d commit ae1b26b
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 11 deletions.
3 changes: 3 additions & 0 deletions .changelog/9650.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
xds: deduplicate mesh gateway listeners by address in a stable way to prevent some LDS churn
```
52 changes: 41 additions & 11 deletions agent/xds/listeners.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net"
"net/url"
"regexp"
"sort"
"strconv"
"strings"

Expand Down Expand Up @@ -190,8 +191,12 @@ func (s *Server) listenersFromSnapshotMeshGateway(cInfo connectionInfo, cfgSnap
s.Logger.Warn("failed to parse Connect.Proxy.Config", "error", err)
}

// TODO - prevent invalid configurations of binding to the same port/addr
// twice including with the any addresses
// We'll collect all of the desired listeners first, and deduplicate them later.
type namedAddress struct {
name string
structs.ServiceAddress
}
addrs := make([]namedAddress, 0)

var resources []proto.Message
if !cfg.NoDefaultBind {
Expand All @@ -200,25 +205,50 @@ func (s *Server) listenersFromSnapshotMeshGateway(cInfo connectionInfo, cfgSnap
addr = "0.0.0.0"
}

l, err := s.makeGatewayListener("default", addr, cfgSnap.Port, cfgSnap)
if err != nil {
return nil, err
a := structs.ServiceAddress{
Address: addr,
Port: cfgSnap.Port,
}
resources = append(resources, l)
addrs = append(addrs, namedAddress{name: "default", ServiceAddress: a})
}

if cfg.BindTaggedAddresses {
for name, addrCfg := range cfgSnap.TaggedAddresses {
l, err := s.makeGatewayListener(name, addrCfg.Address, addrCfg.Port, cfgSnap)
if err != nil {
return nil, err
a := structs.ServiceAddress{
Address: addrCfg.Address,
Port: addrCfg.Port,
}
resources = append(resources, l)
addrs = append(addrs, namedAddress{name: name, ServiceAddress: a})
}
}

for name, addrCfg := range cfg.BindAddresses {
l, err := s.makeGatewayListener(name, addrCfg.Address, addrCfg.Port, cfgSnap)
a := structs.ServiceAddress{
Address: addrCfg.Address,
Port: addrCfg.Port,
}
addrs = append(addrs, namedAddress{name: name, ServiceAddress: a})
}

// Prevent invalid configurations of binding to the same port/addr twice
// including with the any addresses
//
// Sort the list and then if two items share a service address, take the
// first one to ensure we generate one listener per address and it's
// stable.
sort.Slice(addrs, func(i, j int) bool {
return addrs[i].name < addrs[j].name
})

// Make listeners and deduplicate on the fly.
seen := make(map[structs.ServiceAddress]bool)
for _, a := range addrs {
if seen[a.ServiceAddress] {
continue
}
seen[a.ServiceAddress] = true

l, err := s.makeGatewayListener(a.name, a.Address, a.Port, cfgSnap)
if err != nil {
return nil, err
}
Expand Down

0 comments on commit ae1b26b

Please sign in to comment.