runtimes/google/ipc: fix ugly sort endpoints code.
Change-Id: Ib5b7f6f8bf9b68a317b80574ee4fc9f963fafec3
diff --git a/runtimes/google/ipc/sort_endpoints.go b/runtimes/google/ipc/sort_endpoints.go
index ab9f730..c31e072 100644
--- a/runtimes/google/ipc/sort_endpoints.go
+++ b/runtimes/google/ipc/sort_endpoints.go
@@ -73,30 +73,27 @@
return se
}
-func sortByProtocol(eps []*serverEndpoint) map[string][]*serverEndpoint {
+// sortByProtocols sorts the supplied slice of serverEndpoints into a hash
+// map keyed by the protocol of each of those endpoints where that protocol
+// is listed in the protocols parameter and keyed by '*' if not so listed.
+func sortByProtocol(eps []*serverEndpoint, protocols []string) (bool, map[string][]*serverEndpoint) {
byProtocol := make(map[string][]*serverEndpoint)
+ matched := false
for _, ep := range eps {
- p := ep.iep.Protocol
- byProtocol[p] = append(byProtocol[p], ep)
- }
- return byProtocol
-}
-
-func unmatchedProtocols(hashed map[string][]*serverEndpoint, protocols []string) []*serverEndpoint {
- unmatched := make([]*serverEndpoint, 0, 10)
- for p, eps := range hashed {
found := false
- for _, preferred := range protocols {
- if p == preferred {
+ for _, p := range protocols {
+ if ep.iep.Protocol == p || (p == "tcp" && strings.HasPrefix(ep.iep.Protocol, "tcp")) {
+ byProtocol[p] = append(byProtocol[p], ep)
found = true
+ matched = true
break
}
}
if !found {
- unmatched = append(unmatched, eps...)
+ byProtocol["*"] = append(byProtocol["*"], ep)
}
}
- return unmatched
+ return matched, byProtocol
}
func orderByLocality(ifcs netstate.AddrList, eps []*serverEndpoint) []*serverEndpoint {
@@ -167,8 +164,21 @@
return r
}
-var defaultPreferredProtocolOrder = []string{"unixfd", "tcp4", "tcp", "tcp6"}
+var defaultPreferredProtocolOrder = []string{"unixfd", "tcp4", "tcp", "*"}
+// filterAndOrderServers returns a set of servers that are compatible with
+// the current client in order of 'preference' specified by the supplied
+// protocols and a notion of 'locality' according to the supplied protocol
+// list as follows:
+// - if the protocol parameter is non-empty, then only servers matching those
+// protocols are returned and the endpoints are ordered first by protocol
+// and then by locality within each protocol. If tcp4 and unixfd are requested
+// for example then only protocols that match tcp4 and unixfd will returned
+// with the tcp4 ones preceeding the unixfd ones.
+// - if the protocol parameter is empty, then a default protocol ordering
+// will be used, but unlike the previous case, any servers that don't support
+// these protocols will be returned also, but following the default
+// preferences.
func filterAndOrderServers(servers []string, protocols []string) ([]string, error) {
errs := newErrorAccumulator()
vlog.VI(3).Infof("Candidates[%v]: %v", protocols, servers)
@@ -178,39 +188,25 @@
}
vlog.VI(3).Infof("Version Compatible: %v", compatible)
+ defaultOrdering := len(protocols) == 0
+
// put the server endpoints into per-protocol lists
- byProtocol := sortByProtocol(compatible)
+ matched, byProtocol := sortByProtocol(compatible, protocols)
- if len(protocols) > 0 {
- found := 0
- for _, p := range protocols {
- found += len(byProtocol[p])
- }
- if found == 0 {
- return nil, fmt.Errorf("failed to find any servers compatible with %v from %s", protocols, servers)
- }
+ if !defaultOrdering && !matched {
+ return nil, fmt.Errorf("failed to find any servers compatible with %v from %s", protocols, servers)
}
-
- // If a set of protocols is specified, then we will order
- // and return endpoints that contain those protocols alone.
- // However, if no protocols are supplied we'll order by
- // a default ordering but append any endpoints that don't belong
- // to that default ordering set to the returned endpoints.
- remaining := []*serverEndpoint{}
preferredProtocolOrder := defaultPreferredProtocolOrder
- if len(protocols) > 0 {
+ if !defaultOrdering {
preferredProtocolOrder = protocols
- } else {
- remaining = unmatchedProtocols(byProtocol, preferredProtocolOrder)
}
vlog.VI(3).Infof("Have Protocols(%v): %v", protocols, byProtocol)
networks, err := netstate.GetAll()
if err != nil {
- r := sliceByProtocol(byProtocol, preferredProtocolOrder)
- r = append(r, slice(remaining)...)
- return r, nil
+ // return whatever we have now, just not sorted by locality.
+ return sliceByProtocol(byProtocol, preferredProtocolOrder), nil
}
ordered := make([]*serverEndpoint, 0, len(byProtocol))
@@ -219,9 +215,6 @@
ordered = append(ordered, o...)
}
- if len(protocols) == 0 {
- ordered = append(ordered, remaining...)
- }
vlog.VI(2).Infof("Ordered By Locality: %v", ordered)
return slice(ordered), nil
}
diff --git a/runtimes/google/ipc/sort_internal_test.go b/runtimes/google/ipc/sort_internal_test.go
index 5b9b4cf..c234557 100644
--- a/runtimes/google/ipc/sort_internal_test.go
+++ b/runtimes/google/ipc/sort_internal_test.go
@@ -56,23 +56,44 @@
name := naming.JoinAddressName(naming.FormatEndpoint("foobar", a), "")
servers = append(servers, name)
}
+ for _, a := range []string{"127.0.0.7", "127.0.0.8"} {
+ name := naming.JoinAddressName(naming.FormatEndpoint("tcp6", a), "")
+ servers = append(servers, name)
+ }
- got, err := filterAndOrderServers(servers, []string{"foobar", "tcp"})
+ got, err := filterAndOrderServers(servers, []string{"batman"})
+ if err == nil {
+ t.Fatalf("expected an error")
+ }
+
+ got, err = filterAndOrderServers(servers, []string{"foobar", "tcp4"})
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
- // Just foobar and tcp
+ // Just foobar and tcp4
want := []string{
"/@2@foobar@127.0.0.10@00000000000000000000000000000000@@@@",
"/@2@foobar@127.0.0.11@00000000000000000000000000000000@@@@",
- "/@2@tcp@127.0.0.3@00000000000000000000000000000000@@@@",
- "/@2@tcp@127.0.0.4@00000000000000000000000000000000@@@@",
+ "/@2@tcp4@127.0.0.1@00000000000000000000000000000000@@@@",
+ "/@2@tcp4@127.0.0.2@00000000000000000000000000000000@@@@",
}
if !reflect.DeepEqual(got, want) {
t.Errorf("got: %v, want %v", got, want)
}
+ got, err = filterAndOrderServers(servers, []string{"tcp"})
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ // tcp or tcp4
+ want = []string{
+ "/@2@tcp4@127.0.0.1@00000000000000000000000000000000@@@@",
+ "/@2@tcp4@127.0.0.2@00000000000000000000000000000000@@@@",
+ "/@2@tcp@127.0.0.3@00000000000000000000000000000000@@@@",
+ "/@2@tcp@127.0.0.4@00000000000000000000000000000000@@@@",
+ }
+
// Everything, since we didn't specify a protocol
got, err = filterAndOrderServers(servers, []string{})
if err != nil {
@@ -82,10 +103,12 @@
// original ordering within each protocol, with protocols that
// are not in the default ordering list at the end.
want = []string{
- "/@2@tcp4@127.0.0.1@00000000000000000000000000000000@@@@",
- "/@2@tcp4@127.0.0.2@00000000000000000000000000000000@@@@",
"/@2@tcp@127.0.0.3@00000000000000000000000000000000@@@@",
"/@2@tcp@127.0.0.4@00000000000000000000000000000000@@@@",
+ "/@2@tcp4@127.0.0.1@00000000000000000000000000000000@@@@",
+ "/@2@tcp4@127.0.0.2@00000000000000000000000000000000@@@@",
+ "/@2@tcp6@127.0.0.7@00000000000000000000000000000000@@@@",
+ "/@2@tcp6@127.0.0.8@00000000000000000000000000000000@@@@",
"/@2@foobar@127.0.0.10@00000000000000000000000000000000@@@@",
"/@2@foobar@127.0.0.11@00000000000000000000000000000000@@@@",
}