@databricks/sdk-core 0.0.0-dev → 0.1.0-dev.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +203 -0
- package/README.md +11 -1
- package/dist/api/execute.d.ts +12 -0
- package/dist/api/execute.d.ts.map +1 -0
- package/dist/api/execute.js +77 -0
- package/dist/api/execute.js.map +1 -0
- package/dist/api/index.d.ts +12 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +8 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/limiter.d.ts +9 -0
- package/dist/api/limiter.d.ts.map +1 -0
- package/dist/api/limiter.js +2 -0
- package/dist/api/limiter.js.map +1 -0
- package/dist/api/options.d.ts +22 -0
- package/dist/api/options.d.ts.map +1 -0
- package/dist/api/options.js +2 -0
- package/dist/api/options.js.map +1 -0
- package/dist/api/retrier.d.ts +58 -0
- package/dist/api/retrier.d.ts.map +1 -0
- package/dist/api/retrier.js +70 -0
- package/dist/api/retrier.js.map +1 -0
- package/dist/apierror/apierror.d.ts +53 -0
- package/dist/apierror/apierror.d.ts.map +1 -0
- package/dist/apierror/apierror.js +217 -0
- package/dist/apierror/apierror.js.map +1 -0
- package/dist/apierror/codes/codes.d.ts +140 -0
- package/dist/apierror/codes/codes.d.ts.map +1 -0
- package/dist/apierror/codes/codes.js +167 -0
- package/dist/apierror/codes/codes.js.map +1 -0
- package/dist/apierror/codes/index.d.ts +7 -0
- package/dist/apierror/codes/index.d.ts.map +1 -0
- package/dist/apierror/codes/index.js +7 -0
- package/dist/apierror/codes/index.js.map +1 -0
- package/dist/apierror/details.d.ts +177 -0
- package/dist/apierror/details.d.ts.map +1 -0
- package/dist/apierror/details.js +250 -0
- package/dist/apierror/details.js.map +1 -0
- package/dist/apierror/index.d.ts +8 -0
- package/dist/apierror/index.d.ts.map +1 -0
- package/dist/apierror/index.js +7 -0
- package/dist/apierror/index.js.map +1 -0
- package/dist/clientinfo/agent.d.ts +56 -0
- package/dist/clientinfo/agent.d.ts.map +1 -0
- package/dist/clientinfo/agent.js +114 -0
- package/dist/clientinfo/agent.js.map +1 -0
- package/dist/clientinfo/base.d.ts +39 -0
- package/dist/clientinfo/base.d.ts.map +1 -0
- package/dist/clientinfo/base.js +62 -0
- package/dist/clientinfo/base.js.map +1 -0
- package/dist/clientinfo/clientinfo.d.ts +61 -0
- package/dist/clientinfo/clientinfo.d.ts.map +1 -0
- package/dist/clientinfo/clientinfo.js +96 -0
- package/dist/clientinfo/clientinfo.js.map +1 -0
- package/dist/clientinfo/default.browser.d.ts +17 -0
- package/dist/clientinfo/default.browser.d.ts.map +1 -0
- package/dist/clientinfo/default.browser.js +20 -0
- package/dist/clientinfo/default.browser.js.map +1 -0
- package/dist/clientinfo/default.d.ts +14 -0
- package/dist/clientinfo/default.d.ts.map +1 -0
- package/dist/clientinfo/default.js +104 -0
- package/dist/clientinfo/default.js.map +1 -0
- package/dist/clientinfo/index.browser.d.ts +5 -0
- package/dist/clientinfo/index.browser.d.ts.map +1 -0
- package/dist/clientinfo/index.browser.js +4 -0
- package/dist/clientinfo/index.browser.js.map +1 -0
- package/dist/clientinfo/index.d.ts +5 -0
- package/dist/clientinfo/index.d.ts.map +1 -0
- package/dist/clientinfo/index.js +4 -0
- package/dist/clientinfo/index.js.map +1 -0
- package/dist/http/http.d.ts +40 -0
- package/dist/http/http.d.ts.map +1 -0
- package/dist/http/http.js +37 -0
- package/dist/http/http.js.map +1 -0
- package/dist/http/index.d.ts +8 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +7 -0
- package/dist/http/index.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/logger/index.d.ts +8 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/logger/index.js +7 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/logger/logger.d.ts +49 -0
- package/dist/logger/logger.d.ts.map +1 -0
- package/dist/logger/logger.js +65 -0
- package/dist/logger/logger.js.map +1 -0
- package/dist/profiles/errors.d.ts +17 -0
- package/dist/profiles/errors.d.ts.map +1 -0
- package/dist/profiles/errors.js +19 -0
- package/dist/profiles/errors.js.map +1 -0
- package/dist/profiles/index.browser.d.ts +10 -0
- package/dist/profiles/index.browser.d.ts.map +1 -0
- package/dist/profiles/index.browser.js +8 -0
- package/dist/profiles/index.browser.js.map +1 -0
- package/dist/profiles/index.d.ts +15 -0
- package/dist/profiles/index.d.ts.map +1 -0
- package/dist/profiles/index.js +13 -0
- package/dist/profiles/index.js.map +1 -0
- package/dist/profiles/ini.d.ts +36 -0
- package/dist/profiles/ini.d.ts.map +1 -0
- package/dist/profiles/ini.js +113 -0
- package/dist/profiles/ini.js.map +1 -0
- package/dist/profiles/profile.d.ts +131 -0
- package/dist/profiles/profile.d.ts.map +1 -0
- package/dist/profiles/profile.js +307 -0
- package/dist/profiles/profile.js.map +1 -0
- package/dist/profiles/resolve.d.ts +29 -0
- package/dist/profiles/resolve.d.ts.map +1 -0
- package/dist/profiles/resolve.js +206 -0
- package/dist/profiles/resolve.js.map +1 -0
- package/dist/profiles/secret.d.ts +25 -0
- package/dist/profiles/secret.d.ts.map +1 -0
- package/dist/profiles/secret.js +38 -0
- package/dist/profiles/secret.js.map +1 -0
- package/dist/wkt/fieldmask.d.ts +32 -0
- package/dist/wkt/fieldmask.d.ts.map +1 -0
- package/dist/wkt/fieldmask.js +68 -0
- package/dist/wkt/fieldmask.js.map +1 -0
- package/dist/wkt/index.d.ts +4 -0
- package/dist/wkt/index.d.ts.map +1 -0
- package/dist/wkt/index.js +2 -0
- package/dist/wkt/index.js.map +1 -0
- package/dist/wkt/value.d.ts +13 -0
- package/dist/wkt/value.d.ts.map +1 -0
- package/dist/wkt/value.js +2 -0
- package/dist/wkt/value.js.map +1 -0
- package/package.json +82 -4
- package/src/api/execute.ts +102 -0
- package/src/api/index.ts +12 -0
- package/src/api/limiter.ts +8 -0
- package/src/api/options.ts +22 -0
- package/src/api/retrier.ts +108 -0
- package/src/apierror/apierror.ts +253 -0
- package/src/apierror/codes/codes.ts +189 -0
- package/src/apierror/codes/index.ts +7 -0
- package/src/apierror/details.ts +459 -0
- package/src/apierror/index.ts +24 -0
- package/src/clientinfo/agent.ts +125 -0
- package/src/clientinfo/base.ts +73 -0
- package/src/clientinfo/clientinfo.ts +129 -0
- package/src/clientinfo/default.browser.ts +24 -0
- package/src/clientinfo/default.ts +128 -0
- package/src/clientinfo/index.browser.ts +4 -0
- package/src/clientinfo/index.ts +4 -0
- package/src/http/http.ts +75 -0
- package/src/http/index.ts +8 -0
- package/src/index.ts +5 -0
- package/src/logger/index.ts +8 -0
- package/src/logger/logger.ts +99 -0
- package/src/profiles/errors.ts +28 -0
- package/src/profiles/index.browser.ts +10 -0
- package/src/profiles/index.ts +15 -0
- package/src/profiles/ini.ts +126 -0
- package/src/profiles/profile.ts +467 -0
- package/src/profiles/resolve.ts +251 -0
- package/src/profiles/secret.ts +40 -0
- package/src/wkt/fieldmask.ts +89 -0
- package/src/wkt/index.ts +3 -0
- package/src/wkt/value.ts +19 -0
- package/index.js +0 -1
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profile interface and property mapping for Databricks configuration.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {Secret} from './secret';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Holds configuration values resolved from a databrickscfg file and/or
|
|
11
|
+
* environment variables. All fields are optional; an absent field means the
|
|
12
|
+
* value was not configured.
|
|
13
|
+
*/
|
|
14
|
+
export interface Profile {
|
|
15
|
+
/** Profile name (INI section name). */
|
|
16
|
+
name?: string;
|
|
17
|
+
|
|
18
|
+
/** Databricks workspace or Accounts API endpoint URL. */
|
|
19
|
+
host?: string;
|
|
20
|
+
|
|
21
|
+
/** Databricks Workspace ID, used with unified hosts. */
|
|
22
|
+
workspaceId?: string;
|
|
23
|
+
|
|
24
|
+
/** Databricks Account ID for Accounts API. */
|
|
25
|
+
accountId?: string;
|
|
26
|
+
|
|
27
|
+
/** Personal access token for PAT authentication. */
|
|
28
|
+
token?: Secret;
|
|
29
|
+
|
|
30
|
+
/** Username for basic authentication. */
|
|
31
|
+
username?: string;
|
|
32
|
+
|
|
33
|
+
/** Password for basic authentication. */
|
|
34
|
+
password?: Secret;
|
|
35
|
+
|
|
36
|
+
/** Selects a specific auth method when multiple credentials are available. */
|
|
37
|
+
authType?: string;
|
|
38
|
+
|
|
39
|
+
/** OAuth client ID for M2M authentication. */
|
|
40
|
+
clientId?: string;
|
|
41
|
+
|
|
42
|
+
/** OAuth client secret for M2M authentication. */
|
|
43
|
+
clientSecret?: Secret;
|
|
44
|
+
|
|
45
|
+
/** Path to the Databricks CLI binary (>= 0.100.0) for CLI-based auth. */
|
|
46
|
+
databricksCliPath?: string;
|
|
47
|
+
|
|
48
|
+
/** URL of a metadata service that provides authentication credentials. */
|
|
49
|
+
metadataServiceUrl?: Secret;
|
|
50
|
+
|
|
51
|
+
/** GitHub Actions URL for requesting an OIDC token. */
|
|
52
|
+
actionsIdTokenRequestUrl?: string;
|
|
53
|
+
|
|
54
|
+
/** Bearer token for the GitHub Actions OIDC provider. */
|
|
55
|
+
actionsIdTokenRequestToken?: Secret;
|
|
56
|
+
|
|
57
|
+
/** Name of an environment variable containing an OIDC ID token. */
|
|
58
|
+
oidcTokenEnv?: string;
|
|
59
|
+
|
|
60
|
+
/** Path to a file containing an OIDC ID token. */
|
|
61
|
+
oidcTokenFilePath?: string;
|
|
62
|
+
|
|
63
|
+
/** Audience for Workload Identity Federation ID token requests. */
|
|
64
|
+
tokenAudience?: string;
|
|
65
|
+
|
|
66
|
+
/** OpenID Connect discovery URL override. */
|
|
67
|
+
discoveryUrl?: string;
|
|
68
|
+
|
|
69
|
+
/** Azure AD application (client) ID. */
|
|
70
|
+
azureClientId?: string;
|
|
71
|
+
|
|
72
|
+
/** Azure AD client secret. */
|
|
73
|
+
azureClientSecret?: Secret;
|
|
74
|
+
|
|
75
|
+
/** Azure AD tenant ID. */
|
|
76
|
+
azureTenantId?: string;
|
|
77
|
+
|
|
78
|
+
/** Azure Resource Manager ID for an Azure Databricks workspace. */
|
|
79
|
+
azureResourceId?: string;
|
|
80
|
+
|
|
81
|
+
/** Azure cloud environment (PUBLIC, USGOVERNMENT, CHINA). */
|
|
82
|
+
azureEnvironment?: string;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Azure Login Application ID.
|
|
86
|
+
*
|
|
87
|
+
* @deprecated This field no longer has any effect.
|
|
88
|
+
*/
|
|
89
|
+
azureLoginAppId?: string;
|
|
90
|
+
|
|
91
|
+
/** Whether to use Azure Managed Service Identity authentication. */
|
|
92
|
+
azureUseMsi?: boolean;
|
|
93
|
+
|
|
94
|
+
/** GCP service account credentials JSON. */
|
|
95
|
+
googleCredentials?: Secret;
|
|
96
|
+
|
|
97
|
+
/** GCP service account email for Google ID-based authentication. */
|
|
98
|
+
googleServiceAccount?: string;
|
|
99
|
+
|
|
100
|
+
/** Default Databricks cluster ID for compute operations. */
|
|
101
|
+
clusterId?: string;
|
|
102
|
+
|
|
103
|
+
/** Default Databricks SQL warehouse ID. */
|
|
104
|
+
warehouseId?: string;
|
|
105
|
+
|
|
106
|
+
/** Default serverless compute resource ID. */
|
|
107
|
+
serverlessComputeId?: string;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* INI keys not mapped to a known field. Only populated when loading from a
|
|
111
|
+
* config file; environment variables do not contribute to extra.
|
|
112
|
+
*/
|
|
113
|
+
extra?: Record<string, string>;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Configures the behavior of {@link resolve}. */
|
|
117
|
+
export interface ResolveOptions {
|
|
118
|
+
/**
|
|
119
|
+
* Path to the databrickscfg file. If not set, reads
|
|
120
|
+
* DATABRICKS_CONFIG_FILE from the environment, falling back to
|
|
121
|
+
* ~/.databrickscfg. Setting this implies withFile: true.
|
|
122
|
+
*/
|
|
123
|
+
filePath?: string;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Profile name (INI section) to load. If not set, resolves from the
|
|
127
|
+
* DATABRICKS_CONFIG_PROFILE environment variable, then the default_profile
|
|
128
|
+
* key in the __settings__ section, then falls back to the DEFAULT section.
|
|
129
|
+
* Setting this implies withFile: true.
|
|
130
|
+
*/
|
|
131
|
+
profile?: string;
|
|
132
|
+
|
|
133
|
+
/** Whether to read from the config file. Default: true. */
|
|
134
|
+
withFile?: boolean;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Whether to overlay environment variables on top of config file values.
|
|
138
|
+
* When enabled, environment variables take precedence over file values.
|
|
139
|
+
* Default: true.
|
|
140
|
+
*/
|
|
141
|
+
withEnv?: boolean;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Reserved INI section name for SDK settings. It is never treated as a
|
|
146
|
+
* profile.
|
|
147
|
+
*/
|
|
148
|
+
export const SETTINGS_SECTION = '__settings__';
|
|
149
|
+
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
// Declarative property mapping
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Maps a {@link Profile} field to its environment variable and INI key,
|
|
156
|
+
* with typed getter and setter closures. This avoids reflection while
|
|
157
|
+
* keeping the mapping declarative, matching the Go SDK's approach.
|
|
158
|
+
*/
|
|
159
|
+
export interface PropertyDef {
|
|
160
|
+
/** The Profile field name this property maps to. */
|
|
161
|
+
readonly field: keyof Profile;
|
|
162
|
+
readonly envVar: string;
|
|
163
|
+
readonly iniKey: string;
|
|
164
|
+
/** Sets the profile field from a raw INI/env string value. */
|
|
165
|
+
readonly set: (profile: Profile, raw: string) => void;
|
|
166
|
+
/** Returns the profile field as a string, or undefined if unset. */
|
|
167
|
+
readonly get: (profile: Profile) => string | undefined;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Parses common boolean string representations (true/false, 1/0, t/f and
|
|
172
|
+
* their case variants).
|
|
173
|
+
*/
|
|
174
|
+
function parseBool(value: string): boolean {
|
|
175
|
+
switch (value) {
|
|
176
|
+
case '1':
|
|
177
|
+
case 't':
|
|
178
|
+
case 'T':
|
|
179
|
+
case 'TRUE':
|
|
180
|
+
case 'true':
|
|
181
|
+
case 'True':
|
|
182
|
+
return true;
|
|
183
|
+
case '0':
|
|
184
|
+
case 'f':
|
|
185
|
+
case 'F':
|
|
186
|
+
case 'FALSE':
|
|
187
|
+
case 'false':
|
|
188
|
+
case 'False':
|
|
189
|
+
return false;
|
|
190
|
+
default:
|
|
191
|
+
throw new Error(`invalid boolean value: "${value}"`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Declarative property mapping table for all known profile fields.
|
|
197
|
+
*/
|
|
198
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated -- The table includes azureLoginAppId for backward compatibility.
|
|
199
|
+
export const PROPERTY_DEFS: readonly PropertyDef[] = [
|
|
200
|
+
{
|
|
201
|
+
field: 'host',
|
|
202
|
+
envVar: 'DATABRICKS_HOST',
|
|
203
|
+
iniKey: 'host',
|
|
204
|
+
set: (p: Profile, v: string): void => {
|
|
205
|
+
p.host = v;
|
|
206
|
+
},
|
|
207
|
+
get: (p: Profile): string | undefined => p.host,
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
field: 'workspaceId',
|
|
211
|
+
envVar: 'DATABRICKS_WORKSPACE_ID',
|
|
212
|
+
iniKey: 'workspace_id',
|
|
213
|
+
set: (p: Profile, v: string): void => {
|
|
214
|
+
p.workspaceId = v;
|
|
215
|
+
},
|
|
216
|
+
get: (p: Profile): string | undefined => p.workspaceId,
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
field: 'accountId',
|
|
220
|
+
envVar: 'DATABRICKS_ACCOUNT_ID',
|
|
221
|
+
iniKey: 'account_id',
|
|
222
|
+
set: (p: Profile, v: string): void => {
|
|
223
|
+
p.accountId = v;
|
|
224
|
+
},
|
|
225
|
+
get: (p: Profile): string | undefined => p.accountId,
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
field: 'token',
|
|
229
|
+
envVar: 'DATABRICKS_TOKEN',
|
|
230
|
+
iniKey: 'token',
|
|
231
|
+
set: (p: Profile, v: string): void => {
|
|
232
|
+
p.token = new Secret(v);
|
|
233
|
+
},
|
|
234
|
+
get: (p: Profile): string | undefined => p.token?.value,
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
field: 'username',
|
|
238
|
+
envVar: 'DATABRICKS_USERNAME',
|
|
239
|
+
iniKey: 'username',
|
|
240
|
+
set: (p: Profile, v: string): void => {
|
|
241
|
+
p.username = v;
|
|
242
|
+
},
|
|
243
|
+
get: (p: Profile): string | undefined => p.username,
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
field: 'password',
|
|
247
|
+
envVar: 'DATABRICKS_PASSWORD',
|
|
248
|
+
iniKey: 'password',
|
|
249
|
+
set: (p: Profile, v: string): void => {
|
|
250
|
+
p.password = new Secret(v);
|
|
251
|
+
},
|
|
252
|
+
get: (p: Profile): string | undefined => p.password?.value,
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
field: 'authType',
|
|
256
|
+
envVar: 'DATABRICKS_AUTH_TYPE',
|
|
257
|
+
iniKey: 'auth_type',
|
|
258
|
+
set: (p: Profile, v: string): void => {
|
|
259
|
+
p.authType = v;
|
|
260
|
+
},
|
|
261
|
+
get: (p: Profile): string | undefined => p.authType,
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
field: 'clientId',
|
|
265
|
+
envVar: 'DATABRICKS_CLIENT_ID',
|
|
266
|
+
iniKey: 'client_id',
|
|
267
|
+
set: (p: Profile, v: string): void => {
|
|
268
|
+
p.clientId = v;
|
|
269
|
+
},
|
|
270
|
+
get: (p: Profile): string | undefined => p.clientId,
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
field: 'clientSecret',
|
|
274
|
+
envVar: 'DATABRICKS_CLIENT_SECRET',
|
|
275
|
+
iniKey: 'client_secret',
|
|
276
|
+
set: (p: Profile, v: string): void => {
|
|
277
|
+
p.clientSecret = new Secret(v);
|
|
278
|
+
},
|
|
279
|
+
get: (p: Profile): string | undefined => p.clientSecret?.value,
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
field: 'databricksCliPath',
|
|
283
|
+
envVar: 'DATABRICKS_CLI_PATH',
|
|
284
|
+
iniKey: 'databricks_cli_path',
|
|
285
|
+
set: (p: Profile, v: string): void => {
|
|
286
|
+
p.databricksCliPath = v;
|
|
287
|
+
},
|
|
288
|
+
get: (p: Profile): string | undefined => p.databricksCliPath,
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
field: 'metadataServiceUrl',
|
|
292
|
+
envVar: 'DATABRICKS_METADATA_SERVICE_URL',
|
|
293
|
+
iniKey: 'metadata_service_url',
|
|
294
|
+
set: (p: Profile, v: string): void => {
|
|
295
|
+
p.metadataServiceUrl = new Secret(v);
|
|
296
|
+
},
|
|
297
|
+
get: (p: Profile): string | undefined => p.metadataServiceUrl?.value,
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
field: 'actionsIdTokenRequestUrl',
|
|
301
|
+
envVar: 'ACTIONS_ID_TOKEN_REQUEST_URL',
|
|
302
|
+
iniKey: 'actions_id_token_request_url',
|
|
303
|
+
set: (p: Profile, v: string): void => {
|
|
304
|
+
p.actionsIdTokenRequestUrl = v;
|
|
305
|
+
},
|
|
306
|
+
get: (p: Profile): string | undefined => p.actionsIdTokenRequestUrl,
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
field: 'actionsIdTokenRequestToken',
|
|
310
|
+
envVar: 'ACTIONS_ID_TOKEN_REQUEST_TOKEN',
|
|
311
|
+
iniKey: 'actions_id_token_request_token',
|
|
312
|
+
set: (p: Profile, v: string): void => {
|
|
313
|
+
p.actionsIdTokenRequestToken = new Secret(v);
|
|
314
|
+
},
|
|
315
|
+
get: (p: Profile): string | undefined =>
|
|
316
|
+
p.actionsIdTokenRequestToken?.value,
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
field: 'oidcTokenEnv',
|
|
320
|
+
envVar: 'DATABRICKS_OIDC_TOKEN_ENV',
|
|
321
|
+
iniKey: 'oidc_token_env',
|
|
322
|
+
set: (p: Profile, v: string): void => {
|
|
323
|
+
p.oidcTokenEnv = v;
|
|
324
|
+
},
|
|
325
|
+
get: (p: Profile): string | undefined => p.oidcTokenEnv,
|
|
326
|
+
},
|
|
327
|
+
// Intentional mismatch between envVar and iniKey for backward compatibility.
|
|
328
|
+
{
|
|
329
|
+
field: 'oidcTokenFilePath',
|
|
330
|
+
envVar: 'DATABRICKS_OIDC_TOKEN_FILEPATH',
|
|
331
|
+
iniKey: 'databricks_id_token_filepath',
|
|
332
|
+
set: (p: Profile, v: string): void => {
|
|
333
|
+
p.oidcTokenFilePath = v;
|
|
334
|
+
},
|
|
335
|
+
get: (p: Profile): string | undefined => p.oidcTokenFilePath,
|
|
336
|
+
},
|
|
337
|
+
// Intentional mismatch between envVar and iniKey for backward compatibility.
|
|
338
|
+
{
|
|
339
|
+
field: 'tokenAudience',
|
|
340
|
+
envVar: 'DATABRICKS_TOKEN_AUDIENCE',
|
|
341
|
+
iniKey: 'audience',
|
|
342
|
+
set: (p: Profile, v: string): void => {
|
|
343
|
+
p.tokenAudience = v;
|
|
344
|
+
},
|
|
345
|
+
get: (p: Profile): string | undefined => p.tokenAudience,
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
field: 'discoveryUrl',
|
|
349
|
+
envVar: 'DATABRICKS_DISCOVERY_URL',
|
|
350
|
+
iniKey: 'discovery_url',
|
|
351
|
+
set: (p: Profile, v: string): void => {
|
|
352
|
+
p.discoveryUrl = v;
|
|
353
|
+
},
|
|
354
|
+
get: (p: Profile): string | undefined => p.discoveryUrl,
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
field: 'azureClientId',
|
|
358
|
+
envVar: 'ARM_CLIENT_ID',
|
|
359
|
+
iniKey: 'azure_client_id',
|
|
360
|
+
set: (p: Profile, v: string): void => {
|
|
361
|
+
p.azureClientId = v;
|
|
362
|
+
},
|
|
363
|
+
get: (p: Profile): string | undefined => p.azureClientId,
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
field: 'azureClientSecret',
|
|
367
|
+
envVar: 'ARM_CLIENT_SECRET',
|
|
368
|
+
iniKey: 'azure_client_secret',
|
|
369
|
+
set: (p: Profile, v: string): void => {
|
|
370
|
+
p.azureClientSecret = new Secret(v);
|
|
371
|
+
},
|
|
372
|
+
get: (p: Profile): string | undefined => p.azureClientSecret?.value,
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
field: 'azureTenantId',
|
|
376
|
+
envVar: 'ARM_TENANT_ID',
|
|
377
|
+
iniKey: 'azure_tenant_id',
|
|
378
|
+
set: (p: Profile, v: string): void => {
|
|
379
|
+
p.azureTenantId = v;
|
|
380
|
+
},
|
|
381
|
+
get: (p: Profile): string | undefined => p.azureTenantId,
|
|
382
|
+
},
|
|
383
|
+
// Intentional mismatch between envVar and iniKey for backward compatibility.
|
|
384
|
+
{
|
|
385
|
+
field: 'azureResourceId',
|
|
386
|
+
envVar: 'DATABRICKS_AZURE_RESOURCE_ID',
|
|
387
|
+
iniKey: 'azure_workspace_resource_id',
|
|
388
|
+
set: (p: Profile, v: string): void => {
|
|
389
|
+
p.azureResourceId = v;
|
|
390
|
+
},
|
|
391
|
+
get: (p: Profile): string | undefined => p.azureResourceId,
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
field: 'azureEnvironment',
|
|
395
|
+
envVar: 'ARM_ENVIRONMENT',
|
|
396
|
+
iniKey: 'azure_environment',
|
|
397
|
+
set: (p: Profile, v: string): void => {
|
|
398
|
+
p.azureEnvironment = v;
|
|
399
|
+
},
|
|
400
|
+
get: (p: Profile): string | undefined => p.azureEnvironment,
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
field: 'azureLoginAppId',
|
|
404
|
+
envVar: 'DATABRICKS_AZURE_LOGIN_APP_ID',
|
|
405
|
+
iniKey: 'azure_login_app_id',
|
|
406
|
+
set: (p: Profile, v: string): void => {
|
|
407
|
+
p.azureLoginAppId = v; // eslint-disable-line @typescript-eslint/no-deprecated
|
|
408
|
+
},
|
|
409
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
410
|
+
get: (p: Profile): string | undefined => p.azureLoginAppId,
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
field: 'azureUseMsi',
|
|
414
|
+
envVar: 'ARM_USE_MSI',
|
|
415
|
+
iniKey: 'azure_use_msi',
|
|
416
|
+
set: (p: Profile, v: string): void => {
|
|
417
|
+
p.azureUseMsi = parseBool(v);
|
|
418
|
+
},
|
|
419
|
+
get: (p: Profile): string | undefined =>
|
|
420
|
+
p.azureUseMsi === undefined ? undefined : String(p.azureUseMsi),
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
field: 'googleCredentials',
|
|
424
|
+
envVar: 'GOOGLE_CREDENTIALS',
|
|
425
|
+
iniKey: 'google_credentials',
|
|
426
|
+
set: (p: Profile, v: string): void => {
|
|
427
|
+
p.googleCredentials = new Secret(v);
|
|
428
|
+
},
|
|
429
|
+
get: (p: Profile): string | undefined => p.googleCredentials?.value,
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
field: 'googleServiceAccount',
|
|
433
|
+
envVar: 'DATABRICKS_GOOGLE_SERVICE_ACCOUNT',
|
|
434
|
+
iniKey: 'google_service_account',
|
|
435
|
+
set: (p: Profile, v: string): void => {
|
|
436
|
+
p.googleServiceAccount = v;
|
|
437
|
+
},
|
|
438
|
+
get: (p: Profile): string | undefined => p.googleServiceAccount,
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
field: 'clusterId',
|
|
442
|
+
envVar: 'DATABRICKS_CLUSTER_ID',
|
|
443
|
+
iniKey: 'cluster_id',
|
|
444
|
+
set: (p: Profile, v: string): void => {
|
|
445
|
+
p.clusterId = v;
|
|
446
|
+
},
|
|
447
|
+
get: (p: Profile): string | undefined => p.clusterId,
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
field: 'warehouseId',
|
|
451
|
+
envVar: 'DATABRICKS_WAREHOUSE_ID',
|
|
452
|
+
iniKey: 'warehouse_id',
|
|
453
|
+
set: (p: Profile, v: string): void => {
|
|
454
|
+
p.warehouseId = v;
|
|
455
|
+
},
|
|
456
|
+
get: (p: Profile): string | undefined => p.warehouseId,
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
field: 'serverlessComputeId',
|
|
460
|
+
envVar: 'DATABRICKS_SERVERLESS_COMPUTE_ID',
|
|
461
|
+
iniKey: 'serverless_compute_id',
|
|
462
|
+
set: (p: Profile, v: string): void => {
|
|
463
|
+
p.serverlessComputeId = v;
|
|
464
|
+
},
|
|
465
|
+
get: (p: Profile): string | undefined => p.serverlessComputeId,
|
|
466
|
+
},
|
|
467
|
+
];
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js implementation of profile resolution.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {access, readFile} from 'node:fs/promises';
|
|
8
|
+
import {homedir} from 'node:os';
|
|
9
|
+
import {join} from 'node:path';
|
|
10
|
+
|
|
11
|
+
import {ProfileError} from './errors';
|
|
12
|
+
import {parseIni} from './ini';
|
|
13
|
+
import type {Profile, ResolveOptions} from './profile';
|
|
14
|
+
import {PROPERTY_DEFS, SETTINGS_SECTION} from './profile';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Reports whether a section is an empty DEFAULT section that should be
|
|
18
|
+
* treated as non-existent.
|
|
19
|
+
*
|
|
20
|
+
* In the INI format, DEFAULT is typically a special fallback section, not a
|
|
21
|
+
* regular section. Databricks historically treats DEFAULT as a regular
|
|
22
|
+
* profile name. An intentionally empty [DEFAULT] section is silently ignored.
|
|
23
|
+
*/
|
|
24
|
+
function isPhantomDefault(name: string, section: Map<string, string>): boolean {
|
|
25
|
+
return name === 'DEFAULT' && section.size === 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Returns ~/.databrickscfg without checking environment variables.
|
|
30
|
+
*/
|
|
31
|
+
function defaultConfigFilePath(): string {
|
|
32
|
+
try {
|
|
33
|
+
return join(homedir(), '.databrickscfg');
|
|
34
|
+
} catch {
|
|
35
|
+
return '';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Returns true if the file at the given path exists. */
|
|
40
|
+
async function fileExists(path: string): Promise<boolean> {
|
|
41
|
+
try {
|
|
42
|
+
await access(path);
|
|
43
|
+
return true;
|
|
44
|
+
} catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Loads a profile from a databrickscfg file. If filePath is undefined,
|
|
51
|
+
* reads DATABRICKS_CONFIG_FILE from the environment, falling back to
|
|
52
|
+
* ~/.databrickscfg.
|
|
53
|
+
*/
|
|
54
|
+
async function loadFile(
|
|
55
|
+
filePath: string | undefined,
|
|
56
|
+
profileName: string | undefined
|
|
57
|
+
): Promise<Profile> {
|
|
58
|
+
// Determine file path.
|
|
59
|
+
let explicitFile = filePath !== undefined;
|
|
60
|
+
let path: string;
|
|
61
|
+
|
|
62
|
+
if (filePath !== undefined) {
|
|
63
|
+
path = filePath;
|
|
64
|
+
} else {
|
|
65
|
+
const envPath = process.env.DATABRICKS_CONFIG_FILE ?? '';
|
|
66
|
+
if (envPath !== '') {
|
|
67
|
+
path = envPath;
|
|
68
|
+
explicitFile = true;
|
|
69
|
+
} else {
|
|
70
|
+
path = defaultConfigFilePath();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// A missing default file is silently skipped.
|
|
75
|
+
if (!(await fileExists(path))) {
|
|
76
|
+
if (explicitFile) {
|
|
77
|
+
throw new ProfileError(
|
|
78
|
+
'CONFIG_FILE_NOT_FOUND',
|
|
79
|
+
`config file not found: ${path}`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
return {};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const content = await readFile(path, 'utf8');
|
|
86
|
+
const data = parseIni(content);
|
|
87
|
+
|
|
88
|
+
// Profile name resolution chain:
|
|
89
|
+
// 1. Explicit profile name (from option).
|
|
90
|
+
// 2. DATABRICKS_CONFIG_PROFILE environment variable.
|
|
91
|
+
// 3. default_profile key in __settings__ section.
|
|
92
|
+
// 4. DEFAULT section (fallback).
|
|
93
|
+
let resolved = profileName;
|
|
94
|
+
|
|
95
|
+
if (resolved === undefined) {
|
|
96
|
+
const envProfile = process.env.DATABRICKS_CONFIG_PROFILE ?? '';
|
|
97
|
+
if (envProfile !== '') {
|
|
98
|
+
resolved = envProfile;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (resolved === undefined) {
|
|
103
|
+
const settings = data.get(SETTINGS_SECTION);
|
|
104
|
+
if (settings !== undefined) {
|
|
105
|
+
const dp = settings.get('default_profile') ?? '';
|
|
106
|
+
if (dp !== '') {
|
|
107
|
+
resolved = dp;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (resolved === SETTINGS_SECTION) {
|
|
113
|
+
throw new ProfileError(
|
|
114
|
+
'INVALID_PROFILE_NAME',
|
|
115
|
+
`invalid profile name: "${resolved}" is a reserved section`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const explicitProfile = resolved !== undefined;
|
|
120
|
+
resolved ??= 'DEFAULT';
|
|
121
|
+
|
|
122
|
+
const section = data.get(resolved);
|
|
123
|
+
if (section === undefined || isPhantomDefault(resolved, section)) {
|
|
124
|
+
if (explicitProfile) {
|
|
125
|
+
throw new ProfileError(
|
|
126
|
+
'PROFILE_NOT_FOUND',
|
|
127
|
+
`profile not found: "${resolved}" in ${path}`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
return {};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Build profile from section values.
|
|
134
|
+
const profile: Profile = {name: resolved};
|
|
135
|
+
|
|
136
|
+
const knownKeys = new Set<string>();
|
|
137
|
+
for (const def of PROPERTY_DEFS) {
|
|
138
|
+
knownKeys.add(def.iniKey);
|
|
139
|
+
const value = section.get(def.iniKey);
|
|
140
|
+
if (value !== undefined) {
|
|
141
|
+
def.set(profile, value);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Collect extra keys that are not in the property mapping.
|
|
146
|
+
for (const [key, value] of section) {
|
|
147
|
+
if (!knownKeys.has(key)) {
|
|
148
|
+
profile.extra ??= {};
|
|
149
|
+
profile.extra[key] = value;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return profile;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Builds a profile from environment variables. Empty environment variables
|
|
158
|
+
* are treated as unset. Returns only the fields that were found.
|
|
159
|
+
*/
|
|
160
|
+
function loadEnv(): Profile {
|
|
161
|
+
const profile: Profile = {};
|
|
162
|
+
for (const def of PROPERTY_DEFS) {
|
|
163
|
+
const value = process.env[def.envVar] ?? '';
|
|
164
|
+
if (value !== '') {
|
|
165
|
+
def.set(profile, value);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return profile;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Creates a {@link Profile} from a databrickscfg file and/or environment
|
|
173
|
+
* variables.
|
|
174
|
+
*
|
|
175
|
+
* With no options, the defaults are used: read the default config file and
|
|
176
|
+
* overlay environment variables.
|
|
177
|
+
*
|
|
178
|
+
* When any explicit option is provided, only the requested behaviors are
|
|
179
|
+
* enabled.
|
|
180
|
+
*/
|
|
181
|
+
export async function resolve(options?: ResolveOptions): Promise<Profile> {
|
|
182
|
+
if (options?.filePath === '') {
|
|
183
|
+
throw new ProfileError('EMPTY_PATH', 'empty path');
|
|
184
|
+
}
|
|
185
|
+
if (options?.profile === '') {
|
|
186
|
+
throw new ProfileError('EMPTY_PROFILE', 'empty profile');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const shouldReadFile =
|
|
190
|
+
options === undefined ||
|
|
191
|
+
options.filePath !== undefined ||
|
|
192
|
+
options.profile !== undefined ||
|
|
193
|
+
(options.withFile ?? false);
|
|
194
|
+
const shouldReadEnv = options === undefined || (options.withEnv ?? false);
|
|
195
|
+
|
|
196
|
+
const fileProfile = shouldReadFile
|
|
197
|
+
? await loadFile(options?.filePath, options?.profile)
|
|
198
|
+
: {};
|
|
199
|
+
const envProfile = shouldReadEnv ? loadEnv() : {};
|
|
200
|
+
|
|
201
|
+
return {...fileProfile, ...envProfile};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Returns the path to the default databrickscfg file. Reads
|
|
206
|
+
* DATABRICKS_CONFIG_FILE from the environment, falling back to
|
|
207
|
+
* ~/.databrickscfg.
|
|
208
|
+
*/
|
|
209
|
+
export function defaultConfigFile(): string {
|
|
210
|
+
const envPath = process.env.DATABRICKS_CONFIG_FILE ?? '';
|
|
211
|
+
if (envPath !== '') {
|
|
212
|
+
return envPath;
|
|
213
|
+
}
|
|
214
|
+
return defaultConfigFilePath();
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Returns the names of all profiles (INI sections) in the given config
|
|
219
|
+
* file. The DEFAULT section is included only if it has keys.
|
|
220
|
+
*/
|
|
221
|
+
export async function listProfiles(path: string): Promise<string[]> {
|
|
222
|
+
if (path === '') {
|
|
223
|
+
throw new ProfileError(
|
|
224
|
+
'CONFIG_FILE_NOT_FOUND',
|
|
225
|
+
'config file not found: empty path'
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (!(await fileExists(path))) {
|
|
230
|
+
throw new ProfileError(
|
|
231
|
+
'CONFIG_FILE_NOT_FOUND',
|
|
232
|
+
`config file not found: ${path}`
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const content = await readFile(path, 'utf8');
|
|
237
|
+
const data = parseIni(content);
|
|
238
|
+
|
|
239
|
+
const names: string[] = [];
|
|
240
|
+
for (const [name, keys] of data) {
|
|
241
|
+
if (name === SETTINGS_SECTION) {
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (isPhantomDefault(name, keys)) {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
names.push(name);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return names;
|
|
251
|
+
}
|