Skip to content

Send X-Databricks-Workspace-Id on workspace-routed API calls#5368

Open
Divyansh-db wants to merge 5 commits into
mainfrom
new-workspace-parameter
Open

Send X-Databricks-Workspace-Id on workspace-routed API calls#5368
Divyansh-db wants to merge 5 commits into
mainfrom
new-workspace-parameter

Conversation

@Divyansh-db
Copy link
Copy Markdown
Contributor

Summary

On unified Databricks hosts that serve multiple workspaces, the CLI sends a routing header so the gateway can dispatch each request to the correct workspace. The platform is consolidating workspace addressing onto a single header name. This PR switches the header sent on workspace-scoped API calls from X-Databricks-Org-Id to X-Databricks-Workspace-Id. The previous header continues to be accepted by the platform, so older CLI versions keep working.

The value continues to come from the workspace_id config field or DATABRICKS_WORKSPACE_ID environment variable.

Scope

Hand-written CLI paths that issue workspace-routed API calls without going through generated SDK service methods:

  • databricks api {verb} (cmd/api/api.go)
  • Files API filer (libs/filer/files_client.go)
  • Workspace files filer (libs/filer/workspace_files_client.go)
  • Telemetry uploads (libs/telemetry/logger.go)

The matching change inside the SDK landed in databricks/databricks-sdk-go#1688 and will reach generated service methods on the next SDK bump.

Asymmetric migration

Only the request-side header flips. The SDK's CurrentWorkspaceID() still reads X-Databricks-Org-Id from the response on /api/2.0/preview/scim/v2/Me, because the server continues to echo the legacy response header. The two CLI test fakes that support that flow stay on the legacy name intentionally:

  • libs/testserver/handlers.go/Me mock response header
  • libs/testproxy/server.goincludeResponseHeaders for the proxy

Test plan

  • go test ./cmd/api/... ./libs/filer/... ./libs/telemetry/... — green
  • go test ./acceptance -run 'TestAccept/cmd/api|TestAccept/telemetry/(failure|timeout|partial)' — green (acceptance fixtures regenerated via -update)
  • ./task lint-q — 0 issues; ./task fmt — no changes
  • git grep -in 'X-Databricks-Org' -- ':!*.md' ':!*.sum' ':!bundle/' — only the two intentional response-side fakes plus a context comment in cmd/api/api.go

On unified Databricks hosts that serve multiple workspaces, the CLI sends a
routing header so the gateway can dispatch each request to the correct
workspace. The platform is consolidating workspace addressing onto a single
header name. Switch the header sent on workspace-scoped API calls from
X-Databricks-Org-Id to X-Databricks-Workspace-Id. The previous header
continues to be accepted by the platform, so older CLI versions keep working.

This change covers the hand-written paths in the CLI that issue workspace-
routed API calls without going through generated SDK service methods:

- databricks api {verb} (cmd/api/api.go)
- Files API filer (libs/filer/files_client.go)
- Workspace files filer (libs/filer/workspace_files_client.go)
- Telemetry uploads (libs/telemetry/logger.go)

The value continues to come from the workspace_id config field or
DATABRICKS_WORKSPACE_ID.

The CurrentWorkspaceID() helper in the SDK still reads X-Databricks-Org-Id
from the response on /api/2.0/preview/scim/v2/Me, so the response-side
references in libs/testserver/handlers.go and libs/testproxy/server.go
intentionally remain on the legacy header name.
@eng-dev-ecosystem-bot
Copy link
Copy Markdown
Collaborator

eng-dev-ecosystem-bot commented May 28, 2026

Commit: 38859c1

Run: 26753469103

The cmd_exec_id placeholder regressed from [CMD-EXEC-ID] to [UUID] when
the fixture was regenerated locally with jq 1.6. The test script uses
jq '.headers."User-Agent".[0]' syntax that only works in jq 1.7+; on the
older runtime that pipeline silently produced no replacement, leaving
the raw UUID in place which then matched the generic UUID replacement.

