cmd/tailscale/cli: make exit-node list not random

The output was changing randomly per run, due to range over a map.

Then some misc style tweaks I noticed while debugging.

Fixes #11629

Change-Id: I67aef0e68566994e5744d4828002f6eb70810ee1
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/11644/head
Brad Fitzpatrick 2 months ago committed by Brad Fitzpatrick
parent 6e334e64a1
commit ac2522092d

@ -70,7 +70,6 @@ func runExitNodeList(ctx context.Context, args []string) error {
// We only show exit nodes under the exit-node subcommand. // We only show exit nodes under the exit-node subcommand.
continue continue
} }
peers = append(peers, ps) peers = append(peers, ps)
} }
@ -90,7 +89,6 @@ func runExitNodeList(ctx context.Context, args []string) error {
for _, country := range filteredPeers.Countries { for _, country := range filteredPeers.Countries {
for _, city := range country.Cities { for _, city := range country.Cities {
for _, peer := range city.Peers { for _, peer := range city.Peers {
fmt.Fprintf(w, "\n %s\t%s\t%s\t%s\t%s\t", peer.TailscaleIPs[0], strings.Trim(peer.DNSName, "."), country.Name, city.Name, peerStatus(peer)) fmt.Fprintf(w, "\n %s\t%s\t%s\t%s\t%s\t", peer.TailscaleIPs[0], strings.Trim(peer.DNSName, "."), country.Name, city.Name, peerStatus(peer))
} }
} }
@ -137,46 +135,51 @@ type filteredCity struct {
const noLocationData = "-" const noLocationData = "-"
var noLocation = &tailcfg.Location{
Country: noLocationData,
CountryCode: noLocationData,
City: noLocationData,
CityCode: noLocationData,
}
// filterFormatAndSortExitNodes filters and sorts exit nodes into // filterFormatAndSortExitNodes filters and sorts exit nodes into
// alphabetical order, by country, city and then by priority if // alphabetical order, by country, city and then by priority if
// present. // present.
// If an exit node has location data, and the country has more than // If an exit node has location data, and the country has more than
// once city, an `Any` city is added to the country that contains the // one city, an `Any` city is added to the country that contains the
// highest priority exit node within that country. // highest priority exit node within that country.
// For exit nodes without location data, their country fields are // For exit nodes without location data, their country fields are
// defined as '-' to indicate that the data is not available. // defined as '-' to indicate that the data is not available.
func filterFormatAndSortExitNodes(peers []*ipnstate.PeerStatus, filterBy string) filteredExitNodes { func filterFormatAndSortExitNodes(peers []*ipnstate.PeerStatus, filterBy string) filteredExitNodes {
// first get peers into some fixed order, as code below doesn't break ties
// and our input comes from a random range-over-map.
slices.SortFunc(peers, func(a, b *ipnstate.PeerStatus) int {
return strings.Compare(a.DNSName, b.DNSName)
})
countries := make(map[string]*filteredCountry) countries := make(map[string]*filteredCountry)
cities := make(map[string]*filteredCity) cities := make(map[string]*filteredCity)
for _, ps := range peers { for _, ps := range peers {
if ps.Location == nil { loc := cmp.Or(ps.Location, noLocation)
ps.Location = &tailcfg.Location{
Country: noLocationData,
CountryCode: noLocationData,
City: noLocationData,
CityCode: noLocationData,
}
}
if filterBy != "" && ps.Location.Country != filterBy { if filterBy != "" && loc.Country != filterBy {
continue continue
} }
co, coOK := countries[ps.Location.CountryCode] co, ok := countries[loc.CountryCode]
if !coOK { if !ok {
co = &filteredCountry{ co = &filteredCountry{
Name: ps.Location.Country, Name: loc.Country,
} }
countries[ps.Location.CountryCode] = co countries[loc.CountryCode] = co
} }
ci, ciOK := cities[ps.Location.CityCode] ci, ok := cities[loc.CityCode]
if !ciOK { if !ok {
ci = &filteredCity{ ci = &filteredCity{
Name: ps.Location.City, Name: loc.City,
} }
cities[ps.Location.CityCode] = ci cities[loc.CityCode] = ci
co.Cities = append(co.Cities, ci) co.Cities = append(co.Cities, ci)
} }
ci.Peers = append(ci.Peers, ps) ci.Peers = append(ci.Peers, ps)
@ -193,10 +196,10 @@ func filterFormatAndSortExitNodes(peers []*ipnstate.PeerStatus, filterBy string)
continue continue
} }
var countryANYPeer []*ipnstate.PeerStatus var countryAnyPeer []*ipnstate.PeerStatus
for _, city := range country.Cities { for _, city := range country.Cities {
sortPeersByPriority(city.Peers) sortPeersByPriority(city.Peers)
countryANYPeer = append(countryANYPeer, city.Peers...) countryAnyPeer = append(countryAnyPeer, city.Peers...)
var reducedCityPeers []*ipnstate.PeerStatus var reducedCityPeers []*ipnstate.PeerStatus
for i, peer := range city.Peers { for i, peer := range city.Peers {
if i == 0 || peer.ExitNode { if i == 0 || peer.ExitNode {
@ -208,7 +211,7 @@ func filterFormatAndSortExitNodes(peers []*ipnstate.PeerStatus, filterBy string)
city.Peers = reducedCityPeers city.Peers = reducedCityPeers
} }
sortByCityName(country.Cities) sortByCityName(country.Cities)
sortPeersByPriority(countryANYPeer) sortPeersByPriority(countryAnyPeer)
if len(country.Cities) > 1 { if len(country.Cities) > 1 {
// For countries with more than one city, we want to return the // For countries with more than one city, we want to return the
@ -216,7 +219,7 @@ func filterFormatAndSortExitNodes(peers []*ipnstate.PeerStatus, filterBy string)
country.Cities = append([]*filteredCity{ country.Cities = append([]*filteredCity{
{ {
Name: "Any", Name: "Any",
Peers: []*ipnstate.PeerStatus{countryANYPeer[0]}, Peers: []*ipnstate.PeerStatus{countryAnyPeer[0]},
}, },
}, country.Cities...) }, country.Cities...)
} }

Loading…
Cancel
Save