Skip to content

chore: add DRPC tailnet & cli network telemetry #13687

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cli/ping.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ func (r *RootCmd) ping() *serpent.Command {
_, _ = fmt.Fprintln(inv.Stderr, "Direct connections disabled.")
opts.BlockEndpoints = true
}
if !r.disableNetworkTelemetry {
opts.EnableTelemetry = true
}
conn, err := workspacesdk.New(client).DialAgent(ctx, workspaceAgent.ID, opts)
if err != nil {
return err
Expand Down
3 changes: 3 additions & 0 deletions cli/portforward.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ func (r *RootCmd) portForward() *serpent.Command {
_, _ = fmt.Fprintln(inv.Stderr, "Direct connections disabled.")
opts.BlockEndpoints = true
}
if !r.disableNetworkTelemetry {
opts.EnableTelemetry = true
}
conn, err := workspacesdk.New(client).DialAgent(ctx, workspaceAgent.ID, opts)
if err != nil {
return err
Expand Down
39 changes: 24 additions & 15 deletions cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,20 @@ var (
)

const (
varURL = "url"
varToken = "token"
varAgentToken = "agent-token"
varAgentTokenFile = "agent-token-file"
varAgentURL = "agent-url"
varHeader = "header"
varHeaderCommand = "header-command"
varNoOpen = "no-open"
varNoVersionCheck = "no-version-warning"
varNoFeatureWarning = "no-feature-warning"
varForceTty = "force-tty"
varVerbose = "verbose"
varDisableDirect = "disable-direct-connections"
varURL = "url"
varToken = "token"
varAgentToken = "agent-token"
varAgentTokenFile = "agent-token-file"
varAgentURL = "agent-url"
varHeader = "header"
varHeaderCommand = "header-command"
varNoOpen = "no-open"
varNoVersionCheck = "no-version-warning"
varNoFeatureWarning = "no-feature-warning"
varForceTty = "force-tty"
varVerbose = "verbose"
varDisableDirect = "disable-direct-connections"
varDisableNetworkTelemetry = "disable-network-telemetry"

notLoggedInMessage = "You are not logged in. Try logging in using 'coder login <url>'."

Expand Down Expand Up @@ -435,6 +436,13 @@ func (r *RootCmd) Command(subcommands []*serpent.Command) (*serpent.Command, err
Value: serpent.BoolOf(&r.disableDirect),
Group: globalGroup,
},
{
Flag: varDisableNetworkTelemetry,
Env: "CODER_DISABLE_NETWORK_TELEMETRY",
Description: "Disable network telemetry. Network telemetry is collected when connecting to workspaces using the CLI, and is forwarded to the server. If telemetry is also enabled on the server, it may be sent to Coder. Network telemetry is used to measure network quality and detect regressions.",
Value: serpent.BoolOf(&r.disableNetworkTelemetry),
Group: globalGroup,
},
{
Flag: "debug-http",
Description: "Debug codersdk HTTP requests.",
Expand Down Expand Up @@ -481,8 +489,9 @@ type RootCmd struct {
disableDirect bool
debugHTTP bool

noVersionCheck bool
noFeatureWarning bool
disableNetworkTelemetry bool
noVersionCheck bool
noFeatureWarning bool
}

// InitClient authenticates the client with files from disk
Expand Down
4 changes: 4 additions & 0 deletions cli/speedtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ func (r *RootCmd) speedtest() *serpent.Command {
_, _ = fmt.Fprintln(inv.Stderr, "Direct connections disabled.")
opts.BlockEndpoints = true
}
if !r.disableNetworkTelemetry {
opts.EnableTelemetry = true
}
if pcapFile != "" {
s := capture.New()
opts.CaptureHook = s.LogPacket
Expand Down Expand Up @@ -183,6 +186,7 @@ func (r *RootCmd) speedtest() *serpent.Command {
outputResult.Intervals[i] = interval
}
}
conn.Conn.SendSpeedtestTelemetry(outputResult.Overall.ThroughputMbits)
out, err := formatter.Format(inv.Context(), outputResult)
if err != nil {
return err
Expand Down
6 changes: 4 additions & 2 deletions cli/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,9 @@ func (r *RootCmd) ssh() *serpent.Command {
}
conn, err := workspacesdk.New(client).
DialAgent(ctx, workspaceAgent.ID, &workspacesdk.DialAgentOptions{
Logger: logger,
BlockEndpoints: r.disableDirect,
Logger: logger,
BlockEndpoints: r.disableDirect,
EnableTelemetry: !r.disableNetworkTelemetry,
})
if err != nil {
return xerrors.Errorf("dial agent: %w", err)
Expand Down Expand Up @@ -436,6 +437,7 @@ func (r *RootCmd) ssh() *serpent.Command {
}

err = sshSession.Wait()
conn.SendDisconnectedTelemetry("ssh")
if err != nil {
if exitErr := (&gossh.ExitError{}); errors.As(err, &exitErr) {
// Clear the error since it's not useful beyond
Expand Down
7 changes: 7 additions & 0 deletions cli/testdata/coder_--help.golden
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ variables or flags.
--disable-direct-connections bool, $CODER_DISABLE_DIRECT_CONNECTIONS
Disable direct (P2P) connections to workspaces.

--disable-network-telemetry bool, $CODER_DISABLE_NETWORK_TELEMETRY
Disable network telemetry. Network telemetry is collected when
connecting to workspaces using the CLI, and is forwarded to the
server. If telemetry is also enabled on the server, it may be sent to
Coder. Network telemetry is used to measure network quality and detect
regressions.

--global-config string, $CODER_CONFIG_DIR (default: ~/.config/coderv2)
Path to the global `coder` config directory.

Expand Down
40 changes: 14 additions & 26 deletions coderd/telemetry/telemetry.go
Original file line number Diff line number Diff line change
Expand Up @@ -1163,9 +1163,9 @@ type Netcheck struct {
IPv4 bool `json:"ipv4"`
IPv6CanSend bool `json:"ipv6_can_send"`
IPv4CanSend bool `json:"ipv4_can_send"`
OSHasIPv6 bool `json:"os_has_ipv6"`
ICMPv4 bool `json:"icmpv4"`

OSHasIPv6 *bool `json:"os_has_ipv6"`
MappingVariesByDestIP *bool `json:"mapping_varies_by_dest_ip"`
HairPinning *bool `json:"hair_pinning"`
UPnP *bool `json:"upnp"`
Expand Down Expand Up @@ -1210,9 +1210,9 @@ func netcheckFromProto(proto *tailnetproto.Netcheck) Netcheck {
IPv4: proto.IPv4,
IPv6CanSend: proto.IPv6CanSend,
IPv4CanSend: proto.IPv4CanSend,
OSHasIPv6: proto.OSHasIPv6,
ICMPv4: proto.ICMPv4,

OSHasIPv6: protoBool(proto.OSHasIPv6),
MappingVariesByDestIP: protoBool(proto.MappingVariesByDestIP),
HairPinning: protoBool(proto.HairPinning),
UPnP: protoBool(proto.UPnP),
Expand All @@ -1221,33 +1221,28 @@ func netcheckFromProto(proto *tailnetproto.Netcheck) Netcheck {

PreferredDERP: proto.PreferredDERP,

RegionLatency: durationMapFromProto(proto.RegionLatency),
RegionV4Latency: durationMapFromProto(proto.RegionV4Latency),
RegionV6Latency: durationMapFromProto(proto.RegionV6Latency),

GlobalV4: netcheckIPFromProto(proto.GlobalV4),
GlobalV6: netcheckIPFromProto(proto.GlobalV6),

CaptivePortal: protoBool(proto.CaptivePortal),
}
}

// NetworkEvent and all related structs come from tailnet.proto.
type NetworkEvent struct {
ID uuid.UUID `json:"id"`
Time time.Time `json:"time"`
Application string `json:"application"`
Status string `json:"status"` // connected, disconnected
DisconnectionReason string `json:"disconnection_reason"`
ClientType string `json:"client_type"` // cli, agent, coderd, wsproxy
NodeIDSelf uint64 `json:"node_id_self"`
NodeIDRemote uint64 `json:"node_id_remote"`
P2PEndpoint NetworkEventP2PEndpoint `json:"p2p_endpoint"`
LogIPHashes map[string]NetworkEventIPFields `json:"log_ip_hashes"`
HomeDERP string `json:"home_derp"`
Logs []string `json:"logs"`
DERPMap DERPMap `json:"derp_map"`
LatestNetcheck Netcheck `json:"latest_netcheck"`
ID uuid.UUID `json:"id"`
Time time.Time `json:"time"`
Application string `json:"application"`
Status string `json:"status"` // connected, disconnected
DisconnectionReason string `json:"disconnection_reason"`
ClientType string `json:"client_type"` // cli, agent, coderd, wsproxy
NodeIDSelf uint64 `json:"node_id_self"`
NodeIDRemote uint64 `json:"node_id_remote"`
P2PEndpoint NetworkEventP2PEndpoint `json:"p2p_endpoint"`
HomeDERP string `json:"home_derp"`
DERPMap DERPMap `json:"derp_map"`
LatestNetcheck Netcheck `json:"latest_netcheck"`

ConnectionAge *time.Duration `json:"connection_age"`
ConnectionSetup *time.Duration `json:"connection_setup"`
Expand Down Expand Up @@ -1281,11 +1276,6 @@ func NetworkEventFromProto(proto *tailnetproto.TelemetryEvent) (NetworkEvent, er
return NetworkEvent{}, xerrors.Errorf("parse id %q: %w", proto.Id, err)
}

logIPHashes := make(map[string]NetworkEventIPFields, len(proto.LogIpHashes))
for k, v := range proto.LogIpHashes {
logIPHashes[k] = ipFieldsFromProto(v)
}

return NetworkEvent{
ID: id,
Time: proto.Time.AsTime(),
Expand All @@ -1296,9 +1286,7 @@ func NetworkEventFromProto(proto *tailnetproto.TelemetryEvent) (NetworkEvent, er
NodeIDSelf: proto.NodeIdSelf,
NodeIDRemote: proto.NodeIdRemote,
P2PEndpoint: p2pEndpointFromProto(proto.P2PEndpoint),
LogIPHashes: logIPHashes,
HomeDERP: proto.HomeDerp,
Logs: proto.Logs,
DERPMap: derpMapFromProto(proto.DerpMap),
LatestNetcheck: netcheckFromProto(proto.LatestNetcheck),

Expand Down
6 changes: 6 additions & 0 deletions codersdk/workspacesdk/agentconn.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ func (c *AgentConn) SSH(ctx context.Context) (*gonet.TCPConn, error) {
return nil, xerrors.Errorf("workspace agent not reachable in time: %v", ctx.Err())
}

c.Conn.SendConnectedTelemetry(c.agentAddress(), tailnet.TelemetryApplicationSSH)
return c.Conn.DialContextTCP(ctx, netip.AddrPortFrom(c.agentAddress(), AgentSSHPort))
}

Expand Down Expand Up @@ -185,6 +186,7 @@ func (c *AgentConn) Speedtest(ctx context.Context, direction speedtest.Direction
return nil, xerrors.Errorf("workspace agent not reachable in time: %v", ctx.Err())
}

c.Conn.SendConnectedTelemetry(c.agentAddress(), tailnet.TelemetryApplicationSpeedtest)
speedConn, err := c.Conn.DialContextTCP(ctx, netip.AddrPortFrom(c.agentAddress(), AgentSpeedtestPort))
if err != nil {
return nil, xerrors.Errorf("dial speedtest: %w", err)
Expand Down Expand Up @@ -378,3 +380,7 @@ func (c *AgentConn) apiClient() *http.Client {
func (c *AgentConn) GetPeerDiagnostics() tailnet.PeerDiagnostics {
return c.Conn.GetPeerDiagnostics(c.opts.AgentID)
}

func (c *AgentConn) SendDisconnectedTelemetry(application string) {
c.Conn.SendDisconnectedTelemetry(c.agentAddress(), application)
}
Loading
Loading