FindClosestNode pings each node and returns the one with the lowest latency.
(nodes []Node)
| 74 | |
| 75 | // FindClosestNode pings each node and returns the one with the lowest latency. |
| 76 | func FindClosestNode(nodes []Node) (Node, error) { |
| 77 | if len(nodes) == 0 { |
| 78 | return Node{}, xerrors.New("no wgtunnel nodes") |
| 79 | } |
| 80 | |
| 81 | // Copy the nodes so we don't mutate the original. |
| 82 | nodes = append([]Node{}, nodes...) |
| 83 | |
| 84 | var ( |
| 85 | nodesMu sync.Mutex |
| 86 | eg = errgroup.Group{} |
| 87 | ) |
| 88 | for i, node := range nodes { |
| 89 | eg.Go(func() error { |
| 90 | pinger, err := ping.NewPinger(node.HostnameHTTPS) |
| 91 | if err != nil { |
| 92 | return err |
| 93 | } |
| 94 | |
| 95 | if runtime.GOOS == "windows" { |
| 96 | pinger.SetPrivileged(true) |
| 97 | } |
| 98 | |
| 99 | pinger.Count = 5 |
| 100 | pinger.Timeout = 5 * time.Second |
| 101 | err = pinger.Run() |
| 102 | if err != nil { |
| 103 | return err |
| 104 | } |
| 105 | |
| 106 | nodesMu.Lock() |
| 107 | nodes[i].AvgLatency = pinger.Statistics().AvgRtt |
| 108 | nodesMu.Unlock() |
| 109 | return nil |
| 110 | }) |
| 111 | } |
| 112 | |
| 113 | err := eg.Wait() |
| 114 | if err != nil { |
| 115 | return Node{}, err |
| 116 | } |
| 117 | |
| 118 | slices.SortFunc(nodes, func(a, b Node) int { |
| 119 | return slice.Ascending(a.AvgLatency, b.AvgLatency) |
| 120 | }) |
| 121 | return nodes[0], nil |
| 122 | } |