datacrunch 1.7.1__py3-none-any.whl → 1.8.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.
- datacrunch/__version__.py +1 -1
- datacrunch/containers/__init__.py +30 -0
- datacrunch/containers/containers.py +673 -0
- datacrunch/datacrunch.py +5 -0
- datacrunch/http_client/http_client.py +30 -0
- datacrunch/instances/instances.py +2 -2
- {datacrunch-1.7.1.dist-info → datacrunch-1.8.0.dist-info}/METADATA +3 -2
- {datacrunch-1.7.1.dist-info → datacrunch-1.8.0.dist-info}/RECORD +13 -9
- {datacrunch-1.7.1.dist-info → datacrunch-1.8.0.dist-info}/WHEEL +1 -1
- tests/unit_tests/containers/__init__.py +1 -0
- tests/unit_tests/containers/test_containers.py +890 -0
- {datacrunch-1.7.1.dist-info → datacrunch-1.8.0.dist-info/licenses}/LICENSE +0 -0
- {datacrunch-1.7.1.dist-info → datacrunch-1.8.0.dist-info}/top_level.txt +0 -0
datacrunch/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
VERSION = '1.
|
|
1
|
+
VERSION = '1.8.0'
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from .containers import (
|
|
2
|
+
EnvVar,
|
|
3
|
+
EnvVarType,
|
|
4
|
+
ContainerRegistryType,
|
|
5
|
+
ContainerDeploymentStatus,
|
|
6
|
+
HealthcheckSettings,
|
|
7
|
+
EntrypointOverridesSettings,
|
|
8
|
+
VolumeMount,
|
|
9
|
+
VolumeMountType,
|
|
10
|
+
Container,
|
|
11
|
+
ContainerRegistryCredentials,
|
|
12
|
+
ContainerRegistrySettings,
|
|
13
|
+
ComputeResource,
|
|
14
|
+
ScalingPolicy,
|
|
15
|
+
QueueLoadScalingTrigger,
|
|
16
|
+
UtilizationScalingTrigger,
|
|
17
|
+
ScalingTriggers,
|
|
18
|
+
ScalingOptions,
|
|
19
|
+
Deployment,
|
|
20
|
+
ReplicaInfo,
|
|
21
|
+
Secret,
|
|
22
|
+
RegistryCredential,
|
|
23
|
+
ContainersService,
|
|
24
|
+
BaseRegistryCredentials,
|
|
25
|
+
DockerHubCredentials,
|
|
26
|
+
GithubCredentials,
|
|
27
|
+
GCRCredentials,
|
|
28
|
+
AWSECRCredentials,
|
|
29
|
+
CustomRegistryCredentials,
|
|
30
|
+
)
|
|
@@ -0,0 +1,673 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from dataclasses_json import dataclass_json, Undefined # type: ignore
|
|
3
|
+
from typing import List, Optional, Dict
|
|
4
|
+
from enum import Enum
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# API endpoints
|
|
8
|
+
CONTAINER_DEPLOYMENTS_ENDPOINT = '/container-deployments'
|
|
9
|
+
SERVERLESS_COMPUTE_RESOURCES_ENDPOINT = '/serverless-compute-resources'
|
|
10
|
+
CONTAINER_REGISTRY_CREDENTIALS_ENDPOINT = '/container-registry-credentials'
|
|
11
|
+
SECRETS_ENDPOINT = '/secrets'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class EnvVarType(str, Enum):
|
|
15
|
+
PLAIN = "plain"
|
|
16
|
+
SECRET = "secret"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class VolumeMountType(str, Enum):
|
|
20
|
+
SCRATCH = "scratch"
|
|
21
|
+
SECRET = "secret"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ContainerRegistryType(str, Enum):
|
|
25
|
+
GCR = "gcr"
|
|
26
|
+
DOCKERHUB = "dockerhub"
|
|
27
|
+
GITHUB = "ghcr"
|
|
28
|
+
AWS_ECR = "aws-ecr"
|
|
29
|
+
CUSTOM = "custom"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ContainerDeploymentStatus(str, Enum):
|
|
33
|
+
INITIALIZING = "initializing"
|
|
34
|
+
HEALTHY = "healthy"
|
|
35
|
+
DEGRADED = "degraded"
|
|
36
|
+
UNHEALTHY = "unhealthy"
|
|
37
|
+
PAUSED = "paused"
|
|
38
|
+
QUOTA_REACHED = "quota_reached"
|
|
39
|
+
IMAGE_PULLING = "image_pulling"
|
|
40
|
+
VERSION_UPDATING = "version_updating"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass_json
|
|
44
|
+
@dataclass
|
|
45
|
+
class HealthcheckSettings:
|
|
46
|
+
"""Settings for container health checking.
|
|
47
|
+
|
|
48
|
+
:param enabled: Whether health checking is enabled
|
|
49
|
+
:param port: Port number to perform health check on
|
|
50
|
+
:param path: HTTP path to perform health check on
|
|
51
|
+
"""
|
|
52
|
+
enabled: bool = True
|
|
53
|
+
port: Optional[int] = None
|
|
54
|
+
path: Optional[str] = None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass_json
|
|
58
|
+
@dataclass
|
|
59
|
+
class EntrypointOverridesSettings:
|
|
60
|
+
"""Settings for overriding container entrypoint and command.
|
|
61
|
+
|
|
62
|
+
:param enabled: Whether entrypoint overrides are enabled
|
|
63
|
+
:param entrypoint: List of strings forming the entrypoint command
|
|
64
|
+
:param cmd: List of strings forming the command arguments
|
|
65
|
+
"""
|
|
66
|
+
enabled: bool = True
|
|
67
|
+
entrypoint: Optional[List[str]] = None
|
|
68
|
+
cmd: Optional[List[str]] = None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass_json
|
|
72
|
+
@dataclass
|
|
73
|
+
class EnvVar:
|
|
74
|
+
"""Environment variable configuration for containers.
|
|
75
|
+
|
|
76
|
+
:param name: Name of the environment variable
|
|
77
|
+
:param value_or_reference_to_secret: Direct value or reference to a secret
|
|
78
|
+
:param type: Type of the environment variable
|
|
79
|
+
"""
|
|
80
|
+
name: str
|
|
81
|
+
value_or_reference_to_secret: str
|
|
82
|
+
type: EnvVarType
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dataclass_json
|
|
86
|
+
@dataclass
|
|
87
|
+
class VolumeMount:
|
|
88
|
+
"""Volume mount configuration for containers.
|
|
89
|
+
|
|
90
|
+
:param type: Type of volume mount
|
|
91
|
+
:param mount_path: Path where the volume should be mounted in the container
|
|
92
|
+
"""
|
|
93
|
+
type: VolumeMountType
|
|
94
|
+
mount_path: str
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@dataclass_json
|
|
98
|
+
@dataclass
|
|
99
|
+
class Container:
|
|
100
|
+
"""Container configuration for deployments.
|
|
101
|
+
|
|
102
|
+
:param name: Name of the container
|
|
103
|
+
:param image: Container image to use
|
|
104
|
+
:param exposed_port: Port to expose from the container
|
|
105
|
+
:param healthcheck: Optional health check configuration
|
|
106
|
+
:param entrypoint_overrides: Optional entrypoint override settings
|
|
107
|
+
:param env: Optional list of environment variables
|
|
108
|
+
:param volume_mounts: Optional list of volume mounts
|
|
109
|
+
"""
|
|
110
|
+
name: str
|
|
111
|
+
image: str
|
|
112
|
+
exposed_port: int
|
|
113
|
+
healthcheck: Optional[HealthcheckSettings] = None
|
|
114
|
+
entrypoint_overrides: Optional[EntrypointOverridesSettings] = None
|
|
115
|
+
env: Optional[List[EnvVar]] = None
|
|
116
|
+
volume_mounts: Optional[List[VolumeMount]] = None
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@dataclass_json
|
|
120
|
+
@dataclass
|
|
121
|
+
class ContainerRegistryCredentials:
|
|
122
|
+
"""Credentials for accessing a container registry.
|
|
123
|
+
|
|
124
|
+
:param name: Name of the credentials
|
|
125
|
+
"""
|
|
126
|
+
name: str
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@dataclass_json
|
|
130
|
+
@dataclass
|
|
131
|
+
class ContainerRegistrySettings:
|
|
132
|
+
"""Settings for container registry access.
|
|
133
|
+
|
|
134
|
+
:param is_private: Whether the registry is private
|
|
135
|
+
:param credentials: Optional credentials for accessing private registry
|
|
136
|
+
"""
|
|
137
|
+
is_private: bool
|
|
138
|
+
credentials: Optional[ContainerRegistryCredentials] = None
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass_json
|
|
142
|
+
@dataclass
|
|
143
|
+
class ComputeResource:
|
|
144
|
+
"""Compute resource configuration.
|
|
145
|
+
|
|
146
|
+
:param name: Name of the compute resource
|
|
147
|
+
:param size: Size of the compute resource
|
|
148
|
+
:param is_available: Whether the compute resource is currently available
|
|
149
|
+
"""
|
|
150
|
+
name: str
|
|
151
|
+
size: int
|
|
152
|
+
# Made optional since it's only used in API responses
|
|
153
|
+
is_available: Optional[bool] = None
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@dataclass_json
|
|
157
|
+
@dataclass
|
|
158
|
+
class ScalingPolicy:
|
|
159
|
+
"""Policy for controlling scaling behavior.
|
|
160
|
+
|
|
161
|
+
:param delay_seconds: Number of seconds to wait before applying scaling action
|
|
162
|
+
"""
|
|
163
|
+
delay_seconds: int
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@dataclass_json
|
|
167
|
+
@dataclass
|
|
168
|
+
class QueueLoadScalingTrigger:
|
|
169
|
+
"""Trigger for scaling based on queue load.
|
|
170
|
+
|
|
171
|
+
:param threshold: Queue load threshold that triggers scaling
|
|
172
|
+
"""
|
|
173
|
+
threshold: float
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@dataclass_json
|
|
177
|
+
@dataclass
|
|
178
|
+
class UtilizationScalingTrigger:
|
|
179
|
+
"""Trigger for scaling based on resource utilization.
|
|
180
|
+
|
|
181
|
+
:param enabled: Whether this trigger is enabled
|
|
182
|
+
:param threshold: Utilization threshold that triggers scaling
|
|
183
|
+
"""
|
|
184
|
+
enabled: bool
|
|
185
|
+
threshold: Optional[float] = None
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@dataclass_json
|
|
189
|
+
@dataclass
|
|
190
|
+
class ScalingTriggers:
|
|
191
|
+
"""Collection of triggers that can cause scaling actions.
|
|
192
|
+
|
|
193
|
+
:param queue_load: Optional trigger based on queue load
|
|
194
|
+
:param cpu_utilization: Optional trigger based on CPU utilization
|
|
195
|
+
:param gpu_utilization: Optional trigger based on GPU utilization
|
|
196
|
+
"""
|
|
197
|
+
queue_load: Optional[QueueLoadScalingTrigger] = None
|
|
198
|
+
cpu_utilization: Optional[UtilizationScalingTrigger] = None
|
|
199
|
+
gpu_utilization: Optional[UtilizationScalingTrigger] = None
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@dataclass_json
|
|
203
|
+
@dataclass
|
|
204
|
+
class ScalingOptions:
|
|
205
|
+
"""Configuration for automatic scaling behavior.
|
|
206
|
+
|
|
207
|
+
:param min_replica_count: Minimum number of replicas to maintain
|
|
208
|
+
:param max_replica_count: Maximum number of replicas allowed
|
|
209
|
+
:param scale_down_policy: Policy for scaling down replicas
|
|
210
|
+
:param scale_up_policy: Policy for scaling up replicas
|
|
211
|
+
:param queue_message_ttl_seconds: Time-to-live for queue messages in seconds
|
|
212
|
+
:param concurrent_requests_per_replica: Number of concurrent requests each replica can handle
|
|
213
|
+
:param scaling_triggers: Configuration for various scaling triggers
|
|
214
|
+
"""
|
|
215
|
+
min_replica_count: int
|
|
216
|
+
max_replica_count: int
|
|
217
|
+
scale_down_policy: ScalingPolicy
|
|
218
|
+
scale_up_policy: ScalingPolicy
|
|
219
|
+
queue_message_ttl_seconds: int
|
|
220
|
+
concurrent_requests_per_replica: int
|
|
221
|
+
scaling_triggers: ScalingTriggers
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
225
|
+
@dataclass
|
|
226
|
+
class Deployment:
|
|
227
|
+
"""Configuration for a container deployment.
|
|
228
|
+
|
|
229
|
+
:param name: Name of the deployment
|
|
230
|
+
:param container_registry_settings: Settings for accessing container registry
|
|
231
|
+
:param containers: List of containers in the deployment
|
|
232
|
+
:param compute: Compute resource configuration
|
|
233
|
+
:param is_spot: Whether is spot deployment
|
|
234
|
+
:param endpoint_base_url: Optional base URL for the deployment endpoint
|
|
235
|
+
:param scaling: Optional scaling configuration
|
|
236
|
+
:param created_at: Timestamp when the deployment was created
|
|
237
|
+
"""
|
|
238
|
+
name: str
|
|
239
|
+
container_registry_settings: ContainerRegistrySettings
|
|
240
|
+
containers: List[Container]
|
|
241
|
+
compute: ComputeResource
|
|
242
|
+
is_spot: bool = False
|
|
243
|
+
endpoint_base_url: Optional[str] = None
|
|
244
|
+
scaling: Optional[ScalingOptions] = None
|
|
245
|
+
created_at: Optional[str] = None
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@dataclass_json
|
|
249
|
+
@dataclass
|
|
250
|
+
class ReplicaInfo:
|
|
251
|
+
"""Information about a deployment replica.
|
|
252
|
+
|
|
253
|
+
:param id: Unique identifier of the replica
|
|
254
|
+
:param status: Current status of the replica
|
|
255
|
+
:param started_at: Timestamp when the replica was started
|
|
256
|
+
"""
|
|
257
|
+
id: str
|
|
258
|
+
status: str
|
|
259
|
+
started_at: str
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
@dataclass_json
|
|
263
|
+
@dataclass
|
|
264
|
+
class Secret:
|
|
265
|
+
"""A secret model class"""
|
|
266
|
+
name: str
|
|
267
|
+
created_at: str
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
@dataclass_json
|
|
271
|
+
@dataclass
|
|
272
|
+
class RegistryCredential:
|
|
273
|
+
"""A container registry credential model class"""
|
|
274
|
+
name: str
|
|
275
|
+
created_at: str
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
@dataclass_json
|
|
279
|
+
@dataclass
|
|
280
|
+
class BaseRegistryCredentials:
|
|
281
|
+
"""Base class for registry credentials"""
|
|
282
|
+
name: str
|
|
283
|
+
type: ContainerRegistryType
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
@dataclass_json
|
|
287
|
+
@dataclass
|
|
288
|
+
class DockerHubCredentials(BaseRegistryCredentials):
|
|
289
|
+
"""Credentials for DockerHub registry"""
|
|
290
|
+
username: str
|
|
291
|
+
access_token: str
|
|
292
|
+
|
|
293
|
+
def __init__(self, name: str, username: str, access_token: str):
|
|
294
|
+
super().__init__(name=name, type=ContainerRegistryType.DOCKERHUB)
|
|
295
|
+
self.username = username
|
|
296
|
+
self.access_token = access_token
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
@dataclass_json
|
|
300
|
+
@dataclass
|
|
301
|
+
class GithubCredentials(BaseRegistryCredentials):
|
|
302
|
+
"""Credentials for GitHub Container Registry"""
|
|
303
|
+
username: str
|
|
304
|
+
access_token: str
|
|
305
|
+
|
|
306
|
+
def __init__(self, name: str, username: str, access_token: str):
|
|
307
|
+
super().__init__(name=name, type=ContainerRegistryType.GITHUB)
|
|
308
|
+
self.username = username
|
|
309
|
+
self.access_token = access_token
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
@dataclass_json
|
|
313
|
+
@dataclass
|
|
314
|
+
class GCRCredentials(BaseRegistryCredentials):
|
|
315
|
+
"""Credentials for Google Container Registry"""
|
|
316
|
+
service_account_key: str
|
|
317
|
+
|
|
318
|
+
def __init__(self, name: str, service_account_key: str):
|
|
319
|
+
super().__init__(name=name, type=ContainerRegistryType.GCR)
|
|
320
|
+
self.service_account_key = service_account_key
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
@dataclass_json
|
|
324
|
+
@dataclass
|
|
325
|
+
class AWSECRCredentials(BaseRegistryCredentials):
|
|
326
|
+
"""Credentials for AWS Elastic Container Registry"""
|
|
327
|
+
access_key_id: str
|
|
328
|
+
secret_access_key: str
|
|
329
|
+
region: str
|
|
330
|
+
ecr_repo: str
|
|
331
|
+
|
|
332
|
+
def __init__(self, name: str, access_key_id: str, secret_access_key: str, region: str, ecr_repo: str):
|
|
333
|
+
super().__init__(name=name, type=ContainerRegistryType.AWS_ECR)
|
|
334
|
+
self.access_key_id = access_key_id
|
|
335
|
+
self.secret_access_key = secret_access_key
|
|
336
|
+
self.region = region
|
|
337
|
+
self.ecr_repo = ecr_repo
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
@dataclass_json
|
|
341
|
+
@dataclass
|
|
342
|
+
class CustomRegistryCredentials(BaseRegistryCredentials):
|
|
343
|
+
"""Credentials for custom container registries"""
|
|
344
|
+
docker_config_json: str
|
|
345
|
+
|
|
346
|
+
def __init__(self, name: str, docker_config_json: str):
|
|
347
|
+
super().__init__(name=name, type=ContainerRegistryType.CUSTOM)
|
|
348
|
+
self.docker_config_json = docker_config_json
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
class ContainersService:
|
|
352
|
+
"""Service for managing container deployments"""
|
|
353
|
+
|
|
354
|
+
def __init__(self, http_client) -> None:
|
|
355
|
+
"""Initialize the containers service
|
|
356
|
+
|
|
357
|
+
:param http_client: HTTP client for making API requests
|
|
358
|
+
:type http_client: Any
|
|
359
|
+
"""
|
|
360
|
+
self.client = http_client
|
|
361
|
+
|
|
362
|
+
def get_deployments(self) -> List[Deployment]:
|
|
363
|
+
"""Get all deployments
|
|
364
|
+
|
|
365
|
+
:return: list of deployments
|
|
366
|
+
:rtype: List[Deployment]
|
|
367
|
+
"""
|
|
368
|
+
response = self.client.get(CONTAINER_DEPLOYMENTS_ENDPOINT)
|
|
369
|
+
return [Deployment.from_dict(deployment, infer_missing=True) for deployment in response.json()]
|
|
370
|
+
|
|
371
|
+
def get_deployment_by_name(self, deployment_name: str) -> Deployment:
|
|
372
|
+
"""Get a deployment by name
|
|
373
|
+
|
|
374
|
+
:param deployment_name: name of the deployment
|
|
375
|
+
:type deployment_name: str
|
|
376
|
+
:return: deployment
|
|
377
|
+
:rtype: Deployment
|
|
378
|
+
"""
|
|
379
|
+
response = self.client.get(
|
|
380
|
+
f"{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}")
|
|
381
|
+
return Deployment.from_dict(response.json(), infer_missing=True)
|
|
382
|
+
|
|
383
|
+
def create_deployment(
|
|
384
|
+
self,
|
|
385
|
+
deployment: Deployment
|
|
386
|
+
) -> Deployment:
|
|
387
|
+
"""Create a new deployment
|
|
388
|
+
|
|
389
|
+
:param deployment: deployment configuration
|
|
390
|
+
:type deployment: Deployment
|
|
391
|
+
:return: created deployment
|
|
392
|
+
:rtype: Deployment
|
|
393
|
+
"""
|
|
394
|
+
response = self.client.post(
|
|
395
|
+
CONTAINER_DEPLOYMENTS_ENDPOINT,
|
|
396
|
+
deployment.to_dict()
|
|
397
|
+
)
|
|
398
|
+
return Deployment.from_dict(response.json(), infer_missing=True)
|
|
399
|
+
|
|
400
|
+
def update_deployment(self, deployment_name: str, deployment: Deployment) -> Deployment:
|
|
401
|
+
"""Update an existing deployment
|
|
402
|
+
|
|
403
|
+
:param deployment_name: name of the deployment to update
|
|
404
|
+
:type deployment_name: str
|
|
405
|
+
:param deployment: updated deployment
|
|
406
|
+
:type deployment: Deployment
|
|
407
|
+
:return: updated deployment
|
|
408
|
+
:rtype: Deployment
|
|
409
|
+
"""
|
|
410
|
+
response = self.client.patch(
|
|
411
|
+
f"{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}",
|
|
412
|
+
deployment.to_dict()
|
|
413
|
+
)
|
|
414
|
+
return Deployment.from_dict(response.json(), infer_missing=True)
|
|
415
|
+
|
|
416
|
+
def delete_deployment(self, deployment_name: str) -> None:
|
|
417
|
+
"""Delete a deployment
|
|
418
|
+
|
|
419
|
+
:param deployment_name: name of the deployment to delete
|
|
420
|
+
:type deployment_name: str
|
|
421
|
+
"""
|
|
422
|
+
self.client.delete(
|
|
423
|
+
f"{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}")
|
|
424
|
+
|
|
425
|
+
def get_deployment_status(self, deployment_name: str) -> ContainerDeploymentStatus:
|
|
426
|
+
"""Get deployment status
|
|
427
|
+
|
|
428
|
+
:param deployment_name: name of the deployment
|
|
429
|
+
:type deployment_name: str
|
|
430
|
+
:return: deployment status
|
|
431
|
+
:rtype: ContainerDeploymentStatus
|
|
432
|
+
"""
|
|
433
|
+
response = self.client.get(
|
|
434
|
+
f"{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/status")
|
|
435
|
+
return ContainerDeploymentStatus(response.json()["status"])
|
|
436
|
+
|
|
437
|
+
def restart_deployment(self, deployment_name: str) -> None:
|
|
438
|
+
"""Restart a deployment
|
|
439
|
+
|
|
440
|
+
:param deployment_name: name of the deployment to restart
|
|
441
|
+
:type deployment_name: str
|
|
442
|
+
"""
|
|
443
|
+
self.client.post(
|
|
444
|
+
f"{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/restart")
|
|
445
|
+
|
|
446
|
+
def get_deployment_scaling_options(self, deployment_name: str) -> ScalingOptions:
|
|
447
|
+
"""Get deployment scaling options
|
|
448
|
+
|
|
449
|
+
:param deployment_name: name of the deployment
|
|
450
|
+
:type deployment_name: str
|
|
451
|
+
:return: scaling options
|
|
452
|
+
:rtype: ScalingOptions
|
|
453
|
+
"""
|
|
454
|
+
response = self.client.get(
|
|
455
|
+
f"{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/scaling")
|
|
456
|
+
return ScalingOptions.from_dict(response.json())
|
|
457
|
+
|
|
458
|
+
def update_deployment_scaling_options(self, deployment_name: str, scaling_options: ScalingOptions) -> ScalingOptions:
|
|
459
|
+
"""Update deployment scaling options
|
|
460
|
+
|
|
461
|
+
:param deployment_name: name of the deployment
|
|
462
|
+
:type deployment_name: str
|
|
463
|
+
:param scaling_options: new scaling options
|
|
464
|
+
:type scaling_options: ScalingOptions
|
|
465
|
+
:return: updated scaling options
|
|
466
|
+
:rtype: ScalingOptions
|
|
467
|
+
"""
|
|
468
|
+
response = self.client.patch(
|
|
469
|
+
f"{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/scaling",
|
|
470
|
+
scaling_options.to_dict()
|
|
471
|
+
)
|
|
472
|
+
return ScalingOptions.from_dict(response.json())
|
|
473
|
+
|
|
474
|
+
def get_deployment_replicas(self, deployment_name: str) -> List[ReplicaInfo]:
|
|
475
|
+
"""Get deployment replicas
|
|
476
|
+
|
|
477
|
+
:param deployment_name: name of the deployment
|
|
478
|
+
:type deployment_name: str
|
|
479
|
+
:return: list of replicas information
|
|
480
|
+
:rtype: List[ReplicaInfo]
|
|
481
|
+
"""
|
|
482
|
+
response = self.client.get(
|
|
483
|
+
f"{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/replicas")
|
|
484
|
+
return [ReplicaInfo.from_dict(replica) for replica in response.json()["list"]]
|
|
485
|
+
|
|
486
|
+
def purge_deployment_queue(self, deployment_name: str) -> None:
|
|
487
|
+
"""Purge deployment queue
|
|
488
|
+
|
|
489
|
+
:param deployment_name: name of the deployment
|
|
490
|
+
:type deployment_name: str
|
|
491
|
+
"""
|
|
492
|
+
self.client.post(
|
|
493
|
+
f"{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/purge-queue")
|
|
494
|
+
|
|
495
|
+
def pause_deployment(self, deployment_name: str) -> None:
|
|
496
|
+
"""Pause a deployment
|
|
497
|
+
|
|
498
|
+
:param deployment_name: name of the deployment to pause
|
|
499
|
+
:type deployment_name: str
|
|
500
|
+
"""
|
|
501
|
+
self.client.post(
|
|
502
|
+
f"{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/pause")
|
|
503
|
+
|
|
504
|
+
def resume_deployment(self, deployment_name: str) -> None:
|
|
505
|
+
"""Resume a deployment
|
|
506
|
+
|
|
507
|
+
:param deployment_name: name of the deployment to resume
|
|
508
|
+
:type deployment_name: str
|
|
509
|
+
"""
|
|
510
|
+
self.client.post(
|
|
511
|
+
f"{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/resume")
|
|
512
|
+
|
|
513
|
+
def get_deployment_environment_variables(self, deployment_name: str) -> Dict[str, List[EnvVar]]:
|
|
514
|
+
"""Get deployment environment variables
|
|
515
|
+
|
|
516
|
+
:param deployment_name: name of the deployment
|
|
517
|
+
:type deployment_name: str
|
|
518
|
+
:return: dictionary mapping container names to their environment variables
|
|
519
|
+
:rtype: Dict[str, List[EnvVar]]
|
|
520
|
+
"""
|
|
521
|
+
response = self.client.get(
|
|
522
|
+
f"{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/environment-variables")
|
|
523
|
+
result = {}
|
|
524
|
+
for item in response.json():
|
|
525
|
+
container_name = item["container_name"]
|
|
526
|
+
env_vars = item["env"]
|
|
527
|
+
result[container_name] = [EnvVar.from_dict(
|
|
528
|
+
env_var) for env_var in env_vars]
|
|
529
|
+
return result
|
|
530
|
+
|
|
531
|
+
def add_deployment_environment_variables(self, deployment_name: str, container_name: str, env_vars: List[EnvVar]) -> Dict[str, List[EnvVar]]:
|
|
532
|
+
"""Add environment variables to a container
|
|
533
|
+
|
|
534
|
+
:param deployment_name: name of the deployment
|
|
535
|
+
:type deployment_name: str
|
|
536
|
+
:param container_name: name of the container
|
|
537
|
+
:type container_name: str
|
|
538
|
+
:param env_vars: environment variables to add
|
|
539
|
+
:type env_vars: List[EnvVar]
|
|
540
|
+
:return: updated environment variables
|
|
541
|
+
:rtype: Dict[str, List[EnvVar]]
|
|
542
|
+
"""
|
|
543
|
+
response = self.client.post(
|
|
544
|
+
f"{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/environment-variables",
|
|
545
|
+
{"container_name": container_name, "env": [
|
|
546
|
+
env_var.to_dict() for env_var in env_vars]}
|
|
547
|
+
)
|
|
548
|
+
result = {}
|
|
549
|
+
for item in response.json():
|
|
550
|
+
container_name = item["container_name"]
|
|
551
|
+
env_vars = item["env"]
|
|
552
|
+
result[container_name] = [EnvVar.from_dict(
|
|
553
|
+
env_var) for env_var in env_vars]
|
|
554
|
+
return result
|
|
555
|
+
|
|
556
|
+
def update_deployment_environment_variables(self, deployment_name: str, container_name: str, env_vars: List[EnvVar]) -> Dict[str, List[EnvVar]]:
|
|
557
|
+
"""Update environment variables of a container
|
|
558
|
+
|
|
559
|
+
:param deployment_name: name of the deployment
|
|
560
|
+
:type deployment_name: str
|
|
561
|
+
:param container_name: name of the container
|
|
562
|
+
:type container_name: str
|
|
563
|
+
:param env_vars: updated environment variables
|
|
564
|
+
:type env_vars: List[EnvVar]
|
|
565
|
+
:return: updated environment variables
|
|
566
|
+
:rtype: Dict[str, List[EnvVar]]
|
|
567
|
+
"""
|
|
568
|
+
response = self.client.patch(
|
|
569
|
+
f"{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/environment-variables",
|
|
570
|
+
{"container_name": container_name, "env": [
|
|
571
|
+
env_var.to_dict() for env_var in env_vars]}
|
|
572
|
+
)
|
|
573
|
+
result = {}
|
|
574
|
+
item = response.json()
|
|
575
|
+
container_name = item["container_name"]
|
|
576
|
+
env_vars = item["env"]
|
|
577
|
+
result[container_name] = [EnvVar.from_dict(
|
|
578
|
+
env_var) for env_var in env_vars]
|
|
579
|
+
return result
|
|
580
|
+
|
|
581
|
+
def delete_deployment_environment_variables(self, deployment_name: str, container_name: str, env_var_names: List[str]) -> Dict[str, List[EnvVar]]:
|
|
582
|
+
"""Delete environment variables from a container
|
|
583
|
+
|
|
584
|
+
:param deployment_name: name of the deployment
|
|
585
|
+
:type deployment_name: str
|
|
586
|
+
:param container_name: name of the container
|
|
587
|
+
:type container_name: str
|
|
588
|
+
:param env_var_names: names of environment variables to delete
|
|
589
|
+
:type env_var_names: List[str]
|
|
590
|
+
:return: remaining environment variables
|
|
591
|
+
:rtype: Dict[str, List[EnvVar]]
|
|
592
|
+
"""
|
|
593
|
+
response = self.client.delete(
|
|
594
|
+
f"{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/environment-variables",
|
|
595
|
+
{"container_name": container_name, "env": env_var_names}
|
|
596
|
+
)
|
|
597
|
+
result = {}
|
|
598
|
+
for item in response.json():
|
|
599
|
+
container_name = item["container_name"]
|
|
600
|
+
env_vars = item["env"]
|
|
601
|
+
result[container_name] = [EnvVar.from_dict(
|
|
602
|
+
env_var) for env_var in env_vars]
|
|
603
|
+
return result
|
|
604
|
+
|
|
605
|
+
def get_compute_resources(self) -> List[ComputeResource]:
|
|
606
|
+
"""Get available compute resources
|
|
607
|
+
|
|
608
|
+
:return: list of compute resources
|
|
609
|
+
:rtype: List[ComputeResource]
|
|
610
|
+
"""
|
|
611
|
+
response = self.client.get(SERVERLESS_COMPUTE_RESOURCES_ENDPOINT)
|
|
612
|
+
resources = []
|
|
613
|
+
for resource_group in response.json():
|
|
614
|
+
for resource in resource_group:
|
|
615
|
+
resources.append(ComputeResource.from_dict(resource))
|
|
616
|
+
return resources
|
|
617
|
+
|
|
618
|
+
def get_secrets(self) -> List[Secret]:
|
|
619
|
+
"""Get all secrets
|
|
620
|
+
|
|
621
|
+
:return: list of secrets
|
|
622
|
+
:rtype: List[Secret]
|
|
623
|
+
"""
|
|
624
|
+
response = self.client.get(SECRETS_ENDPOINT)
|
|
625
|
+
return [Secret.from_dict(secret) for secret in response.json()]
|
|
626
|
+
|
|
627
|
+
def create_secret(self, name: str, value: str) -> None:
|
|
628
|
+
"""Create a new secret
|
|
629
|
+
|
|
630
|
+
:param name: name of the secret
|
|
631
|
+
:type name: str
|
|
632
|
+
:param value: value of the secret
|
|
633
|
+
:type value: str
|
|
634
|
+
"""
|
|
635
|
+
self.client.post(SECRETS_ENDPOINT, {"name": name, "value": value})
|
|
636
|
+
|
|
637
|
+
def delete_secret(self, secret_name: str, force: bool = False) -> None:
|
|
638
|
+
"""Delete a secret
|
|
639
|
+
|
|
640
|
+
:param secret_name: name of the secret to delete
|
|
641
|
+
:type secret_name: str
|
|
642
|
+
:param force: force delete even if secret is in use
|
|
643
|
+
:type force: bool
|
|
644
|
+
"""
|
|
645
|
+
self.client.delete(
|
|
646
|
+
f"{SECRETS_ENDPOINT}/{secret_name}", params={"force": str(force).lower()})
|
|
647
|
+
|
|
648
|
+
def get_registry_credentials(self) -> List[RegistryCredential]:
|
|
649
|
+
"""Get all registry credentials
|
|
650
|
+
|
|
651
|
+
:return: list of registry credentials
|
|
652
|
+
:rtype: List[RegistryCredential]
|
|
653
|
+
"""
|
|
654
|
+
response = self.client.get(CONTAINER_REGISTRY_CREDENTIALS_ENDPOINT)
|
|
655
|
+
return [RegistryCredential.from_dict(credential) for credential in response.json()]
|
|
656
|
+
|
|
657
|
+
def add_registry_credentials(self, credentials: BaseRegistryCredentials) -> None:
|
|
658
|
+
"""Add registry credentials
|
|
659
|
+
|
|
660
|
+
:param credentials: Registry credentials object
|
|
661
|
+
:type credentials: BaseRegistryCredentials
|
|
662
|
+
"""
|
|
663
|
+
data = credentials.to_dict()
|
|
664
|
+
self.client.post(CONTAINER_REGISTRY_CREDENTIALS_ENDPOINT, data)
|
|
665
|
+
|
|
666
|
+
def delete_registry_credentials(self, credentials_name: str) -> None:
|
|
667
|
+
"""Delete registry credentials
|
|
668
|
+
|
|
669
|
+
:param credentials_name: name of the credentials to delete
|
|
670
|
+
:type credentials_name: str
|
|
671
|
+
"""
|
|
672
|
+
self.client.delete(
|
|
673
|
+
f"{CONTAINER_REGISTRY_CREDENTIALS_ENDPOINT}/{credentials_name}")
|