Skip to content
Open
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
5 changes: 5 additions & 0 deletions acceptance/ssh/connect-local-cluster/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

75 changes: 75 additions & 0 deletions acceptance/ssh/connect-local-cluster/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{
"method": "GET",
"path": "/api/2.0/preview/scim/v2/Me"
}
{
"method": "GET",
"path": "/api/2.0/preview/scim/v2/Me"
}
{
"method": "GET",
"path": "/api/2.0/preview/scim/v2/Me"
}
{
"method": "GET",
"path": "/api/2.0/secrets/get",
"q": {
"key": "client-private-key",
"scope": "[USERNAME]-test-cluster-123-ssh-tunnel-keys"
}
}
{
"method": "GET",
"path": "/api/2.0/secrets/get",
"q": {
"key": "client-private-key",
"scope": "[USERNAME]-test-cluster-123-ssh-tunnel-keys"
}
}
{
"method": "GET",
"path": "/api/2.0/secrets/get",
"q": {
"key": "client-public-key",
"scope": "[USERNAME]-test-cluster-123-ssh-tunnel-keys"
}
}
{
"method": "GET",
"path": "/api/2.0/secrets/list",
"q": {
"scope": "[USERNAME]-test-cluster-123-ssh-tunnel-keys"
}
}
{
"method": "GET",
"path": "/api/2.0/secrets/list",
"q": {
"scope": "[USERNAME]-test-cluster-123-ssh-tunnel-keys"
}
}
{
"method": "POST",
"path": "/api/2.0/secrets/put",
"body": {
"key": "client-private-key",
"scope": "[USERNAME]-test-cluster-123-ssh-tunnel-keys",
"string_value": "[PRIVATE_KEY]"
}
}
{
"method": "POST",
"path": "/api/2.0/secrets/put",
"body": {
"key": "client-public-key",
"scope": "[USERNAME]-test-cluster-123-ssh-tunnel-keys",
"string_value": "[PUBLIC_KEY]"
}
}
{
"method": "POST",
"path": "/api/2.0/secrets/scopes/create",
"body": {
"scope": "[USERNAME]-test-cluster-123-ssh-tunnel-keys"
}
}
8 changes: 8 additions & 0 deletions acceptance/ssh/connect-local-cluster/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Test the SSH connect flow for a classic cluster with pre-set metadata.
# The --metadata flag bypasses job submission, so this tests:
# cluster state check, secret scope creation, SSH key generation/storage, metadata parsing.
# The flow will fail at SSH spawn (expected, no real SSH server).
errcode $CLI ssh connect --cluster=test-cluster-123 --metadata=root,2222,test-cluster-123 2>LOG.stderr

# Verify the API sequence: secrets scope creation + key storage
print_requests.py //secrets //scim --get --sort
18 changes: 18 additions & 0 deletions acceptance/ssh/connect-local-cluster/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Local = true
Cloud = false
RecordRequests = true
Timeout = "10s"

# Return a running cluster for the cluster check
[[Server]]
Pattern = "GET /api/2.1/clusters/get"
Response.Body = '{"cluster_id":"test-cluster-123","state":"RUNNING"}'

# Replace generated RSA key content in request recordings
[[Repls]]
Old = '"-----BEGIN RSA PRIVATE KEY-----\\n[^"]*-----END RSA PRIVATE KEY-----\\n"'
New = '"[PRIVATE_KEY]"'

[[Repls]]
Old = '"ssh-rsa [A-Za-z0-9+/=]+\\n"'
New = '"[PUBLIC_KEY]"'
5 changes: 5 additions & 0 deletions acceptance/ssh/connect-local-validation/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions acceptance/ssh/connect-local-validation/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

=== Accelerator without serverless
Error: --accelerator flag can only be used with serverless compute (--name flag)

Exit code: 1

=== Invalid accelerator value
Error: invalid accelerator value: "CPU_1x", expected "GPU_1xA10" or "GPU_8xH100"

Exit code: 1

=== Invalid connection name
Error: connection name "bad name!" must consist of letters, numbers, dashes, and underscores

Exit code: 1

=== Invalid IDE value
Error: invalid IDE value: "vim", expected "vscode" or "cursor"

Exit code: 1

