Skip to content

Commit 8f000b1

Browse files
Wire HostMetadata discovery into DatabricksConfig (#680)
## 🥞 Stacked PR Use this [link](https://github.com/databricks/databricks-sdk-java/pull/680/files) to review incremental changes. - [**stack/config-auto-complete-3**](#680) [[Files changed](https://github.com/databricks/databricks-sdk-java/pull/680/files)] --------- ## Summary Wires host metadata discovery into `DatabricksConfig` via a new package-private `resolveHostMetadata()` helper, mirroring `Config._resolve_host_metadata()` in the Python SDK. ## Why Users configuring the SDK today have no way to let it auto-discover `accountId`, `workspaceId`, or the OIDC endpoint from the host — they must supply each field manually. The `discoveryUrl` config property already allows providing a direct OIDC discovery URL; `resolveHostMetadata()` completes the picture by deriving all three fields automatically from the host's `/.well-known/databricks-config` endpoint. ## What changed ### Interface changes - **`DatabricksConfig.resolveHostMetadata()` (package-private)** — Calls `getHostMetadata()` on the configured host and back-fills `accountId`, `workspaceId`, and `discoveryUrl` (with any `{account_id}` placeholder substituted) if those fields are not already set. Throws `DatabricksException` if `accountId` cannot be resolved or if no `oidc_endpoint` is present in the metadata. ### Behavioral changes - `resolveHostMetadata()` is not called automatically — it is opt-in. - Existing fields are never overwritten. ### Internal changes Tests in `DatabricksConfigTest`: - `testResolveHostMetadataWorkspacePopulatesAllFields` — workspace populates all three fields. - `testResolveHostMetadataAccountSubstitutesAccountId` — account host substitutes `{account_id}` in the OIDC endpoint. - `testResolveHostMetadataDoesNotOverwriteExistingFields` — existing fields are not overwritten. - `testResolveHostMetadataRaisesWhenAccountIdUnresolvable` — raises when `accountId` cannot be resolved. - `testResolveHostMetadataRaisesWhenOidcEndpointMissing` — raises when `oidc_endpoint` is absent. - `testResolveHostMetadataRaisesOnHttpError` — raises when the well-known endpoint returns an error. ## How is this tested? Unit tests in `DatabricksConfigTest` using `FixtureServer`. NO_CHANGELOG=true
1 parent da6adf7 commit 8f000b1

2 files changed

Lines changed: 138 additions & 0 deletions

File tree

databricks-sdk-java/src/main/java/com/databricks/sdk/core/DatabricksConfig.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,45 @@ HostMetadata getHostMetadata() throws IOException {
827827
}
828828
}
829829

