Skip to content

Commit 6ee3bfa

Browse files
committed
chore: add additional network telemetry stats & events
1 parent fecc5b3 commit 6ee3bfa

File tree

7 files changed

+75
-30
lines changed

7 files changed

+75
-30
lines changed

cli/ssh.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
"github.com/coder/coder/v2/codersdk/workspacesdk"
3838
"github.com/coder/coder/v2/cryptorand"
3939
"github.com/coder/coder/v2/pty"
40+
"github.com/coder/coder/v2/tailnet"
4041
"github.com/coder/retry"
4142
"github.com/coder/serpent"
4243
)
@@ -437,7 +438,7 @@ func (r *RootCmd) ssh() *serpent.Command {
437438
}
438439

439440
err = sshSession.Wait()
440-
conn.SendDisconnectedTelemetry("ssh")
441+
conn.SendDisconnectedTelemetry(tailnet.TelemetryApplicationSSH)
441442
if err != nil {
442443
if exitErr := (&gossh.ExitError{}); errors.As(err, &exitErr) {
443444
// Clear the error since it's not useful beyond

coderd/telemetry/telemetry.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,7 +1240,7 @@ type NetworkEvent struct {
12401240
NodeIDSelf uint64 `json:"node_id_self"`
12411241
NodeIDRemote uint64 `json:"node_id_remote"`
12421242
P2PEndpoint NetworkEventP2PEndpoint `json:"p2p_endpoint"`
1243-
HomeDERP string `json:"home_derp"`
1243+
HomeDERP int `json:"home_derp"`
12441244
DERPMap DERPMap `json:"derp_map"`
12451245
LatestNetcheck Netcheck `json:"latest_netcheck"`
12461246

@@ -1286,7 +1286,7 @@ func NetworkEventFromProto(proto *tailnetproto.TelemetryEvent) (NetworkEvent, er
12861286
NodeIDSelf: proto.NodeIdSelf,
12871287
NodeIDRemote: proto.NodeIdRemote,
12881288
P2PEndpoint: p2pEndpointFromProto(proto.P2PEndpoint),
1289-
HomeDERP: proto.HomeDerp,
1289+
HomeDERP: int(proto.HomeDerp),
12901290
DERPMap: derpMapFromProto(proto.DerpMap),
12911291
LatestNetcheck: netcheckFromProto(proto.LatestNetcheck),
12921292

codersdk/workspacesdk/connector_internal_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -228,12 +228,12 @@ func TestTailnetAPIConnector_TelemetryUnimplemented(t *testing.T) {
228228
return uut.client != nil
229229
}, testutil.WaitShort, testutil.IntervalFast)
230230

231-
fakeDRPCClient.telemeteryErorr = drpcerr.WithCode(xerrors.New("Unimplemented"), 0)
231+
fakeDRPCClient.telemetryError = drpcerr.WithCode(xerrors.New("Unimplemented"), 0)
232232
uut.SendTelemetryEvent(&proto.TelemetryEvent{})
233233
require.False(t, uut.telemetryUnavailable.Load())
234234
require.Equal(t, int64(1), atomic.LoadInt64(&fakeDRPCClient.postTelemetryCalls))
235235

236-
fakeDRPCClient.telemeteryErorr = drpcerr.WithCode(xerrors.New("Unimplemented"), drpcerr.Unimplemented)
236+
fakeDRPCClient.telemetryError = drpcerr.WithCode(xerrors.New("Unimplemented"), drpcerr.Unimplemented)
237237
uut.SendTelemetryEvent(&proto.TelemetryEvent{})
238238
require.True(t, uut.telemetryUnavailable.Load())
239239
uut.SendTelemetryEvent(&proto.TelemetryEvent{})
@@ -268,12 +268,12 @@ func TestTailnetAPIConnector_TelemetryNotRecognised(t *testing.T) {
268268
return uut.client != nil
269269
}, testutil.WaitShort, testutil.IntervalFast)
270270

271-
fakeDRPCClient.telemeteryErorr = drpc.ProtocolError.New("Protocol Error")
271+
fakeDRPCClient.telemetryError = drpc.ProtocolError.New("Protocol Error")
272272
uut.SendTelemetryEvent(&proto.TelemetryEvent{})
273273
require.False(t, uut.telemetryUnavailable.Load())
274274
require.Equal(t, int64(1), atomic.LoadInt64(&fakeDRPCClient.postTelemetryCalls))
275275

276-
fakeDRPCClient.telemeteryErorr = drpc.ProtocolError.New("unknown rpc: /coder.tailnet.v2.Tailnet/PostTelemetry")
276+
fakeDRPCClient.telemetryError = drpc.ProtocolError.New("unknown rpc: /coder.tailnet.v2.Tailnet/PostTelemetry")
277277
uut.SendTelemetryEvent(&proto.TelemetryEvent{})
278278
require.True(t, uut.telemetryUnavailable.Load())
279279
uut.SendTelemetryEvent(&proto.TelemetryEvent{})
@@ -301,7 +301,7 @@ func newFakeTailnetConn() *fakeTailnetConn {
301301

302302
type fakeDRPCClient struct {
303303
postTelemetryCalls int64
304-
telemeteryErorr error
304+
telemetryError error
305305
fakeDRPPCMapStream
306306
}
307307

@@ -331,7 +331,7 @@ func (*fakeDRPCClient) DRPCConn() drpc.Conn {
331331
// PostTelemetry implements proto.DRPCTailnetClient.
332332
func (f *fakeDRPCClient) PostTelemetry(_ context.Context, _ *proto.TelemetryRequest) (*proto.TelemetryResponse, error) {
333333
atomic.AddInt64(&f.postTelemetryCalls, 1)
334-
return nil, f.telemeteryErorr
334+
return nil, f.telemetryError
335335
}
336336

337337
// StreamDERPMaps implements proto.DRPCTailnetClient.

tailnet/conn.go

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -290,13 +290,15 @@ func NewConn(options *Options) (conn *Conn, err error) {
290290
configMaps: cfgMaps,
291291
nodeUpdater: nodeUp,
292292
telemetrySink: options.TelemetrySink,
293-
telemeteryStore: telemetryStore,
293+
telemetryStore: telemetryStore,
294+
createdAt: time.Now(),
294295
}
295296
defer func() {
296297
if err != nil {
297298
_ = server.Close()
298299
}
299300
}()
301+
server.SetNodeCallback(nil)
300302

301303
netStack.GetTCPHandlerForFlow = server.forwardTCP
302304

@@ -351,11 +353,12 @@ type Conn struct {
351353
wireguardEngine wgengine.Engine
352354
listeners map[listenKey]*listener
353355
clientType proto.TelemetryEvent_ClientType
356+
createdAt time.Time
354357

355358
telemetrySink TelemetrySink
356-
// telemeteryStore will be nil if telemetrySink is nil.
357-
telemeteryStore *TelemetryStore
358-
telemetryWg sync.WaitGroup
359+
// telemetryStore will be nil if telemetrySink is nil.
360+
telemetryStore *TelemetryStore
361+
telemetryWg sync.WaitGroup
359362

360363
trafficStats *connstats.Statistics
361364
}
@@ -384,14 +387,25 @@ func (c *Conn) SetAddresses(ips []netip.Prefix) error {
384387
return nil
385388
}
386389

390+
// Sets the callback for when the node is updated.
391+
// If telemetry is enabled, the callback will first update the telemetry store,
392+
// send the updated telemetry, and then call the provided callback.
387393
func (c *Conn) SetNodeCallback(callback func(node *Node)) {
388-
c.nodeUpdater.setCallback(callback)
394+
if c.telemetryStore != nil {
395+
c.nodeUpdater.setCallback(func(node *Node) {
396+
c.telemetryStore.updateByNode(node)
397+
c.sendUpdatedTelemetry()
398+
callback(node)
399+
})
400+
} else {
401+
c.nodeUpdater.setCallback(callback)
402+
}
389403
}
390404

391405
// SetDERPMap updates the DERPMap of a connection.
392406
func (c *Conn) SetDERPMap(derpMap *tailcfg.DERPMap) {
393-
if c.configMaps.setDERPMap(derpMap) && c.telemeteryStore != nil {
394-
c.telemeteryStore.updateDerpMap(derpMap)
407+
if c.configMaps.setDERPMap(derpMap) && c.telemetryStore != nil {
408+
c.telemetryStore.updateDerpMap(derpMap)
395409
}
396410
}
397411

@@ -715,6 +729,7 @@ func (c *Conn) SendConnectedTelemetry(ip netip.Addr, application string) {
715729
}
716730
e := c.newTelemetryEvent()
717731
e.Status = proto.TelemetryEvent_CONNECTED
732+
e.ConnectionSetup = durationpb.New(time.Since(c.createdAt))
718733
e.Application = application
719734
pip, ok := c.wireguardEngine.PeerForIP(ip)
720735
if ok {
@@ -727,6 +742,21 @@ func (c *Conn) SendConnectedTelemetry(ip netip.Addr, application string) {
727742
}()
728743
}
729744

745+
// Called whenever the Node is updated.
746+
// Expects that the telemetry store has the latest node information.
747+
func (c *Conn) sendUpdatedTelemetry() {
748+
if c.telemetrySink == nil {
749+
return
750+
}
751+
e := c.newTelemetryEvent()
752+
e.Status = proto.TelemetryEvent_CONNECTED
753+
c.telemetryWg.Add(1)
754+
go func() {
755+
defer c.telemetryWg.Done()
756+
c.telemetrySink.SendTelemetryEvent(e)
757+
}()
758+
}
759+
730760
func (c *Conn) SendDisconnectedTelemetry(ip netip.Addr, application string) {
731761
if c.telemetrySink == nil {
732762
return
@@ -769,7 +799,7 @@ func (c *Conn) sendPingTelemetry(pr *ipnstate.PingResult) {
769799
latency := durationpb.New(time.Duration(pr.LatencySeconds * float64(time.Second)))
770800
if pr.Endpoint != "" {
771801
e.P2PLatency = latency
772-
e.P2PEndpoint = c.telemeteryStore.toEndpoint(pr.Endpoint)
802+
e.P2PEndpoint = c.telemetryStore.toEndpoint(pr.Endpoint)
773803
} else {
774804
e.DerpLatency = latency
775805
}
@@ -785,12 +815,10 @@ func (c *Conn) sendPingTelemetry(pr *ipnstate.PingResult) {
785815
func (c *Conn) newTelemetryEvent() *proto.TelemetryEvent {
786816
// Infallible
787817
id, _ := c.id.MarshalBinary()
788-
event := c.telemeteryStore.newEvent()
818+
event := c.telemetryStore.newEvent()
789819
event.ClientType = c.clientType
790820
event.Id = id
791-
selfNode := c.Node()
792-
event.NodeIdSelf = uint64(selfNode.ID)
793-
event.HomeDerp = strconv.Itoa(selfNode.PreferredDERP)
821+
event.ConnectionAge = durationpb.New(time.Since(c.createdAt))
794822
return event
795823
}
796824

tailnet/proto/tailnet.pb.go

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tailnet/proto/tailnet.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ message TelemetryEvent {
169169
uint64 node_id_self = 7;
170170
uint64 node_id_remote = 8;
171171
P2PEndpoint p2p_endpoint = 9;
172-
string home_derp = 10;
172+
int32 home_derp = 10;
173173
DERPMap derp_map = 11;
174174
Netcheck latest_netcheck = 12;
175175

tailnet/telemetry.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ type TelemetryStore struct {
3131

3232
cleanDerpMap *tailcfg.DERPMap
3333
cleanNetCheck *proto.Netcheck
34+
nodeID uint64
35+
homeDerp int32
3436
}
3537

3638
func newTelemetryStore() (*TelemetryStore, error) {
@@ -53,11 +55,11 @@ func (b *TelemetryStore) newEvent() *proto.TelemetryEvent {
5355
Time: timestamppb.Now(),
5456
DerpMap: DERPMapToProto(b.cleanDerpMap),
5557
LatestNetcheck: b.cleanNetCheck,
58+
NodeIdSelf: b.nodeID,
59+
HomeDerp: b.homeDerp,
5660

5761
// TODO(ethanndickson):
58-
ConnectionAge: &durationpb.Duration{},
59-
ConnectionSetup: &durationpb.Duration{},
60-
P2PSetup: &durationpb.Duration{},
62+
P2PSetup: &durationpb.Duration{},
6163
}
6264
}
6365

@@ -85,11 +87,24 @@ func (b *TelemetryStore) updateDerpMap(cur *tailcfg.DERPMap) {
8587
b.cleanDerpMap = cleanMap
8688
}
8789

90+
func (b *TelemetryStore) updateByNode(n *Node) {
91+
b.mu.Lock()
92+
defer b.mu.Unlock()
93+
94+
b.nodeID = uint64(n.ID)
95+
b.homeDerp = int32(n.PreferredDERP)
96+
}
97+
8898
// Store an anonymized proto.Netcheck given a tailscale NetInfo.
89-
func (b *TelemetryStore) setNetInfo(ni *tailcfg.NetInfo) {
99+
func (b *TelemetryStore) setNetInfo(ni *tailcfg.NetInfo) bool {
90100
b.mu.Lock()
91101
defer b.mu.Unlock()
92102

103+
derpHomeChanged := false
104+
if b.cleanNetCheck != nil {
105+
derpHomeChanged = b.cleanNetCheck.PreferredDERP != int64(ni.PreferredDERP)
106+
}
107+
93108
b.cleanNetCheck = &proto.Netcheck{
94109
UDP: ni.UDP,
95110
IPv6: ni.IPv6,
@@ -127,6 +142,7 @@ func (b *TelemetryStore) setNetInfo(ni *tailcfg.NetInfo) {
127142
for rid, seconds := range ni.DERPLatencyV6 {
128143
b.cleanNetCheck.RegionV6Latency[int64(rid)] = durationpb.New(time.Duration(seconds * float64(time.Second)))
129144
}
145+
return derpHomeChanged
130146
}
131147

132148
func (b *TelemetryStore) toEndpoint(ipport string) *proto.TelemetryEvent_P2PEndpoint {

0 commit comments

Comments
 (0)