Skip to content

experimental/ssh: surface connect failures instead of hanging - #5456

Merged
anton-107 merged 4 commits into
mainfrom
anton-107/ssh-surface-connect-errors
Jun 9, 2026
Merged

experimental/ssh: surface connect failures instead of hanging#5456
anton-107 merged 4 commits into
mainfrom
anton-107/ssh-surface-connect-errors

Conversation

@anton-107

Copy link
Copy Markdown
Contributor

Why

Originated from a customer case: databricks ssh connect to a dedicated cluster whose
Docker container image was missing an OpenSSH server (/usr/sbin/sshd). The failure
surfaced terribly — either a generic server metadata error / metadata.json doesn't exist,
or the client just hung (the local ssh waited on its 360s ConnectTimeout). The root
cause was buried in the cluster's job-run logs.

This PR improves the diagnostics for ssh connect failures.

What

  1. Surface bootstrap job-run errors. When the SSH server bootstrap job reaches a
    terminal/failed state, fetch the run's state message, notebook error/trace, and run-page
    URL and show them — both when the task terminates before reaching RUNNING and when it dies
    after, during metadata polling. (experimental/ssh/internal/client/client.go)

  2. Guard against hangs when the server is up but the handshake never completes. If the
    container image has no sshd, the server can't launch /usr/sbin/sshd on connect and
    holds the websocket open, so both proxy loops block forever. The client now runs the
    proxy loops in the background and aborts after a handshake timeout (no server response)
    with an actionable hint, and also exits promptly when the server does close the
    connection. (experimental/ssh/internal/proxy/client.go)

  3. openssh-server hint when ssh exits with its connection-failure code (255).
    (spawnSSHClient)

Tests

  • client_internal_test.go: failed-run message formatting (state message + trace + run URL),
    truncation, terminal-state detection (SDK mocks).
  • proxy/client_server_test.go: fast exit when the server closes the connection; abort on the
    handshake timeout when the server sends nothing.

All experimental/ssh/... tests pass; lint clean.

Status / follow-ups (WIP)

  • The missing-sshd path still incurs a ~30s handshake-timeout wait before failing. The
    cleaner fix is a server-side pre-flight sshd check (fail the bootstrap job immediately
    with a clear message), tracked separately — that would turn this case into an instant,
    clear job failure handled by improvement Bump github.com/databrickslabs/terraform-provider-databricks from 0.5.7 to 0.5.8 #1.
  • The handshake timeout (30s) is conservative and currently a package constant; could be
    shortened or made configurable.
  • The proxy error and the outer 255 hint are slightly redundant; may consolidate.

This pull request and its description were written by Isaac.