=== Environment version too low
Error: environment version must be >= 4, got 3

Exit code: 1
14 changes: 14 additions & 0 deletions acceptance/ssh/connect-local-validation/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
title "Accelerator without serverless\n"
errcode $CLI ssh connect --cluster abc --accelerator GPU_1xA10

title "Invalid accelerator value\n"
errcode $CLI ssh connect --name my-conn --accelerator CPU_1x

title "Invalid connection name\n"
errcode $CLI ssh connect --name "bad name!"

title "Invalid IDE value\n"
errcode $CLI ssh connect --cluster abc --ide vim

title "Environment version too low\n"
errcode $CLI ssh connect --cluster abc --environment-version 3
2 changes: 2 additions & 0 deletions acceptance/ssh/connect-local-validation/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Local = true
Cloud = false
1 change: 1 addition & 0 deletions acceptance/ssh/connect-serverless-cpu/out.stdout.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Connection successful
9 changes: 9 additions & 0 deletions acceptance/ssh/connect-serverless-cpu/out.test.toml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions acceptance/ssh/connect-serverless-cpu/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

6 changes: 6 additions & 0 deletions acceptance/ssh/connect-serverless-cpu/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
errcode $CLI ssh connect --name serverless-cpu-test --releases-dir=$CLI_RELEASES_DIR -- "echo 'Connection successful'" >out.stdout.txt 2>LOG.stderr

if ! grep -q "Connection successful" out.stdout.txt; then
run_id=$(cat LOG.stderr | grep -o "Job submitted successfully with run ID: [0-9]*" | grep -o "[0-9]*$")
trace $CLI jobs get-run "$run_id" > LOG.job
fi
12 changes: 12 additions & 0 deletions acceptance/ssh/connect-serverless-cpu/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Local = false
Cloud = false

# Serverless CPU is only available in newer environments
RequiresUnityCatalog = true

# Serverless CPU is not available in GCP yet
[CloudEnvs]
gcp = false

[EnvMatrix]
DATABRICKS_BUNDLE_ENGINE = ["direct"]
106 changes: 106 additions & 0 deletions experimental/ssh/internal/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,112 @@ func TestGenerateDefaultConnectionNameMatchesRegex(t *testing.T) {
}
}

func TestFormatMetadata(t *testing.T) {
tests := []struct {
name string
userName string
port int
clusterID string
want string
}{
{
name: "with cluster ID",
userName: "root",
port: 2222,
clusterID: "abc-123",
want: "root,2222,abc-123",
},
{
name: "without cluster ID",
userName: "root",
port: 2222,
want: "root,2222",
},
{
name: "empty userName returns empty",
port: 2222,
want: "",
},
{
name: "zero port returns empty",
userName: "root",
want: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := client.FormatMetadata(tt.userName, tt.port, tt.clusterID)
assert.Equal(t, tt.want, got)
})
}
}

func TestIsServerlessMode(t *testing.T) {
tests := []struct {
name string
opts client.ClientOptions
want bool
}{
{
name: "cluster only",
opts: client.ClientOptions{ClusterID: "abc-123"},
want: false,
},
{
name: "connection name only",
opts: client.ClientOptions{ConnectionName: "my-conn"},
want: true,
},
{
name: "both cluster and connection name",
opts: client.ClientOptions{ClusterID: "abc-123", ConnectionName: "my-conn"},
want: false,
},
{
name: "neither",
opts: client.ClientOptions{},
want: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, tt.opts.IsServerlessMode())
})
}
}

func TestSessionIdentifier(t *testing.T) {
tests := []struct {
name string
opts client.ClientOptions
want string
}{
{
name: "cluster mode",
opts: client.ClientOptions{ClusterID: "abc-123"},
want: "abc-123",
},
{
name: "serverless mode",
opts: client.ClientOptions{ConnectionName: "my-conn"},
want: "my-conn",
},
{
name: "both returns cluster ID",
opts: client.ClientOptions{ClusterID: "abc-123", ConnectionName: "my-conn"},
want: "abc-123",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, tt.opts.SessionIdentifier())
})
}
}

func TestToProxyCommand(t *testing.T) {
exe, err := os.Executable()
require.NoError(t, err)
Expand Down
Loading