datacrunch 1.16.0__py3-none-any.whl → 1.17.1__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/__init__.py +52 -2
- datacrunch/datacrunch.py +44 -81
- datacrunch-1.17.1.dist-info/METADATA +30 -0
- datacrunch-1.17.1.dist-info/RECORD +5 -0
- {datacrunch-1.16.0.dist-info → datacrunch-1.17.1.dist-info}/WHEEL +1 -1
- datacrunch/InferenceClient/__init__.py +0 -3
- datacrunch/InferenceClient/inference_client.py +0 -514
- datacrunch/_version.py +0 -6
- datacrunch/authentication/__init__.py +0 -0
- datacrunch/authentication/authentication.py +0 -105
- datacrunch/balance/__init__.py +0 -0
- datacrunch/balance/balance.py +0 -50
- datacrunch/constants.py +0 -109
- datacrunch/containers/__init__.py +0 -33
- datacrunch/containers/containers.py +0 -1109
- datacrunch/exceptions.py +0 -29
- datacrunch/helpers.py +0 -18
- datacrunch/http_client/__init__.py +0 -0
- datacrunch/http_client/http_client.py +0 -241
- datacrunch/images/__init__.py +0 -0
- datacrunch/images/images.py +0 -93
- datacrunch/instance_types/__init__.py +0 -0
- datacrunch/instance_types/instance_types.py +0 -195
- datacrunch/instances/__init__.py +0 -0
- datacrunch/instances/instances.py +0 -259
- datacrunch/locations/__init__.py +0 -0
- datacrunch/locations/locations.py +0 -15
- datacrunch/ssh_keys/__init__.py +0 -0
- datacrunch/ssh_keys/ssh_keys.py +0 -111
- datacrunch/startup_scripts/__init__.py +0 -0
- datacrunch/startup_scripts/startup_scripts.py +0 -115
- datacrunch/volume_types/__init__.py +0 -0
- datacrunch/volume_types/volume_types.py +0 -68
- datacrunch/volumes/__init__.py +0 -0
- datacrunch/volumes/volumes.py +0 -385
- datacrunch-1.16.0.dist-info/METADATA +0 -182
- datacrunch-1.16.0.dist-info/RECORD +0 -35
|
@@ -1,1109 +0,0 @@
|
|
|
1
|
-
"""Container deployment and management service for DataCrunch.
|
|
2
|
-
|
|
3
|
-
This module provides functionality for managing container deployments, including
|
|
4
|
-
creation, updates, deletion, and monitoring of containerized applications.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import base64
|
|
8
|
-
import os
|
|
9
|
-
from dataclasses import dataclass, field
|
|
10
|
-
from dataclasses_json import dataclass_json, Undefined # type: ignore
|
|
11
|
-
from typing import List, Optional, Dict, Any, Union
|
|
12
|
-
from enum import Enum
|
|
13
|
-
|
|
14
|
-
from datacrunch.http_client.http_client import HTTPClient
|
|
15
|
-
from datacrunch.InferenceClient import InferenceClient, InferenceResponse
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
# API endpoints
|
|
19
|
-
CONTAINER_DEPLOYMENTS_ENDPOINT = '/container-deployments'
|
|
20
|
-
SERVERLESS_COMPUTE_RESOURCES_ENDPOINT = '/serverless-compute-resources'
|
|
21
|
-
CONTAINER_REGISTRY_CREDENTIALS_ENDPOINT = '/container-registry-credentials'
|
|
22
|
-
SECRETS_ENDPOINT = '/secrets'
|
|
23
|
-
FILESET_SECRETS_ENDPOINT = '/file-secrets'
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class EnvVarType(str, Enum):
|
|
27
|
-
"""Types of environment variables that can be set in containers."""
|
|
28
|
-
|
|
29
|
-
PLAIN = 'plain'
|
|
30
|
-
SECRET = 'secret'
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class SecretType(str, Enum):
|
|
34
|
-
"""Types of secrets that can be set in containers."""
|
|
35
|
-
|
|
36
|
-
GENERIC = 'generic' # Regular secret, can be used in env vars
|
|
37
|
-
FILESET = 'file-secret' # A file secret that can be mounted into the container
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class VolumeMountType(str, Enum):
|
|
41
|
-
"""Types of volume mounts that can be configured for containers."""
|
|
42
|
-
|
|
43
|
-
SCRATCH = 'scratch'
|
|
44
|
-
SECRET = 'secret'
|
|
45
|
-
MEMORY = 'memory'
|
|
46
|
-
SHARED = 'shared'
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
class ContainerRegistryType(str, Enum):
|
|
50
|
-
"""Supported container registry types."""
|
|
51
|
-
|
|
52
|
-
GCR = 'gcr'
|
|
53
|
-
DOCKERHUB = 'dockerhub'
|
|
54
|
-
GITHUB = 'ghcr'
|
|
55
|
-
AWS_ECR = 'aws-ecr'
|
|
56
|
-
CUSTOM = 'custom'
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
class ContainerDeploymentStatus(str, Enum):
|
|
60
|
-
"""Possible states of a container deployment."""
|
|
61
|
-
|
|
62
|
-
INITIALIZING = 'initializing'
|
|
63
|
-
HEALTHY = 'healthy'
|
|
64
|
-
DEGRADED = 'degraded'
|
|
65
|
-
UNHEALTHY = 'unhealthy'
|
|
66
|
-
PAUSED = 'paused'
|
|
67
|
-
QUOTA_REACHED = 'quota_reached'
|
|
68
|
-
IMAGE_PULLING = 'image_pulling'
|
|
69
|
-
VERSION_UPDATING = 'version_updating'
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
@dataclass_json
|
|
73
|
-
@dataclass
|
|
74
|
-
class HealthcheckSettings:
|
|
75
|
-
"""Configuration for container health checking.
|
|
76
|
-
|
|
77
|
-
Attributes:
|
|
78
|
-
enabled: Whether health checking is enabled.
|
|
79
|
-
port: Port number to perform health check on.
|
|
80
|
-
path: HTTP path to perform health check on.
|
|
81
|
-
"""
|
|
82
|
-
|
|
83
|
-
enabled: bool = True
|
|
84
|
-
port: Optional[int] = None
|
|
85
|
-
path: Optional[str] = None
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
@dataclass_json
|
|
89
|
-
@dataclass
|
|
90
|
-
class EntrypointOverridesSettings:
|
|
91
|
-
"""Configuration for overriding container entrypoint and command.
|
|
92
|
-
|
|
93
|
-
Attributes:
|
|
94
|
-
enabled: Whether entrypoint overrides are enabled.
|
|
95
|
-
entrypoint: List of strings forming the entrypoint command.
|
|
96
|
-
cmd: List of strings forming the command arguments.
|
|
97
|
-
"""
|
|
98
|
-
|
|
99
|
-
enabled: bool = True
|
|
100
|
-
entrypoint: Optional[List[str]] = None
|
|
101
|
-
cmd: Optional[List[str]] = None
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
@dataclass_json
|
|
105
|
-
@dataclass
|
|
106
|
-
class EnvVar:
|
|
107
|
-
"""Environment variable configuration for containers.
|
|
108
|
-
|
|
109
|
-
Attributes:
|
|
110
|
-
name: Name of the environment variable.
|
|
111
|
-
value_or_reference_to_secret: Direct value or reference to a secret.
|
|
112
|
-
type: Type of the environment variable.
|
|
113
|
-
"""
|
|
114
|
-
|
|
115
|
-
name: str
|
|
116
|
-
value_or_reference_to_secret: str
|
|
117
|
-
type: EnvVarType
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
121
|
-
@dataclass
|
|
122
|
-
class VolumeMount:
|
|
123
|
-
"""Base class for volume mount configurations.
|
|
124
|
-
|
|
125
|
-
Attributes:
|
|
126
|
-
type: Type of volume mount.
|
|
127
|
-
mount_path: Path where the volume should be mounted in the container.
|
|
128
|
-
size_in_mb: Size of the volume in megabytes. Deprecated: use MemoryMount for memory volumes instead.
|
|
129
|
-
"""
|
|
130
|
-
|
|
131
|
-
type: VolumeMountType
|
|
132
|
-
mount_path: str
|
|
133
|
-
# Deprecated: use MemoryMount for memory volumes instead.
|
|
134
|
-
size_in_mb: Optional[int] = field(default=None, kw_only=True)
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
138
|
-
@dataclass
|
|
139
|
-
class GeneralStorageMount(VolumeMount):
|
|
140
|
-
"""General storage volume mount configuration."""
|
|
141
|
-
|
|
142
|
-
def __init__(self, mount_path: str):
|
|
143
|
-
"""Initialize a general scratch volume mount.
|
|
144
|
-
|
|
145
|
-
Args:
|
|
146
|
-
mount_path: Path where the volume should be mounted in the container.
|
|
147
|
-
"""
|
|
148
|
-
super().__init__(type=VolumeMountType.SCRATCH, mount_path=mount_path)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
152
|
-
@dataclass
|
|
153
|
-
class SecretMount(VolumeMount):
|
|
154
|
-
"""Secret volume mount configuration.
|
|
155
|
-
|
|
156
|
-
A secret volume mount allows mounting secret files into the container.
|
|
157
|
-
|
|
158
|
-
Attributes:
|
|
159
|
-
secret_name: The name of the fileset secret to mount. This secret must be created in advance, for example using `create_fileset_secret_from_file_paths`
|
|
160
|
-
file_names: List of file names that are part of the fileset secret.
|
|
161
|
-
"""
|
|
162
|
-
|
|
163
|
-
secret_name: str
|
|
164
|
-
file_names: Optional[List[str]] = None
|
|
165
|
-
|
|
166
|
-
def __init__(self, mount_path: str, secret_name: str, file_names: Optional[List[str]] = None):
|
|
167
|
-
self.secret_name = secret_name
|
|
168
|
-
self.file_names = file_names
|
|
169
|
-
super().__init__(type=VolumeMountType.SECRET, mount_path=mount_path)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
173
|
-
@dataclass
|
|
174
|
-
class MemoryMount(VolumeMount):
|
|
175
|
-
"""Memory volume mount configuration.
|
|
176
|
-
|
|
177
|
-
A memory volume mount provides high-speed, ephemeral in-memory storage inside your container.
|
|
178
|
-
The mount path is currently hardcoded to /dev/shm and cannot be changed.
|
|
179
|
-
|
|
180
|
-
Attributes:
|
|
181
|
-
size_in_mb: Size of the memory volume in megabytes.
|
|
182
|
-
"""
|
|
183
|
-
|
|
184
|
-
size_in_mb: int
|
|
185
|
-
|
|
186
|
-
def __init__(self, size_in_mb: int):
|
|
187
|
-
super().__init__(type=VolumeMountType.MEMORY, mount_path='/dev/shm')
|
|
188
|
-
self.size_in_mb = size_in_mb
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
192
|
-
@dataclass
|
|
193
|
-
class SharedFileSystemMount(VolumeMount):
|
|
194
|
-
"""Shared filesystem volume mount configuration.
|
|
195
|
-
|
|
196
|
-
A shared filesystem volume mount allows mounting a shared filesystem into the container.
|
|
197
|
-
"""
|
|
198
|
-
|
|
199
|
-
volume_id: str # The ID of the shared filesystem volume to mount, needs to be created first
|
|
200
|
-
|
|
201
|
-
def __init__(self, mount_path: str, volume_id: str):
|
|
202
|
-
super().__init__(type=VolumeMountType.SHARED, mount_path=mount_path)
|
|
203
|
-
self.volume_id = volume_id
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
@dataclass_json
|
|
207
|
-
@dataclass
|
|
208
|
-
class Container:
|
|
209
|
-
"""Container configuration for deployment creation and updates.
|
|
210
|
-
|
|
211
|
-
Attributes:
|
|
212
|
-
image: Container image to use.
|
|
213
|
-
exposed_port: Port to expose from the container.
|
|
214
|
-
name: Name of the container (system-managed, read-only).
|
|
215
|
-
healthcheck: Optional health check configuration.
|
|
216
|
-
entrypoint_overrides: Optional entrypoint override settings.
|
|
217
|
-
env: Optional list of environment variables.
|
|
218
|
-
volume_mounts: Optional list of volume mounts.
|
|
219
|
-
"""
|
|
220
|
-
|
|
221
|
-
image: Union[str, dict]
|
|
222
|
-
exposed_port: int
|
|
223
|
-
name: Optional[str] = None
|
|
224
|
-
healthcheck: Optional[HealthcheckSettings] = None
|
|
225
|
-
entrypoint_overrides: Optional[EntrypointOverridesSettings] = None
|
|
226
|
-
env: Optional[List[EnvVar]] = None
|
|
227
|
-
volume_mounts: Optional[List[VolumeMount]] = None
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
@dataclass_json
|
|
231
|
-
@dataclass
|
|
232
|
-
class ContainerRegistryCredentials:
|
|
233
|
-
"""Credentials for accessing a container registry.
|
|
234
|
-
|
|
235
|
-
Attributes:
|
|
236
|
-
name: Name of the credentials.
|
|
237
|
-
"""
|
|
238
|
-
|
|
239
|
-
name: str
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
@dataclass_json
|
|
243
|
-
@dataclass
|
|
244
|
-
class ContainerRegistrySettings:
|
|
245
|
-
"""Settings for container registry access.
|
|
246
|
-
|
|
247
|
-
Attributes:
|
|
248
|
-
is_private: Whether the registry is private.
|
|
249
|
-
credentials: Optional credentials for accessing private registry.
|
|
250
|
-
"""
|
|
251
|
-
|
|
252
|
-
is_private: bool
|
|
253
|
-
credentials: Optional[ContainerRegistryCredentials] = None
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
@dataclass_json
|
|
257
|
-
@dataclass
|
|
258
|
-
class ComputeResource:
|
|
259
|
-
"""Compute resource configuration.
|
|
260
|
-
|
|
261
|
-
Attributes:
|
|
262
|
-
name: Name of the compute resource.
|
|
263
|
-
size: Size of the compute resource.
|
|
264
|
-
is_available: Whether the compute resource is currently available.
|
|
265
|
-
"""
|
|
266
|
-
|
|
267
|
-
name: str
|
|
268
|
-
size: int
|
|
269
|
-
# Made optional since it's only used in API responses
|
|
270
|
-
is_available: Optional[bool] = None
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
@dataclass_json
|
|
274
|
-
@dataclass
|
|
275
|
-
class ScalingPolicy:
|
|
276
|
-
"""Policy for controlling scaling behavior.
|
|
277
|
-
|
|
278
|
-
Attributes:
|
|
279
|
-
delay_seconds: Number of seconds to wait before applying scaling action.
|
|
280
|
-
"""
|
|
281
|
-
|
|
282
|
-
delay_seconds: int
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
@dataclass_json
|
|
286
|
-
@dataclass
|
|
287
|
-
class QueueLoadScalingTrigger:
|
|
288
|
-
"""Trigger for scaling based on queue load.
|
|
289
|
-
|
|
290
|
-
Attributes:
|
|
291
|
-
threshold: Queue load threshold that triggers scaling.
|
|
292
|
-
"""
|
|
293
|
-
|
|
294
|
-
threshold: float
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
@dataclass_json
|
|
298
|
-
@dataclass
|
|
299
|
-
class UtilizationScalingTrigger:
|
|
300
|
-
"""Trigger for scaling based on resource utilization.
|
|
301
|
-
|
|
302
|
-
Attributes:
|
|
303
|
-
enabled: Whether this trigger is enabled.
|
|
304
|
-
threshold: Utilization threshold that triggers scaling.
|
|
305
|
-
"""
|
|
306
|
-
|
|
307
|
-
enabled: bool
|
|
308
|
-
threshold: Optional[float] = None
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
@dataclass_json
|
|
312
|
-
@dataclass
|
|
313
|
-
class ScalingTriggers:
|
|
314
|
-
"""Collection of triggers that can cause scaling actions.
|
|
315
|
-
|
|
316
|
-
Attributes:
|
|
317
|
-
queue_load: Optional trigger based on queue load.
|
|
318
|
-
cpu_utilization: Optional trigger based on CPU utilization.
|
|
319
|
-
gpu_utilization: Optional trigger based on GPU utilization.
|
|
320
|
-
"""
|
|
321
|
-
|
|
322
|
-
queue_load: Optional[QueueLoadScalingTrigger] = None
|
|
323
|
-
cpu_utilization: Optional[UtilizationScalingTrigger] = None
|
|
324
|
-
gpu_utilization: Optional[UtilizationScalingTrigger] = None
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
@dataclass_json
|
|
328
|
-
@dataclass
|
|
329
|
-
class ScalingOptions:
|
|
330
|
-
"""Configuration for automatic scaling behavior.
|
|
331
|
-
|
|
332
|
-
Attributes:
|
|
333
|
-
min_replica_count: Minimum number of replicas to maintain.
|
|
334
|
-
max_replica_count: Maximum number of replicas allowed.
|
|
335
|
-
scale_down_policy: Policy for scaling down replicas.
|
|
336
|
-
scale_up_policy: Policy for scaling up replicas.
|
|
337
|
-
queue_message_ttl_seconds: Time-to-live for queue messages in seconds.
|
|
338
|
-
concurrent_requests_per_replica: Number of concurrent requests each replica can handle.
|
|
339
|
-
scaling_triggers: Configuration for various scaling triggers.
|
|
340
|
-
"""
|
|
341
|
-
|
|
342
|
-
min_replica_count: int
|
|
343
|
-
max_replica_count: int
|
|
344
|
-
scale_down_policy: ScalingPolicy
|
|
345
|
-
scale_up_policy: ScalingPolicy
|
|
346
|
-
queue_message_ttl_seconds: int
|
|
347
|
-
concurrent_requests_per_replica: int
|
|
348
|
-
scaling_triggers: ScalingTriggers
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
352
|
-
@dataclass
|
|
353
|
-
class Deployment:
|
|
354
|
-
"""Configuration for creating or updating a container deployment.
|
|
355
|
-
|
|
356
|
-
Attributes:
|
|
357
|
-
name: Name of the deployment.
|
|
358
|
-
container_registry_settings: Settings for accessing container registry.
|
|
359
|
-
containers: List of container specifications in the deployment.
|
|
360
|
-
compute: Compute resource configuration.
|
|
361
|
-
is_spot: Whether is spot deployment.
|
|
362
|
-
endpoint_base_url: Optional base URL for the deployment endpoint.
|
|
363
|
-
scaling: Optional scaling configuration.
|
|
364
|
-
created_at: Optional timestamp when the deployment was created.
|
|
365
|
-
"""
|
|
366
|
-
|
|
367
|
-
name: str
|
|
368
|
-
containers: List[Container]
|
|
369
|
-
compute: ComputeResource
|
|
370
|
-
container_registry_settings: ContainerRegistrySettings = field(
|
|
371
|
-
default_factory=lambda: ContainerRegistrySettings(is_private=False)
|
|
372
|
-
)
|
|
373
|
-
is_spot: bool = False
|
|
374
|
-
endpoint_base_url: Optional[str] = None
|
|
375
|
-
scaling: Optional[ScalingOptions] = None
|
|
376
|
-
created_at: Optional[str] = None
|
|
377
|
-
|
|
378
|
-
_inference_client: Optional[InferenceClient] = None
|
|
379
|
-
|
|
380
|
-
def __str__(self):
|
|
381
|
-
"""Returns a string representation of the deployment, excluding sensitive information.
|
|
382
|
-
|
|
383
|
-
Returns:
|
|
384
|
-
str: A formatted string representation of the deployment.
|
|
385
|
-
"""
|
|
386
|
-
# Get all attributes except _inference_client
|
|
387
|
-
attrs = {k: v for k, v in self.__dict__.items() if k != '_inference_client'}
|
|
388
|
-
# Format each attribute
|
|
389
|
-
attr_strs = [f'{k}={repr(v)}' for k, v in attrs.items()]
|
|
390
|
-
return f'Deployment({", ".join(attr_strs)})'
|
|
391
|
-
|
|
392
|
-
def __repr__(self):
|
|
393
|
-
"""Returns a repr representation of the deployment, excluding sensitive information.
|
|
394
|
-
|
|
395
|
-
Returns:
|
|
396
|
-
str: A formatted string representation of the deployment.
|
|
397
|
-
"""
|
|
398
|
-
return self.__str__()
|
|
399
|
-
|
|
400
|
-
@classmethod
|
|
401
|
-
def from_dict_with_inference_key(
|
|
402
|
-
cls, data: Dict[str, Any], inference_key: str = None
|
|
403
|
-
) -> 'Deployment':
|
|
404
|
-
"""Creates a Deployment instance from a dictionary with an inference key.
|
|
405
|
-
|
|
406
|
-
Args:
|
|
407
|
-
data: Dictionary containing deployment data.
|
|
408
|
-
inference_key: Inference key to set on the deployment.
|
|
409
|
-
|
|
410
|
-
Returns:
|
|
411
|
-
Deployment: A new Deployment instance with the inference client initialized.
|
|
412
|
-
"""
|
|
413
|
-
deployment = Deployment.from_dict(data, infer_missing=True)
|
|
414
|
-
if inference_key and deployment.endpoint_base_url:
|
|
415
|
-
deployment._inference_client = InferenceClient(
|
|
416
|
-
inference_key=inference_key,
|
|
417
|
-
endpoint_base_url=deployment.endpoint_base_url,
|
|
418
|
-
)
|
|
419
|
-
return deployment
|
|
420
|
-
|
|
421
|
-
def set_inference_client(self, inference_key: str) -> None:
|
|
422
|
-
"""Sets the inference client for this deployment.
|
|
423
|
-
|
|
424
|
-
Args:
|
|
425
|
-
inference_key: The inference key to use for authentication.
|
|
426
|
-
|
|
427
|
-
Raises:
|
|
428
|
-
ValueError: If endpoint_base_url is not set.
|
|
429
|
-
"""
|
|
430
|
-
if self.endpoint_base_url is None:
|
|
431
|
-
raise ValueError('Endpoint base URL must be set to use inference client')
|
|
432
|
-
self._inference_client = InferenceClient(
|
|
433
|
-
inference_key=inference_key, endpoint_base_url=self.endpoint_base_url
|
|
434
|
-
)
|
|
435
|
-
|
|
436
|
-
def _validate_inference_client(self) -> None:
|
|
437
|
-
"""Validates that the inference client is initialized.
|
|
438
|
-
|
|
439
|
-
Raises:
|
|
440
|
-
ValueError: If inference client is not initialized.
|
|
441
|
-
"""
|
|
442
|
-
if self._inference_client is None:
|
|
443
|
-
raise ValueError(
|
|
444
|
-
'Inference client not initialized. Use from_dict_with_inference_key or set_inference_client to initialize inference capabilities.'
|
|
445
|
-
)
|
|
446
|
-
|
|
447
|
-
def run_sync(
|
|
448
|
-
self,
|
|
449
|
-
data: Dict[str, Any],
|
|
450
|
-
path: str = '',
|
|
451
|
-
timeout_seconds: int = 60 * 5,
|
|
452
|
-
headers: Optional[Dict[str, str]] = None,
|
|
453
|
-
http_method: str = 'POST',
|
|
454
|
-
stream: bool = False,
|
|
455
|
-
) -> InferenceResponse:
|
|
456
|
-
"""Runs a synchronous inference request.
|
|
457
|
-
|
|
458
|
-
Args:
|
|
459
|
-
data: The data to send in the request.
|
|
460
|
-
path: The endpoint path to send the request to.
|
|
461
|
-
timeout_seconds: Maximum time to wait for the response.
|
|
462
|
-
headers: Optional headers to include in the request.
|
|
463
|
-
http_method: The HTTP method to use for the request.
|
|
464
|
-
stream: Whether to stream the response.
|
|
465
|
-
|
|
466
|
-
Returns:
|
|
467
|
-
InferenceResponse: The response from the inference request.
|
|
468
|
-
|
|
469
|
-
Raises:
|
|
470
|
-
ValueError: If the inference client is not initialized.
|
|
471
|
-
"""
|
|
472
|
-
self._validate_inference_client()
|
|
473
|
-
return self._inference_client.run_sync(
|
|
474
|
-
data, path, timeout_seconds, headers, http_method, stream
|
|
475
|
-
)
|
|
476
|
-
|
|
477
|
-
def run(
|
|
478
|
-
self,
|
|
479
|
-
data: Dict[str, Any],
|
|
480
|
-
path: str = '',
|
|
481
|
-
timeout_seconds: int = 60 * 5,
|
|
482
|
-
headers: Optional[Dict[str, str]] = None,
|
|
483
|
-
http_method: str = 'POST',
|
|
484
|
-
stream: bool = False,
|
|
485
|
-
):
|
|
486
|
-
"""Runs an asynchronous inference request.
|
|
487
|
-
|
|
488
|
-
Args:
|
|
489
|
-
data: The data to send in the request.
|
|
490
|
-
path: The endpoint path to send the request to.
|
|
491
|
-
timeout_seconds: Maximum time to wait for the response.
|
|
492
|
-
headers: Optional headers to include in the request.
|
|
493
|
-
http_method: The HTTP method to use for the request.
|
|
494
|
-
stream: Whether to stream the response.
|
|
495
|
-
|
|
496
|
-
Returns:
|
|
497
|
-
The response from the inference request.
|
|
498
|
-
|
|
499
|
-
Raises:
|
|
500
|
-
ValueError: If the inference client is not initialized.
|
|
501
|
-
"""
|
|
502
|
-
self._validate_inference_client()
|
|
503
|
-
return self._inference_client.run(data, path, timeout_seconds, headers, http_method, stream)
|
|
504
|
-
|
|
505
|
-
def health(self):
|
|
506
|
-
"""Checks the health of the deployed application.
|
|
507
|
-
|
|
508
|
-
Returns:
|
|
509
|
-
The health check response.
|
|
510
|
-
|
|
511
|
-
Raises:
|
|
512
|
-
ValueError: If the inference client is not initialized.
|
|
513
|
-
"""
|
|
514
|
-
self._validate_inference_client()
|
|
515
|
-
# build healthcheck path
|
|
516
|
-
healthcheck_path = '/health'
|
|
517
|
-
if (
|
|
518
|
-
self.containers
|
|
519
|
-
and self.containers[0].healthcheck
|
|
520
|
-
and self.containers[0].healthcheck.path
|
|
521
|
-
):
|
|
522
|
-
healthcheck_path = self.containers[0].healthcheck.path
|
|
523
|
-
|
|
524
|
-
return self._inference_client.health(healthcheck_path)
|
|
525
|
-
|
|
526
|
-
# Function alias
|
|
527
|
-
healthcheck = health
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
@dataclass_json
|
|
531
|
-
@dataclass
|
|
532
|
-
class ReplicaInfo:
|
|
533
|
-
"""Information about a deployment replica.
|
|
534
|
-
|
|
535
|
-
Attributes:
|
|
536
|
-
id: Unique identifier of the replica.
|
|
537
|
-
status: Current status of the replica.
|
|
538
|
-
started_at: Timestamp when the replica was started.
|
|
539
|
-
"""
|
|
540
|
-
|
|
541
|
-
id: str
|
|
542
|
-
status: str
|
|
543
|
-
started_at: str
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
@dataclass_json
|
|
547
|
-
@dataclass
|
|
548
|
-
class Secret:
|
|
549
|
-
"""A secret model class.
|
|
550
|
-
|
|
551
|
-
Attributes:
|
|
552
|
-
name: Name of the secret.
|
|
553
|
-
created_at: Timestamp when the secret was created.
|
|
554
|
-
secret_type: Type of the secret.
|
|
555
|
-
"""
|
|
556
|
-
|
|
557
|
-
name: str
|
|
558
|
-
created_at: str
|
|
559
|
-
secret_type: SecretType
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
@dataclass_json
|
|
563
|
-
@dataclass
|
|
564
|
-
class RegistryCredential:
|
|
565
|
-
"""A container registry credential model class.
|
|
566
|
-
|
|
567
|
-
Attributes:
|
|
568
|
-
name: Name of the registry credential.
|
|
569
|
-
created_at: Timestamp when the credential was created.
|
|
570
|
-
"""
|
|
571
|
-
|
|
572
|
-
name: str
|
|
573
|
-
created_at: str
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
@dataclass_json
|
|
577
|
-
@dataclass
|
|
578
|
-
class BaseRegistryCredentials:
|
|
579
|
-
"""Base class for registry credentials.
|
|
580
|
-
|
|
581
|
-
Attributes:
|
|
582
|
-
name: Name of the registry credential.
|
|
583
|
-
type: Type of the container registry.
|
|
584
|
-
"""
|
|
585
|
-
|
|
586
|
-
name: str
|
|
587
|
-
type: ContainerRegistryType
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
@dataclass_json
|
|
591
|
-
@dataclass
|
|
592
|
-
class DockerHubCredentials(BaseRegistryCredentials):
|
|
593
|
-
"""Credentials for DockerHub registry.
|
|
594
|
-
|
|
595
|
-
Attributes:
|
|
596
|
-
username: DockerHub username.
|
|
597
|
-
access_token: DockerHub access token.
|
|
598
|
-
"""
|
|
599
|
-
|
|
600
|
-
username: str
|
|
601
|
-
access_token: str
|
|
602
|
-
|
|
603
|
-
def __init__(self, name: str, username: str, access_token: str):
|
|
604
|
-
"""Initializes DockerHub credentials.
|
|
605
|
-
|
|
606
|
-
Args:
|
|
607
|
-
name: Name of the credentials.
|
|
608
|
-
username: DockerHub username.
|
|
609
|
-
access_token: DockerHub access token.
|
|
610
|
-
"""
|
|
611
|
-
super().__init__(name=name, type=ContainerRegistryType.DOCKERHUB)
|
|
612
|
-
self.username = username
|
|
613
|
-
self.access_token = access_token
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
@dataclass_json
|
|
617
|
-
@dataclass
|
|
618
|
-
class GithubCredentials(BaseRegistryCredentials):
|
|
619
|
-
"""Credentials for GitHub Container Registry.
|
|
620
|
-
|
|
621
|
-
Attributes:
|
|
622
|
-
username: GitHub username.
|
|
623
|
-
access_token: GitHub access token.
|
|
624
|
-
"""
|
|
625
|
-
|
|
626
|
-
username: str
|
|
627
|
-
access_token: str
|
|
628
|
-
|
|
629
|
-
def __init__(self, name: str, username: str, access_token: str):
|
|
630
|
-
"""Initializes GitHub credentials.
|
|
631
|
-
|
|
632
|
-
Args:
|
|
633
|
-
name: Name of the credentials.
|
|
634
|
-
username: GitHub username.
|
|
635
|
-
access_token: GitHub access token.
|
|
636
|
-
"""
|
|
637
|
-
super().__init__(name=name, type=ContainerRegistryType.GITHUB)
|
|
638
|
-
self.username = username
|
|
639
|
-
self.access_token = access_token
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
@dataclass_json
|
|
643
|
-
@dataclass
|
|
644
|
-
class GCRCredentials(BaseRegistryCredentials):
|
|
645
|
-
"""Credentials for Google Container Registry.
|
|
646
|
-
|
|
647
|
-
Attributes:
|
|
648
|
-
service_account_key: GCP service account key JSON.
|
|
649
|
-
"""
|
|
650
|
-
|
|
651
|
-
service_account_key: str
|
|
652
|
-
|
|
653
|
-
def __init__(self, name: str, service_account_key: str):
|
|
654
|
-
"""Initializes GCR credentials.
|
|
655
|
-
|
|
656
|
-
Args:
|
|
657
|
-
name: Name of the credentials.
|
|
658
|
-
service_account_key: GCP service account key JSON.
|
|
659
|
-
"""
|
|
660
|
-
super().__init__(name=name, type=ContainerRegistryType.GCR)
|
|
661
|
-
self.service_account_key = service_account_key
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
@dataclass_json
|
|
665
|
-
@dataclass
|
|
666
|
-
class AWSECRCredentials(BaseRegistryCredentials):
|
|
667
|
-
"""Credentials for AWS Elastic Container Registry.
|
|
668
|
-
|
|
669
|
-
Attributes:
|
|
670
|
-
access_key_id: AWS access key ID.
|
|
671
|
-
secret_access_key: AWS secret access key.
|
|
672
|
-
region: AWS region.
|
|
673
|
-
ecr_repo: ECR repository name.
|
|
674
|
-
"""
|
|
675
|
-
|
|
676
|
-
access_key_id: str
|
|
677
|
-
secret_access_key: str
|
|
678
|
-
region: str
|
|
679
|
-
ecr_repo: str
|
|
680
|
-
|
|
681
|
-
def __init__(
|
|
682
|
-
self,
|
|
683
|
-
name: str,
|
|
684
|
-
access_key_id: str,
|
|
685
|
-
secret_access_key: str,
|
|
686
|
-
region: str,
|
|
687
|
-
ecr_repo: str,
|
|
688
|
-
):
|
|
689
|
-
"""Initializes AWS ECR credentials.
|
|
690
|
-
|
|
691
|
-
Args:
|
|
692
|
-
name: Name of the credentials.
|
|
693
|
-
access_key_id: AWS access key ID.
|
|
694
|
-
secret_access_key: AWS secret access key.
|
|
695
|
-
region: AWS region.
|
|
696
|
-
ecr_repo: ECR repository name.
|
|
697
|
-
"""
|
|
698
|
-
super().__init__(name=name, type=ContainerRegistryType.AWS_ECR)
|
|
699
|
-
self.access_key_id = access_key_id
|
|
700
|
-
self.secret_access_key = secret_access_key
|
|
701
|
-
self.region = region
|
|
702
|
-
self.ecr_repo = ecr_repo
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
@dataclass_json
|
|
706
|
-
@dataclass
|
|
707
|
-
class CustomRegistryCredentials(BaseRegistryCredentials):
|
|
708
|
-
"""Credentials for custom container registries.
|
|
709
|
-
|
|
710
|
-
Attributes:
|
|
711
|
-
docker_config_json: Docker config JSON containing registry credentials.
|
|
712
|
-
"""
|
|
713
|
-
|
|
714
|
-
docker_config_json: str
|
|
715
|
-
|
|
716
|
-
def __init__(self, name: str, docker_config_json: str):
|
|
717
|
-
"""Initializes custom registry credentials.
|
|
718
|
-
|
|
719
|
-
Args:
|
|
720
|
-
name: Name of the credentials.
|
|
721
|
-
docker_config_json: Docker config JSON containing registry credentials.
|
|
722
|
-
"""
|
|
723
|
-
super().__init__(name=name, type=ContainerRegistryType.CUSTOM)
|
|
724
|
-
self.docker_config_json = docker_config_json
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
class ContainersService:
|
|
728
|
-
"""Service for managing container deployments.
|
|
729
|
-
|
|
730
|
-
This class provides methods for interacting with the DataCrunch container
|
|
731
|
-
deployment API, including CRUD operations for deployments and related resources.
|
|
732
|
-
"""
|
|
733
|
-
|
|
734
|
-
def __init__(self, http_client: HTTPClient, inference_key: str = None) -> None:
|
|
735
|
-
"""Initializes the containers service.
|
|
736
|
-
|
|
737
|
-
Args:
|
|
738
|
-
http_client: HTTP client for making API requests.
|
|
739
|
-
inference_key: Optional inference key for authenticating inference requests.
|
|
740
|
-
"""
|
|
741
|
-
self.client = http_client
|
|
742
|
-
self._inference_key = inference_key
|
|
743
|
-
|
|
744
|
-
def get_deployments(self) -> List[Deployment]:
|
|
745
|
-
"""Retrieves all container deployments.
|
|
746
|
-
|
|
747
|
-
Returns:
|
|
748
|
-
List[Deployment]: List of all deployments.
|
|
749
|
-
"""
|
|
750
|
-
response = self.client.get(CONTAINER_DEPLOYMENTS_ENDPOINT)
|
|
751
|
-
return [
|
|
752
|
-
Deployment.from_dict_with_inference_key(deployment, self._inference_key)
|
|
753
|
-
for deployment in response.json()
|
|
754
|
-
]
|
|
755
|
-
|
|
756
|
-
def get_deployment_by_name(self, deployment_name: str) -> Deployment:
|
|
757
|
-
"""Retrieves a specific deployment by name.
|
|
758
|
-
|
|
759
|
-
Args:
|
|
760
|
-
deployment_name: Name of the deployment to retrieve.
|
|
761
|
-
|
|
762
|
-
Returns:
|
|
763
|
-
Deployment: The requested deployment.
|
|
764
|
-
"""
|
|
765
|
-
response = self.client.get(f'{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}')
|
|
766
|
-
return Deployment.from_dict_with_inference_key(response.json(), self._inference_key)
|
|
767
|
-
|
|
768
|
-
# Function alias
|
|
769
|
-
get_deployment = get_deployment_by_name
|
|
770
|
-
|
|
771
|
-
def create_deployment(self, deployment: Deployment) -> Deployment:
|
|
772
|
-
"""Creates a new container deployment.
|
|
773
|
-
|
|
774
|
-
Args:
|
|
775
|
-
deployment: Deployment configuration to create.
|
|
776
|
-
|
|
777
|
-
Returns:
|
|
778
|
-
Deployment: The created deployment.
|
|
779
|
-
"""
|
|
780
|
-
response = self.client.post(CONTAINER_DEPLOYMENTS_ENDPOINT, deployment.to_dict())
|
|
781
|
-
return Deployment.from_dict_with_inference_key(response.json(), self._inference_key)
|
|
782
|
-
|
|
783
|
-
def update_deployment(self, deployment_name: str, deployment: Deployment) -> Deployment:
|
|
784
|
-
"""Updates an existing deployment.
|
|
785
|
-
|
|
786
|
-
Args:
|
|
787
|
-
deployment_name: Name of the deployment to update.
|
|
788
|
-
deployment: Updated deployment configuration.
|
|
789
|
-
|
|
790
|
-
Returns:
|
|
791
|
-
Deployment: The updated deployment.
|
|
792
|
-
"""
|
|
793
|
-
response = self.client.patch(
|
|
794
|
-
f'{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}', deployment.to_dict()
|
|
795
|
-
)
|
|
796
|
-
return Deployment.from_dict_with_inference_key(response.json(), self._inference_key)
|
|
797
|
-
|
|
798
|
-
def delete_deployment(self, deployment_name: str) -> None:
|
|
799
|
-
"""Deletes a deployment.
|
|
800
|
-
|
|
801
|
-
Args:
|
|
802
|
-
deployment_name: Name of the deployment to delete.
|
|
803
|
-
"""
|
|
804
|
-
self.client.delete(f'{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}')
|
|
805
|
-
|
|
806
|
-
def get_deployment_status(self, deployment_name: str) -> ContainerDeploymentStatus:
|
|
807
|
-
"""Retrieves the current status of a deployment.
|
|
808
|
-
|
|
809
|
-
Args:
|
|
810
|
-
deployment_name: Name of the deployment.
|
|
811
|
-
|
|
812
|
-
Returns:
|
|
813
|
-
ContainerDeploymentStatus: Current status of the deployment.
|
|
814
|
-
"""
|
|
815
|
-
response = self.client.get(f'{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/status')
|
|
816
|
-
return ContainerDeploymentStatus(response.json()['status'])
|
|
817
|
-
|
|
818
|
-
def restart_deployment(self, deployment_name: str) -> None:
|
|
819
|
-
"""Restarts a deployment.
|
|
820
|
-
|
|
821
|
-
Args:
|
|
822
|
-
deployment_name: Name of the deployment to restart.
|
|
823
|
-
"""
|
|
824
|
-
self.client.post(f'{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/restart')
|
|
825
|
-
|
|
826
|
-
def get_deployment_scaling_options(self, deployment_name: str) -> ScalingOptions:
|
|
827
|
-
"""Retrieves the scaling options for a deployment.
|
|
828
|
-
|
|
829
|
-
Args:
|
|
830
|
-
deployment_name: Name of the deployment.
|
|
831
|
-
|
|
832
|
-
Returns:
|
|
833
|
-
ScalingOptions: Current scaling options for the deployment.
|
|
834
|
-
"""
|
|
835
|
-
response = self.client.get(f'{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/scaling')
|
|
836
|
-
return ScalingOptions.from_dict(response.json())
|
|
837
|
-
|
|
838
|
-
def update_deployment_scaling_options(
|
|
839
|
-
self, deployment_name: str, scaling_options: ScalingOptions
|
|
840
|
-
) -> ScalingOptions:
|
|
841
|
-
"""Updates the scaling options for a deployment.
|
|
842
|
-
|
|
843
|
-
Args:
|
|
844
|
-
deployment_name: Name of the deployment.
|
|
845
|
-
scaling_options: New scaling options to apply.
|
|
846
|
-
|
|
847
|
-
Returns:
|
|
848
|
-
ScalingOptions: Updated scaling options for the deployment.
|
|
849
|
-
"""
|
|
850
|
-
response = self.client.patch(
|
|
851
|
-
f'{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/scaling',
|
|
852
|
-
scaling_options.to_dict(),
|
|
853
|
-
)
|
|
854
|
-
return ScalingOptions.from_dict(response.json())
|
|
855
|
-
|
|
856
|
-
def get_deployment_replicas(self, deployment_name: str) -> List[ReplicaInfo]:
|
|
857
|
-
"""Retrieves information about deployment replicas.
|
|
858
|
-
|
|
859
|
-
Args:
|
|
860
|
-
deployment_name: Name of the deployment.
|
|
861
|
-
|
|
862
|
-
Returns:
|
|
863
|
-
List[ReplicaInfo]: List of replica information.
|
|
864
|
-
"""
|
|
865
|
-
response = self.client.get(f'{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/replicas')
|
|
866
|
-
return [ReplicaInfo.from_dict(replica) for replica in response.json()['list']]
|
|
867
|
-
|
|
868
|
-
def purge_deployment_queue(self, deployment_name: str) -> None:
|
|
869
|
-
"""Purges the deployment queue.
|
|
870
|
-
|
|
871
|
-
Args:
|
|
872
|
-
deployment_name: Name of the deployment.
|
|
873
|
-
"""
|
|
874
|
-
self.client.post(f'{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/purge-queue')
|
|
875
|
-
|
|
876
|
-
def pause_deployment(self, deployment_name: str) -> None:
|
|
877
|
-
"""Pauses a deployment.
|
|
878
|
-
|
|
879
|
-
Args:
|
|
880
|
-
deployment_name: Name of the deployment to pause.
|
|
881
|
-
"""
|
|
882
|
-
self.client.post(f'{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/pause')
|
|
883
|
-
|
|
884
|
-
def resume_deployment(self, deployment_name: str) -> None:
|
|
885
|
-
"""Resumes a paused deployment.
|
|
886
|
-
|
|
887
|
-
Args:
|
|
888
|
-
deployment_name: Name of the deployment to resume.
|
|
889
|
-
"""
|
|
890
|
-
self.client.post(f'{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/resume')
|
|
891
|
-
|
|
892
|
-
def get_deployment_environment_variables(self, deployment_name: str) -> Dict[str, List[EnvVar]]:
|
|
893
|
-
"""Retrieves environment variables for a deployment.
|
|
894
|
-
|
|
895
|
-
Args:
|
|
896
|
-
deployment_name: Name of the deployment.
|
|
897
|
-
|
|
898
|
-
Returns:
|
|
899
|
-
Dict[str, List[EnvVar]]: Dictionary mapping container names to their environment variables.
|
|
900
|
-
"""
|
|
901
|
-
response = self.client.get(
|
|
902
|
-
f'{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/environment-variables'
|
|
903
|
-
)
|
|
904
|
-
result = {}
|
|
905
|
-
for item in response.json():
|
|
906
|
-
container_name = item['container_name']
|
|
907
|
-
env_vars = item['env']
|
|
908
|
-
result[container_name] = [EnvVar.from_dict(env_var) for env_var in env_vars]
|
|
909
|
-
return result
|
|
910
|
-
|
|
911
|
-
def add_deployment_environment_variables(
|
|
912
|
-
self, deployment_name: str, container_name: str, env_vars: List[EnvVar]
|
|
913
|
-
) -> Dict[str, List[EnvVar]]:
|
|
914
|
-
"""Adds environment variables to a container in a deployment.
|
|
915
|
-
|
|
916
|
-
Args:
|
|
917
|
-
deployment_name: Name of the deployment.
|
|
918
|
-
container_name: Name of the container.
|
|
919
|
-
env_vars: List of environment variables to add.
|
|
920
|
-
|
|
921
|
-
Returns:
|
|
922
|
-
Dict[str, List[EnvVar]]: Updated environment variables for all containers.
|
|
923
|
-
"""
|
|
924
|
-
response = self.client.post(
|
|
925
|
-
f'{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/environment-variables',
|
|
926
|
-
{
|
|
927
|
-
'container_name': container_name,
|
|
928
|
-
'env': [env_var.to_dict() for env_var in env_vars],
|
|
929
|
-
},
|
|
930
|
-
)
|
|
931
|
-
result = {}
|
|
932
|
-
for item in response.json():
|
|
933
|
-
container_name = item['container_name']
|
|
934
|
-
env_vars = item['env']
|
|
935
|
-
result[container_name] = [EnvVar.from_dict(env_var) for env_var in env_vars]
|
|
936
|
-
return result
|
|
937
|
-
|
|
938
|
-
def update_deployment_environment_variables(
|
|
939
|
-
self, deployment_name: str, container_name: str, env_vars: List[EnvVar]
|
|
940
|
-
) -> Dict[str, List[EnvVar]]:
|
|
941
|
-
"""Updates environment variables for a container in a deployment.
|
|
942
|
-
|
|
943
|
-
Args:
|
|
944
|
-
deployment_name: Name of the deployment.
|
|
945
|
-
container_name: Name of the container.
|
|
946
|
-
env_vars: List of updated environment variables.
|
|
947
|
-
|
|
948
|
-
Returns:
|
|
949
|
-
Dict[str, List[EnvVar]]: Updated environment variables for all containers.
|
|
950
|
-
"""
|
|
951
|
-
response = self.client.patch(
|
|
952
|
-
f'{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/environment-variables',
|
|
953
|
-
{
|
|
954
|
-
'container_name': container_name,
|
|
955
|
-
'env': [env_var.to_dict() for env_var in env_vars],
|
|
956
|
-
},
|
|
957
|
-
)
|
|
958
|
-
result = {}
|
|
959
|
-
item = response.json()
|
|
960
|
-
container_name = item['container_name']
|
|
961
|
-
env_vars = item['env']
|
|
962
|
-
result[container_name] = [EnvVar.from_dict(env_var) for env_var in env_vars]
|
|
963
|
-
return result
|
|
964
|
-
|
|
965
|
-
def delete_deployment_environment_variables(
|
|
966
|
-
self, deployment_name: str, container_name: str, env_var_names: List[str]
|
|
967
|
-
) -> Dict[str, List[EnvVar]]:
|
|
968
|
-
"""Deletes environment variables from a container in a deployment.
|
|
969
|
-
|
|
970
|
-
Args:
|
|
971
|
-
deployment_name: Name of the deployment.
|
|
972
|
-
container_name: Name of the container.
|
|
973
|
-
env_var_names: List of environment variable names to delete.
|
|
974
|
-
|
|
975
|
-
Returns:
|
|
976
|
-
Dict[str, List[EnvVar]]: Updated environment variables for all containers.
|
|
977
|
-
"""
|
|
978
|
-
response = self.client.delete(
|
|
979
|
-
f'{CONTAINER_DEPLOYMENTS_ENDPOINT}/{deployment_name}/environment-variables',
|
|
980
|
-
{'container_name': container_name, 'env': env_var_names},
|
|
981
|
-
)
|
|
982
|
-
result = {}
|
|
983
|
-
for item in response.json():
|
|
984
|
-
container_name = item['container_name']
|
|
985
|
-
env_vars = item['env']
|
|
986
|
-
result[container_name] = [EnvVar.from_dict(env_var) for env_var in env_vars]
|
|
987
|
-
return result
|
|
988
|
-
|
|
989
|
-
def get_compute_resources(
|
|
990
|
-
self, size: int = None, is_available: bool = None
|
|
991
|
-
) -> List[ComputeResource]:
|
|
992
|
-
"""Retrieves compute resources, optionally filtered by size and availability.
|
|
993
|
-
|
|
994
|
-
Args:
|
|
995
|
-
size: Optional size to filter resources by (e.g. 8 for 8x GPUs)
|
|
996
|
-
available: Optional boolean to filter by availability status
|
|
997
|
-
|
|
998
|
-
Returns:
|
|
999
|
-
List[ComputeResource]: List of compute resources matching the filters.
|
|
1000
|
-
If no filters provided, returns all resources.
|
|
1001
|
-
"""
|
|
1002
|
-
response = self.client.get(SERVERLESS_COMPUTE_RESOURCES_ENDPOINT)
|
|
1003
|
-
resources = []
|
|
1004
|
-
for resource_group in response.json():
|
|
1005
|
-
for resource in resource_group:
|
|
1006
|
-
resources.append(ComputeResource.from_dict(resource))
|
|
1007
|
-
if size:
|
|
1008
|
-
resources = [r for r in resources if r.size == size]
|
|
1009
|
-
if is_available:
|
|
1010
|
-
resources = [r for r in resources if r.is_available == is_available]
|
|
1011
|
-
return resources
|
|
1012
|
-
|
|
1013
|
-
# Function alias
|
|
1014
|
-
get_gpus = get_compute_resources
|
|
1015
|
-
|
|
1016
|
-
def get_secrets(self) -> List[Secret]:
|
|
1017
|
-
"""Retrieves all secrets.
|
|
1018
|
-
|
|
1019
|
-
Returns:
|
|
1020
|
-
List[Secret]: List of all secrets.
|
|
1021
|
-
"""
|
|
1022
|
-
response = self.client.get(SECRETS_ENDPOINT)
|
|
1023
|
-
return [Secret.from_dict(secret) for secret in response.json()]
|
|
1024
|
-
|
|
1025
|
-
def create_secret(self, name: str, value: str) -> None:
|
|
1026
|
-
"""Creates a new secret.
|
|
1027
|
-
|
|
1028
|
-
Args:
|
|
1029
|
-
name: Name of the secret.
|
|
1030
|
-
value: Value of the secret.
|
|
1031
|
-
"""
|
|
1032
|
-
self.client.post(SECRETS_ENDPOINT, {'name': name, 'value': value})
|
|
1033
|
-
|
|
1034
|
-
def delete_secret(self, secret_name: str, force: bool = False) -> None:
|
|
1035
|
-
"""Deletes a secret.
|
|
1036
|
-
|
|
1037
|
-
Args:
|
|
1038
|
-
secret_name: Name of the secret to delete.
|
|
1039
|
-
force: Whether to force delete even if secret is in use.
|
|
1040
|
-
"""
|
|
1041
|
-
self.client.delete(
|
|
1042
|
-
f'{SECRETS_ENDPOINT}/{secret_name}', params={'force': str(force).lower()}
|
|
1043
|
-
)
|
|
1044
|
-
|
|
1045
|
-
def get_registry_credentials(self) -> List[RegistryCredential]:
|
|
1046
|
-
"""Retrieves all registry credentials.
|
|
1047
|
-
|
|
1048
|
-
Returns:
|
|
1049
|
-
List[RegistryCredential]: List of all registry credentials.
|
|
1050
|
-
"""
|
|
1051
|
-
response = self.client.get(CONTAINER_REGISTRY_CREDENTIALS_ENDPOINT)
|
|
1052
|
-
return [RegistryCredential.from_dict(credential) for credential in response.json()]
|
|
1053
|
-
|
|
1054
|
-
def add_registry_credentials(self, credentials: BaseRegistryCredentials) -> None:
|
|
1055
|
-
"""Adds new registry credentials.
|
|
1056
|
-
|
|
1057
|
-
Args:
|
|
1058
|
-
credentials: Registry credentials to add.
|
|
1059
|
-
"""
|
|
1060
|
-
data = credentials.to_dict()
|
|
1061
|
-
self.client.post(CONTAINER_REGISTRY_CREDENTIALS_ENDPOINT, data)
|
|
1062
|
-
|
|
1063
|
-
def delete_registry_credentials(self, credentials_name: str) -> None:
|
|
1064
|
-
"""Deletes registry credentials.
|
|
1065
|
-
|
|
1066
|
-
Args:
|
|
1067
|
-
credentials_name: Name of the credentials to delete.
|
|
1068
|
-
"""
|
|
1069
|
-
self.client.delete(f'{CONTAINER_REGISTRY_CREDENTIALS_ENDPOINT}/{credentials_name}')
|
|
1070
|
-
|
|
1071
|
-
def get_fileset_secrets(self) -> List[Secret]:
|
|
1072
|
-
"""Retrieves all fileset secrets.
|
|
1073
|
-
|
|
1074
|
-
Returns:
|
|
1075
|
-
List of all fileset secrets.
|
|
1076
|
-
"""
|
|
1077
|
-
response = self.client.get(FILESET_SECRETS_ENDPOINT)
|
|
1078
|
-
return [Secret.from_dict(secret) for secret in response.json()]
|
|
1079
|
-
|
|
1080
|
-
def delete_fileset_secret(self, secret_name: str) -> None:
|
|
1081
|
-
"""Deletes a fileset secret.
|
|
1082
|
-
|
|
1083
|
-
Args:
|
|
1084
|
-
secret_name: Name of the secret to delete.
|
|
1085
|
-
"""
|
|
1086
|
-
self.client.delete(f'{FILESET_SECRETS_ENDPOINT}/{secret_name}')
|
|
1087
|
-
|
|
1088
|
-
def create_fileset_secret_from_file_paths(
|
|
1089
|
-
self, secret_name: str, file_paths: List[str]
|
|
1090
|
-
) -> None:
|
|
1091
|
-
"""Creates a new fileset secret.
|
|
1092
|
-
A fileset secret is a secret that contains several files,
|
|
1093
|
-
and can be used to mount a directory with the files in a container.
|
|
1094
|
-
|
|
1095
|
-
Args:
|
|
1096
|
-
secret_name: Name of the secret.
|
|
1097
|
-
file_paths: List of file paths to include in the secret.
|
|
1098
|
-
"""
|
|
1099
|
-
processed_files = []
|
|
1100
|
-
for file_path in file_paths:
|
|
1101
|
-
with open(file_path, 'rb') as f:
|
|
1102
|
-
base64_content = base64.b64encode(f.read()).decode('utf-8')
|
|
1103
|
-
processed_files.append(
|
|
1104
|
-
{
|
|
1105
|
-
'file_name': os.path.basename(file_path),
|
|
1106
|
-
'base64_content': base64_content,
|
|
1107
|
-
}
|
|
1108
|
-
)
|
|
1109
|
-
self.client.post(FILESET_SECRETS_ENDPOINT, {'name': secret_name, 'files': processed_files})
|