Skip to content

Commit 1e4db52

Browse files
committed
Add telemetry event for SSH tunnel connections
1 parent 518b428 commit 1e4db52

3 files changed

Lines changed: 88 additions & 0 deletions

File tree

experimental/ssh/internal/client/client.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import (
2626
"github.com/databricks/cli/internal/build"
2727
"github.com/databricks/cli/libs/cmdio"
2828
"github.com/databricks/cli/libs/log"
29+
"github.com/databricks/cli/libs/telemetry"
30+
"github.com/databricks/cli/libs/telemetry/protos"
2931
"github.com/databricks/databricks-sdk-go"
3032
"github.com/databricks/databricks-sdk-go/retries"
3133
"github.com/databricks/databricks-sdk-go/service/compute"
@@ -283,6 +285,9 @@ func Run(ctx context.Context, client *databricks.WorkspaceClient, opts ClientOpt
283285

284286
version := build.GetInfo().Version
285287

288+
isReconnect := opts.ServerMetadata != ""
289+
var serverStartTimeMs int64
290+
286291
if opts.ServerMetadata == "" {
287292
cmdio.LogString(ctx, "Uploading binaries...")
288293
sp := cmdio.NewSpinner(ctx, cmdio.WithElapsedTime())
@@ -292,10 +297,13 @@ func Run(ctx context.Context, client *databricks.WorkspaceClient, opts ClientOpt
292297
if err != nil {
293298
return fmt.Errorf("failed to upload ssh-tunnel binaries: %w", err)
294299
}
300+
serverStartTime := time.Now()
295301
userName, serverPort, clusterID, err = ensureSSHServerIsRunning(ctx, client, version, secretScopeName, opts)
296302
if err != nil {
303+
logSshTunnelEvent(ctx, opts, false, isReconnect, 0)
297304
return fmt.Errorf("failed to ensure that ssh server is running: %w", err)
298305
}
306+
serverStartTimeMs = time.Since(serverStartTime).Milliseconds()
299307
} else {
300308
// Metadata format: "<user_name>,<port>,<cluster_id>"
301309
metadata := strings.Split(opts.ServerMetadata, ",")
@@ -332,6 +340,8 @@ func Run(ctx context.Context, client *databricks.WorkspaceClient, opts ClientOpt
332340
cmdio.LogString(ctx, "Connected!")
333341
}
334342

343+
logSshTunnelEvent(ctx, opts, true, isReconnect, serverStartTimeMs)
344+
335345
if opts.ProxyMode {
336346
return runSSHProxy(ctx, client, serverPort, clusterID, opts)
337347
} else if opts.IDE != "" {
@@ -704,3 +714,33 @@ func ensureSSHServerIsRunning(ctx context.Context, client *databricks.WorkspaceC
704714

705715
return userName, serverPort, effectiveClusterID, nil
706716
}
717+
718+
func logSshTunnelEvent(ctx context.Context, opts ClientOptions, isSuccess, isReconnect bool, serverStartTimeMs int64) {
719+
computeType := protos.SshTunnelComputeTypeDedicated
720+
if opts.IsServerlessMode() {
721+
computeType = protos.SshTunnelComputeTypeServerless
722+
}
723+
724+
var clientMode protos.SshTunnelClientMode
725+
switch {
726+
case opts.ProxyMode:
727+
clientMode = protos.SshTunnelClientModeProxy
728+
case opts.IDE != "":
729+
clientMode = protos.SshTunnelClientModeIDE
730+
default:
731+
clientMode = protos.SshTunnelClientModeSSH
732+
}
733+
734+
telemetry.Log(ctx, protos.DatabricksCliLog{
735+
SshTunnelEvent: &protos.SshTunnelEvent{
736+
ComputeType: computeType,
737+
AcceleratorType: opts.Accelerator,
738+
IdeType: opts.IDE,
739+
ClientMode: clientMode,
740+
IsReconnect: isReconnect,
741+
AutoStartCluster: opts.AutoStartCluster,
742+
ServerStartTimeMs: serverStartTimeMs,
743+
IsSuccess: isSuccess,
744+
},
745+
})
746+
}

libs/telemetry/protos/frontend_log.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,5 @@ type DatabricksCliLog struct {
1919
CliTestEvent *CliTestEvent `json:"cli_test_event,omitempty"`
2020
BundleInitEvent *BundleInitEvent `json:"bundle_init_event,omitempty"`
2121
BundleDeployEvent *BundleDeployEvent `json:"bundle_deploy_event,omitempty"`
22+
SshTunnelEvent *SshTunnelEvent `json:"ssh_tunnel_event,omitempty"`
2223
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package protos
2+
3+
type SshTunnelComputeType string
4+
5+
const (
6+
SshTunnelComputeTypeUnspecified SshTunnelComputeType = "TYPE_UNSPECIFIED"
7+
SshTunnelComputeTypeDedicated SshTunnelComputeType = "DEDICATED"
8+
SshTunnelComputeTypeServerless SshTunnelComputeType = "SERVERLESS"
9+
)
10+
11+
type SshTunnelClientMode string
12+
13+
const (
14+
SshTunnelClientModeUnspecified SshTunnelClientMode = "TYPE_UNSPECIFIED"
15+
SshTunnelClientModeSSH SshTunnelClientMode = "SSH_CLIENT"
16+
SshTunnelClientModeProxy SshTunnelClientMode = "PROXY"
17+
SshTunnelClientModeIDE SshTunnelClientMode = "IDE"
18+
)
19+
20+
// SshTunnelEvent is emitted when a user establishes an SSH tunnel connection
21+
// via the Databricks CLI.
22+
type SshTunnelEvent struct {
23+
// Type of compute: dedicated cluster or serverless.
24+
ComputeType SshTunnelComputeType `json:"compute_type,omitempty"`
25+
26+
// GPU accelerator type for serverless compute.
27+
AcceleratorType string `json:"accelerator_type,omitempty"`
28+
29+
// IDE that initiated the connection (e.g., "vscode", "cursor").
30+
IdeType string `json:"ide_type,omitempty"`
31+
32+
// How the client is used: SSH client, proxy mode, or IDE mode.
33+
ClientMode SshTunnelClientMode `json:"client_mode,omitempty"`
34+
35+
// Whether this is a reconnection to an existing session.
36+
IsReconnect bool `json:"is_reconnect,omitempty"`
37+
38+
// Whether the cluster was auto-started by the CLI.
39+
AutoStartCluster bool `json:"auto_start_cluster,omitempty"`
40+
41+
// Time in milliseconds spent starting the SSH server.
42+
// Zero if server was already running.
43+
ServerStartTimeMs int64 `json:"server_start_time_ms"`
44+
45+
// Whether the connection was successful.
46+
IsSuccess bool `json:"is_success,omitempty"`
47+
}

0 commit comments

Comments
 (0)