Skip to content

Commit 39ecf38

Browse files
simonfaltumclaude
andcommitted
Add support for default_profile in [__settings__] section
Adds support for the __settings__ section in .databrickscfg, enabling default_profile to specify which profile to use when none is explicitly set. The resolved profile is written back to DatabricksConfig so downstream consumers (e.g. DatabricksCliCredentialsProvider) see it. Rejects __settings__ as a profile target to prevent self-referencing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 8ca9a05 commit 39ecf38

File tree

8 files changed

+179
-0
lines changed

8 files changed

+179
-0
lines changed

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,17 @@ 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) && !"__settings__".equals(defaultProfile)) {
102+
profile = defaultProfile;
103+
hasExplicitProfile = true;
104+
cfg.setProfile(profile);
105+
}
106+
}
107+
}
97108
if (!hasExplicitProfile) {
98109
profile = "DEFAULT";
99110
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
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 and is written back to config */
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+
assertEquals("my-workspace", config.getProfile());
34+
}
35+
36+
/** Test 2: default_profile takes precedence over [DEFAULT] */
37+
@Test
38+
public void testDefaultProfileTakesPrecedenceOverDefault() {
39+
StaticEnv env =
40+
new StaticEnv()
41+
.with("HOME", TestOSUtils.resource("/testdata/default_profile_precedence"));
42+
DatabricksConfig config = createConfigWithMockClient();
43+
resolveConfig(config, env);
44+
config.authenticate();
45+
46+
assertEquals("pat", config.getAuthType());
47+
assertEquals("https://my-workspace.cloud.databricks.com", config.getHost());
48+
}
49+
50+
/** Test 3: Legacy fallback when no [__settings__] */
51+
@Test
52+
public void testLegacyFallbackWhenNoSettings() {
53+
StaticEnv env = new StaticEnv().with("HOME", TestOSUtils.resource("/testdata"));
54+
DatabricksConfig config = createConfigWithMockClient();
55+
resolveConfig(config, env);
56+
config.authenticate();
57+
58+
assertEquals("pat", config.getAuthType());
59+
assertEquals("https://dbc-XXXXXXXX-YYYY.cloud.databricks.com", config.getHost());
60+
}
61+
62+
/** Test 4: Legacy fallback when default_profile is empty */
63+
@Test
64+
public void testLegacyFallbackWhenDefaultProfileEmpty() {
65+
StaticEnv env =
66+
new StaticEnv()
67+
.with("HOME", TestOSUtils.resource("/testdata/default_profile_empty_settings"));
68+
DatabricksConfig config = createConfigWithMockClient();
69+
resolveConfig(config, env);
70+
config.authenticate();
71+
72+
assertEquals("pat", config.getAuthType());
73+
assertEquals("https://default.cloud.databricks.com", config.getHost());
74+
}
75+
76+
/** Test 5: default_profile = __settings__ is rejected and falls back to DEFAULT */
77+
@Test
78+
public void testSettingsSelfReferenceIsRejected() {
79+
StaticEnv env =
80+
new StaticEnv()
81+
.with("HOME", TestOSUtils.resource("/testdata/default_profile_settings_self_ref"));
82+
DatabricksConfig config = createConfigWithMockClient();
83+
resolveConfig(config, env);
84+
config.authenticate();
85+
86+
// __settings__ as a profile target should be ignored, falling back to [DEFAULT]
87+
assertEquals("https://default.cloud.databricks.com", config.getHost());
88+
assertEquals("pat", config.getAuthType());
89+
}
90+
91+
/** Test 6: Explicit --profile overrides default_profile */
92+
@Test
93+
public void testExplicitProfileOverridesDefaultProfile() {
94+
StaticEnv env =
95+
new StaticEnv()
96+
.with("DATABRICKS_CONFIG_PROFILE", "other")
97+
.with("HOME", TestOSUtils.resource("/testdata/default_profile_explicit_override"));
98+
DatabricksConfig config = createConfigWithMockClient();
99+
resolveConfig(config, env);
100+
config.authenticate();
101+
102+
assertEquals("pat", config.getAuthType());
103+
assertEquals("https://other.cloud.databricks.com", config.getHost());
104+
}
105+
106+
/** Test 7: default_profile pointing to nonexistent section */
107+
@Test
108+
public void testDefaultProfileNonexistentSection() {
109+
StaticEnv env =
110+
new StaticEnv()
111+
.with("HOME", TestOSUtils.resource("/testdata/default_profile_nonexistent"));
112+
DatabricksConfig config = createConfigWithMockClient();
113+
114+
DatabricksException ex =
115+
assertThrows(
116+
DatabricksException.class,
117+
() -> {
118+
resolveConfig(config, env);
119+
config.authenticate();
120+
});
121+
assertTrue(
122+
ex.getMessage().contains("deleted-profile"),
123+
"Error should mention the missing profile name: " + ex.getMessage());
124+
}
125+
}
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
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[__settings__]
2+
default_profile = __settings__
3+
4+
[DEFAULT]
5+
host = https://default.cloud.databricks.com
6+
token = dapiXYZ

0 commit comments

Comments
 (0)