Skip to content

Commit 59ad59e

Browse files
authored
config: extract ?o=/?a= from Host URL (#1699)
## Changes Pasting a SPOG URL from the Databricks UI (e.g. `https://acme.databricks.net/?o=12345`) into `Config.Host` previously dropped the workspace identifier on the way through `fixHostIfNeeded`: the function stripped path and query without promoting them to dedicated fields. The request then went out without an `X-Databricks-Org-Id` header, the server couldn't route it, and the response came back as the login HTML page, surfacing as `ErrHTMLContent` (`"received HTML response instead of JSON"`). This change recognizes `?o=`/`?workspace_id=` and `?a=`/`?account_id=` as part of host sanitization and promotes them into `Config.WorkspaceID`/`Config.AccountID` when those fields are empty. Existing values are never overwritten. The same fix is going in at the CLI level in [databricks/cli#5337](databricks/cli#5337) as a stopgap that pre-processes `DATABRICKS_HOST`. Once this SDK fix lands and the CLI bumps the SDK version, the CLI-side workaround can be removed. ## Tests - New table-driven test `TestConfig_fixHostIfNeeded_extractsWorkspaceIDFromQuery` in `config/config_test.go` covers: `?o=` promotion, `?workspace_id=` promotion, `?a=` promotion, both together, existing `WorkspaceID`/`AccountID` preserved, non-numeric `?o=` dropped, host without query unchanged. - `make fmt test lint` clean. NO_CHANGELOG=true --------- Signed-off-by: simon <simon.faltum@databricks.com>
1 parent 0a030bd commit 59ad59e

2 files changed

Lines changed: 113 additions & 0 deletions

File tree

config/config.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"net/http"
99
"net/url"
1010
"reflect"
11+
"strconv"
1112
"strings"
1213
"sync"
1314
"time"
@@ -627,6 +628,20 @@ func (c *Config) fixHostIfNeeded() error {
627628
if parsedHost.Hostname() == "" {
628629
return ErrNoHostConfigured
629630
}
631+
// SPOG URLs pasted from the Databricks UI carry the workspace ID as
632+
// ?o= (or ?workspace_id=) and the account ID as ?a= (or ?account_id=).
633+
// Promote those into WorkspaceID/AccountID before we strip the query,
634+
// so requests get the X-Databricks-Org-Id header instead of hitting the
635+
// SPOG without routing and getting back the login HTML page.
636+
if parsedHost.RawQuery != "" {
637+
q := parsedHost.Query()
638+
if c.WorkspaceID == "" {
639+
c.WorkspaceID = workspaceIDFromQuery(q)
640+
}
641+
if c.AccountID == "" {
642+
c.AccountID = accountIDFromQuery(q)
643+
}
644+
}
630645
// Create new instance to ensure other fields are initialized as empty.
631646
parsedHost = &url.URL{
632647
Scheme: parsedHost.Scheme,
@@ -637,6 +652,28 @@ func (c *Config) fixHostIfNeeded() error {
637652
return nil
638653
}
639654

655+
func workspaceIDFromQuery(q url.Values) string {
656+
for _, key := range []string{"o", "workspace_id"} {
657+
v := q.Get(key)
658+
if v == "" {
659+
continue
660+
}
661+
if _, err := strconv.ParseInt(v, 10, 64); err == nil {
662+
return v
663+
}
664+
}
665+
return ""
666+
}
667+
668+
func accountIDFromQuery(q url.Values) string {
669+
for _, key := range []string{"a", "account_id"} {
670+
if v := q.Get(key); v != "" {
671+
return v
672+
}
673+
}
674+
return ""
675+
}
676+
640677
// ErrNoHostConfigured is the error returned when a user tries to authenticate
641678
// without a host configured. Applications can check for this error to provide
642679
// more user-friendly error messages.

config/config_test.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,3 +1307,79 @@ func TestDefaultHostMetadataResolverFactory_NilResolverFromFactoryFallsThroughTo
13071307

13081308
assert.Equal(t, testHMAccountID, cfg.AccountID)
13091309
}
1310+
1311+
func TestConfig_fixHostIfNeeded_extractsWorkspaceIDFromQuery(t *testing.T) {
1312+
tests := []struct {
1313+
name string
1314+
host string
1315+
workspaceID string
1316+
accountID string
1317+
wantHost string
1318+
wantWorkspaceID string
1319+
wantAccountID string
1320+
}{
1321+
{
1322+
name: "?o= promoted to WorkspaceID",
1323+
host: "https://acme.databricks.net/?o=12345",
1324+
wantHost: "https://acme.databricks.net",
1325+
wantWorkspaceID: "12345",
1326+
},
1327+
{
1328+
name: "?workspace_id= promoted to WorkspaceID",
1329+
host: "https://acme.databricks.net/?workspace_id=12345",
1330+
wantHost: "https://acme.databricks.net",
1331+
wantWorkspaceID: "12345",
1332+
},
1333+
{
1334+
name: "?a= promoted to AccountID",
1335+
host: "https://acme.databricks.net/?a=abc",
1336+
wantHost: "https://acme.databricks.net",
1337+
wantAccountID: "abc",
1338+
},
1339+
{
1340+
name: "?o= and ?a= both promoted",
1341+
host: "https://acme.databricks.net/?o=12345&a=abc",
1342+
wantHost: "https://acme.databricks.net",
1343+
wantWorkspaceID: "12345",
1344+
wantAccountID: "abc",
1345+
},
1346+
{
1347+
name: "existing WorkspaceID is preserved",
1348+
host: "https://acme.databricks.net/?o=12345",
1349+
workspaceID: "99999",
1350+
wantHost: "https://acme.databricks.net",
1351+
wantWorkspaceID: "99999",
1352+
},
1353+
{
1354+
name: "existing AccountID is preserved",
1355+
host: "https://acme.databricks.net/?a=other",
1356+
accountID: "kept",
1357+
wantHost: "https://acme.databricks.net",
1358+
wantAccountID: "kept",
1359+
},
1360+
{
1361+
name: "non-numeric ?o= is dropped",
1362+
host: "https://acme.databricks.net/?o=notanumber",
1363+
wantHost: "https://acme.databricks.net",
1364+
},
1365+
{
1366+
name: "host without query is unchanged",
1367+
host: "https://acme.databricks.net",
1368+
wantHost: "https://acme.databricks.net",
1369+
},
1370+
}
1371+
1372+
for _, tt := range tests {
1373+
t.Run(tt.name, func(t *testing.T) {
1374+
cfg := &Config{
1375+
Host: tt.host,
1376+
WorkspaceID: tt.workspaceID,
1377+
AccountID: tt.accountID,
1378+
}
1379+
require.NoError(t, cfg.fixHostIfNeeded())
1380+
assert.Equal(t, tt.wantHost, cfg.Host)
1381+
assert.Equal(t, tt.wantWorkspaceID, cfg.WorkspaceID)
1382+
assert.Equal(t, tt.wantAccountID, cfg.AccountID)
1383+
})
1384+
}
1385+
}

0 commit comments

Comments
 (0)