datacrunch 1.15.0__py3-none-any.whl → 1.16.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.
Files changed (58) hide show
  1. datacrunch/InferenceClient/inference_client.py +200 -65
  2. datacrunch/__init__.py +2 -0
  3. datacrunch/_version.py +6 -0
  4. datacrunch/authentication/authentication.py +7 -14
  5. datacrunch/balance/balance.py +1 -3
  6. datacrunch/constants.py +19 -17
  7. datacrunch/containers/containers.py +151 -123
  8. datacrunch/datacrunch.py +18 -18
  9. datacrunch/helpers.py +7 -2
  10. datacrunch/http_client/http_client.py +14 -14
  11. datacrunch/images/images.py +9 -3
  12. datacrunch/instance_types/instance_types.py +42 -35
  13. datacrunch/instances/instances.py +62 -50
  14. datacrunch/locations/locations.py +1 -2
  15. datacrunch/ssh_keys/ssh_keys.py +3 -4
  16. datacrunch/startup_scripts/startup_scripts.py +10 -8
  17. datacrunch/volume_types/volume_types.py +10 -8
  18. datacrunch/volumes/volumes.py +60 -73
  19. {datacrunch-1.15.0.dist-info → datacrunch-1.16.0.dist-info}/METADATA +46 -72
  20. datacrunch-1.16.0.dist-info/RECORD +35 -0
  21. datacrunch-1.16.0.dist-info/WHEEL +4 -0
  22. datacrunch/__version__.py +0 -1
  23. datacrunch-1.15.0.dist-info/RECORD +0 -69
  24. datacrunch-1.15.0.dist-info/WHEEL +0 -5
  25. datacrunch-1.15.0.dist-info/licenses/LICENSE +0 -21
  26. datacrunch-1.15.0.dist-info/top_level.txt +0 -2
  27. tests/__init__.py +0 -0
  28. tests/integration_tests/__init__.py +0 -0
  29. tests/integration_tests/conftest.py +0 -20
  30. tests/integration_tests/test_instances.py +0 -36
  31. tests/integration_tests/test_locations.py +0 -65
  32. tests/integration_tests/test_volumes.py +0 -94
  33. tests/unit_tests/__init__.py +0 -0
  34. tests/unit_tests/authentication/__init__.py +0 -0
  35. tests/unit_tests/authentication/test_authentication.py +0 -202
  36. tests/unit_tests/balance/__init__.py +0 -0
  37. tests/unit_tests/balance/test_balance.py +0 -25
  38. tests/unit_tests/conftest.py +0 -21
  39. tests/unit_tests/containers/__init__.py +0 -1
  40. tests/unit_tests/containers/test_containers.py +0 -959
  41. tests/unit_tests/http_client/__init__.py +0 -0
  42. tests/unit_tests/http_client/test_http_client.py +0 -193
  43. tests/unit_tests/images/__init__.py +0 -0
  44. tests/unit_tests/images/test_images.py +0 -41
  45. tests/unit_tests/instance_types/__init__.py +0 -0
  46. tests/unit_tests/instance_types/test_instance_types.py +0 -87
  47. tests/unit_tests/instances/__init__.py +0 -0
  48. tests/unit_tests/instances/test_instances.py +0 -483
  49. tests/unit_tests/ssh_keys/__init__.py +0 -0
  50. tests/unit_tests/ssh_keys/test_ssh_keys.py +0 -198
  51. tests/unit_tests/startup_scripts/__init__.py +0 -0
  52. tests/unit_tests/startup_scripts/test_startup_scripts.py +0 -196
  53. tests/unit_tests/test_datacrunch.py +0 -65
  54. tests/unit_tests/test_exceptions.py +0 -33
  55. tests/unit_tests/volume_types/__init__.py +0 -0
  56. tests/unit_tests/volume_types/test_volume_types.py +0 -50
  57. tests/unit_tests/volumes/__init__.py +0 -0
  58. tests/unit_tests/volumes/test_volumes.py +0 -641