Improve diagnostics when `databricks ssh connect` fails.
- Surface bootstrap job-run errors: when the SSH server bootstrap job
reaches a terminal/failed state, fetch the run's state message,
notebook error/trace, and run-page URL and show them, instead of the
generic "server metadata error / metadata.json doesn't exist".
- Guard against hangs when the server is up but the handshake never
completes (e.g. the container image has no OpenSSH server, so the
server can't launch /usr/sbin/sshd and holds the websocket open). The
client now aborts after a handshake timeout with an actionable hint,
and exits promptly when the server closes the connection, instead of
hanging until ssh's ConnectTimeout.
- Add an openssh-server hint when ssh exits with its connection-failure
code (255).
Tests cover the failed-run message formatting, the fast exit on server
close, and the handshake timeout.
WIP: the missing-sshd path still incurs a handshake-timeout wait; a
server-side pre-flight sshd check (tracked separately) would turn it
into an immediate, clear job failure.
Co-authored-by: Isaac
@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Commit: 6bec572

Run: 27145528424

Env🟨​KNOWN🔄​flaky💚​RECOVERED🙈​SKIP✅​pass🙈​skipTime
🟨​aws linux7152619248:12
🟨​aws windows71526392215:14
💚​aws-ucws linux7153578388:27
💚​aws-ucws windows71535983612:26
💚​azure linux1172649227:20
💚​azure windows11726692011:59
💚​azure-ucws linux1173628349:38
💚​azure-ucws windows11736483210:45
🔄​gcp linux21172589259:59
💚​gcp windows11726292312:18
24 interesting tests: 15 SKIP, 7 KNOWN, 2 flaky
Test Nameaws linuxaws windowsaws-ucws linuxaws-ucws windowsazure linuxazure windowsazure-ucws linuxazure-ucws windowsgcp linuxgcp windows
🟨​TestAccept🟨​K🟨​K💚​R💚​R💚​R💚​R💚​R💚​R💚​R💚​R
🙈​TestAccept/bundle/invariant/no_drift🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/permissions🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🟨​TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions🟨​K🟨​K💚​R💚​R🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🟨​TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=direct🟨​K🟨​K💚​R💚​R
🟨​TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=terraform🟨​K🟨​K💚​R💚​R
🟨​TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions🟨​K🟨​K💚​R💚​R🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🟨​TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions/DATABRICKS_BUNDLE_ENGINE=direct🟨​K🟨​K💚​R💚​R
🟨​TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions/DATABRICKS_BUNDLE_ENGINE=terraform🟨​K🟨​K💚​R💚​R
🙈​TestAccept/bundle/resources/postgres_branches/basic🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/postgres_branches/recreate🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/postgres_branches/replace_existing🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/postgres_branches/update_protected🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/postgres_branches/without_branch_id🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/postgres_endpoints/basic🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/postgres_endpoints/recreate🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/postgres_projects/update_display_name🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/synced_database_tables/basic🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/vector_search_indexes/basic🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/vector_search_indexes/grants/select🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/ssh/connection🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🔄​TestFetchRepositoryInfoAPI_FromRepo/root✅​p✅​p✅​p✅​p✅​p✅​p✅​p✅​p🔄​f✅​p
🔄​TestFetchRepositoryInfoAPI_FromRepo/subdir✅​p✅​p✅​p✅​p✅​p✅​p✅​p✅​p🔄​f✅​p
Top 28 slowest tests (at least 2 minutes):
durationenvtestname
7:16azure windowsTestAccept
6:29aws-ucws windowsTestAccept
6:23gcp windowsTestAccept
5:14azure-ucws windowsTestAccept
4:49gcp linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
4:44gcp windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:34gcp linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:58gcp windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:45azure-ucws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:21azure-ucws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:21aws-ucws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:11aws-ucws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:06azure windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:04aws-ucws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:00aws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:59azure linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:58aws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:55aws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:53azure linuxTestAccept
2:51gcp linuxTestAccept
2:51azure windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:50aws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:49azure-ucws linuxTestAccept
2:48azure-ucws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:41aws-ucws linuxTestAccept
2:39aws-ucws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:35azure-ucws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:26azure linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct

Add FAILURE_MODES.md describing how to reproduce, and what the user sees
for, the two `databricks ssh connect` container failure modes: a missing
OpenSSH server (sshd launched lazily, connection drops after "Connected!")
and a container that can't run the Python bootstrap ("Could not reach
driver" before sshd matters). Includes example Dockerfiles, a cluster
spec, a working control image, how to read the bootstrap job logs, and
the local unit tests that cover the same paths. Linked from README.
Co-authored-by: Isaac
@anton-107
anton-107 requested a review from rclareyJune 8, 2026 07:57
@anton-107
anton-107 marked this pull request as ready for review June 8, 2026 14:41
@anton-107
anton-107 requested a review from pieternJune 9, 2026 07:05

@rclareyrclarey left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code LGTM, didn't do any of the manual testing described in FAILURE_MODES.md but I can if you want a second person's confirmation

@anton-107
anton-107 added this pull request to the merge queueJun 9, 2026
Merged via the queue into main with commit a934f8cJun 9, 2026
22 checks passed
@anton-107
anton-107 deleted the anton-107/ssh-surface-connect-errors branch June 9, 2026 14:28
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@anton-107@eng-dev-ecosystem-bot@rclarey