databricks-sdk 0.32.3__py3-none-any.whl → 0.34.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 +10 -0
- databricks/sdk/_base_client.py +323 -0
- databricks/sdk/core.py +25 -292
- databricks/sdk/mixins/files.py +9 -9
- databricks/sdk/service/apps.py +366 -113
- databricks/sdk/service/catalog.py +269 -7
- databricks/sdk/service/compute.py +68 -18
- databricks/sdk/service/dashboards.py +34 -11
- databricks/sdk/service/jobs.py +104 -86
- databricks/sdk/service/pipelines.py +58 -1
- databricks/sdk/service/serving.py +326 -10
- databricks/sdk/service/settings.py +573 -1
- databricks/sdk/service/sharing.py +1 -1
- databricks/sdk/service/sql.py +10 -246
- databricks/sdk/service/workspace.py +268 -107
- databricks/sdk/version.py +1 -1
- {databricks_sdk-0.32.3.dist-info → databricks_sdk-0.34.0.dist-info}/METADATA +1 -1
- {databricks_sdk-0.32.3.dist-info → databricks_sdk-0.34.0.dist-info}/RECORD +22 -21
- {databricks_sdk-0.32.3.dist-info → databricks_sdk-0.34.0.dist-info}/LICENSE +0 -0
- {databricks_sdk-0.32.3.dist-info → databricks_sdk-0.34.0.dist-info}/NOTICE +0 -0
- {databricks_sdk-0.32.3.dist-info → databricks_sdk-0.34.0.dist-info}/WHEEL +0 -0
- {databricks_sdk-0.32.3.dist-info → databricks_sdk-0.34.0.dist-info}/top_level.txt +0 -0
databricks/sdk/service/apps.py
CHANGED
|
@@ -25,7 +25,12 @@ class App:
|
|
|
25
25
|
It must be unique within the workspace."""
|
|
26
26
|
|
|
27
27
|
active_deployment: Optional[AppDeployment] = None
|
|
28
|
-
"""The active deployment of the app.
|
|
28
|
+
"""The active deployment of the app. A deployment is considered active when it has been deployed to
|
|
29
|
+
the app compute."""
|
|
30
|
+
|
|
31
|
+
app_status: Optional[ApplicationStatus] = None
|
|
32
|
+
|
|
33
|
+
compute_status: Optional[ComputeStatus] = None
|
|
29
34
|
|
|
30
35
|
create_time: Optional[str] = None
|
|
31
36
|
"""The creation time of the app. Formatted timestamp in ISO 6801."""
|
|
@@ -33,18 +38,24 @@ class App:
|
|
|
33
38
|
creator: Optional[str] = None
|
|
34
39
|
"""The email of the user that created the app."""
|
|
35
40
|
|
|
41
|
+
default_source_code_path: Optional[str] = None
|
|
42
|
+
"""The default workspace file system path of the source code from which app deployment are created.
|
|
43
|
+
This field tracks the workspace source code path of the last active deployment."""
|
|
44
|
+
|
|
36
45
|
description: Optional[str] = None
|
|
37
46
|
"""The description of the app."""
|
|
38
47
|
|
|
39
48
|
pending_deployment: Optional[AppDeployment] = None
|
|
40
|
-
"""The pending deployment of the app.
|
|
49
|
+
"""The pending deployment of the app. A deployment is considered pending when it is being prepared
|
|
50
|
+
for deployment to the app compute."""
|
|
51
|
+
|
|
52
|
+
resources: Optional[List[AppResource]] = None
|
|
53
|
+
"""Resources for the app."""
|
|
41
54
|
|
|
42
55
|
service_principal_id: Optional[int] = None
|
|
43
56
|
|
|
44
57
|
service_principal_name: Optional[str] = None
|
|
45
58
|
|
|
46
|
-
status: Optional[AppStatus] = None
|
|
47
|
-
|
|
48
59
|
update_time: Optional[str] = None
|
|
49
60
|
"""The update time of the app. Formatted timestamp in ISO 6801."""
|
|
50
61
|
|
|
@@ -58,15 +69,19 @@ class App:
|
|
|
58
69
|
"""Serializes the App into a dictionary suitable for use as a JSON request body."""
|
|
59
70
|
body = {}
|
|
60
71
|
if self.active_deployment: body['active_deployment'] = self.active_deployment.as_dict()
|
|
72
|
+
if self.app_status: body['app_status'] = self.app_status.as_dict()
|
|
73
|
+
if self.compute_status: body['compute_status'] = self.compute_status.as_dict()
|
|
61
74
|
if self.create_time is not None: body['create_time'] = self.create_time
|
|
62
75
|
if self.creator is not None: body['creator'] = self.creator
|
|
76
|
+
if self.default_source_code_path is not None:
|
|
77
|
+
body['default_source_code_path'] = self.default_source_code_path
|
|
63
78
|
if self.description is not None: body['description'] = self.description
|
|
64
79
|
if self.name is not None: body['name'] = self.name
|
|
65
80
|
if self.pending_deployment: body['pending_deployment'] = self.pending_deployment.as_dict()
|
|
81
|
+
if self.resources: body['resources'] = [v.as_dict() for v in self.resources]
|
|
66
82
|
if self.service_principal_id is not None: body['service_principal_id'] = self.service_principal_id
|
|
67
83
|
if self.service_principal_name is not None:
|
|
68
84
|
body['service_principal_name'] = self.service_principal_name
|
|
69
|
-
if self.status: body['status'] = self.status.as_dict()
|
|
70
85
|
if self.update_time is not None: body['update_time'] = self.update_time
|
|
71
86
|
if self.updater is not None: body['updater'] = self.updater
|
|
72
87
|
if self.url is not None: body['url'] = self.url
|
|
@@ -76,14 +91,17 @@ class App:
|
|
|
76
91
|
def from_dict(cls, d: Dict[str, any]) -> App:
|
|
77
92
|
"""Deserializes the App from a dictionary."""
|
|
78
93
|
return cls(active_deployment=_from_dict(d, 'active_deployment', AppDeployment),
|
|
94
|
+
app_status=_from_dict(d, 'app_status', ApplicationStatus),
|
|
95
|
+
compute_status=_from_dict(d, 'compute_status', ComputeStatus),
|
|
79
96
|
create_time=d.get('create_time', None),
|
|
80
97
|
creator=d.get('creator', None),
|
|
98
|
+
default_source_code_path=d.get('default_source_code_path', None),
|
|
81
99
|
description=d.get('description', None),
|
|
82
100
|
name=d.get('name', None),
|
|
83
101
|
pending_deployment=_from_dict(d, 'pending_deployment', AppDeployment),
|
|
102
|
+
resources=_repeated_dict(d, 'resources', AppResource),
|
|
84
103
|
service_principal_id=d.get('service_principal_id', None),
|
|
85
104
|
service_principal_name=d.get('service_principal_name', None),
|
|
86
|
-
status=_from_dict(d, 'status', AppStatus),
|
|
87
105
|
update_time=d.get('update_time', None),
|
|
88
106
|
updater=d.get('updater', None),
|
|
89
107
|
url=d.get('url', None))
|
|
@@ -162,13 +180,6 @@ class AppAccessControlResponse:
|
|
|
162
180
|
|
|
163
181
|
@dataclass
|
|
164
182
|
class AppDeployment:
|
|
165
|
-
source_code_path: str
|
|
166
|
-
"""The workspace file system path of the source code used to create the app deployment. This is
|
|
167
|
-
different from `deployment_artifacts.source_code_path`, which is the path used by the deployed
|
|
168
|
-
app. The former refers to the original source code location of the app in the workspace during
|
|
169
|
-
deployment creation, whereas the latter provides a system generated stable snapshotted source
|
|
170
|
-
code path used by the deployment."""
|
|
171
|
-
|
|
172
183
|
create_time: Optional[str] = None
|
|
173
184
|
"""The creation time of the deployment. Formatted timestamp in ISO 6801."""
|
|
174
185
|
|
|
@@ -184,6 +195,13 @@ class AppDeployment:
|
|
|
184
195
|
mode: Optional[AppDeploymentMode] = None
|
|
185
196
|
"""The mode of which the deployment will manage the source code."""
|
|
186
197
|
|
|
198
|
+
source_code_path: Optional[str] = None
|
|
199
|
+
"""The workspace file system path of the source code used to create the app deployment. This is
|
|
200
|
+
different from `deployment_artifacts.source_code_path`, which is the path used by the deployed
|
|
201
|
+
app. The former refers to the original source code location of the app in the workspace during
|
|
202
|
+
deployment creation, whereas the latter provides a system generated stable snapshotted source
|
|
203
|
+
code path used by the deployment."""
|
|
204
|
+
|
|
187
205
|
status: Optional[AppDeploymentStatus] = None
|
|
188
206
|
"""Status and status message of the deployment"""
|
|
189
207
|
|
|
@@ -241,9 +259,9 @@ class AppDeploymentMode(Enum):
|
|
|
241
259
|
|
|
242
260
|
class AppDeploymentState(Enum):
|
|
243
261
|
|
|
262
|
+
CANCELLED = 'CANCELLED'
|
|
244
263
|
FAILED = 'FAILED'
|
|
245
264
|
IN_PROGRESS = 'IN_PROGRESS'
|
|
246
|
-
STOPPED = 'STOPPED'
|
|
247
265
|
SUCCEEDED = 'SUCCEEDED'
|
|
248
266
|
|
|
249
267
|
|
|
@@ -368,57 +386,254 @@ class AppPermissionsRequest:
|
|
|
368
386
|
app_name=d.get('app_name', None))
|
|
369
387
|
|
|
370
388
|
|
|
371
|
-
|
|
389
|
+
@dataclass
|
|
390
|
+
class AppResource:
|
|
391
|
+
name: str
|
|
392
|
+
"""Name of the App Resource."""
|
|
393
|
+
|
|
394
|
+
description: Optional[str] = None
|
|
395
|
+
"""Description of the App Resource."""
|
|
396
|
+
|
|
397
|
+
job: Optional[AppResourceJob] = None
|
|
398
|
+
|
|
399
|
+
secret: Optional[AppResourceSecret] = None
|
|
400
|
+
|
|
401
|
+
serving_endpoint: Optional[AppResourceServingEndpoint] = None
|
|
402
|
+
|
|
403
|
+
sql_warehouse: Optional[AppResourceSqlWarehouse] = None
|
|
404
|
+
|
|
405
|
+
def as_dict(self) -> dict:
|
|
406
|
+
"""Serializes the AppResource into a dictionary suitable for use as a JSON request body."""
|
|
407
|
+
body = {}
|
|
408
|
+
if self.description is not None: body['description'] = self.description
|
|
409
|
+
if self.job: body['job'] = self.job.as_dict()
|
|
410
|
+
if self.name is not None: body['name'] = self.name
|
|
411
|
+
if self.secret: body['secret'] = self.secret.as_dict()
|
|
412
|
+
if self.serving_endpoint: body['serving_endpoint'] = self.serving_endpoint.as_dict()
|
|
413
|
+
if self.sql_warehouse: body['sql_warehouse'] = self.sql_warehouse.as_dict()
|
|
414
|
+
return body
|
|
415
|
+
|
|
416
|
+
@classmethod
|
|
417
|
+
def from_dict(cls, d: Dict[str, any]) -> AppResource:
|
|
418
|
+
"""Deserializes the AppResource from a dictionary."""
|
|
419
|
+
return cls(description=d.get('description', None),
|
|
420
|
+
job=_from_dict(d, 'job', AppResourceJob),
|
|
421
|
+
name=d.get('name', None),
|
|
422
|
+
secret=_from_dict(d, 'secret', AppResourceSecret),
|
|
423
|
+
serving_endpoint=_from_dict(d, 'serving_endpoint', AppResourceServingEndpoint),
|
|
424
|
+
sql_warehouse=_from_dict(d, 'sql_warehouse', AppResourceSqlWarehouse))
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
@dataclass
|
|
428
|
+
class AppResourceJob:
|
|
429
|
+
id: str
|
|
430
|
+
"""Id of the job to grant permission on."""
|
|
431
|
+
|
|
432
|
+
permission: AppResourceJobJobPermission
|
|
433
|
+
"""Permissions to grant on the Job. Supported permissions are: "CAN_MANAGE", "IS_OWNER",
|
|
434
|
+
"CAN_MANAGE_RUN", "CAN_VIEW"."""
|
|
435
|
+
|
|
436
|
+
def as_dict(self) -> dict:
|
|
437
|
+
"""Serializes the AppResourceJob into a dictionary suitable for use as a JSON request body."""
|
|
438
|
+
body = {}
|
|
439
|
+
if self.id is not None: body['id'] = self.id
|
|
440
|
+
if self.permission is not None: body['permission'] = self.permission.value
|
|
441
|
+
return body
|
|
442
|
+
|
|
443
|
+
@classmethod
|
|
444
|
+
def from_dict(cls, d: Dict[str, any]) -> AppResourceJob:
|
|
445
|
+
"""Deserializes the AppResourceJob from a dictionary."""
|
|
446
|
+
return cls(id=d.get('id', None), permission=_enum(d, 'permission', AppResourceJobJobPermission))
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
class AppResourceJobJobPermission(Enum):
|
|
450
|
+
|
|
451
|
+
CAN_MANAGE = 'CAN_MANAGE'
|
|
452
|
+
CAN_MANAGE_RUN = 'CAN_MANAGE_RUN'
|
|
453
|
+
CAN_VIEW = 'CAN_VIEW'
|
|
454
|
+
IS_OWNER = 'IS_OWNER'
|
|
372
455
|
|
|
373
|
-
|
|
374
|
-
|
|
456
|
+
|
|
457
|
+
@dataclass
|
|
458
|
+
class AppResourceSecret:
|
|
459
|
+
scope: str
|
|
460
|
+
"""Scope of the secret to grant permission on."""
|
|
461
|
+
|
|
462
|
+
key: str
|
|
463
|
+
"""Key of the secret to grant permission on."""
|
|
464
|
+
|
|
465
|
+
permission: AppResourceSecretSecretPermission
|
|
466
|
+
"""Permission to grant on the secret scope. For secrets, only one permission is allowed. Permission
|
|
467
|
+
must be one of: "READ", "WRITE", "MANAGE"."""
|
|
468
|
+
|
|
469
|
+
def as_dict(self) -> dict:
|
|
470
|
+
"""Serializes the AppResourceSecret into a dictionary suitable for use as a JSON request body."""
|
|
471
|
+
body = {}
|
|
472
|
+
if self.key is not None: body['key'] = self.key
|
|
473
|
+
if self.permission is not None: body['permission'] = self.permission.value
|
|
474
|
+
if self.scope is not None: body['scope'] = self.scope
|
|
475
|
+
return body
|
|
476
|
+
|
|
477
|
+
@classmethod
|
|
478
|
+
def from_dict(cls, d: Dict[str, any]) -> AppResourceSecret:
|
|
479
|
+
"""Deserializes the AppResourceSecret from a dictionary."""
|
|
480
|
+
return cls(key=d.get('key', None),
|
|
481
|
+
permission=_enum(d, 'permission', AppResourceSecretSecretPermission),
|
|
482
|
+
scope=d.get('scope', None))
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
class AppResourceSecretSecretPermission(Enum):
|
|
486
|
+
"""Permission to grant on the secret scope. Supported permissions are: "READ", "WRITE", "MANAGE"."""
|
|
487
|
+
|
|
488
|
+
MANAGE = 'MANAGE'
|
|
489
|
+
READ = 'READ'
|
|
490
|
+
WRITE = 'WRITE'
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
@dataclass
|
|
494
|
+
class AppResourceServingEndpoint:
|
|
495
|
+
name: str
|
|
496
|
+
"""Name of the serving endpoint to grant permission on."""
|
|
497
|
+
|
|
498
|
+
permission: AppResourceServingEndpointServingEndpointPermission
|
|
499
|
+
"""Permission to grant on the serving endpoint. Supported permissions are: "CAN_MANAGE",
|
|
500
|
+
"CAN_QUERY", "CAN_VIEW"."""
|
|
501
|
+
|
|
502
|
+
def as_dict(self) -> dict:
|
|
503
|
+
"""Serializes the AppResourceServingEndpoint into a dictionary suitable for use as a JSON request body."""
|
|
504
|
+
body = {}
|
|
505
|
+
if self.name is not None: body['name'] = self.name
|
|
506
|
+
if self.permission is not None: body['permission'] = self.permission.value
|
|
507
|
+
return body
|
|
508
|
+
|
|
509
|
+
@classmethod
|
|
510
|
+
def from_dict(cls, d: Dict[str, any]) -> AppResourceServingEndpoint:
|
|
511
|
+
"""Deserializes the AppResourceServingEndpoint from a dictionary."""
|
|
512
|
+
return cls(name=d.get('name', None),
|
|
513
|
+
permission=_enum(d, 'permission', AppResourceServingEndpointServingEndpointPermission))
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
class AppResourceServingEndpointServingEndpointPermission(Enum):
|
|
517
|
+
|
|
518
|
+
CAN_MANAGE = 'CAN_MANAGE'
|
|
519
|
+
CAN_QUERY = 'CAN_QUERY'
|
|
520
|
+
CAN_VIEW = 'CAN_VIEW'
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
@dataclass
|
|
524
|
+
class AppResourceSqlWarehouse:
|
|
525
|
+
id: str
|
|
526
|
+
"""Id of the SQL warehouse to grant permission on."""
|
|
527
|
+
|
|
528
|
+
permission: AppResourceSqlWarehouseSqlWarehousePermission
|
|
529
|
+
"""Permission to grant on the SQL warehouse. Supported permissions are: "CAN_MANAGE", "CAN_USE",
|
|
530
|
+
"IS_OWNER"."""
|
|
531
|
+
|
|
532
|
+
def as_dict(self) -> dict:
|
|
533
|
+
"""Serializes the AppResourceSqlWarehouse into a dictionary suitable for use as a JSON request body."""
|
|
534
|
+
body = {}
|
|
535
|
+
if self.id is not None: body['id'] = self.id
|
|
536
|
+
if self.permission is not None: body['permission'] = self.permission.value
|
|
537
|
+
return body
|
|
538
|
+
|
|
539
|
+
@classmethod
|
|
540
|
+
def from_dict(cls, d: Dict[str, any]) -> AppResourceSqlWarehouse:
|
|
541
|
+
"""Deserializes the AppResourceSqlWarehouse from a dictionary."""
|
|
542
|
+
return cls(id=d.get('id', None),
|
|
543
|
+
permission=_enum(d, 'permission', AppResourceSqlWarehouseSqlWarehousePermission))
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
class AppResourceSqlWarehouseSqlWarehousePermission(Enum):
|
|
547
|
+
|
|
548
|
+
CAN_MANAGE = 'CAN_MANAGE'
|
|
549
|
+
CAN_USE = 'CAN_USE'
|
|
550
|
+
IS_OWNER = 'IS_OWNER'
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
class ApplicationState(Enum):
|
|
554
|
+
|
|
555
|
+
CRASHED = 'CRASHED'
|
|
556
|
+
DEPLOYING = 'DEPLOYING'
|
|
557
|
+
RUNNING = 'RUNNING'
|
|
558
|
+
UNAVAILABLE = 'UNAVAILABLE'
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
@dataclass
|
|
562
|
+
class ApplicationStatus:
|
|
563
|
+
message: Optional[str] = None
|
|
564
|
+
"""Application status message"""
|
|
565
|
+
|
|
566
|
+
state: Optional[ApplicationState] = None
|
|
567
|
+
"""State of the application."""
|
|
568
|
+
|
|
569
|
+
def as_dict(self) -> dict:
|
|
570
|
+
"""Serializes the ApplicationStatus into a dictionary suitable for use as a JSON request body."""
|
|
571
|
+
body = {}
|
|
572
|
+
if self.message is not None: body['message'] = self.message
|
|
573
|
+
if self.state is not None: body['state'] = self.state.value
|
|
574
|
+
return body
|
|
575
|
+
|
|
576
|
+
@classmethod
|
|
577
|
+
def from_dict(cls, d: Dict[str, any]) -> ApplicationStatus:
|
|
578
|
+
"""Deserializes the ApplicationStatus from a dictionary."""
|
|
579
|
+
return cls(message=d.get('message', None), state=_enum(d, 'state', ApplicationState))
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
class ComputeState(Enum):
|
|
583
|
+
|
|
584
|
+
ACTIVE = 'ACTIVE'
|
|
375
585
|
DELETING = 'DELETING'
|
|
376
586
|
ERROR = 'ERROR'
|
|
377
|
-
IDLE = 'IDLE'
|
|
378
|
-
RUNNING = 'RUNNING'
|
|
379
587
|
STARTING = 'STARTING'
|
|
588
|
+
STOPPED = 'STOPPED'
|
|
589
|
+
STOPPING = 'STOPPING'
|
|
590
|
+
UPDATING = 'UPDATING'
|
|
380
591
|
|
|
381
592
|
|
|
382
593
|
@dataclass
|
|
383
|
-
class
|
|
594
|
+
class ComputeStatus:
|
|
384
595
|
message: Optional[str] = None
|
|
385
|
-
"""
|
|
596
|
+
"""Compute status message"""
|
|
386
597
|
|
|
387
|
-
state: Optional[
|
|
388
|
-
"""State of the app."""
|
|
598
|
+
state: Optional[ComputeState] = None
|
|
599
|
+
"""State of the app compute."""
|
|
389
600
|
|
|
390
601
|
def as_dict(self) -> dict:
|
|
391
|
-
"""Serializes the
|
|
602
|
+
"""Serializes the ComputeStatus into a dictionary suitable for use as a JSON request body."""
|
|
392
603
|
body = {}
|
|
393
604
|
if self.message is not None: body['message'] = self.message
|
|
394
605
|
if self.state is not None: body['state'] = self.state.value
|
|
395
606
|
return body
|
|
396
607
|
|
|
397
608
|
@classmethod
|
|
398
|
-
def from_dict(cls, d: Dict[str, any]) ->
|
|
399
|
-
"""Deserializes the
|
|
400
|
-
return cls(message=d.get('message', None), state=_enum(d, 'state',
|
|
609
|
+
def from_dict(cls, d: Dict[str, any]) -> ComputeStatus:
|
|
610
|
+
"""Deserializes the ComputeStatus from a dictionary."""
|
|
611
|
+
return cls(message=d.get('message', None), state=_enum(d, 'state', ComputeState))
|
|
401
612
|
|
|
402
613
|
|
|
403
614
|
@dataclass
|
|
404
615
|
class CreateAppDeploymentRequest:
|
|
405
|
-
source_code_path: str
|
|
406
|
-
"""The workspace file system path of the source code used to create the app deployment. This is
|
|
407
|
-
different from `deployment_artifacts.source_code_path`, which is the path used by the deployed
|
|
408
|
-
app. The former refers to the original source code location of the app in the workspace during
|
|
409
|
-
deployment creation, whereas the latter provides a system generated stable snapshotted source
|
|
410
|
-
code path used by the deployment."""
|
|
411
|
-
|
|
412
616
|
app_name: Optional[str] = None
|
|
413
617
|
"""The name of the app."""
|
|
414
618
|
|
|
619
|
+
deployment_id: Optional[str] = None
|
|
620
|
+
"""The unique id of the deployment."""
|
|
621
|
+
|
|
415
622
|
mode: Optional[AppDeploymentMode] = None
|
|
416
623
|
"""The mode of which the deployment will manage the source code."""
|
|
417
624
|
|
|
625
|
+
source_code_path: Optional[str] = None
|
|
626
|
+
"""The workspace file system path of the source code used to create the app deployment. This is
|
|
627
|
+
different from `deployment_artifacts.source_code_path`, which is the path used by the deployed
|
|
628
|
+
app. The former refers to the original source code location of the app in the workspace during
|
|
629
|
+
deployment creation, whereas the latter provides a system generated stable snapshotted source
|
|
630
|
+
code path used by the deployment."""
|
|
631
|
+
|
|
418
632
|
def as_dict(self) -> dict:
|
|
419
633
|
"""Serializes the CreateAppDeploymentRequest into a dictionary suitable for use as a JSON request body."""
|
|
420
634
|
body = {}
|
|
421
635
|
if self.app_name is not None: body['app_name'] = self.app_name
|
|
636
|
+
if self.deployment_id is not None: body['deployment_id'] = self.deployment_id
|
|
422
637
|
if self.mode is not None: body['mode'] = self.mode.value
|
|
423
638
|
if self.source_code_path is not None: body['source_code_path'] = self.source_code_path
|
|
424
639
|
return body
|
|
@@ -427,6 +642,7 @@ class CreateAppDeploymentRequest:
|
|
|
427
642
|
def from_dict(cls, d: Dict[str, any]) -> CreateAppDeploymentRequest:
|
|
428
643
|
"""Deserializes the CreateAppDeploymentRequest from a dictionary."""
|
|
429
644
|
return cls(app_name=d.get('app_name', None),
|
|
645
|
+
deployment_id=d.get('deployment_id', None),
|
|
430
646
|
mode=_enum(d, 'mode', AppDeploymentMode),
|
|
431
647
|
source_code_path=d.get('source_code_path', None))
|
|
432
648
|
|
|
@@ -440,31 +656,23 @@ class CreateAppRequest:
|
|
|
440
656
|
description: Optional[str] = None
|
|
441
657
|
"""The description of the app."""
|
|
442
658
|
|
|
659
|
+
resources: Optional[List[AppResource]] = None
|
|
660
|
+
"""Resources for the app."""
|
|
661
|
+
|
|
443
662
|
def as_dict(self) -> dict:
|
|
444
663
|
"""Serializes the CreateAppRequest into a dictionary suitable for use as a JSON request body."""
|
|
445
664
|
body = {}
|
|
446
665
|
if self.description is not None: body['description'] = self.description
|
|
447
666
|
if self.name is not None: body['name'] = self.name
|
|
667
|
+
if self.resources: body['resources'] = [v.as_dict() for v in self.resources]
|
|
448
668
|
return body
|
|
449
669
|
|
|
450
670
|
@classmethod
|
|
451
671
|
def from_dict(cls, d: Dict[str, any]) -> CreateAppRequest:
|
|
452
672
|
"""Deserializes the CreateAppRequest from a dictionary."""
|
|
453
|
-
return cls(description=d.get('description', None),
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
@dataclass
|
|
457
|
-
class DeleteResponse:
|
|
458
|
-
|
|
459
|
-
def as_dict(self) -> dict:
|
|
460
|
-
"""Serializes the DeleteResponse into a dictionary suitable for use as a JSON request body."""
|
|
461
|
-
body = {}
|
|
462
|
-
return body
|
|
463
|
-
|
|
464
|
-
@classmethod
|
|
465
|
-
def from_dict(cls, d: Dict[str, any]) -> DeleteResponse:
|
|
466
|
-
"""Deserializes the DeleteResponse from a dictionary."""
|
|
467
|
-
return cls()
|
|
673
|
+
return cls(description=d.get('description', None),
|
|
674
|
+
name=d.get('name', None),
|
|
675
|
+
resources=_repeated_dict(d, 'resources', AppResource))
|
|
468
676
|
|
|
469
677
|
|
|
470
678
|
@dataclass
|
|
@@ -538,20 +746,6 @@ class StopAppRequest:
|
|
|
538
746
|
"""The name of the app."""
|
|
539
747
|
|
|
540
748
|
|
|
541
|
-
@dataclass
|
|
542
|
-
class StopAppResponse:
|
|
543
|
-
|
|
544
|
-
def as_dict(self) -> dict:
|
|
545
|
-
"""Serializes the StopAppResponse into a dictionary suitable for use as a JSON request body."""
|
|
546
|
-
body = {}
|
|
547
|
-
return body
|
|
548
|
-
|
|
549
|
-
@classmethod
|
|
550
|
-
def from_dict(cls, d: Dict[str, any]) -> StopAppResponse:
|
|
551
|
-
"""Deserializes the StopAppResponse from a dictionary."""
|
|
552
|
-
return cls()
|
|
553
|
-
|
|
554
|
-
|
|
555
749
|
@dataclass
|
|
556
750
|
class UpdateAppRequest:
|
|
557
751
|
name: str
|
|
@@ -561,17 +755,23 @@ class UpdateAppRequest:
|
|
|
561
755
|
description: Optional[str] = None
|
|
562
756
|
"""The description of the app."""
|
|
563
757
|
|
|
758
|
+
resources: Optional[List[AppResource]] = None
|
|
759
|
+
"""Resources for the app."""
|
|
760
|
+
|
|
564
761
|
def as_dict(self) -> dict:
|
|
565
762
|
"""Serializes the UpdateAppRequest into a dictionary suitable for use as a JSON request body."""
|
|
566
763
|
body = {}
|
|
567
764
|
if self.description is not None: body['description'] = self.description
|
|
568
765
|
if self.name is not None: body['name'] = self.name
|
|
766
|
+
if self.resources: body['resources'] = [v.as_dict() for v in self.resources]
|
|
569
767
|
return body
|
|
570
768
|
|
|
571
769
|
@classmethod
|
|
572
770
|
def from_dict(cls, d: Dict[str, any]) -> UpdateAppRequest:
|
|
573
771
|
"""Deserializes the UpdateAppRequest from a dictionary."""
|
|
574
|
-
return cls(description=d.get('description', None),
|
|
772
|
+
return cls(description=d.get('description', None),
|
|
773
|
+
name=d.get('name', None),
|
|
774
|
+
resources=_repeated_dict(d, 'resources', AppResource))
|
|
575
775
|
|
|
576
776
|
|
|
577
777
|
class AppsAPI:
|
|
@@ -581,27 +781,59 @@ class AppsAPI:
|
|
|
581
781
|
def __init__(self, api_client):
|
|
582
782
|
self._api = api_client
|
|
583
783
|
|
|
584
|
-
def
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
784
|
+
def wait_get_app_active(self,
|
|
785
|
+
name: str,
|
|
786
|
+
timeout=timedelta(minutes=20),
|
|
787
|
+
callback: Optional[Callable[[App], None]] = None) -> App:
|
|
588
788
|
deadline = time.time() + timeout.total_seconds()
|
|
589
|
-
target_states = (
|
|
590
|
-
failure_states = (
|
|
789
|
+
target_states = (ComputeState.ACTIVE, )
|
|
790
|
+
failure_states = (ComputeState.ERROR, )
|
|
591
791
|
status_message = 'polling...'
|
|
592
792
|
attempt = 1
|
|
593
793
|
while time.time() < deadline:
|
|
594
794
|
poll = self.get(name=name)
|
|
595
|
-
status = poll.
|
|
795
|
+
status = poll.compute_status.state
|
|
596
796
|
status_message = f'current status: {status}'
|
|
597
|
-
if poll.
|
|
598
|
-
status_message = poll.
|
|
797
|
+
if poll.compute_status:
|
|
798
|
+
status_message = poll.compute_status.message
|
|
599
799
|
if status in target_states:
|
|
600
800
|
return poll
|
|
601
801
|
if callback:
|
|
602
802
|
callback(poll)
|
|
603
803
|
if status in failure_states:
|
|
604
|
-
msg = f'failed to reach
|
|
804
|
+
msg = f'failed to reach ACTIVE, got {status}: {status_message}'
|
|
805
|
+
raise OperationFailed(msg)
|
|
806
|
+
prefix = f"name={name}"
|
|
807
|
+
sleep = attempt
|
|
808
|
+
if sleep > 10:
|
|
809
|
+
# sleep 10s max per attempt
|
|
810
|
+
sleep = 10
|
|
811
|
+
_LOG.debug(f'{prefix}: ({status}) {status_message} (sleeping ~{sleep}s)')
|
|
812
|
+
time.sleep(sleep + random.random())
|
|
813
|
+
attempt += 1
|
|
814
|
+
raise TimeoutError(f'timed out after {timeout}: {status_message}')
|
|
815
|
+
|
|
816
|
+
def wait_get_app_stopped(self,
|
|
817
|
+
name: str,
|
|
818
|
+
timeout=timedelta(minutes=20),
|
|
819
|
+
callback: Optional[Callable[[App], None]] = None) -> App:
|
|
820
|
+
deadline = time.time() + timeout.total_seconds()
|
|
821
|
+
target_states = (ComputeState.STOPPED, )
|
|
822
|
+
failure_states = (ComputeState.ERROR, )
|
|
823
|
+
status_message = 'polling...'
|
|
824
|
+
attempt = 1
|
|
825
|
+
while time.time() < deadline:
|
|
826
|
+
poll = self.get(name=name)
|
|
827
|
+
status = poll.compute_status.state
|
|
828
|
+
status_message = f'current status: {status}'
|
|
829
|
+
if poll.compute_status:
|
|
830
|
+
status_message = poll.compute_status.message
|
|
831
|
+
if status in target_states:
|
|
832
|
+
return poll
|
|
833
|
+
if callback:
|
|
834
|
+
callback(poll)
|
|
835
|
+
if status in failure_states:
|
|
836
|
+
msg = f'failed to reach STOPPED, got {status}: {status_message}'
|
|
605
837
|
raise OperationFailed(msg)
|
|
606
838
|
prefix = f"name={name}"
|
|
607
839
|
sleep = attempt
|
|
@@ -647,7 +879,11 @@ class AppsAPI:
|
|
|
647
879
|
attempt += 1
|
|
648
880
|
raise TimeoutError(f'timed out after {timeout}: {status_message}')
|
|
649
881
|
|
|
650
|
-
def create(self,
|
|
882
|
+
def create(self,
|
|
883
|
+
name: str,
|
|
884
|
+
*,
|
|
885
|
+
description: Optional[str] = None,
|
|
886
|
+
resources: Optional[List[AppResource]] = None) -> Wait[App]:
|
|
651
887
|
"""Create an app.
|
|
652
888
|
|
|
653
889
|
Creates a new app.
|
|
@@ -657,27 +893,31 @@ class AppsAPI:
|
|
|
657
893
|
must be unique within the workspace.
|
|
658
894
|
:param description: str (optional)
|
|
659
895
|
The description of the app.
|
|
896
|
+
:param resources: List[:class:`AppResource`] (optional)
|
|
897
|
+
Resources for the app.
|
|
660
898
|
|
|
661
899
|
:returns:
|
|
662
900
|
Long-running operation waiter for :class:`App`.
|
|
663
|
-
See :method:
|
|
901
|
+
See :method:wait_get_app_active for more details.
|
|
664
902
|
"""
|
|
665
903
|
body = {}
|
|
666
904
|
if description is not None: body['description'] = description
|
|
667
905
|
if name is not None: body['name'] = name
|
|
906
|
+
if resources is not None: body['resources'] = [v.as_dict() for v in resources]
|
|
668
907
|
headers = {'Accept': 'application/json', 'Content-Type': 'application/json', }
|
|
669
908
|
|
|
670
|
-
op_response = self._api.do('POST', '/api/2.0/
|
|
671
|
-
return Wait(self.
|
|
909
|
+
op_response = self._api.do('POST', '/api/2.0/apps', body=body, headers=headers)
|
|
910
|
+
return Wait(self.wait_get_app_active, response=App.from_dict(op_response), name=op_response['name'])
|
|
672
911
|
|
|
673
912
|
def create_and_wait(self,
|
|
674
913
|
name: str,
|
|
675
914
|
*,
|
|
676
915
|
description: Optional[str] = None,
|
|
916
|
+
resources: Optional[List[AppResource]] = None,
|
|
677
917
|
timeout=timedelta(minutes=20)) -> App:
|
|
678
|
-
return self.create(description=description, name=name).result(timeout=timeout)
|
|
918
|
+
return self.create(description=description, name=name, resources=resources).result(timeout=timeout)
|
|
679
919
|
|
|
680
|
-
def delete(self, name: str):
|
|
920
|
+
def delete(self, name: str) -> App:
|
|
681
921
|
"""Delete an app.
|
|
682
922
|
|
|
683
923
|
Deletes an app.
|
|
@@ -685,44 +925,49 @@ class AppsAPI:
|
|
|
685
925
|
:param name: str
|
|
686
926
|
The name of the app.
|
|
687
927
|
|
|
688
|
-
|
|
928
|
+
:returns: :class:`App`
|
|
689
929
|
"""
|
|
690
930
|
|
|
691
931
|
headers = {'Accept': 'application/json', }
|
|
692
932
|
|
|
693
|
-
self._api.do('DELETE', f'/api/2.0/
|
|
933
|
+
res = self._api.do('DELETE', f'/api/2.0/apps/{name}', headers=headers)
|
|
934
|
+
return App.from_dict(res)
|
|
694
935
|
|
|
695
936
|
def deploy(self,
|
|
696
937
|
app_name: str,
|
|
697
|
-
source_code_path: str,
|
|
698
938
|
*,
|
|
699
|
-
|
|
939
|
+
deployment_id: Optional[str] = None,
|
|
940
|
+
mode: Optional[AppDeploymentMode] = None,
|
|
941
|
+
source_code_path: Optional[str] = None) -> Wait[AppDeployment]:
|
|
700
942
|
"""Create an app deployment.
|
|
701
943
|
|
|
702
944
|
Creates an app deployment for the app with the supplied name.
|
|
703
945
|
|
|
704
946
|
:param app_name: str
|
|
705
947
|
The name of the app.
|
|
706
|
-
:param
|
|
948
|
+
:param deployment_id: str (optional)
|
|
949
|
+
The unique id of the deployment.
|
|
950
|
+
:param mode: :class:`AppDeploymentMode` (optional)
|
|
951
|
+
The mode of which the deployment will manage the source code.
|
|
952
|
+
:param source_code_path: str (optional)
|
|
707
953
|
The workspace file system path of the source code used to create the app deployment. This is
|
|
708
954
|
different from `deployment_artifacts.source_code_path`, which is the path used by the deployed app.
|
|
709
955
|
The former refers to the original source code location of the app in the workspace during deployment
|
|
710
956
|
creation, whereas the latter provides a system generated stable snapshotted source code path used by
|
|
711
957
|
the deployment.
|
|
712
|
-
:param mode: :class:`AppDeploymentMode` (optional)
|
|
713
|
-
The mode of which the deployment will manage the source code.
|
|
714
958
|
|
|
715
959
|
:returns:
|
|
716
960
|
Long-running operation waiter for :class:`AppDeployment`.
|
|
717
961
|
See :method:wait_get_deployment_app_succeeded for more details.
|
|
718
962
|
"""
|
|
719
963
|
body = {}
|
|
964
|
+
if deployment_id is not None: body['deployment_id'] = deployment_id
|
|
720
965
|
if mode is not None: body['mode'] = mode.value
|
|
721
966
|
if source_code_path is not None: body['source_code_path'] = source_code_path
|
|
722
967
|
headers = {'Accept': 'application/json', 'Content-Type': 'application/json', }
|
|
723
968
|
|
|
724
969
|
op_response = self._api.do('POST',
|
|
725
|
-
f'/api/2.0/
|
|
970
|
+
f'/api/2.0/apps/{app_name}/deployments',
|
|
726
971
|
body=body,
|
|
727
972
|
headers=headers)
|
|
728
973
|
return Wait(self.wait_get_deployment_app_succeeded,
|
|
@@ -733,11 +978,14 @@ class AppsAPI:
|
|
|
733
978
|
def deploy_and_wait(
|
|
734
979
|
self,
|
|
735
980
|
app_name: str,
|
|
736
|
-
source_code_path: str,
|
|
737
981
|
*,
|
|
982
|
+
deployment_id: Optional[str] = None,
|
|
738
983
|
mode: Optional[AppDeploymentMode] = None,
|
|
984
|
+
source_code_path: Optional[str] = None,
|
|
739
985
|
timeout=timedelta(minutes=20)) -> AppDeployment:
|
|
740
|
-
return self.deploy(app_name=app_name,
|
|
986
|
+
return self.deploy(app_name=app_name,
|
|
987
|
+
deployment_id=deployment_id,
|
|
988
|
+
mode=mode,
|
|
741
989
|
source_code_path=source_code_path).result(timeout=timeout)
|
|
742
990
|
|
|
743
991
|
def get(self, name: str) -> App:
|
|
@@ -753,7 +1001,7 @@ class AppsAPI:
|
|
|
753
1001
|
|
|
754
1002
|
headers = {'Accept': 'application/json', }
|
|
755
1003
|
|
|
756
|
-
res = self._api.do('GET', f'/api/2.0/
|
|
1004
|
+
res = self._api.do('GET', f'/api/2.0/apps/{name}', headers=headers)
|
|
757
1005
|
return App.from_dict(res)
|
|
758
1006
|
|
|
759
1007
|
def get_deployment(self, app_name: str, deployment_id: str) -> AppDeployment:
|
|
@@ -771,9 +1019,7 @@ class AppsAPI:
|
|
|
771
1019
|
|
|
772
1020
|
headers = {'Accept': 'application/json', }
|
|
773
1021
|
|
|
774
|
-
res = self._api.do('GET',
|
|
775
|
-
f'/api/2.0/preview/apps/{app_name}/deployments/{deployment_id}',
|
|
776
|
-
headers=headers)
|
|
1022
|
+
res = self._api.do('GET', f'/api/2.0/apps/{app_name}/deployments/{deployment_id}', headers=headers)
|
|
777
1023
|
return AppDeployment.from_dict(res)
|
|
778
1024
|
|
|
779
1025
|
def get_permission_levels(self, app_name: str) -> GetAppPermissionLevelsResponse:
|
|
@@ -827,7 +1073,7 @@ class AppsAPI:
|
|
|
827
1073
|
headers = {'Accept': 'application/json', }
|
|
828
1074
|
|
|
829
1075
|
while True:
|
|
830
|
-
json = self._api.do('GET', '/api/2.0/
|
|
1076
|
+
json = self._api.do('GET', '/api/2.0/apps', query=query, headers=headers)
|
|
831
1077
|
if 'apps' in json:
|
|
832
1078
|
for v in json['apps']:
|
|
833
1079
|
yield App.from_dict(v)
|
|
@@ -860,10 +1106,7 @@ class AppsAPI:
|
|
|
860
1106
|
headers = {'Accept': 'application/json', }
|
|
861
1107
|
|
|
862
1108
|
while True:
|
|
863
|
-
json = self._api.do('GET',
|
|
864
|
-
f'/api/2.0/preview/apps/{app_name}/deployments',
|
|
865
|
-
query=query,
|
|
866
|
-
headers=headers)
|
|
1109
|
+
json = self._api.do('GET', f'/api/2.0/apps/{app_name}/deployments', query=query, headers=headers)
|
|
867
1110
|
if 'app_deployments' in json:
|
|
868
1111
|
for v in json['app_deployments']:
|
|
869
1112
|
yield AppDeployment.from_dict(v)
|
|
@@ -894,7 +1137,7 @@ class AppsAPI:
|
|
|
894
1137
|
res = self._api.do('PUT', f'/api/2.0/permissions/apps/{app_name}', body=body, headers=headers)
|
|
895
1138
|
return AppPermissions.from_dict(res)
|
|
896
1139
|
|
|
897
|
-
def start(self, name: str) -> Wait[
|
|
1140
|
+
def start(self, name: str) -> Wait[App]:
|
|
898
1141
|
"""Start an app.
|
|
899
1142
|
|
|
900
1143
|
Start the last active deployment of the app in the workspace.
|
|
@@ -903,22 +1146,19 @@ class AppsAPI:
|
|
|
903
1146
|
The name of the app.
|
|
904
1147
|
|
|
905
1148
|
:returns:
|
|
906
|
-
Long-running operation waiter for :class:`
|
|
907
|
-
See :method:
|
|
1149
|
+
Long-running operation waiter for :class:`App`.
|
|
1150
|
+
See :method:wait_get_app_active for more details.
|
|
908
1151
|
"""
|
|
909
1152
|
|
|
910
1153
|
headers = {'Accept': 'application/json', 'Content-Type': 'application/json', }
|
|
911
1154
|
|
|
912
|
-
op_response = self._api.do('POST', f'/api/2.0/
|
|
913
|
-
return Wait(self.
|
|
914
|
-
response=AppDeployment.from_dict(op_response),
|
|
915
|
-
app_name=name,
|
|
916
|
-
deployment_id=op_response['deployment_id'])
|
|
1155
|
+
op_response = self._api.do('POST', f'/api/2.0/apps/{name}/start', headers=headers)
|
|
1156
|
+
return Wait(self.wait_get_app_active, response=App.from_dict(op_response), name=op_response['name'])
|
|
917
1157
|
|
|
918
|
-
def start_and_wait(self, name: str, timeout=timedelta(minutes=20)) ->
|
|
1158
|
+
def start_and_wait(self, name: str, timeout=timedelta(minutes=20)) -> App:
|
|
919
1159
|
return self.start(name=name).result(timeout=timeout)
|
|
920
1160
|
|
|
921
|
-
def stop(self, name: str):
|
|
1161
|
+
def stop(self, name: str) -> Wait[App]:
|
|
922
1162
|
"""Stop an app.
|
|
923
1163
|
|
|
924
1164
|
Stops the active deployment of the app in the workspace.
|
|
@@ -926,14 +1166,24 @@ class AppsAPI:
|
|
|
926
1166
|
:param name: str
|
|
927
1167
|
The name of the app.
|
|
928
1168
|
|
|
929
|
-
|
|
1169
|
+
:returns:
|
|
1170
|
+
Long-running operation waiter for :class:`App`.
|
|
1171
|
+
See :method:wait_get_app_stopped for more details.
|
|
930
1172
|
"""
|
|
931
1173
|
|
|
932
1174
|
headers = {'Accept': 'application/json', 'Content-Type': 'application/json', }
|
|
933
1175
|
|
|
934
|
-
self._api.do('POST', f'/api/2.0/
|
|
1176
|
+
op_response = self._api.do('POST', f'/api/2.0/apps/{name}/stop', headers=headers)
|
|
1177
|
+
return Wait(self.wait_get_app_stopped, response=App.from_dict(op_response), name=op_response['name'])
|
|
1178
|
+
|
|
1179
|
+
def stop_and_wait(self, name: str, timeout=timedelta(minutes=20)) -> App:
|
|
1180
|
+
return self.stop(name=name).result(timeout=timeout)
|
|
935
1181
|
|
|
936
|
-
def update(self,
|
|
1182
|
+
def update(self,
|
|
1183
|
+
name: str,
|
|
1184
|
+
*,
|
|
1185
|
+
description: Optional[str] = None,
|
|
1186
|
+
resources: Optional[List[AppResource]] = None) -> App:
|
|
937
1187
|
"""Update an app.
|
|
938
1188
|
|
|
939
1189
|
Updates the app with the supplied name.
|
|
@@ -943,14 +1193,17 @@ class AppsAPI:
|
|
|
943
1193
|
must be unique within the workspace.
|
|
944
1194
|
:param description: str (optional)
|
|
945
1195
|
The description of the app.
|
|
1196
|
+
:param resources: List[:class:`AppResource`] (optional)
|
|
1197
|
+
Resources for the app.
|
|
946
1198
|
|
|
947
1199
|
:returns: :class:`App`
|
|
948
1200
|
"""
|
|
949
1201
|
body = {}
|
|
950
1202
|
if description is not None: body['description'] = description
|
|
1203
|
+
if resources is not None: body['resources'] = [v.as_dict() for v in resources]
|
|
951
1204
|
headers = {'Accept': 'application/json', 'Content-Type': 'application/json', }
|
|
952
1205
|
|
|
953
|
-
res = self._api.do('PATCH', f'/api/2.0/
|
|
1206
|
+
res = self._api.do('PATCH', f'/api/2.0/apps/{name}', body=body, headers=headers)
|
|
954
1207
|
return App.from_dict(res)
|
|
955
1208
|
|
|
956
1209
|
def update_permissions(
|