diff --git a/agent/structs/structs.go b/agent/structs/structs.go index 1edf14a996..e2849e5665 100644 --- a/agent/structs/structs.go +++ b/agent/structs/structs.go @@ -2127,6 +2127,14 @@ func ServiceGatewayVirtualIPTag(sn ServiceName) string { type ServiceList []ServiceName +// Len implements sort.Interface. +func (s ServiceList) Len() int { return len(s) } + +// Swap implements sort.Interface. +func (s ServiceList) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +func (s ServiceList) Sort() { sort.Sort(s) } + type IndexedServiceList struct { Services ServiceList QueryMeta diff --git a/agent/structs/structs_oss.go b/agent/structs/structs_oss.go index bf911f5a8c..a5a0def097 100644 --- a/agent/structs/structs_oss.go +++ b/agent/structs/structs_oss.go @@ -114,6 +114,12 @@ func ServiceNameFromString(input string) ServiceName { return ServiceName{Name: id} } +// Less implements sort.Interface. +func (s ServiceList) Less(i, j int) bool { + a, b := s[i], s[j] + return a.Name < b.Name +} + func (cid CheckID) String() string { return string(cid.ID) } diff --git a/agent/structs/structs_test.go b/agent/structs/structs_test.go index 2164b10992..0b6efb3309 100644 --- a/agent/structs/structs_test.go +++ b/agent/structs/structs_test.go @@ -3,6 +3,7 @@ package structs import ( "encoding/json" "fmt" + "math/rand" "reflect" "strings" "testing" @@ -2856,3 +2857,82 @@ func TestGatewayService_IsSame(t *testing.T) { t.Fatalf("should be equal, was %#v VS %#v", g, other) } } + +func TestServiceList_Sort(t *testing.T) { + type testcase struct { + name string + list []ServiceName + expect []ServiceName + } + + run := func(t *testing.T, tc testcase) { + t.Run("written order", func(t *testing.T) { + ServiceList(tc.list).Sort() + require.Equal(t, tc.expect, tc.list) + }) + t.Run("random order", func(t *testing.T) { + rand.Shuffle(len(tc.list), func(i, j int) { + tc.list[i], tc.list[j] = tc.list[j], tc.list[i] + }) + ServiceList(tc.list).Sort() + require.Equal(t, tc.expect, tc.list) + }) + } + + sn := func(name string) ServiceName { + return NewServiceName(name, nil) + } + + cases := []testcase{ + { + name: "nil", + list: nil, + expect: nil, + }, + { + name: "empty", + list: []ServiceName{}, + expect: []ServiceName{}, + }, + { + name: "one", + list: []ServiceName{sn("foo")}, + expect: []ServiceName{sn("foo")}, + }, + { + name: "multiple", + list: []ServiceName{ + sn("food"), + sn("zip"), + sn("Bar"), + sn("ba"), + sn("foo"), + sn("bar"), + sn("Foo"), + sn("Zip"), + sn("foo"), + sn("bar"), + sn("barrier"), + }, + expect: []ServiceName{ + sn("Bar"), + sn("Foo"), + sn("Zip"), + sn("ba"), + sn("bar"), + sn("bar"), + sn("barrier"), + sn("foo"), + sn("foo"), + sn("food"), + sn("zip"), + }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + run(t, tc) + }) + } +}