@@ -1,959 +0,0 @@
1
- import pytest
2
- import responses # https://github.com/getsentry/responses
3
- from responses import matchers
4
-
5
- from datacrunch.containers.containers import (
6
- CONTAINER_DEPLOYMENTS_ENDPOINT,
7
- CONTAINER_REGISTRY_CREDENTIALS_ENDPOINT,
8
- SECRETS_ENDPOINT,
9
- SERVERLESS_COMPUTE_RESOURCES_ENDPOINT,
10
- Container,
11
- ContainerDeploymentStatus,
12
- ContainerRegistrySettings,
13
- ContainersService,
14
- Deployment,
15
- EnvVar,
16
- EnvVarType,
17
- EntrypointOverridesSettings,
18
- HealthcheckSettings,
19
- RegistryCredential,
20
- Secret,
21
- VolumeMount,
22
- VolumeMountType,
23
- ComputeResource,
24
- ScalingOptions,
25
- ScalingPolicy,
26
- ScalingTriggers,
27
- QueueLoadScalingTrigger,
28
- UtilizationScalingTrigger,
29
- DockerHubCredentials,
30
- GithubCredentials,
31
- GCRCredentials,
32
- AWSECRCredentials,
33
- CustomRegistryCredentials,
34
- ReplicaInfo,
35
- )
36
- from datacrunch.exceptions import APIException
37
-
38
- DEPLOYMENT_NAME = "test-deployment"
39
- CONTAINER_NAME = "test-container"
40
- COMPUTE_RESOURCE_NAME_GENERAL_COMPUTE = "General Compute"
41
- COMPUTE_RESOURCE_NAME_H100 = "H100"
42
- SECRET_NAME = "test-secret"
43
- SECRET_VALUE = "test-secret-value"
44
- REGISTRY_CREDENTIAL_NAME = "test-credential"
45
- ENV_VAR_NAME = "TEST_VAR"
46
- ENV_VAR_VALUE = "test-value"
47
-
48
- INVALID_REQUEST = "INVALID_REQUEST"
49
- INVALID_REQUEST_MESSAGE = "Invalid request"
50
-
51
- # Sample deployment data for testing
52
- DEPLOYMENT_DATA = {
53
- "name": DEPLOYMENT_NAME,
54
- "container_registry_settings": {
55
- "is_private": False
56
- },
57
- "containers": [
58
- {
59
- "name": CONTAINER_NAME,
60
- "image": "nginx:latest",
61
- "exposed_port": 80,
62
- "healthcheck": {
63
- "enabled": True,
64
- "port": 80,
65
- "path": "/health"
66
- },
67
- "entrypoint_overrides": {
68
- "enabled": False
69
- },
70
- "env": [
71
- {
72
- "name": "ENV_VAR1",
73
- "value_or_reference_to_secret": "value1",
74
- "type": "plain"
75
- }
76
- ],
77
- "volume_mounts": [
78
- {
79
- "type": "scratch",
80
- "mount_path": "/data"
81
- }
82
- ]
83
- }
84
- ],
85
- "compute": {
86
- "name": COMPUTE_RESOURCE_NAME_GENERAL_COMPUTE,
87
- "size": 1,
88
- "is_available": True
89
- },
90
- "is_spot": False,
91
- "endpoint_base_url": "https://test-deployment.datacrunch.io",
92
- "scaling": {
93
- "min_replica_count": 1,
94
- "max_replica_count": 3,
95
- "scale_down_policy": {
96
- "delay_seconds": 300
97
- },
98
- "scale_up_policy": {
99
- "delay_seconds": 60
100
- },
101
- "queue_message_ttl_seconds": 3600,
102
- "concurrent_requests_per_replica": 10,
103
- "scaling_triggers": {
104
- "queue_load": {
105
- "threshold": 0.75
106
- },
107
- "cpu_utilization": {
108
- "enabled": True,
109
- "threshold": 0.8
110
- },
111
- "gpu_utilization": {
112
- "enabled": False
113
- }
114
- }
115
- },
116
- "created_at": "2023-01-01T00:00:00+00:00"
117
- }
118
-
119
- # Sample compute resources data
120
- COMPUTE_RESOURCES_DATA = [
121
- {
122
- "name": COMPUTE_RESOURCE_NAME_GENERAL_COMPUTE,
123
- "size": 1,
124
- "is_available": True
125
- },
126
- {
127
- "name": COMPUTE_RESOURCE_NAME_H100,
128
- "size": 4,
129
- "is_available": True
130
- }
131
- ]
132
-
133
- # Sample secrets data
134
- SECRETS_DATA = [
135
- {
136
- "name": SECRET_NAME,
137
- "created_at": "2023-01-01T00:00:00+00:00",
138
- "secret_type": "generic"
139
- }
140
- ]
141
-
142
- # Sample registry credentials data
143
- REGISTRY_CREDENTIALS_DATA = [
144
- {
145
- "name": REGISTRY_CREDENTIAL_NAME,
146
- "created_at": "2023-01-01T00:00:00+00:00"
147
- }
148
- ]
149
-
150
- # Sample deployment status data
151
- DEPLOYMENT_STATUS_DATA = {
152
- "status": "healthy"
153
- }
154
-
155
- # Sample replicas data
156
- REPLICAS_DATA = {
157
- "list": [
158
- {
159
- "id": "replica-1",
160
- "status": "running",
161
- "started_at": "2023-01-01T00:00:00+00:00"
162
- }
163
- ]
164
- }
165
-
166
-
167
- # Sample environment variables data
168
- ENV_VARS_DATA = [{
169
- "container_name": CONTAINER_NAME,
170
- "env": [
171
- {
172
- "name": ENV_VAR_NAME,
173
- "value_or_reference_to_secret": ENV_VAR_VALUE,
174
- "type": "plain"
175
- }
176
- ]
177
- }]
178
-
179
-
180
- class TestContainersService:
181
- @pytest.fixture
182
- def containers_service(self, http_client):
183
- return ContainersService(http_client)
184
-
185
- @pytest.fixture
186
- def deployments_endpoint(self, http_client):
187
- return http_client._base_url + CONTAINER_DEPLOYMENTS_ENDPOINT
188
-
189
- @pytest.fixture
190
- def compute_resources_endpoint(self, http_client):
191
- return http_client._base_url + SERVERLESS_COMPUTE_RESOURCES_ENDPOINT
192
-
193
- @pytest.fixture
194
- def secrets_endpoint(self, http_client):
195
- return http_client._base_url + SECRETS_ENDPOINT
196
-
197
- @pytest.fixture
198
- def registry_credentials_endpoint(self, http_client):
199
- return http_client._base_url + CONTAINER_REGISTRY_CREDENTIALS_ENDPOINT
200
-
201
- @responses.activate
202
- def test_get_deployments(self, containers_service, deployments_endpoint):
203
- # arrange - add response mock
204
- responses.add(
205
- responses.GET,
206
- deployments_endpoint,
207
- json=[DEPLOYMENT_DATA],
208
- status=200
209
- )
210
-
211
- # act
212
- deployments = containers_service.get_deployments()
213
- deployment = deployments[0]
214
-
215
- # assert
216
- assert type(deployments) == list
217
- assert len(deployments) == 1
218
- assert type(deployment) == Deployment
219
- assert deployment.name == DEPLOYMENT_NAME
220
- assert len(deployment.containers) == 1
221
- assert type(deployment.containers[0]) == Container
222
- assert type(deployment.compute) == ComputeResource
223
- assert deployment.compute.name == COMPUTE_RESOURCE_NAME_GENERAL_COMPUTE
224
- assert responses.assert_call_count(deployments_endpoint, 1) is True
225
-
226
- @responses.activate
227
- def test_get_deployment_by_name(self, containers_service, deployments_endpoint):
228
- # arrange - add response mock
229
- url = f"{deployments_endpoint}/{DEPLOYMENT_NAME}"
230
- responses.add(
231
- responses.GET,
232
- url,
233
- json=DEPLOYMENT_DATA,
234
- status=200
235
- )
236
-
237
- # act
238
- deployment = containers_service.get_deployment_by_name(DEPLOYMENT_NAME)
239
-
240
- # assert
241
- assert type(deployment) == Deployment
242
- assert deployment.name == DEPLOYMENT_NAME
243
- assert len(deployment.containers) == 1
244
- assert deployment.containers[0].name == CONTAINER_NAME
245
- assert deployment.compute.name == COMPUTE_RESOURCE_NAME_GENERAL_COMPUTE
246
- assert responses.assert_call_count(url, 1) is True
247
-
248
- @responses.activate
249
- def test_get_deployment_by_name_error(self, containers_service, deployments_endpoint):
250
- # arrange - add response mock
251
- url = f"{deployments_endpoint}/nonexistent"
252
- responses.add(
253
- responses.GET,
254
- url,
255
- json={"code": INVALID_REQUEST, "message": INVALID_REQUEST_MESSAGE},
256
- status=400
257
- )
258
-
259
- # act
260
- with pytest.raises(APIException) as excinfo:
261
- containers_service.get_deployment_by_name("nonexistent")
262
-
263
- # assert
264
- assert excinfo.value.code == INVALID_REQUEST
265
- assert excinfo.value.message == INVALID_REQUEST_MESSAGE
266
- assert responses.assert_call_count(url, 1) is True
267
-
268
- @responses.activate
269
- def test_create_deployment(self, containers_service, deployments_endpoint):
270
- # arrange - add response mock
271
- responses.add(
272
- responses.POST,
273
- deployments_endpoint,
274
- json=DEPLOYMENT_DATA,
275
- status=200
276
- )
277
-
278
- container = Container(
279
- image="nginx:latest",
280
- exposed_port=80,
281
- healthcheck=HealthcheckSettings(
282
- enabled=True, port=80, path="/health"),
283
- entrypoint_overrides=EntrypointOverridesSettings(enabled=False),
284
- env=[EnvVar(
285
- name="ENV_VAR1", value_or_reference_to_secret="value1", type=EnvVarType.PLAIN)],
286
- volume_mounts=[VolumeMount(
287
- type=VolumeMountType.SCRATCH, mount_path="/data")]
288
- )
289
-
290
- compute = ComputeResource(
291
- name=COMPUTE_RESOURCE_NAME_GENERAL_COMPUTE, size=1)
292
-
293
- container_registry_settings = ContainerRegistrySettings(
294
- is_private=False)
295
-
296
- # create deployment object
297
- deployment = Deployment(
298
- name=DEPLOYMENT_NAME,
299
- container_registry_settings=container_registry_settings,
300
- containers=[container],
301
- compute=compute,
302
- is_spot=False
303
- )
304
-
305
- # act
306
- created_deployment = containers_service.create_deployment(deployment)
307
-
308
- # assert
309
- assert type(created_deployment) == Deployment
310
- assert created_deployment.name == DEPLOYMENT_NAME
311
- assert len(created_deployment.containers) == 1
312
- assert created_deployment.containers[0].name == CONTAINER_NAME
313
- assert created_deployment.compute.name == COMPUTE_RESOURCE_NAME_GENERAL_COMPUTE
314
- assert responses.assert_call_count(deployments_endpoint, 1) is True
315
-
316
- @responses.activate
317
- def test_update_deployment(self, containers_service, deployments_endpoint):
318
- # arrange - add response mock
319
- url = f"{deployments_endpoint}/{DEPLOYMENT_NAME}"
320
- responses.add(
321
- responses.PATCH,
322
- url,
323
- json=DEPLOYMENT_DATA,
324
- status=200
325
- )
326
-
327
- # create deployment object
328
- container = Container(
329
- name=CONTAINER_NAME,
330
- image="nginx:latest",
331
- exposed_port=80
332
- )
333
-
334
- container_registry_settings = ContainerRegistrySettings(
335
- is_private=False)
336
-
337
- compute = ComputeResource(
338
- name=COMPUTE_RESOURCE_NAME_GENERAL_COMPUTE, size=1)
339
-
340
- deployment = Deployment(
341
- name=DEPLOYMENT_NAME,
342
- container_registry_settings=container_registry_settings,
343
- containers=[container],
344
- compute=compute
345
- )
346
-
347
- # act
348
- updated_deployment = containers_service.update_deployment(
349
- DEPLOYMENT_NAME, deployment)
350
-
351
- # assert
352
- assert type(updated_deployment) == Deployment
353
- assert updated_deployment.name == DEPLOYMENT_NAME
354
- assert len(updated_deployment.containers) == 1
355
- assert updated_deployment.containers[0].name == CONTAINER_NAME
356
- assert updated_deployment.compute.name == COMPUTE_RESOURCE_NAME_GENERAL_COMPUTE
357
- assert responses.assert_call_count(url, 1) is True
358
-
359
- @responses.activate
360
- def test_delete_deployment(self, containers_service, deployments_endpoint):
361
- # arrange - add response mock
362
- url = f"{deployments_endpoint}/{DEPLOYMENT_NAME}"
363
- responses.add(
364
- responses.DELETE,
365
- url,
366
- status=204
367
- )
368
-
369
- # act
370
- containers_service.delete_deployment(DEPLOYMENT_NAME)
371
-
372
- # assert
373
- assert responses.assert_call_count(url, 1) is True
374
-
375
- @responses.activate
376
- def test_get_deployment_status(self, containers_service, deployments_endpoint):
377
- # arrange - add response mock
378
- url = f"{deployments_endpoint}/{DEPLOYMENT_NAME}/status"
379
- responses.add(
380
- responses.GET,
381
- url,
382
- json=DEPLOYMENT_STATUS_DATA,
383
- status=200
384
- )
385
-
386
- # act
387
- status = containers_service.get_deployment_status(DEPLOYMENT_NAME)
388
-
389
- # assert
390
- assert status == ContainerDeploymentStatus.HEALTHY
391
- assert responses.assert_call_count(url, 1) is True
392
-
393
- @responses.activate
394
- def test_restart_deployment(self, containers_service, deployments_endpoint):
395
- # arrange - add response mock
396
- url = f"{deployments_endpoint}/{DEPLOYMENT_NAME}/restart"
397
- responses.add(
398
- responses.POST,
399
- url,
400
- status=204
401
- )
402
-
403
- # act
404
- containers_service.restart_deployment(DEPLOYMENT_NAME)
405
-
406
- # assert
407
- assert responses.assert_call_count(url, 1) is True
408
-
409
- @responses.activate
410
- def test_get_deployment_scaling_options(self, containers_service, deployments_endpoint):
411
- # arrange - add response mock
412
- url = f"{deployments_endpoint}/{DEPLOYMENT_NAME}/scaling"
413
- responses.add(
414
- responses.GET,
415
- url,
416
- json=DEPLOYMENT_DATA["scaling"],
417
- status=200
418
- )
419
-
420
- # act
421
- scaling_options = containers_service.get_deployment_scaling_options(
422
- DEPLOYMENT_NAME)
423
-
424
- # assert
425
- assert isinstance(scaling_options, ScalingOptions)
426
- assert scaling_options.min_replica_count == 1
427
- assert scaling_options.max_replica_count == 3
428
- assert responses.assert_call_count(url, 1) is True
429
-
430
- @responses.activate
431
- def test_update_deployment_scaling_options(self, containers_service, deployments_endpoint):
432
- # arrange - add response mock
433
- url = f"{deployments_endpoint}/{DEPLOYMENT_NAME}/scaling"
434
- responses.add(
435
- responses.PATCH,
436
- url,
437
- json=DEPLOYMENT_DATA["scaling"],
438
- status=200
439
- )
440
-
441
- # create scaling options object
442
- scaling_options = ScalingOptions(
443
- min_replica_count=1,
444
- max_replica_count=5,
445
- scale_down_policy=ScalingPolicy(delay_seconds=300),
446
- scale_up_policy=ScalingPolicy(delay_seconds=60),
447
- queue_message_ttl_seconds=3600,
448
- concurrent_requests_per_replica=10,
449
- scaling_triggers=ScalingTriggers(
450
- queue_load=QueueLoadScalingTrigger(threshold=0.75),
451
- cpu_utilization=UtilizationScalingTrigger(
452
- enabled=True, threshold=0.8),
453
- gpu_utilization=UtilizationScalingTrigger(enabled=False)
454
- )
455
- )
456
-
457
- # act
458
- updated_scaling = containers_service.update_deployment_scaling_options(
459
- DEPLOYMENT_NAME, scaling_options)
460
-
461
- # assert
462
- assert isinstance(updated_scaling, ScalingOptions)
463
- assert updated_scaling.min_replica_count == 1
464
- assert updated_scaling.max_replica_count == 3
465
- assert responses.assert_call_count(url, 1) is True
466
-
467
- @responses.activate
468
- def test_get_deployment_replicas(self, containers_service, deployments_endpoint):
469
- # arrange - add response mock
470
- url = f"{deployments_endpoint}/{DEPLOYMENT_NAME}/replicas"
471
- responses.add(
472
- responses.GET,
473
- url,
474
- json=REPLICAS_DATA,
475
- status=200
476
- )
477
-
478
- # act
479
- replicas = containers_service.get_deployment_replicas(DEPLOYMENT_NAME)
480
-
481
- # assert
482
- assert len(replicas) == 1
483
- assert replicas[0] == ReplicaInfo(
484
- "replica-1", "running", "2023-01-01T00:00:00+00:00")
485
- assert responses.assert_call_count(url, 1) is True
486
-
487
- @responses.activate
488
- def test_purge_deployment_queue(self, containers_service, deployments_endpoint):
489
- # arrange - add response mock
490
- url = f"{deployments_endpoint}/{DEPLOYMENT_NAME}/purge-queue"
491
- responses.add(
492
- responses.POST,
493
- url,
494
- status=204
495
- )
496
-
497
- # act
498
- containers_service.purge_deployment_queue(DEPLOYMENT_NAME)
499
-
500
- # assert
501
- assert responses.assert_call_count(url, 1) is True
502
-
503
- @responses.activate
504
- def test_pause_deployment(self, containers_service, deployments_endpoint):
505
- # arrange - add response mock
506
- url = f"{deployments_endpoint}/{DEPLOYMENT_NAME}/pause"
507
- responses.add(
508
- responses.POST,
509
- url,
510
- status=204
511
- )
512
-
513
- # act
514
- containers_service.pause_deployment(DEPLOYMENT_NAME)
515
-
516
- # assert
517
- assert responses.assert_call_count(url, 1) is True
518
-
519
- @responses.activate
520
- def test_resume_deployment(self, containers_service, deployments_endpoint):
521
- # arrange - add response mock
522
- url = f"{deployments_endpoint}/{DEPLOYMENT_NAME}/resume"
523
- responses.add(
524
- responses.POST,
525
- url,
526
- status=204
527
- )
528
-
529
- # act
530
- containers_service.resume_deployment(DEPLOYMENT_NAME)
531
-
532
- # assert
533
- assert responses.assert_call_count(url, 1) is True
534
-
535
- @responses.activate
536
- def test_get_deployment_environment_variables(self, containers_service, deployments_endpoint):
537
- # arrange - add response mock
538
- url = f"{deployments_endpoint}/{DEPLOYMENT_NAME}/environment-variables"
539
- responses.add(
540
- responses.GET,
541
- url,
542
- json=ENV_VARS_DATA,
543
- status=200
544
- )
545
-
546
- # act
547
- env_vars = containers_service.get_deployment_environment_variables(
548
- DEPLOYMENT_NAME)
549
-
550
- # assert
551
- assert env_vars[CONTAINER_NAME] == [EnvVar(
552
- name=ENV_VAR_NAME,
553
- value_or_reference_to_secret=ENV_VAR_VALUE,
554
- type=EnvVarType.PLAIN
555
- )]
556
- assert responses.assert_call_count(url, 1) is True
557
-
558
- @responses.activate
559
- def test_add_deployment_environment_variables(self, containers_service, deployments_endpoint):
560
- # arrange - add response mock
561
- url = f"{deployments_endpoint}/{DEPLOYMENT_NAME}/environment-variables"
562
- responses.add(
563
- responses.POST,
564
- url,
565
- json=ENV_VARS_DATA,
566
- status=200
567
- )
568
-
569
- # act
570
- env_vars = [EnvVar(
571
- name=ENV_VAR_NAME,
572
- value_or_reference_to_secret=ENV_VAR_VALUE,
573
- type=EnvVarType.PLAIN
574
- )]
575
- result = containers_service.add_deployment_environment_variables(
576
- DEPLOYMENT_NAME, CONTAINER_NAME, env_vars)
577
-
578
- # assert
579
- assert result[CONTAINER_NAME] == [EnvVar(
580
- name=ENV_VAR_NAME,
581
- value_or_reference_to_secret=ENV_VAR_VALUE,
582
- type=EnvVarType.PLAIN
583
- )]
584
- assert responses.assert_call_count(url, 1) is True
585
-
586
- @responses.activate
587
- def test_update_deployment_environment_variables(self, containers_service, deployments_endpoint):
588
- # arrange - add response mock
589
- url = f"{deployments_endpoint}/{DEPLOYMENT_NAME}/environment-variables"
590
- responses.add(
591
- responses.PATCH,
592
- url,
593
- json=ENV_VARS_DATA[0],
594
- status=200
595
- )
596
-
597
- # act
598
- env_vars = [EnvVar(
599
- name=ENV_VAR_NAME,
600
- value_or_reference_to_secret=ENV_VAR_VALUE,
601
- type=EnvVarType.PLAIN
602
- )]
603
- result = containers_service.update_deployment_environment_variables(
604
- DEPLOYMENT_NAME, CONTAINER_NAME, env_vars)
605
-
606
- # assert
607
- assert result[CONTAINER_NAME] == [EnvVar(
608
- name=ENV_VAR_NAME,
609
- value_or_reference_to_secret=ENV_VAR_VALUE,
610
- type=EnvVarType.PLAIN
611
- )]
612
- assert responses.assert_call_count(url, 1) is True
613
-
614
- @responses.activate
615
- def test_delete_deployment_environment_variables(self, containers_service, deployments_endpoint):
616
- # arrange - add response mock
617
- url = f"{deployments_endpoint}/{DEPLOYMENT_NAME}/environment-variables"
618
- responses.add(
619
- responses.DELETE,
620
- url,
621
- json=ENV_VARS_DATA,
622
- status=200
623
- )
624
-
625
- # act
626
- result = containers_service.delete_deployment_environment_variables(
627
- DEPLOYMENT_NAME, CONTAINER_NAME, ["random-env-var-name"])
628
-
629
- # assert
630
- assert result == {CONTAINER_NAME: [EnvVar(
631
- name=ENV_VAR_NAME,
632
- value_or_reference_to_secret=ENV_VAR_VALUE,
633
- type=EnvVarType.PLAIN
634
- )]}
635
- assert responses.assert_call_count(url, 1) is True
636
-
637
- @responses.activate
638
- def test_get_compute_resources(self, containers_service, compute_resources_endpoint):
639
- # arrange - add response mock
640
- responses.add(
641
- responses.GET,
642
- compute_resources_endpoint,
643
- # Wrap in list to simulate resource groups
644
- json=[COMPUTE_RESOURCES_DATA],
645
- status=200
646
- )
647
-
648
- # act
649
- resources = containers_service.get_compute_resources()
650
-
651
- # assert
652
- assert type(resources) == list
653
- assert len(resources) == 2
654
- assert type(resources[0]) == ComputeResource
655
- assert resources[0].name == COMPUTE_RESOURCE_NAME_GENERAL_COMPUTE
656
- assert resources[0].size == 1
657
- assert resources[0].is_available == True
658
- assert responses.assert_call_count(
659
- compute_resources_endpoint, 1) is True
660
-
661
- @responses.activate
662
- def test_get_compute_resources_filter_by_size(self, containers_service, compute_resources_endpoint):
663
- # arrange - add response mock
664
- responses.add(
665
- responses.GET,
666
- compute_resources_endpoint,
667
- json=[COMPUTE_RESOURCES_DATA],
668
- status=200
669
- )
670
-
671
- # act
672
- resources = containers_service.get_compute_resources(size=4)
673
-
674
- # assert
675
- assert type(resources) == list
676
- assert len(resources) == 1
677
- assert type(resources[0]) == ComputeResource
678
- assert resources[0].name == COMPUTE_RESOURCE_NAME_H100
679
- assert resources[0].size == 4
680
- assert resources[0].is_available == True
681
- assert responses.assert_call_count(
682
- compute_resources_endpoint, 1) is True
683
-
684
- @responses.activate
685
- def test_get_compute_resources_filter_by_availability(self, containers_service, compute_resources_endpoint):
686
- # arrange - add response mock
687
- responses.add(
688
- responses.GET,
689
- compute_resources_endpoint,
690
- json=[COMPUTE_RESOURCES_DATA],
691
- status=200
692
- )
693
-
694
- # act
695
- resources = containers_service.get_compute_resources(is_available=True)
696
-
697
- # assert
698
- assert type(resources) == list
699
- assert len(resources) == 2
700
- assert all(r.is_available == True for r in resources)
701
- assert responses.assert_call_count(
702
- compute_resources_endpoint, 1) is True
703
-
704
- @responses.activate
705
- def test_get_compute_resources_filter_by_size_and_availability(self, containers_service, compute_resources_endpoint):
706
- # arrange - add response mock
707
- responses.add(
708
- responses.GET,
709
- compute_resources_endpoint,
710
- json=[COMPUTE_RESOURCES_DATA],
711
- status=200
712
- )
713
-
714
- # act
715
- resources = containers_service.get_compute_resources(
716
- size=1, is_available=True)
717
-
718
- # assert
719
- assert type(resources) == list
720
- assert len(resources) == 1
721
- assert resources[0].name == COMPUTE_RESOURCE_NAME_GENERAL_COMPUTE
722
- assert resources[0].size == 1
723
- assert resources[0].is_available == True
724
- assert responses.assert_call_count(
725
- compute_resources_endpoint, 1) is True
726
-
727
- @responses.activate
728
- def test_get_secrets(self, containers_service, secrets_endpoint):
729
- # arrange - add response mock
730
- responses.add(
731
- responses.GET,
732
- secrets_endpoint,
733
- json=SECRETS_DATA,
734
- status=200
735
- )
736
-
737
- # act
738
- secrets = containers_service.get_secrets()
739
-
740
- # assert
741
- assert type(secrets) == list
742
- assert len(secrets) == 1
743
- assert type(secrets[0]) == Secret
744
- assert secrets[0].name == SECRET_NAME
745
- assert responses.assert_call_count(secrets_endpoint, 1) is True
746
-
747
- @responses.activate
748
- def test_create_secret(self, containers_service, secrets_endpoint):
749
- # arrange - add response mock
750
- responses.add(
751
- responses.POST,
752
- secrets_endpoint,
753
- status=201,
754
- match=[
755
- matchers.json_params_matcher(
756
- # The test will now fail if the request body doesn't match the expected JSON structure
757
- {"name": SECRET_NAME, "value": SECRET_VALUE}
758
- )
759
- ]
760
- )
761
-
762
- # act
763
- containers_service.create_secret(SECRET_NAME, SECRET_VALUE)
764
-
765
- # assert
766
- assert responses.assert_call_count(secrets_endpoint, 1) is True
767
-
768
- @responses.activate
769
- def test_delete_secret(self, containers_service, secrets_endpoint):
770
- # arrange - add response mock
771
- url = f"{secrets_endpoint}/{SECRET_NAME}?force=false"
772
- responses.add(
773
- responses.DELETE,
774
- url,
775
- status=200
776
- )
777
-
778
- # act
779
- containers_service.delete_secret(SECRET_NAME)
780
-
781
- # assert
782
- assert responses.assert_call_count(url, 1) is True
783
- request = responses.calls[0].request
784
- assert "force=false" in request.url
785
-
786
- @responses.activate
787
- def test_delete_secret_with_force(self, containers_service, secrets_endpoint):
788
- # arrange
789
- url = f"{secrets_endpoint}/{SECRET_NAME}?force=true"
790
- responses.add(
791
- responses.DELETE,
792
- url,
793
- status=200
794
- )
795
-
796
- # act
797
- containers_service.delete_secret(SECRET_NAME, force=True)
798
-
799
- # assert
800
- assert responses.assert_call_count(url, 1) is True
801
- request = responses.calls[0].request
802
- assert "force=true" in request.url
803
-
804
- @responses.activate
805
- def test_get_registry_credentials(self, containers_service, registry_credentials_endpoint):
806
- # arrange - add response mock
807
- responses.add(
808
- responses.GET,
809
- registry_credentials_endpoint,
810
- json=REGISTRY_CREDENTIALS_DATA,
811
- status=200
812
- )
813
-
814
- # act
815
- credentials = containers_service.get_registry_credentials()
816
-
817
- # assert
818
- assert type(credentials) == list
819
- assert len(credentials) == 1
820
- assert type(credentials[0]) == RegistryCredential
821
- assert credentials[0].name == REGISTRY_CREDENTIAL_NAME
822
- assert responses.assert_call_count(
823
- registry_credentials_endpoint, 1) is True
824
-
825
- @responses.activate
826
- def test_add_registry_credentials(self, containers_service, registry_credentials_endpoint):
827
- USERNAME = "username"
828
- ACCESS_TOKEN = "token"
829
- # arrange - add response mock
830
- responses.add(
831
- responses.POST,
832
- registry_credentials_endpoint,
833
- status=201
834
- )
835
-
836
- # act
837
- creds = DockerHubCredentials(
838
- name=REGISTRY_CREDENTIAL_NAME,
839
- username=USERNAME,
840
- access_token=ACCESS_TOKEN
841
- )
842
- containers_service.add_registry_credentials(creds)
843
-
844
- # assert
845
- assert responses.assert_call_count(
846
- registry_credentials_endpoint, 1) is True
847
- assert responses.calls[0].request.body.decode(
848
- 'utf-8') == '{"name": "test-credential", "type": "dockerhub", "username": "username", "access_token": "token"}'
849
-
850
- @responses.activate
851
- def test_add_registry_credentials_github(self, containers_service, registry_credentials_endpoint):
852
- # arrange
853
- responses.add(
854
- responses.POST,
855
- registry_credentials_endpoint,
856
- status=201
857
- )
858
-
859
- # act
860
- creds = GithubCredentials(
861
- name=REGISTRY_CREDENTIAL_NAME,
862
- username="test-username",
863
- access_token="test-token"
864
- )
865
- containers_service.add_registry_credentials(creds)
866
-
867
- # assert
868
- assert responses.assert_call_count(
869
- registry_credentials_endpoint, 1) is True
870
- assert responses.calls[0].request.body.decode(
871
- 'utf-8') == '{"name": "test-credential", "type": "ghcr", "username": "test-username", "access_token": "test-token"}'
872
-
873
- @responses.activate
874
- def test_add_registry_credentials_gcr(self, containers_service, registry_credentials_endpoint):
875
- # arrange
876
- responses.add(
877
- responses.POST,
878
- registry_credentials_endpoint,
879
- status=201
880
- )
881
-
882
- # act
883
- service_account_key = '{"key": "value"}'
884
- creds = GCRCredentials(
885
- name=REGISTRY_CREDENTIAL_NAME,
886
- service_account_key=service_account_key
887
- )
888
- containers_service.add_registry_credentials(creds)
889
-
890
- # assert
891
- assert responses.assert_call_count(
892
- registry_credentials_endpoint, 1) is True
893
- assert responses.calls[0].request.body.decode(
894
- 'utf-8') == '{"name": "test-credential", "type": "gcr", "service_account_key": "{\\"key\\": \\"value\\"}"}'
895
-
896
- @responses.activate
897
- def test_add_registry_credentials_aws_ecr(self, containers_service, registry_credentials_endpoint):
898
- # arrange
899
- responses.add(
900
- responses.POST,
901
- registry_credentials_endpoint,
902
- status=201
903
- )
904
-
905
- # act
906
- creds = AWSECRCredentials(
907
- name=REGISTRY_CREDENTIAL_NAME,
908
- access_key_id="test-key",
909
- secret_access_key="test-secret",
910
- region="us-west-2",
911
- ecr_repo="test.ecr.aws.com"
912
- )
913
- containers_service.add_registry_credentials(creds)
914
-
915
- # assert
916
- assert responses.assert_call_count(
917
- registry_credentials_endpoint, 1) is True
918
- assert responses.calls[0].request.body.decode(
919
- 'utf-8') == '{"name": "test-credential", "type": "aws-ecr", "access_key_id": "test-key", "secret_access_key": "test-secret", "region": "us-west-2", "ecr_repo": "test.ecr.aws.com"}'
920
-
921
- @responses.activate
922
- def test_add_registry_credentials_custom(self, containers_service, registry_credentials_endpoint):
923
- # arrange
924
- responses.add(
925
- responses.POST,
926
- registry_credentials_endpoint,
927
- status=201
928
- )
929
-
930
- # act
931
- docker_config = '{"auths": {"registry.example.com": {"auth": "base64-encoded"}}}'
932
- creds = CustomRegistryCredentials(
933
- name=REGISTRY_CREDENTIAL_NAME,
934
- docker_config_json=docker_config
935
- )
936
- containers_service.add_registry_credentials(creds)
937
-
938
- # assert
939
- assert responses.assert_call_count(
940
- registry_credentials_endpoint, 1) is True
941
- assert responses.calls[0].request.body.decode(
942
- 'utf-8') == '{"name": "test-credential", "type": "custom", "docker_config_json": "{\\"auths\\": {\\"registry.example.com\\": {\\"auth\\": \\"base64-encoded\\"}}}"}'
943
-
944
- @responses.activate
945
- def test_delete_registry_credentials(self, containers_service, registry_credentials_endpoint):
946
- # arrange - add response mock
947
- url = f"{registry_credentials_endpoint}/{REGISTRY_CREDENTIAL_NAME}"
948
- responses.add(
949
- responses.DELETE,
950
- url,
951
- status=200
952
- )
953
-
954
- # act
955
- containers_service.delete_registry_credentials(
956
- REGISTRY_CREDENTIAL_NAME)
957
-
958
- # assert
959
- assert responses.assert_call_count(url, 1) is True