mirror of
https://github.com/psviderski/uncloud.git
synced 2026-10-08 22:24:54 +00:00
* Add corrosion admin client function to get RTT to other machines in the cluster * Add `uc machine rtt` showing all pair-wise RTT stats from corrosion * Add long description to machine rtt command * Include machine peer RTTs in InspectMachine instead of adding new gRPC API for it * Add nil check on InspectMachine's Rtt field to protect from a potential edge case * Generate docs for machine rtt command * Handle m.Message being nil when a node is down or unavailble * Update cli-docs * Use tui Table instead of tabwriter * Change reported RTT to the median rather than mean. Still calculate include stddev, as it might be a useful indication of network jitter. * Show RTT to peers in `wg show` output * Update cli docs
107 lines
2.6 KiB
Go
107 lines
2.6 KiB
Go
package machine
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"math"
|
|
"sort"
|
|
|
|
"github.com/psviderski/uncloud/internal/cli"
|
|
"github.com/psviderski/uncloud/internal/cli/tui"
|
|
"github.com/spf13/cobra"
|
|
"google.golang.org/protobuf/types/known/emptypb"
|
|
)
|
|
|
|
func NewRTTCommand() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "rtt",
|
|
Short: "Show round-trip times between machines.",
|
|
Long: `Show round-trip times between machines.
|
|
|
|
Round-trip time statistics are collected from the Corrosion gossip protocol
|
|
and represent the median of recent RTT samples between each pair of machines
|
|
in the cluster. The values shown include the median RTT and standard deviation
|
|
for each machine-to-machine connection.`,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
uncli := cmd.Context().Value("cli").(*cli.CLI)
|
|
return rtt(cmd.Context(), uncli)
|
|
},
|
|
}
|
|
return cmd
|
|
}
|
|
|
|
func rtt(ctx context.Context, uncli *cli.CLI) error {
|
|
client, err := uncli.ConnectCluster(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("connect to cluster: %w", err)
|
|
}
|
|
defer client.Close()
|
|
|
|
// Setup context to proxy request to all machines.
|
|
ctx, _, err = client.ProxyMachinesContext(ctx, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("setup proxy context: %w", err)
|
|
}
|
|
|
|
resp, err := client.MachineClient.InspectMachine(ctx, &emptypb.Empty{})
|
|
if err != nil {
|
|
return fmt.Errorf("inspect machines: %w", err)
|
|
}
|
|
|
|
// Map machine IDs to names for display from the response.
|
|
machineNames := make(map[string]string)
|
|
for _, m := range resp.Machines {
|
|
if m.Machine == nil {
|
|
continue
|
|
}
|
|
machineNames[m.Machine.Id] = m.Machine.Name
|
|
}
|
|
|
|
type row struct {
|
|
machine string
|
|
peer string
|
|
median float64
|
|
stdDev float64
|
|
}
|
|
var rows []row
|
|
|
|
for _, m := range resp.Machines {
|
|
// Unlikely to occur, but might be a possible edge case when
|
|
// a machine is still initializing. So just to be safe..
|
|
if m.Machine == nil || m.Rtts == nil {
|
|
continue
|
|
}
|
|
for peerID, stats := range m.Rtts {
|
|
peerName := peerID
|
|
if name, ok := machineNames[peerID]; ok {
|
|
peerName = name
|
|
}
|
|
rows = append(rows, row{
|
|
machine: m.Machine.Name,
|
|
peer: peerName,
|
|
median: stats.Median,
|
|
stdDev: stats.StdDev,
|
|
})
|
|
}
|
|
}
|
|
|
|
// Sort by machine name then peer name
|
|
sort.Slice(rows, func(i, j int) bool {
|
|
if rows[i].machine == rows[j].machine {
|
|
return rows[i].peer < rows[j].peer
|
|
}
|
|
return rows[i].machine < rows[j].machine
|
|
})
|
|
|
|
// Print table
|
|
t := tui.NewTable()
|
|
t.Headers("MACHINE", "PEER", "MEDIAN", "STDDEV")
|
|
|
|
for _, r := range rows {
|
|
t.Row(r.machine, r.peer, fmt.Sprintf("%dms", int64(math.Ceil(r.median))), fmt.Sprintf("±%.1fms", r.stdDev))
|
|
}
|
|
|
|
fmt.Println(t)
|
|
return nil
|
|
}
|