830+
/**
831+
* [Experimental] Populate missing config fields from the host's /.well-known/databricks-config
832+
* discovery endpoint.
833+
*
834+
* <p>Fills in {@code accountId}, {@code workspaceId}, and {@code discoveryUrl} (derived from
835+
* {@code oidc_endpoint}, with any {@code {account_id}} placeholder substituted) if not already
836+
* set.
837+
*
838+
* <p><b>Note:</b> This API is experimental and may change or be removed in future releases
839+
* without notice.
840+
*
841+
* @throws DatabricksException if {@code accountId} cannot be resolved or {@code oidc_endpoint} is
842+
* missing from the host metadata.
843+
*/
844+
void resolveHostMetadata() throws IOException {
845+
if (host == null) {
846+
return;
847+
}
848+
HostMetadata meta = getHostMetadata();
849+
if (accountId == null && meta.getAccountId() != null) {
850+
accountId = meta.getAccountId();
851+
}
852+
if (accountId == null) {
853+
throw new DatabricksException(
854+
"account_id is not configured and could not be resolved from host metadata");
855+
}
856+
if (workspaceId == null && meta.getWorkspaceId() != null) {
857+
workspaceId = meta.getWorkspaceId();
858+
}
859+
if (discoveryUrl == null) {
860+
if (meta.getOidcEndpoint() != null && !meta.getOidcEndpoint().isEmpty()) {
861+
discoveryUrl = meta.getOidcEndpoint().replace("{account_id}", accountId);
862+
} else {
863+
throw new DatabricksException(
864+
"discovery_url is not configured and could not be resolved from host metadata");
865+
}
866+
}
867+
}
868+
830869
private OpenIDConnectEndpoints fetchOidcEndpointsFromDiscovery() {
831870
try {
832871
Request request = new Request("GET", discoveryUrl);

databricks-sdk-java/src/test/java/com/databricks/sdk/core/DatabricksConfigTest.java

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,105 @@ public void testGetHostMetadataRaisesOnHttpError() throws IOException {
480480
}
481481
}
482482

483+
// --- resolveHostMetadata tests ---
484+
485+
@Test
486+
public void testResolveHostMetadataWorkspacePopulatesAllFields() throws IOException {
487+
String response =
488+
"{\"oidc_endpoint\":\"https://ws.databricks.com/oidc\","
489+
+ "\"account_id\":\""
490+
+ DUMMY_ACCOUNT_ID
491+
+ "\","
492+
+ "\"workspace_id\":\""
493+
+ DUMMY_WORKSPACE_ID
494+
+ "\"}";
495+
try (FixtureServer server =
496+
new FixtureServer().with("GET", "/.well-known/databricks-config", response, 200)) {
497+
DatabricksConfig config = new DatabricksConfig().setHost(server.getUrl());
498+
config.resolve(emptyEnv());
499+
config.resolveHostMetadata();
500+
assertEquals(DUMMY_ACCOUNT_ID, config.getAccountId());
501+
assertEquals(DUMMY_WORKSPACE_ID, config.getWorkspaceId());
502+
assertEquals("https://ws.databricks.com/oidc", config.getDiscoveryUrl());
503+
}
504+
}
505+
506+
@Test
507+
public void testResolveHostMetadataAccountSubstitutesAccountId() throws IOException {
508+
String response =
509+
"{\"oidc_endpoint\":\"https://acc.databricks.com/oidc/accounts/{account_id}\"}";
510+
try (FixtureServer server =
511+
new FixtureServer().with("GET", "/.well-known/databricks-config", response, 200)) {
512+
DatabricksConfig config =
513+
new DatabricksConfig().setHost(server.getUrl()).setAccountId(DUMMY_ACCOUNT_ID);
514+
config.resolve(emptyEnv());
515+
config.resolveHostMetadata();
516+
assertEquals(
517+
"https://acc.databricks.com/oidc/accounts/" + DUMMY_ACCOUNT_ID, config.getDiscoveryUrl());
518+
}
519+
}
520+
521+
@Test
522+
public void testResolveHostMetadataDoesNotOverwriteExistingFields() throws IOException {
523+
String existingAccountId = "existing-account-id";
524+
String existingWorkspaceId = "existing-workspace-id";
525+
String response =
526+
"{\"oidc_endpoint\":\"https://ws.databricks.com/oidc\","
527+
+ "\"account_id\":\"other-account\","
528+
+ "\"workspace_id\":\"other-ws\"}";
529+
try (FixtureServer server =
530+
new FixtureServer().with("GET", "/.well-known/databricks-config", response, 200)) {
531+
DatabricksConfig config =
532+
new DatabricksConfig()
533+
.setHost(server.getUrl())
534+
.setAccountId(existingAccountId)
535+
.setWorkspaceId(existingWorkspaceId);
536+
config.resolve(emptyEnv());
537+
config.resolveHostMetadata();
538+
assertEquals(existingAccountId, config.getAccountId());
539+
assertEquals(existingWorkspaceId, config.getWorkspaceId());
540+
}
541+
}
542+
543+
@Test
544+
public void testResolveHostMetadataRaisesWhenAccountIdUnresolvable() throws IOException {
545+
String response =
546+
"{\"oidc_endpoint\":\"https://acc.databricks.com/oidc/accounts/{account_id}\"}";
547+
try (FixtureServer server =
548+
new FixtureServer().with("GET", "/.well-known/databricks-config", response, 200)) {
549+
DatabricksConfig config = new DatabricksConfig().setHost(server.getUrl());
550+
config.resolve(emptyEnv());
551+
DatabricksException ex =
552+
assertThrows(DatabricksException.class, () -> config.resolveHostMetadata());
553+
assertTrue(ex.getMessage().contains("account_id is not configured"));
554+
}
555+
}
556+
557+
@Test
558+
public void testResolveHostMetadataRaisesWhenOidcEndpointMissing() throws IOException {
559+
String response = "{\"account_id\":\"" + DUMMY_ACCOUNT_ID + "\"}";
560+
try (FixtureServer server =
561+
new FixtureServer().with("GET", "/.well-known/databricks-config", response, 200)) {
562+
DatabricksConfig config = new DatabricksConfig().setHost(server.getUrl());
563+
config.resolve(emptyEnv());
564+
DatabricksException ex =
565+
assertThrows(DatabricksException.class, () -> config.resolveHostMetadata());
566+
assertTrue(ex.getMessage().contains("discovery_url is not configured"));
567+
}
568+
}
569+
570+
@Test
571+
public void testResolveHostMetadataRaisesOnHttpError() throws IOException {
572+
try (FixtureServer server =
573+
new FixtureServer().with("GET", "/.well-known/databricks-config", "{}", 500)) {
574+
DatabricksConfig config = new DatabricksConfig().setHost(server.getUrl());
575+
config.resolve(emptyEnv());
576+
DatabricksException ex =
577+
assertThrows(DatabricksException.class, () -> config.resolveHostMetadata());
578+
assertTrue(ex.getMessage().contains("Failed to fetch host metadata"));
579+
}
580+
}
581+
483582
// --- discoveryUrl / OIDC endpoint tests ---
484583

485584
@Test

0 commit comments

Comments
 (0)