Sort nodes before creating consul catalog config
The watch of consul can return for various reasons and not of all of them require a reload of the config. The order of nodes provided by consul is not stable so to ensure a identical config is generated for an identical server set the nodes needs to be sorted before creating the config.
This commit is contained in:
parent
8e561d9f95
commit
70305266dc
2 changed files with 226 additions and 1 deletions
|
@ -2,6 +2,7 @@ package provider
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
@ -41,6 +42,35 @@ type catalogUpdate struct {
|
|||
Nodes []*api.ServiceEntry
|
||||
}
|
||||
|
||||
type nodeSorter []*api.ServiceEntry
|
||||
|
||||
func (a nodeSorter) Len() int {
|
||||
return len(a)
|
||||
}
|
||||
|
||||
func (a nodeSorter) Swap(i int, j int) {
|
||||
a[i], a[j] = a[j], a[i]
|
||||
}
|
||||
|
||||
func (a nodeSorter) Less(i int, j int) bool {
|
||||
lentr := a[i]
|
||||
rentr := a[j]
|
||||
|
||||
ls := strings.ToLower(lentr.Service.Service)
|
||||
lr := strings.ToLower(rentr.Service.Service)
|
||||
|
||||
if ls != lr {
|
||||
return ls < lr
|
||||
}
|
||||
if lentr.Service.Address != rentr.Service.Address {
|
||||
return lentr.Service.Address < rentr.Service.Address
|
||||
}
|
||||
if lentr.Node.Address != rentr.Node.Address {
|
||||
return lentr.Node.Address < rentr.Node.Address
|
||||
}
|
||||
return lentr.Service.Port < rentr.Service.Port
|
||||
}
|
||||
|
||||
func (provider *ConsulCatalog) watchServices(stopCh <-chan struct{}) <-chan map[string][]string {
|
||||
watchCh := make(chan map[string][]string)
|
||||
|
||||
|
@ -139,7 +169,7 @@ func (provider *ConsulCatalog) getBackendAddress(node *api.ServiceEntry) string
|
|||
}
|
||||
|
||||
func (provider *ConsulCatalog) getBackendName(node *api.ServiceEntry, index int) string {
|
||||
serviceName := node.Service.Service + "--" + node.Service.Address + "--" + strconv.Itoa(node.Service.Port)
|
||||
serviceName := strings.ToLower(node.Service.Service) + "--" + node.Service.Address + "--" + strconv.Itoa(node.Service.Port)
|
||||
|
||||
for _, tag := range node.Service.Tags {
|
||||
serviceName += "--" + normalize(tag)
|
||||
|
@ -200,6 +230,8 @@ func (provider *ConsulCatalog) buildConfig(catalog []catalogUpdate) *types.Confi
|
|||
|
||||
}
|
||||
}
|
||||
// Ensure a stable ordering of nodes so that identical configurations may be detected
|
||||
sort.Sort(nodeSorter(allNodes))
|
||||
|
||||
templateObjects := struct {
|
||||
Services []*serviceUpdate
|
||||
|
|
|
@ -2,6 +2,7 @@ package provider
|
|||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/types"
|
||||
|
@ -274,3 +275,195 @@ func TestConsulCatalogBuildConfig(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConsulCatalogNodeSorter(t *testing.T) {
|
||||
cases := []struct {
|
||||
nodes []*api.ServiceEntry
|
||||
expected []*api.ServiceEntry
|
||||
}{
|
||||
{
|
||||
nodes: []*api.ServiceEntry{},
|
||||
expected: []*api.ServiceEntry{},
|
||||
},
|
||||
{
|
||||
nodes: []*api.ServiceEntry{
|
||||
{
|
||||
Service: &api.AgentService{
|
||||
Service: "foo",
|
||||
Address: "127.0.0.1",
|
||||
Port: 80,
|
||||
},
|
||||
Node: &api.Node{
|
||||
Node: "localhost",
|
||||
Address: "127.0.0.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []*api.ServiceEntry{
|
||||
{
|
||||
Service: &api.AgentService{
|
||||
Service: "foo",
|
||||
Address: "127.0.0.1",
|
||||
Port: 80,
|
||||
},
|
||||
Node: &api.Node{
|
||||
Node: "localhost",
|
||||
Address: "127.0.0.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
nodes: []*api.ServiceEntry{
|
||||
{
|
||||
Service: &api.AgentService{
|
||||
Service: "foo",
|
||||
Address: "127.0.0.2",
|
||||
Port: 80,
|
||||
},
|
||||
Node: &api.Node{
|
||||
Node: "localhost",
|
||||
Address: "127.0.0.2",
|
||||
},
|
||||
},
|
||||
{
|
||||
Service: &api.AgentService{
|
||||
Service: "bar",
|
||||
Address: "127.0.0.2",
|
||||
Port: 81,
|
||||
},
|
||||
Node: &api.Node{
|
||||
Node: "localhost",
|
||||
Address: "127.0.0.2",
|
||||
},
|
||||
},
|
||||
{
|
||||
Service: &api.AgentService{
|
||||
Service: "foo",
|
||||
Address: "127.0.0.1",
|
||||
Port: 80,
|
||||
},
|
||||
Node: &api.Node{
|
||||
Node: "localhost",
|
||||
Address: "127.0.0.1",
|
||||
},
|
||||
},
|
||||
{
|
||||
Service: &api.AgentService{
|
||||
Service: "bar",
|
||||
Address: "127.0.0.2",
|
||||
Port: 80,
|
||||
},
|
||||
Node: &api.Node{
|
||||
Node: "localhost",
|
||||
Address: "127.0.0.2",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []*api.ServiceEntry{
|
||||
{
|
||||
Service: &api.AgentService{
|
||||
Service: "bar",
|
||||
Address: "127.0.0.2",
|
||||
Port: 80,
|
||||
},
|
||||
Node: &api.Node{
|
||||
Node: "localhost",
|
||||
Address: "127.0.0.2",
|
||||
},
|
||||
},
|
||||
{
|
||||
Service: &api.AgentService{
|
||||
Service: "bar",
|
||||
Address: "127.0.0.2",
|
||||
Port: 81,
|
||||
},
|
||||
Node: &api.Node{
|
||||
Node: "localhost",
|
||||
Address: "127.0.0.2",
|
||||
},
|
||||
},
|
||||
{
|
||||
Service: &api.AgentService{
|
||||
Service: "foo",
|
||||
Address: "127.0.0.1",
|
||||
Port: 80,
|
||||
},
|
||||
Node: &api.Node{
|
||||
Node: "localhost",
|
||||
Address: "127.0.0.1",
|
||||
},
|
||||
},
|
||||
{
|
||||
Service: &api.AgentService{
|
||||
Service: "foo",
|
||||
Address: "127.0.0.2",
|
||||
Port: 80,
|
||||
},
|
||||
Node: &api.Node{
|
||||
Node: "localhost",
|
||||
Address: "127.0.0.2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
nodes: []*api.ServiceEntry{
|
||||
{
|
||||
Service: &api.AgentService{
|
||||
Service: "foo",
|
||||
Address: "",
|
||||
Port: 80,
|
||||
},
|
||||
Node: &api.Node{
|
||||
Node: "localhost",
|
||||
Address: "127.0.0.2",
|
||||
},
|
||||
},
|
||||
{
|
||||
Service: &api.AgentService{
|
||||
Service: "foo",
|
||||
Address: "",
|
||||
Port: 80,
|
||||
},
|
||||
Node: &api.Node{
|
||||
Node: "localhost",
|
||||
Address: "127.0.0.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []*api.ServiceEntry{
|
||||
{
|
||||
Service: &api.AgentService{
|
||||
Service: "foo",
|
||||
Address: "",
|
||||
Port: 80,
|
||||
},
|
||||
Node: &api.Node{
|
||||
Node: "localhost",
|
||||
Address: "127.0.0.1",
|
||||
},
|
||||
},
|
||||
{
|
||||
Service: &api.AgentService{
|
||||
Service: "foo",
|
||||
Address: "",
|
||||
Port: 80,
|
||||
},
|
||||
Node: &api.Node{
|
||||
Node: "localhost",
|
||||
Address: "127.0.0.2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
sort.Sort(nodeSorter(c.nodes))
|
||||
actual := c.nodes
|
||||
if !reflect.DeepEqual(actual, c.expected) {
|
||||
t.Fatalf("expected %q, got %q", c.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue