diff --git a/api/agent.go b/api/agent.go index 7633a4538e7a..32c0c87d5b48 100644 --- a/api/agent.go +++ b/api/agent.go @@ -178,3 +178,22 @@ type AgentMember struct { DelegateMax uint8 DelegateCur uint8 } + +// AgentMembersNameSort implements sort.Interface for []*AgentMembersNameSort +// based on the Name, DC and Region +type AgentMembersNameSort []*AgentMember + +func (a AgentMembersNameSort) Len() int { return len(a) } +func (a AgentMembersNameSort) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a AgentMembersNameSort) Less(i, j int) bool { + if a[i].Tags["region"] != a[j].Tags["region"] { + return a[i].Tags["region"] < a[j].Tags["region"] + } + + if a[i].Tags["dc"] != a[j].Tags["dc"] { + return a[i].Tags["dc"] < a[j].Tags["dc"] + } + + return a[i].Name < a[j].Name + +} diff --git a/api/agent_test.go b/api/agent_test.go index adb55640c227..d83ff2cf2a8e 100644 --- a/api/agent_test.go +++ b/api/agent_test.go @@ -1,6 +1,8 @@ package api import ( + "reflect" + "sort" "testing" "github.com/hashicorp/nomad/testutil" @@ -154,3 +156,117 @@ func TestAgent_SetServers(t *testing.T) { t.Fatalf("bad server list: %v", out) } } + +func (a *AgentMember) String() string { + return "{Name: " + a.Name + " Region: " + a.Tags["region"] + " DC: " + a.Tags["dc"] + "}" +} + +func TestAgents_Sort(t *testing.T) { + var sortTests = []struct { + in []*AgentMember + out []*AgentMember + }{ + { + []*AgentMember{ + &AgentMember{Name: "nomad-2.vac.us-east", + Tags: map[string]string{"region": "us-east", "dc": "us-east-1c"}}, + &AgentMember{Name: "nomad-1.global", + Tags: map[string]string{"region": "global", "dc": "dc1"}}, + &AgentMember{Name: "nomad-1.vac.us-east", + Tags: map[string]string{"region": "us-east", "dc": "us-east-1c"}}, + }, + []*AgentMember{ + &AgentMember{Name: "nomad-1.global", + Tags: map[string]string{"region": "global", "dc": "dc1"}}, + &AgentMember{Name: "nomad-1.vac.us-east", + Tags: map[string]string{"region": "us-east", "dc": "us-east-1c"}}, + &AgentMember{Name: "nomad-2.vac.us-east", + Tags: map[string]string{"region": "us-east", "dc": "us-east-1c"}}, + }, + }, + { + []*AgentMember{ + &AgentMember{Name: "nomad-02.tam.us-east", + Tags: map[string]string{"region": "us-east", "dc": "tampa"}}, + &AgentMember{Name: "nomad-02.pal.us-west", + Tags: map[string]string{"region": "us-west", "dc": "palo_alto"}}, + &AgentMember{Name: "nomad-01.pal.us-west", + Tags: map[string]string{"region": "us-west", "dc": "palo_alto"}}, + &AgentMember{Name: "nomad-01.tam.us-east", + Tags: map[string]string{"region": "us-east", "dc": "tampa"}}, + }, + []*AgentMember{ + &AgentMember{Name: "nomad-01.tam.us-east", + Tags: map[string]string{"region": "us-east", "dc": "tampa"}}, + &AgentMember{Name: "nomad-02.tam.us-east", + Tags: map[string]string{"region": "us-east", "dc": "tampa"}}, + &AgentMember{Name: "nomad-01.pal.us-west", + Tags: map[string]string{"region": "us-west", "dc": "palo_alto"}}, + &AgentMember{Name: "nomad-02.pal.us-west", + Tags: map[string]string{"region": "us-west", "dc": "palo_alto"}}, + }, + }, + { + []*AgentMember{ + &AgentMember{Name: "nomad-02.tam.us-east", + Tags: map[string]string{"region": "us-east", "dc": "tampa"}}, + &AgentMember{Name: "nomad-02.ams.europe", + Tags: map[string]string{"region": "europe", "dc": "amsterdam"}}, + &AgentMember{Name: "nomad-01.tam.us-east", + Tags: map[string]string{"region": "us-east", "dc": "tampa"}}, + &AgentMember{Name: "nomad-01.ams.europe", + Tags: map[string]string{"region": "europe", "dc": "amsterdam"}}, + }, + []*AgentMember{ + &AgentMember{Name: "nomad-01.ams.europe", + Tags: map[string]string{"region": "europe", "dc": "amsterdam"}}, + &AgentMember{Name: "nomad-02.ams.europe", + Tags: map[string]string{"region": "europe", "dc": "amsterdam"}}, + &AgentMember{Name: "nomad-01.tam.us-east", + Tags: map[string]string{"region": "us-east", "dc": "tampa"}}, + &AgentMember{Name: "nomad-02.tam.us-east", + Tags: map[string]string{"region": "us-east", "dc": "tampa"}}, + }, + }, + { + []*AgentMember{ + &AgentMember{Name: "nomad-02.ber.europe", + Tags: map[string]string{"region": "europe", "dc": "berlin"}}, + &AgentMember{Name: "nomad-02.ams.europe", + Tags: map[string]string{"region": "europe", "dc": "amsterdam"}}, + &AgentMember{Name: "nomad-01.ams.europe", + Tags: map[string]string{"region": "europe", "dc": "amsterdam"}}, + &AgentMember{Name: "nomad-01.ber.europe", + Tags: map[string]string{"region": "europe", "dc": "berlin"}}, + }, + []*AgentMember{ + &AgentMember{Name: "nomad-01.ams.europe", + Tags: map[string]string{"region": "europe", "dc": "amsterdam"}}, + &AgentMember{Name: "nomad-02.ams.europe", + Tags: map[string]string{"region": "europe", "dc": "amsterdam"}}, + &AgentMember{Name: "nomad-01.ber.europe", + Tags: map[string]string{"region": "europe", "dc": "berlin"}}, + &AgentMember{Name: "nomad-02.ber.europe", + Tags: map[string]string{"region": "europe", "dc": "berlin"}}, + }, + }, + { + []*AgentMember{ + &AgentMember{Name: "nomad-1.global"}, + &AgentMember{Name: "nomad-3.global"}, + &AgentMember{Name: "nomad-2.global"}, + }, + []*AgentMember{ + &AgentMember{Name: "nomad-1.global"}, + &AgentMember{Name: "nomad-2.global"}, + &AgentMember{Name: "nomad-3.global"}, + }, + }, + } + for _, tt := range sortTests { + sort.Sort(AgentMembersNameSort(tt.in)) + if !reflect.DeepEqual(tt.in, tt.out) { + t.Errorf("\necpected: %s\nget : %s", tt.in, tt.out) + } + } +} diff --git a/command/server_members.go b/command/server_members.go index 779de332d423..2129f2375637 100644 --- a/command/server_members.go +++ b/command/server_members.go @@ -2,6 +2,7 @@ package command import ( "fmt" + "sort" "strings" "github.com/hashicorp/nomad/api" @@ -68,6 +69,9 @@ func (c *ServerMembersCommand) Run(args []string) int { return 1 } + // Sort the members + sort.Sort(api.AgentMembersNameSort(mem)) + // Format the list var out []string if detailed {