feat(rtt): 'machine rtt' command to show round-trip time between macines usign using gossip data (#226)

* 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
This commit is contained in:
Justin Bradford
2026-04-20 08:01:20 +10:00
committed by GitHub
parent 1c0d48cb46
commit 8d023f5c53
10 changed files with 564 additions and 156 deletions
+43
View File
@@ -912,6 +912,14 @@ func (m *Machine) InspectMachine(ctx context.Context, _ *emptypb.Empty) (*pb.Ins
return nil, status.Errorf(codes.Internal, "get database version of the cluster store: %v", err)
}
var rtts map[string]*pb.RTTStats
if m.Initialised() {
rtts, err = m.getMachineRTTs(ctx)
if err != nil {
return nil, err
}
}
return &pb.InspectMachineResponse{
Machines: []*pb.MachineDetails{
{
@@ -926,11 +934,46 @@ func (m *Machine) InspectMachine(ctx context.Context, _ *emptypb.Empty) (*pb.Ins
},
},
StoreDbVersion: dbVersion,
Rtts: rtts,
},
},
}, nil
}
// getMachineRTTs retrieves round-trip times to other machines in the cluster.
func (m *Machine) getMachineRTTs(ctx context.Context) (map[string]*pb.RTTStats, error) {
rtts, err := m.cluster.MemberRTTs()
if err != nil {
return nil, status.Errorf(codes.Internal, "get member rtts: %v", err)
}
// List machines to map IPs to Machine IDs.
machines, err := m.store.ListMachines(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "list machines: %v", err)
}
// Map Management IP -> Machine ID
ipToMachineID := make(map[netip.Addr]string)
for _, mach := range machines {
ip, _ := mach.Network.ManagementIp.ToAddr()
ipToMachineID[ip] = mach.Id
}
pbRTTs := make(map[string]*pb.RTTStats)
for _, stats := range rtts {
// Corrosion uses the management IP for gossip.
if mid, ok := ipToMachineID[stats.Addr.Addr()]; ok {
pbRTTs[mid] = &pb.RTTStats{
Median: stats.Median,
StdDev: stats.StdDev,
}
}
}
return pbRTTs, nil
}
// IsNetworkReady returns true if the Docker network is ready for containers.
func (m *Machine) IsNetworkReady() bool {
if !m.Initialised() {