Re-regenerated with jq 1.7. CI was already running 1.7+ which is why the
discrepancy only showed up against the wire output, not locally.
Comment thread libs/telemetry/logger.go
func attempt(ctx context.Context, apiClient *client.DatabricksClient, protoLogs []string) (*ResponseBody, error) {
resp := &ResponseBody{}
err := apiClient.Do(ctx, http.MethodPost, "/telemetry-ext", orgIDHeaders(apiClient), nil, RequestBody{
err := apiClient.Do(ctx, http.MethodPost, "/telemetry-ext", workspaceIDHeaders(apiClient), nil, RequestBody{
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.

There are a bunch of places where we call ApiClient.Do but don't set the headers like for example

cli/libs/git/info.go

Lines 64 to 78 in 6795f16

err = apiClient.Do(
ctx,
http.MethodGet,
apiEndpoint,
nil,
nil,
map[string]string{
"path": path,
"return_git_info": "true",
},
&response,
)
if err != nil {
return result, err
}

@simonfaltum any thoughts on it?

@andrewnester andrewnester requested a review from simonfaltum May 29, 2026 12:55
bundle/deploy/filer.go has its own stateFiler that fetches the bundle
deployment state from /api/2.0/workspace-files/ using client.Do() directly
(bypassing generated SDK service methods). Apply the same X-Databricks-Org-Id
→ X-Databricks-Workspace-Id swap here so bundle deploy/summary against
unified hosts uses the new routing header consistently with the rest of the
CLI.

Method renamed orgIDHeaders → workspaceIDHeaders for consistency with the
other filer helpers updated in this PR.
Copy link
Copy Markdown
Member

@simonfaltum simonfaltum left a comment

Choose a reason for hiding this comment

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

Code review note:

The PR still misses some raw workspace API callers that bypass generated SDK methods and send no workspace routing header. The PR scope says it covers hand-written workspace-routed calls, but these remain uncovered:

  • bundle/generate/downloader.go:161
  • cmd/pipelines/utils.go:185
  • libs/databrickscfg/cfgpickers/warehouses.go:140
  • libs/apps/prompt/listers.go:178
  • libs/git/info.go:64

On unified hosts with WorkspaceID, those calls can still route incorrectly or fail. I would add a shared helper for the new X-Databricks-Workspace-Id request header and use it for these raw callers, or explicitly document why each one is out of scope. This matches the existing review comment from @andrewnester, with a few more examples.

@hectorcast-db
Copy link
Copy Markdown
Contributor

Code review note:

The PR still misses some raw workspace API callers that bypass generated SDK methods and send no workspace routing header. The PR scope says it covers hand-written workspace-routed calls, but these remain uncovered:

  • bundle/generate/downloader.go:161
  • cmd/pipelines/utils.go:185
  • libs/databrickscfg/cfgpickers/warehouses.go:140
  • libs/apps/prompt/listers.go:178
  • libs/git/info.go:64

On unified hosts with WorkspaceID, those calls can still route incorrectly or fail. I would add a shared helper for the new X-Databricks-Workspace-Id request header and use it for these raw callers, or explicitly document why each one is out of scope. This matches the existing review comment from @andrewnester, with a few more examples.

We should definitely check those ASAP, but from what I understand, this PR is about changing an existing header. Easy change, no analysis required. I think we can merge this and analyze whether those where a gap we missed.

hectorcast-db

This comment was marked as duplicate.

## Summary

The Databricks UI is migrating from `?o=<workspace-id>` to
`?w=<workspace-id>` as the SPOG URL query parameter, matching the new
workspace addressing header. This PR extends the CLI's URL parsers to
recognize `?w=` in addition to the existing `?o=` and `?workspace_id=`
spellings. Pure addition; no existing URL changes meaning.

Stacked on top of #5368 (which renames the request header to
`X-Databricks-Workspace-Id`); the two together complete the input + wire
side of the URL/header migration for the core CLI.

## Affected entry points

- `databricks api <verb> <path?w=...>` — workspace ID is extracted from
the path and sent as the routing header on the call.
- `databricks auth login --host "https://...?w=..."` — workspace ID is
extracted from the host URL and persisted to the profile.
- `workspace.host` in `databricks.yml` — uses the same shared parser
(`libs/auth.ExtractHostQueryParams`).

## Precedence

When more than one spelling appears on a single URL, **`?o=` > `?w=` >
`?workspace_id=`**. The `o`-first rule preserves the resolution of any
URL already pasted from older UI builds, shell history, or committed
`databricks.yml` files.

## Rename

`extractOrgIDFromQuery` (in `cmd/api/api.go`) →
`extractWorkspaceIDFromQuery`. The helper now returns the value under
any of the recognized parameter names, so the old name became
misleading. Unexported, single call site; updated atomically.

## Files

- `libs/auth/hostparams.go` — adds the `q.Get("w")` branch in
`ExtractHostQueryParams`; comment refreshed to document the three
accepted forms and precedence.
- `cmd/api/api.go` — adds `workspaceIDQueryParam = "w"` const; renames
extractor and updates its body to check `o` then `w`.
- `cmd/auth/login.go` — help text updated to recommend `?w=` and note
that `?o=` / `?workspace_id=` are still accepted.
- `libs/auth/hostparams_test.go` — new cases for `?w=`, precedence, and
non-numeric rejection.
- `cmd/api/api_test.go` — new cases in `TestExtractWorkspaceIDFromQuery`
(renamed) and `TestResolveOrgID` covering `?w=` and the
`o`-wins-over-`w` precedence.
- `acceptance/cmd/api/workspace-id-from-w-query/` — new acceptance test
mirroring `workspace-id-from-query/` but exercising the `?w=` path. The
original `?o=` test stays unchanged as a regression check.

## Test plan

- [x] \`go test ./libs/auth/... ./cmd/api/... ./cmd/auth/... -count=1\`
— green
- [x] \`go test ./acceptance -run
'TestAccept/cmd/api|TestAccept/cmd/auth|TestAccept/auth'\` — green
- [x] \`./task lint-q\` — 0 issues
- [x] \`./task fmt\` — no changes
Sign up for free to 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.

5 participants