Skip to content

Commit 9dd595d

Browse files
simonfaltumclaude
andcommitted
Add support for default_profile in [__settings__] section
When no profile is explicitly configured, check [__settings__].default_profile in ~/.databrickscfg before falling back to the DEFAULT section. This aligns the Java SDK with the CLI's 'auth switch' feature, which writes the user's preferred profile to [__settings__]. Profile resolution order: 1. Explicit profile (--profile flag, env var, or programmatic config) 2. [__settings__].default_profile (new) 3. [DEFAULT] section (legacy fallback) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 8ca9a05 commit 9dd595d

File tree

7 files changed

+170
-0
lines changed

7 files changed

+170
-0
lines changed

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,16 @@ static void loadFromConfig(DatabricksConfig cfg) throws IllegalAccessException {
9494

9595
String profile = cfg.getProfile();
9696
boolean hasExplicitProfile = !isNullOrEmpty(profile);
97+
if (!hasExplicitProfile) {
98+
SubnodeConfiguration settings = ini.getSection("__settings__");
99+
if (settings != null && !settings.isEmpty()) {
100+
String defaultProfile = settings.getString("default_profile");
101+
if (!isNullOrEmpty(defaultProfile)) {
102+
profile = defaultProfile;
103+
hasExplicitProfile = true;
104+
}
105+
}
106+
}
97107
if (!hasExplicitProfile) {
98108
profile = "DEFAULT";
99109
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package com.databricks.sdk;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
import static org.junit.jupiter.api.Assertions.assertTrue;
6+
import static org.mockito.Mockito.mock;
7+
8+
import com.databricks.sdk.core.ConfigResolving;
9+
import com.databricks.sdk.core.DatabricksConfig;
10+
import com.databricks.sdk.core.DatabricksException;
11+
import com.databricks.sdk.core.http.HttpClient;
12+
import com.databricks.sdk.core.utils.TestOSUtils;
13+
import org.junit.jupiter.api.Test;
14+
15+
public class DefaultProfileTest implements ConfigResolving {
16+
17+
private DatabricksConfig createConfigWithMockClient() {
18+
HttpClient mockClient = mock(HttpClient.class);
19+
return new DatabricksConfig().setHttpClient(mockClient);
20+
}
21+
22+
/** Test 1: default_profile resolves correctly */
23+
@Test
24+
public void testDefaultProfileResolvesCorrectly() {
25+
StaticEnv env =
26+
new StaticEnv().with("HOME", TestOSUtils.resource("/testdata/default_profile"));
27+
DatabricksConfig config = createConfigWithMockClient();
28+
resolveConfig(config, env);
29+
config.authenticate();
30+
31+
assertEquals("pat", config.getAuthType());
32+
assertEquals("https://my-workspace.cloud.databricks.com", config.getHost());
33+
}
34+
35+
/** Test 2: default_profile takes precedence over [DEFAULT] */
36+
@Test
37+
public void testDefaultProfileTakesPrecedenceOverDefault() {
38+
StaticEnv env =
39+
new StaticEnv()
40+
.with("HOME", TestOSUtils.resource("/testdata/default_profile_precedence"));
41+
DatabricksConfig config = createConfigWithMockClient();
42+
resolveConfig(config, env);
43+
config.authenticate();
44+
45+
assertEquals("pat", config.getAuthType());
46+
assertEquals("https://my-workspace.cloud.databricks.com", config.getHost());
47+
}
48+
49+
/** Test 3: Legacy fallback when no [__settings__] */
50+
@Test
51+
public void testLegacyFallbackWhenNoSettings() {
52+
StaticEnv env = new StaticEnv().with("HOME", TestOSUtils.resource("/testdata"));
53+
DatabricksConfig config = createConfigWithMockClient();
54+
resolveConfig(config, env);
55+
config.authenticate();
56+
57+
assertEquals("pat", config.getAuthType());
58+
assertEquals("https://dbc-XXXXXXXX-YYYY.cloud.databricks.com", config.getHost());
59+
}
60+
61+
/** Test 4: Legacy fallback when default_profile is empty */
62+
@Test
63+
public void testLegacyFallbackWhenDefaultProfileEmpty() {
64+
StaticEnv env =
65+
new StaticEnv()
66+
.with("HOME", TestOSUtils.resource("/testdata/default_profile_empty_settings"));
67+
DatabricksConfig config = createConfigWithMockClient();
68+
resolveConfig(config, env);
69+
config.authenticate();
70+
71+
assertEquals("pat", config.getAuthType());
72+
assertEquals("https://default.cloud.databricks.com", config.getHost());
73+
}
74+
75+
/** Test 5: [__settings__] is not a profile */
76+
@Test
77+
public void testSettingsIsNotAProfile() {
78+
StaticEnv env =
79+
new StaticEnv().with("HOME", TestOSUtils.resource("/testdata/default_profile"));
80+
DatabricksConfig config = createConfigWithMockClient();
81+
resolveConfig(config, env);
82+
config.authenticate();
83+
84+
// The config should load from my-workspace (the default_profile), not __settings__
85+
assertEquals("https://my-workspace.cloud.databricks.com", config.getHost());
86+
assertEquals("pat", config.getAuthType());
87+
}
88+
89+
/** Test 6: Explicit --profile overrides default_profile */
90+
@Test
91+
public void testExplicitProfileOverridesDefaultProfile() {
92+
StaticEnv env =
93+
new StaticEnv()
94+
.with("DATABRICKS_CONFIG_PROFILE", "other")
95+
.with("HOME", TestOSUtils.resource("/testdata/default_profile_explicit_override"));
96+
DatabricksConfig config = createConfigWithMockClient();
97+
resolveConfig(config, env);
98+
config.authenticate();
99+
100+
assertEquals("pat", config.getAuthType());
101+
assertEquals("https://other.cloud.databricks.com", config.getHost());
102+
}
103+
104+
/** Test 7: default_profile pointing to nonexistent section */
105+
@Test
106+
public void testDefaultProfileNonexistentSection() {
107+
StaticEnv env =
108+
new StaticEnv()
109+
.with("HOME", TestOSUtils.resource("/testdata/default_profile_nonexistent"));
110+
DatabricksConfig config = createConfigWithMockClient();
111+
112+
DatabricksException ex =
113+
assertThrows(
114+
DatabricksException.class,
115+
() -> {
116+
resolveConfig(config, env);
117+
config.authenticate();
118+
});
119+
assertTrue(
120+
ex.getMessage().contains("deleted-profile"),
121+
"Error should mention the missing profile name: " + ex.getMessage());
122+
}
123+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[__settings__]
2+
default_profile = my-workspace
3+
4+
[my-workspace]
5+
host = https://my-workspace.cloud.databricks.com
6+
token = dapiXYZ
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[__settings__]
2+
3+
[DEFAULT]
4+
host = https://default.cloud.databricks.com
5+
token = dapiXYZ
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[__settings__]
2+
default_profile = my-workspace
3+
4+
[my-workspace]
5+
host = https://my-workspace.cloud.databricks.com
6+
token = dapiXYZ
7+
8+
[other]
9+
host = https://other.cloud.databricks.com
10+
token = dapiOTHER
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[__settings__]
2+
default_profile = deleted-profile
3+
4+
[my-workspace]
5+
host = https://my-workspace.cloud.databricks.com
6+
token = dapiXYZ
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[__settings__]
2+
default_profile = my-workspace
3+
4+
[DEFAULT]
5+
host = https://default.cloud.databricks.com
6+
token = dapiOLD
7+
8+
[my-workspace]
9+
host = https://my-workspace.cloud.databricks.com
10+
token = dapiXYZ

0 commit comments

Comments
 (0)