databricks-sdk 0.53.0__py3-none-any.whl → 0.55.0__py3-none-any.whl
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.
Potentially problematic release.
This version of databricks-sdk might be problematic. Click here for more details.
- databricks/sdk/__init__.py +298 -255
- databricks/sdk/config.py +15 -4
- databricks/sdk/credentials_provider.py +101 -55
- databricks/sdk/errors/base.py +1 -30
- databricks/sdk/oauth.py +0 -5
- databricks/sdk/oidc.py +206 -0
- databricks/sdk/service/apps.py +58 -0
- databricks/sdk/service/catalog.py +1198 -181
- databricks/sdk/service/cleanrooms.py +116 -1
- databricks/sdk/service/compute.py +33 -68
- databricks/sdk/service/dashboards.py +7 -0
- databricks/sdk/service/iam.py +167 -103
- databricks/sdk/service/jobs.py +7 -6
- databricks/sdk/service/ml.py +1230 -55
- databricks/sdk/service/oauth2.py +17 -0
- databricks/sdk/service/pipelines.py +105 -0
- databricks/sdk/service/serving.py +314 -0
- databricks/sdk/service/settings.py +1284 -59
- databricks/sdk/service/sharing.py +388 -2
- databricks/sdk/service/sql.py +53 -84
- databricks/sdk/service/vectorsearch.py +0 -28
- databricks/sdk/version.py +1 -1
- {databricks_sdk-0.53.0.dist-info → databricks_sdk-0.55.0.dist-info}/METADATA +1 -1
- {databricks_sdk-0.53.0.dist-info → databricks_sdk-0.55.0.dist-info}/RECORD +28 -27
- {databricks_sdk-0.53.0.dist-info → databricks_sdk-0.55.0.dist-info}/WHEEL +1 -1
- {databricks_sdk-0.53.0.dist-info → databricks_sdk-0.55.0.dist-info}/licenses/LICENSE +0 -0
- {databricks_sdk-0.53.0.dist-info → databricks_sdk-0.55.0.dist-info}/licenses/NOTICE +0 -0
- {databricks_sdk-0.53.0.dist-info → databricks_sdk-0.55.0.dist-info}/top_level.txt +0 -0
databricks/sdk/config.py
CHANGED
|
@@ -60,10 +60,21 @@ def with_user_agent_extra(key: str, value: str):
|
|
|
60
60
|
class Config:
|
|
61
61
|
host: str = ConfigAttribute(env="DATABRICKS_HOST")
|
|
62
62
|
account_id: str = ConfigAttribute(env="DATABRICKS_ACCOUNT_ID")
|
|
63
|
+
|
|
64
|
+
# PAT token.
|
|
63
65
|
token: str = ConfigAttribute(env="DATABRICKS_TOKEN", auth="pat", sensitive=True)
|
|
66
|
+
|
|
67
|
+
# Audience for OIDC ID token source accepting an audience as a parameter.
|
|
68
|
+
# For example, the GitHub action ID token source.
|
|
64
69
|
token_audience: str = ConfigAttribute(env="DATABRICKS_TOKEN_AUDIENCE", auth="github-oidc")
|
|
70
|
+
|
|
71
|
+
# Environment variable for OIDC token.
|
|
72
|
+
oidc_token_env: str = ConfigAttribute(env="DATABRICKS_OIDC_TOKEN_ENV", auth="env-oidc")
|
|
73
|
+
oidc_token_filepath: str = ConfigAttribute(env="DATABRICKS_OIDC_TOKEN_FILE", auth="file-oidc")
|
|
74
|
+
|
|
65
75
|
username: str = ConfigAttribute(env="DATABRICKS_USERNAME", auth="basic")
|
|
66
76
|
password: str = ConfigAttribute(env="DATABRICKS_PASSWORD", auth="basic", sensitive=True)
|
|
77
|
+
|
|
67
78
|
client_id: str = ConfigAttribute(env="DATABRICKS_CLIENT_ID", auth="oauth")
|
|
68
79
|
client_secret: str = ConfigAttribute(env="DATABRICKS_CLIENT_SECRET", auth="oauth", sensitive=True)
|
|
69
80
|
profile: str = ConfigAttribute(env="DATABRICKS_CONFIG_PROFILE")
|
|
@@ -194,7 +205,7 @@ class Config:
|
|
|
194
205
|
def wrap_debug_info(self, message: str) -> str:
|
|
195
206
|
debug_string = self.debug_string()
|
|
196
207
|
if debug_string:
|
|
197
|
-
message = f
|
|
208
|
+
message = f"{message.rstrip('.')}. {debug_string}"
|
|
198
209
|
return message
|
|
199
210
|
|
|
200
211
|
@staticmethod
|
|
@@ -337,9 +348,9 @@ class Config:
|
|
|
337
348
|
safe = "***" if attr.sensitive else f"{value}"
|
|
338
349
|
attrs_used.append(f"{attr.name}={safe}")
|
|
339
350
|
if attrs_used:
|
|
340
|
-
buf.append(f
|
|
351
|
+
buf.append(f"Config: {', '.join(attrs_used)}")
|
|
341
352
|
if envs_used:
|
|
342
|
-
buf.append(f
|
|
353
|
+
buf.append(f"Env: {', '.join(envs_used)}")
|
|
343
354
|
return ". ".join(buf)
|
|
344
355
|
|
|
345
356
|
def to_dict(self) -> Dict[str, any]:
|
|
@@ -481,7 +492,7 @@ class Config:
|
|
|
481
492
|
if profile not in profiles:
|
|
482
493
|
raise ValueError(f"resolve: {config_path} has no {profile} profile configured")
|
|
483
494
|
raw_config = profiles[profile]
|
|
484
|
-
logger.info(f
|
|
495
|
+
logger.info(f"loading {profile} profile from {config_file}: {', '.join(raw_config.keys())}")
|
|
485
496
|
for k, v in raw_config.items():
|
|
486
497
|
if k in self._inner:
|
|
487
498
|
# don't overwrite a value previously set
|
|
@@ -20,10 +20,7 @@ from google.auth import impersonated_credentials # type: ignore
|
|
|
20
20
|
from google.auth.transport.requests import Request # type: ignore
|
|
21
21
|
from google.oauth2 import service_account # type: ignore
|
|
22
22
|
|
|
23
|
-
from .
|
|
24
|
-
from .oauth import (ClientCredentials, OAuthClient, Refreshable, Token,
|
|
25
|
-
TokenCache, TokenSource)
|
|
26
|
-
from .oidc_token_supplier import GitHubOIDCTokenSupplier
|
|
23
|
+
from . import azure, oauth, oidc, oidc_token_supplier
|
|
27
24
|
|
|
28
25
|
CredentialsProvider = Callable[[], Dict[str, str]]
|
|
29
26
|
|
|
@@ -36,7 +33,7 @@ class OAuthCredentialsProvider:
|
|
|
36
33
|
def __init__(
|
|
37
34
|
self,
|
|
38
35
|
credentials_provider: CredentialsProvider,
|
|
39
|
-
token_provider: Callable[[], Token],
|
|
36
|
+
token_provider: Callable[[], oauth.Token],
|
|
40
37
|
):
|
|
41
38
|
self._credentials_provider = credentials_provider
|
|
42
39
|
self._token_provider = token_provider
|
|
@@ -44,7 +41,7 @@ class OAuthCredentialsProvider:
|
|
|
44
41
|
def __call__(self) -> Dict[str, str]:
|
|
45
42
|
return self._credentials_provider()
|
|
46
43
|
|
|
47
|
-
def oauth_token(self) -> Token:
|
|
44
|
+
def oauth_token(self) -> oauth.Token:
|
|
48
45
|
return self._token_provider()
|
|
49
46
|
|
|
50
47
|
|
|
@@ -77,7 +74,7 @@ class OauthCredentialsStrategy(CredentialsStrategy):
|
|
|
77
74
|
def __call__(self, cfg: "Config") -> OAuthCredentialsProvider:
|
|
78
75
|
return self._headers_provider(cfg)
|
|
79
76
|
|
|
80
|
-
def oauth_token(self, cfg: "Config") -> Token:
|
|
77
|
+
def oauth_token(self, cfg: "Config") -> oauth.Token:
|
|
81
78
|
return self._headers_provider(cfg).oauth_token()
|
|
82
79
|
|
|
83
80
|
|
|
@@ -89,7 +86,6 @@ def credentials_strategy(name: str, require: List[str]):
|
|
|
89
86
|
def inner(
|
|
90
87
|
func: Callable[["Config"], CredentialsProvider],
|
|
91
88
|
) -> CredentialsStrategy:
|
|
92
|
-
|
|
93
89
|
@functools.wraps(func)
|
|
94
90
|
def wrapper(cfg: "Config") -> Optional[CredentialsProvider]:
|
|
95
91
|
for attr in require:
|
|
@@ -112,7 +108,6 @@ def oauth_credentials_strategy(name: str, require: List[str]):
|
|
|
112
108
|
def inner(
|
|
113
109
|
func: Callable[["Config"], OAuthCredentialsProvider],
|
|
114
110
|
) -> OauthCredentialsStrategy:
|
|
115
|
-
|
|
116
111
|
@functools.wraps(func)
|
|
117
112
|
def wrapper(cfg: "Config") -> Optional[OAuthCredentialsProvider]:
|
|
118
113
|
for attr in require:
|
|
@@ -186,7 +181,7 @@ def oauth_service_principal(cfg: "Config") -> Optional[CredentialsProvider]:
|
|
|
186
181
|
if oidc is None:
|
|
187
182
|
return None
|
|
188
183
|
|
|
189
|
-
token_source = ClientCredentials(
|
|
184
|
+
token_source = oauth.ClientCredentials(
|
|
190
185
|
client_id=cfg.client_id,
|
|
191
186
|
client_secret=cfg.client_secret,
|
|
192
187
|
token_url=oidc.token_endpoint,
|
|
@@ -199,7 +194,7 @@ def oauth_service_principal(cfg: "Config") -> Optional[CredentialsProvider]:
|
|
|
199
194
|
token = token_source.token()
|
|
200
195
|
return {"Authorization": f"{token.token_type} {token.access_token}"}
|
|
201
196
|
|
|
202
|
-
def token() -> Token:
|
|
197
|
+
def token() -> oauth.Token:
|
|
203
198
|
return token_source.token()
|
|
204
199
|
|
|
205
200
|
return OAuthCredentialsProvider(inner, token)
|
|
@@ -224,7 +219,7 @@ def external_browser(cfg: "Config") -> Optional[CredentialsProvider]:
|
|
|
224
219
|
# local to the Python SDK and not reused by other SDKs.
|
|
225
220
|
oidc_endpoints = cfg.oidc_endpoints
|
|
226
221
|
redirect_url = "http://localhost:8020"
|
|
227
|
-
token_cache = TokenCache(
|
|
222
|
+
token_cache = oauth.TokenCache(
|
|
228
223
|
host=cfg.host,
|
|
229
224
|
oidc_endpoints=oidc_endpoints,
|
|
230
225
|
client_id=client_id,
|
|
@@ -243,7 +238,7 @@ def external_browser(cfg: "Config") -> Optional[CredentialsProvider]:
|
|
|
243
238
|
except Exception as e:
|
|
244
239
|
logger.warning(f"Failed to refresh cached token: {e}. Initiating new OAuth login flow")
|
|
245
240
|
|
|
246
|
-
oauth_client = OAuthClient(
|
|
241
|
+
oauth_client = oauth.OAuthClient(
|
|
247
242
|
oidc_endpoints=oidc_endpoints,
|
|
248
243
|
client_id=client_id,
|
|
249
244
|
redirect_url=redirect_url,
|
|
@@ -258,7 +253,7 @@ def external_browser(cfg: "Config") -> Optional[CredentialsProvider]:
|
|
|
258
253
|
return credentials(cfg)
|
|
259
254
|
|
|
260
255
|
|
|
261
|
-
def _ensure_host_present(cfg: "Config", token_source_for: Callable[[str], TokenSource]):
|
|
256
|
+
def _ensure_host_present(cfg: "Config", token_source_for: Callable[[str], oauth.TokenSource]):
|
|
262
257
|
"""Resolves Azure Databricks workspace URL from ARM Resource ID"""
|
|
263
258
|
if cfg.host:
|
|
264
259
|
return
|
|
@@ -284,9 +279,9 @@ def azure_service_principal(cfg: "Config") -> CredentialsProvider:
|
|
|
284
279
|
to every request, while automatically resolving different Azure environment endpoints.
|
|
285
280
|
"""
|
|
286
281
|
|
|
287
|
-
def token_source_for(resource: str) -> TokenSource:
|
|
282
|
+
def token_source_for(resource: str) -> oauth.TokenSource:
|
|
288
283
|
aad_endpoint = cfg.arm_environment.active_directory_endpoint
|
|
289
|
-
return ClientCredentials(
|
|
284
|
+
return oauth.ClientCredentials(
|
|
290
285
|
client_id=cfg.azure_client_id,
|
|
291
286
|
client_secret=cfg.azure_client_secret,
|
|
292
287
|
token_url=f"{aad_endpoint}{cfg.azure_tenant_id}/oauth2/token",
|
|
@@ -305,18 +300,63 @@ def azure_service_principal(cfg: "Config") -> CredentialsProvider:
|
|
|
305
300
|
headers = {
|
|
306
301
|
"Authorization": f"Bearer {inner.token().access_token}",
|
|
307
302
|
}
|
|
308
|
-
add_workspace_id_header(cfg, headers)
|
|
309
|
-
add_sp_management_token(cloud, headers)
|
|
303
|
+
azure.add_workspace_id_header(cfg, headers)
|
|
304
|
+
azure.add_sp_management_token(cloud, headers)
|
|
310
305
|
return headers
|
|
311
306
|
|
|
312
|
-
def token() -> Token:
|
|
307
|
+
def token() -> oauth.Token:
|
|
313
308
|
return inner.token()
|
|
314
309
|
|
|
315
310
|
return OAuthCredentialsProvider(refreshed_headers, token)
|
|
316
311
|
|
|
317
312
|
|
|
313
|
+
@credentials_strategy("env-oidc", ["host"])
|
|
314
|
+
def env_oidc(cfg) -> Optional[CredentialsProvider]:
|
|
315
|
+
# Search for an OIDC ID token in DATABRICKS_OIDC_TOKEN environment variable
|
|
316
|
+
# by default. This can be overridden by setting DATABRICKS_OIDC_TOKEN_ENV
|
|
317
|
+
# to the name of an environment variable that contains the OIDC ID token.
|
|
318
|
+
env_var = "DATABRICKS_OIDC_TOKEN"
|
|
319
|
+
if cfg.oidc_token_env:
|
|
320
|
+
env_var = cfg.oidc_token_env
|
|
321
|
+
|
|
322
|
+
return _oidc_credentials_provider(cfg, oidc.EnvIdTokenSource(env_var))
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
@credentials_strategy("file-oidc", ["host", "oidc_token_filepath"])
|
|
326
|
+
def file_oidc(cfg) -> Optional[CredentialsProvider]:
|
|
327
|
+
return _oidc_credentials_provider(cfg, oidc.FileIdTokenSource(cfg.oidc_token_filepath))
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
# This function is a helper function to create an OIDC CredentialsProvider
|
|
331
|
+
# that provides a Databricks token from an IdTokenSource.
|
|
332
|
+
def _oidc_credentials_provider(cfg, id_token_source: oidc.IdTokenSource) -> Optional[CredentialsProvider]:
|
|
333
|
+
try:
|
|
334
|
+
id_token = id_token_source.id_token()
|
|
335
|
+
except Exception as e:
|
|
336
|
+
logger.debug(f"Failed to get OIDC token: {e}")
|
|
337
|
+
return None
|
|
338
|
+
|
|
339
|
+
token_source = oidc.DatabricksOidcTokenSource(
|
|
340
|
+
host=cfg.host,
|
|
341
|
+
token_endpoint=cfg.oidc_endpoints.token_endpoint,
|
|
342
|
+
client_id=cfg.client_id,
|
|
343
|
+
account_id=cfg.account_id,
|
|
344
|
+
id_token=id_token,
|
|
345
|
+
disable_async=cfg.disable_async_token_refresh,
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
def refreshed_headers() -> Dict[str, str]:
|
|
349
|
+
token = token_source.token()
|
|
350
|
+
return {"Authorization": f"{token.token_type} {token.access_token}"}
|
|
351
|
+
|
|
352
|
+
def token() -> oauth.Token:
|
|
353
|
+
return token_source.token()
|
|
354
|
+
|
|
355
|
+
return OAuthCredentialsProvider(refreshed_headers, token)
|
|
356
|
+
|
|
357
|
+
|
|
318
358
|
@oauth_credentials_strategy("github-oidc", ["host", "client_id"])
|
|
319
|
-
def
|
|
359
|
+
def github_oidc(cfg: "Config") -> Optional[CredentialsProvider]:
|
|
320
360
|
"""
|
|
321
361
|
DatabricksWIFCredentials uses a Token Supplier to get a JWT Token and exchanges
|
|
322
362
|
it for a Databricks Token.
|
|
@@ -324,7 +364,7 @@ def databricks_wif(cfg: "Config") -> Optional[CredentialsProvider]:
|
|
|
324
364
|
Supported suppliers:
|
|
325
365
|
- GitHub OIDC
|
|
326
366
|
"""
|
|
327
|
-
supplier = GitHubOIDCTokenSupplier()
|
|
367
|
+
supplier = oidc_token_supplier.GitHubOIDCTokenSupplier()
|
|
328
368
|
|
|
329
369
|
audience = cfg.token_audience
|
|
330
370
|
if audience is None and cfg.is_account_client:
|
|
@@ -337,21 +377,21 @@ def databricks_wif(cfg: "Config") -> Optional[CredentialsProvider]:
|
|
|
337
377
|
if not id_token:
|
|
338
378
|
return None
|
|
339
379
|
|
|
340
|
-
def token_source_for(audience: str) -> TokenSource:
|
|
380
|
+
def token_source_for(audience: str) -> oauth.TokenSource:
|
|
341
381
|
id_token = supplier.get_oidc_token(audience)
|
|
342
382
|
if not id_token:
|
|
343
383
|
# Should not happen, since we checked it above.
|
|
344
384
|
raise Exception("Cannot get OIDC token")
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
"subject_token": id_token,
|
|
348
|
-
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
|
|
349
|
-
}
|
|
350
|
-
return ClientCredentials(
|
|
385
|
+
|
|
386
|
+
return oauth.ClientCredentials(
|
|
351
387
|
client_id=cfg.client_id,
|
|
352
388
|
client_secret="", # we have no (rotatable) secrets in OIDC flow
|
|
353
389
|
token_url=cfg.oidc_endpoints.token_endpoint,
|
|
354
|
-
endpoint_params=
|
|
390
|
+
endpoint_params={
|
|
391
|
+
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
|
|
392
|
+
"subject_token": id_token,
|
|
393
|
+
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
|
|
394
|
+
},
|
|
355
395
|
scopes=["all-apis"],
|
|
356
396
|
use_params=True,
|
|
357
397
|
disable_async=cfg.disable_async_token_refresh,
|
|
@@ -361,7 +401,7 @@ def databricks_wif(cfg: "Config") -> Optional[CredentialsProvider]:
|
|
|
361
401
|
token = token_source_for(audience).token()
|
|
362
402
|
return {"Authorization": f"{token.token_type} {token.access_token}"}
|
|
363
403
|
|
|
364
|
-
def token() -> Token:
|
|
404
|
+
def token() -> oauth.Token:
|
|
365
405
|
return token_source_for(audience).token()
|
|
366
406
|
|
|
367
407
|
return OAuthCredentialsProvider(refreshed_headers, token)
|
|
@@ -378,7 +418,7 @@ def github_oidc_azure(cfg: "Config") -> Optional[CredentialsProvider]:
|
|
|
378
418
|
if not cfg.is_azure:
|
|
379
419
|
return None
|
|
380
420
|
|
|
381
|
-
token = GitHubOIDCTokenSupplier().get_oidc_token("api://AzureADTokenExchange")
|
|
421
|
+
token = oidc_token_supplier.GitHubOIDCTokenSupplier().get_oidc_token("api://AzureADTokenExchange")
|
|
382
422
|
if not token:
|
|
383
423
|
return None
|
|
384
424
|
|
|
@@ -386,21 +426,22 @@ def github_oidc_azure(cfg: "Config") -> Optional[CredentialsProvider]:
|
|
|
386
426
|
"Configured AAD token for GitHub Actions OIDC (%s)",
|
|
387
427
|
cfg.azure_client_id,
|
|
388
428
|
)
|
|
389
|
-
|
|
390
|
-
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
|
|
391
|
-
"resource": cfg.effective_azure_login_app_id,
|
|
392
|
-
"client_assertion": token,
|
|
393
|
-
}
|
|
429
|
+
|
|
394
430
|
aad_endpoint = cfg.arm_environment.active_directory_endpoint
|
|
395
431
|
if not cfg.azure_tenant_id:
|
|
396
432
|
# detect Azure AD Tenant ID if it's not specified directly
|
|
397
433
|
token_endpoint = cfg.oidc_endpoints.token_endpoint
|
|
398
434
|
cfg.azure_tenant_id = token_endpoint.replace(aad_endpoint, "").split("/")[0]
|
|
399
|
-
|
|
435
|
+
|
|
436
|
+
inner = oauth.ClientCredentials(
|
|
400
437
|
client_id=cfg.azure_client_id,
|
|
401
438
|
client_secret="", # we have no (rotatable) secrets in OIDC flow
|
|
402
439
|
token_url=f"{aad_endpoint}{cfg.azure_tenant_id}/oauth2/token",
|
|
403
|
-
endpoint_params=
|
|
440
|
+
endpoint_params={
|
|
441
|
+
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
|
|
442
|
+
"resource": cfg.effective_azure_login_app_id,
|
|
443
|
+
"client_assertion": token,
|
|
444
|
+
},
|
|
404
445
|
use_params=True,
|
|
405
446
|
disable_async=cfg.disable_async_token_refresh,
|
|
406
447
|
)
|
|
@@ -409,7 +450,7 @@ def github_oidc_azure(cfg: "Config") -> Optional[CredentialsProvider]:
|
|
|
409
450
|
token = inner.token()
|
|
410
451
|
return {"Authorization": f"{token.token_type} {token.access_token}"}
|
|
411
452
|
|
|
412
|
-
def token() -> Token:
|
|
453
|
+
def token() -> oauth.Token:
|
|
413
454
|
return inner.token()
|
|
414
455
|
|
|
415
456
|
return OAuthCredentialsProvider(refreshed_headers, token)
|
|
@@ -442,7 +483,7 @@ def google_credentials(cfg: "Config") -> Optional[CredentialsProvider]:
|
|
|
442
483
|
|
|
443
484
|
gcp_credentials = service_account.Credentials.from_service_account_info(info=account_info, scopes=GcpScopes)
|
|
444
485
|
|
|
445
|
-
def token() -> Token:
|
|
486
|
+
def token() -> oauth.Token:
|
|
446
487
|
credentials.refresh(request)
|
|
447
488
|
return credentials.token
|
|
448
489
|
|
|
@@ -483,7 +524,7 @@ def google_id(cfg: "Config") -> Optional[CredentialsProvider]:
|
|
|
483
524
|
|
|
484
525
|
request = Request()
|
|
485
526
|
|
|
486
|
-
def token() -> Token:
|
|
527
|
+
def token() -> oauth.Token:
|
|
487
528
|
id_creds.refresh(request)
|
|
488
529
|
return id_creds.token
|
|
489
530
|
|
|
@@ -498,8 +539,7 @@ def google_id(cfg: "Config") -> Optional[CredentialsProvider]:
|
|
|
498
539
|
return OAuthCredentialsProvider(refreshed_headers, token)
|
|
499
540
|
|
|
500
541
|
|
|
501
|
-
class CliTokenSource(Refreshable):
|
|
502
|
-
|
|
542
|
+
class CliTokenSource(oauth.Refreshable):
|
|
503
543
|
def __init__(
|
|
504
544
|
self,
|
|
505
545
|
cmd: List[str],
|
|
@@ -525,12 +565,12 @@ class CliTokenSource(Refreshable):
|
|
|
525
565
|
if last_e:
|
|
526
566
|
raise last_e
|
|
527
567
|
|
|
528
|
-
def refresh(self) -> Token:
|
|
568
|
+
def refresh(self) -> oauth.Token:
|
|
529
569
|
try:
|
|
530
570
|
out = _run_subprocess(self._cmd, capture_output=True, check=True)
|
|
531
571
|
it = json.loads(out.stdout.decode())
|
|
532
572
|
expires_on = self._parse_expiry(it[self._expiry_field])
|
|
533
|
-
return Token(
|
|
573
|
+
return oauth.Token(
|
|
534
574
|
access_token=it[self._access_token_field],
|
|
535
575
|
token_type=it[self._token_type_field],
|
|
536
576
|
expiry=expires_on,
|
|
@@ -558,7 +598,7 @@ def _run_subprocess(
|
|
|
558
598
|
kwargs["shell"] = sys.platform.startswith("win")
|
|
559
599
|
# windows requires shell=True to be able to execute 'az login' or other commands
|
|
560
600
|
# cannot use shell=True all the time, as it breaks macOS
|
|
561
|
-
logging.debug(f
|
|
601
|
+
logging.debug(f"Running command: {' '.join(popenargs)}")
|
|
562
602
|
return subprocess.run(
|
|
563
603
|
popenargs,
|
|
564
604
|
input=input,
|
|
@@ -689,7 +729,7 @@ def azure_cli(cfg: "Config") -> Optional[CredentialsProvider]:
|
|
|
689
729
|
mgmt_token_source = AzureCliTokenSource.for_resource(cfg, management_endpoint)
|
|
690
730
|
except Exception as e:
|
|
691
731
|
logger.debug(
|
|
692
|
-
|
|
732
|
+
"Not including service management token in headers",
|
|
693
733
|
exc_info=e,
|
|
694
734
|
)
|
|
695
735
|
mgmt_token_source = None
|
|
@@ -700,9 +740,9 @@ def azure_cli(cfg: "Config") -> Optional[CredentialsProvider]:
|
|
|
700
740
|
def inner() -> Dict[str, str]:
|
|
701
741
|
token = token_source.token()
|
|
702
742
|
headers = {"Authorization": f"{token.token_type} {token.access_token}"}
|
|
703
|
-
add_workspace_id_header(cfg, headers)
|
|
743
|
+
azure.add_workspace_id_header(cfg, headers)
|
|
704
744
|
if mgmt_token_source:
|
|
705
|
-
add_sp_management_token(mgmt_token_source, headers)
|
|
745
|
+
azure.add_sp_management_token(mgmt_token_source, headers)
|
|
706
746
|
return headers
|
|
707
747
|
|
|
708
748
|
return inner
|
|
@@ -784,13 +824,13 @@ def databricks_cli(cfg: "Config") -> Optional[CredentialsProvider]:
|
|
|
784
824
|
token = token_source.token()
|
|
785
825
|
return {"Authorization": f"{token.token_type} {token.access_token}"}
|
|
786
826
|
|
|
787
|
-
def token() -> Token:
|
|
827
|
+
def token() -> oauth.Token:
|
|
788
828
|
return token_source.token()
|
|
789
829
|
|
|
790
830
|
return OAuthCredentialsProvider(inner, token)
|
|
791
831
|
|
|
792
832
|
|
|
793
|
-
class MetadataServiceTokenSource(Refreshable):
|
|
833
|
+
class MetadataServiceTokenSource(oauth.Refreshable):
|
|
794
834
|
"""Obtain the token granted by Databricks Metadata Service"""
|
|
795
835
|
|
|
796
836
|
METADATA_SERVICE_VERSION = "1"
|
|
@@ -803,7 +843,7 @@ class MetadataServiceTokenSource(Refreshable):
|
|
|
803
843
|
self.url = cfg.metadata_service_url
|
|
804
844
|
self.host = cfg.host
|
|
805
845
|
|
|
806
|
-
def refresh(self) -> Token:
|
|
846
|
+
def refresh(self) -> oauth.Token:
|
|
807
847
|
resp = requests.get(
|
|
808
848
|
self.url,
|
|
809
849
|
timeout=self._metadata_service_timeout,
|
|
@@ -831,7 +871,7 @@ class MetadataServiceTokenSource(Refreshable):
|
|
|
831
871
|
except:
|
|
832
872
|
raise ValueError("Metadata Service returned invalid expiry")
|
|
833
873
|
|
|
834
|
-
return Token(access_token=access_token, token_type=token_type, expiry=expiry)
|
|
874
|
+
return oauth.Token(access_token=access_token, token_type=token_type, expiry=expiry)
|
|
835
875
|
|
|
836
876
|
|
|
837
877
|
@credentials_strategy("metadata-service", ["host", "metadata_service_url"])
|
|
@@ -972,7 +1012,9 @@ class DefaultCredentials:
|
|
|
972
1012
|
basic_auth,
|
|
973
1013
|
metadata_service,
|
|
974
1014
|
oauth_service_principal,
|
|
975
|
-
|
|
1015
|
+
env_oidc,
|
|
1016
|
+
file_oidc,
|
|
1017
|
+
github_oidc,
|
|
976
1018
|
azure_service_principal,
|
|
977
1019
|
github_oidc_azure,
|
|
978
1020
|
azure_cli,
|
|
@@ -987,7 +1029,7 @@ class DefaultCredentials:
|
|
|
987
1029
|
def auth_type(self) -> str:
|
|
988
1030
|
return self._auth_type
|
|
989
1031
|
|
|
990
|
-
def oauth_token(self, cfg: "Config") -> Token:
|
|
1032
|
+
def oauth_token(self, cfg: "Config") -> oauth.Token:
|
|
991
1033
|
for provider in self._auth_providers:
|
|
992
1034
|
auth_type = provider.auth_type()
|
|
993
1035
|
if auth_type != self._auth_type:
|
|
@@ -1004,9 +1046,13 @@ class DefaultCredentials:
|
|
|
1004
1046
|
continue
|
|
1005
1047
|
logger.debug(f"Attempting to configure auth: {auth_type}")
|
|
1006
1048
|
try:
|
|
1049
|
+
# The header factory might be None if the provider cannot be
|
|
1050
|
+
# configured for the current environment. For example, if the
|
|
1051
|
+
# provider requires some missing environment variables.
|
|
1007
1052
|
header_factory = provider(cfg)
|
|
1008
1053
|
if not header_factory:
|
|
1009
1054
|
continue
|
|
1055
|
+
|
|
1010
1056
|
self._auth_type = auth_type
|
|
1011
1057
|
return header_factory
|
|
1012
1058
|
except Exception as e:
|
databricks/sdk/errors/base.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import re
|
|
2
|
-
import warnings
|
|
3
2
|
from dataclasses import dataclass
|
|
4
3
|
from typing import Any, Dict, List, Optional
|
|
5
4
|
|
|
@@ -62,31 +61,6 @@ class DatabricksError(IOError):
|
|
|
62
61
|
:param details:
|
|
63
62
|
:param kwargs:
|
|
64
63
|
"""
|
|
65
|
-
# SCIM-specific parameters are deprecated.
|
|
66
|
-
if detail:
|
|
67
|
-
warnings.warn(
|
|
68
|
-
"The 'detail' parameter of DatabricksError is deprecated and will be removed in a future version."
|
|
69
|
-
)
|
|
70
|
-
if scimType:
|
|
71
|
-
warnings.warn(
|
|
72
|
-
"The 'scimType' parameter of DatabricksError is deprecated and will be removed in a future version."
|
|
73
|
-
)
|
|
74
|
-
if status:
|
|
75
|
-
warnings.warn(
|
|
76
|
-
"The 'status' parameter of DatabricksError is deprecated and will be removed in a future version."
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
# API 1.2-specific parameters are deprecated.
|
|
80
|
-
if error:
|
|
81
|
-
warnings.warn(
|
|
82
|
-
"The 'error' parameter of DatabricksError is deprecated and will be removed in a future version."
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
# Retry-after is deprecated.
|
|
86
|
-
if retry_after_secs:
|
|
87
|
-
warnings.warn(
|
|
88
|
-
"The 'retry_after_secs' parameter of DatabricksError is deprecated and will be removed in a future version."
|
|
89
|
-
)
|
|
90
64
|
|
|
91
65
|
if detail:
|
|
92
66
|
# Handle SCIM error message details
|
|
@@ -114,12 +88,9 @@ class DatabricksError(IOError):
|
|
|
114
88
|
self.details.append(ErrorDetail.from_dict(d))
|
|
115
89
|
|
|
116
90
|
def get_error_info(self) -> List[ErrorDetail]:
|
|
117
|
-
return self._get_details_by_type(errdetails._ERROR_INFO_TYPE)
|
|
118
|
-
|
|
119
|
-
def _get_details_by_type(self, error_type) -> List[ErrorDetail]:
|
|
120
91
|
if self.details is None:
|
|
121
92
|
return []
|
|
122
|
-
return [detail for detail in self.details if detail.type ==
|
|
93
|
+
return [detail for detail in self.details if detail.type == errdetails._ERROR_INFO_TYPE]
|
|
123
94
|
|
|
124
95
|
def get_error_details(self) -> errdetails.ErrorDetails:
|
|
125
96
|
return self._error_details
|
databricks/sdk/oauth.py
CHANGED
|
@@ -156,7 +156,6 @@ class Token:
|
|
|
156
156
|
|
|
157
157
|
|
|
158
158
|
class TokenSource:
|
|
159
|
-
|
|
160
159
|
@abstractmethod
|
|
161
160
|
def token(self) -> Token:
|
|
162
161
|
pass
|
|
@@ -341,7 +340,6 @@ class Refreshable(TokenSource):
|
|
|
341
340
|
|
|
342
341
|
|
|
343
342
|
class _OAuthCallback(BaseHTTPRequestHandler):
|
|
344
|
-
|
|
345
343
|
def __init__(self, feedback: list, *args):
|
|
346
344
|
self._feedback = feedback
|
|
347
345
|
super().__init__(*args)
|
|
@@ -418,7 +416,6 @@ def get_azure_entra_id_workspace_endpoints(
|
|
|
418
416
|
|
|
419
417
|
|
|
420
418
|
class SessionCredentials(Refreshable):
|
|
421
|
-
|
|
422
419
|
def __init__(
|
|
423
420
|
self,
|
|
424
421
|
token: Token,
|
|
@@ -493,7 +490,6 @@ class SessionCredentials(Refreshable):
|
|
|
493
490
|
|
|
494
491
|
|
|
495
492
|
class Consent:
|
|
496
|
-
|
|
497
493
|
def __init__(
|
|
498
494
|
self,
|
|
499
495
|
state: str,
|
|
@@ -627,7 +623,6 @@ class OAuthClient:
|
|
|
627
623
|
scopes: List[str] = None,
|
|
628
624
|
client_secret: str = None,
|
|
629
625
|
):
|
|
630
|
-
|
|
631
626
|
if not scopes:
|
|
632
627
|
# all-apis ensures that the returned OAuth token can be used with all APIs, aside
|
|
633
628
|
# from direct-to-dataplane APIs.
|