dstack 0.18.44__py3-none-any.whl → 0.19.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of dstack might be problematic. Click here for more details.
- dstack/_internal/cli/commands/gateway.py +15 -3
- dstack/_internal/cli/commands/logs.py +0 -22
- dstack/_internal/cli/commands/stats.py +8 -17
- dstack/_internal/cli/main.py +1 -5
- dstack/_internal/cli/services/configurators/fleet.py +4 -39
- dstack/_internal/cli/services/configurators/run.py +22 -21
- dstack/_internal/cli/services/profile.py +34 -83
- dstack/_internal/cli/utils/gateway.py +1 -1
- dstack/_internal/core/backends/__init__.py +56 -39
- dstack/_internal/core/backends/aws/__init__.py +0 -25
- dstack/_internal/core/backends/aws/auth.py +1 -10
- dstack/_internal/core/backends/aws/backend.py +26 -0
- dstack/_internal/core/backends/aws/compute.py +20 -45
- dstack/_internal/{server/services/backends/configurators/aws.py → core/backends/aws/configurator.py} +46 -85
- dstack/_internal/core/backends/aws/models.py +135 -0
- dstack/_internal/core/backends/aws/resources.py +1 -1
- dstack/_internal/core/backends/azure/__init__.py +0 -20
- dstack/_internal/core/backends/azure/auth.py +2 -11
- dstack/_internal/core/backends/azure/backend.py +21 -0
- dstack/_internal/core/backends/azure/compute.py +13 -27
- dstack/_internal/{server/services/backends/configurators/azure.py → core/backends/azure/configurator.py} +141 -210
- dstack/_internal/core/backends/azure/models.py +89 -0
- dstack/_internal/core/backends/base/__init__.py +0 -12
- dstack/_internal/core/backends/base/backend.py +18 -0
- dstack/_internal/core/backends/base/compute.py +153 -33
- dstack/_internal/core/backends/base/configurator.py +105 -0
- dstack/_internal/core/backends/base/models.py +14 -0
- dstack/_internal/core/backends/configurators.py +138 -0
- dstack/_internal/core/backends/cudo/__init__.py +0 -15
- dstack/_internal/core/backends/cudo/backend.py +16 -0
- dstack/_internal/core/backends/cudo/compute.py +8 -26
- dstack/_internal/core/backends/cudo/configurator.py +72 -0
- dstack/_internal/core/backends/cudo/models.py +37 -0
- dstack/_internal/core/backends/datacrunch/__init__.py +0 -15
- dstack/_internal/core/backends/datacrunch/backend.py +16 -0
- dstack/_internal/core/backends/datacrunch/compute.py +8 -25
- dstack/_internal/core/backends/datacrunch/configurator.py +66 -0
- dstack/_internal/core/backends/datacrunch/models.py +38 -0
- dstack/_internal/core/{models/backends/dstack.py → backends/dstack/models.py} +7 -7
- dstack/_internal/core/backends/gcp/__init__.py +0 -16
- dstack/_internal/core/backends/gcp/auth.py +2 -11
- dstack/_internal/core/backends/gcp/backend.py +17 -0
- dstack/_internal/core/backends/gcp/compute.py +13 -43
- dstack/_internal/{server/services/backends/configurators/gcp.py → core/backends/gcp/configurator.py} +46 -103
- dstack/_internal/core/backends/gcp/models.py +125 -0
- dstack/_internal/core/backends/kubernetes/__init__.py +0 -15
- dstack/_internal/core/backends/kubernetes/backend.py +16 -0
- dstack/_internal/core/backends/kubernetes/compute.py +16 -5
- dstack/_internal/core/backends/kubernetes/configurator.py +55 -0
- dstack/_internal/core/backends/kubernetes/models.py +72 -0
- dstack/_internal/core/backends/lambdalabs/__init__.py +0 -16
- dstack/_internal/core/backends/lambdalabs/backend.py +17 -0
- dstack/_internal/core/backends/lambdalabs/compute.py +7 -28
- dstack/_internal/core/backends/lambdalabs/configurator.py +82 -0
- dstack/_internal/core/backends/lambdalabs/models.py +37 -0
- dstack/_internal/core/backends/local/__init__.py +0 -13
- dstack/_internal/core/backends/local/backend.py +14 -0
- dstack/_internal/core/backends/local/compute.py +16 -2
- dstack/_internal/core/backends/models.py +128 -0
- dstack/_internal/core/backends/oci/__init__.py +0 -15
- dstack/_internal/core/backends/oci/auth.py +1 -5
- dstack/_internal/core/backends/oci/backend.py +16 -0
- dstack/_internal/core/backends/oci/compute.py +9 -23
- dstack/_internal/{server/services/backends/configurators/oci.py → core/backends/oci/configurator.py} +40 -85
- dstack/_internal/core/{models/backends/oci.py → backends/oci/models.py} +24 -25
- dstack/_internal/core/backends/oci/region.py +1 -1
- dstack/_internal/core/backends/runpod/__init__.py +0 -15
- dstack/_internal/core/backends/runpod/backend.py +16 -0
- dstack/_internal/core/backends/runpod/compute.py +7 -3
- dstack/_internal/core/backends/runpod/configurator.py +59 -0
- dstack/_internal/core/backends/runpod/models.py +54 -0
- dstack/_internal/core/backends/template/__init__.py +0 -0
- dstack/_internal/core/backends/tensordock/__init__.py +0 -15
- dstack/_internal/core/backends/tensordock/backend.py +16 -0
- dstack/_internal/core/backends/tensordock/compute.py +8 -27
- dstack/_internal/core/backends/tensordock/configurator.py +68 -0
- dstack/_internal/core/backends/tensordock/models.py +38 -0
- dstack/_internal/core/backends/vastai/__init__.py +0 -15
- dstack/_internal/core/backends/vastai/backend.py +16 -0
- dstack/_internal/core/backends/vastai/compute.py +2 -2
- dstack/_internal/core/backends/vastai/configurator.py +66 -0
- dstack/_internal/core/backends/vastai/models.py +37 -0
- dstack/_internal/core/backends/vultr/__init__.py +0 -15
- dstack/_internal/core/backends/vultr/backend.py +16 -0
- dstack/_internal/core/backends/vultr/compute.py +10 -24
- dstack/_internal/core/backends/vultr/configurator.py +64 -0
- dstack/_internal/core/backends/vultr/models.py +34 -0
- dstack/_internal/core/models/backends/__init__.py +0 -184
- dstack/_internal/core/models/backends/base.py +0 -19
- dstack/_internal/core/models/configurations.py +20 -15
- dstack/_internal/core/models/envs.py +4 -3
- dstack/_internal/core/models/fleets.py +17 -22
- dstack/_internal/core/models/gateways.py +3 -3
- dstack/_internal/core/models/instances.py +24 -0
- dstack/_internal/core/models/profiles.py +41 -46
- dstack/_internal/core/models/projects.py +1 -1
- dstack/_internal/core/models/repos/base.py +0 -5
- dstack/_internal/core/models/repos/local.py +3 -3
- dstack/_internal/core/models/repos/remote.py +26 -12
- dstack/_internal/core/models/repos/virtual.py +1 -1
- dstack/_internal/core/models/resources.py +45 -76
- dstack/_internal/core/models/runs.py +17 -19
- dstack/_internal/core/models/volumes.py +1 -3
- dstack/_internal/core/services/profiles.py +7 -16
- dstack/_internal/core/services/repos.py +0 -4
- dstack/_internal/server/app.py +0 -3
- dstack/_internal/server/background/tasks/process_gateways.py +4 -8
- dstack/_internal/server/background/tasks/process_instances.py +14 -9
- dstack/_internal/server/background/tasks/process_metrics.py +1 -1
- dstack/_internal/server/background/tasks/process_placement_groups.py +4 -1
- dstack/_internal/server/background/tasks/process_prometheus_metrics.py +1 -1
- dstack/_internal/server/background/tasks/process_running_jobs.py +14 -5
- dstack/_internal/server/background/tasks/process_submitted_jobs.py +16 -37
- dstack/_internal/server/background/tasks/process_volumes.py +5 -2
- dstack/_internal/server/migrations/versions/7bc2586e8b9e_make_instancemodel_pool_id_optional.py +36 -0
- dstack/_internal/server/migrations/versions/bc8ca4a505c6_store_backendtype_as_string.py +171 -0
- dstack/_internal/server/models.py +48 -9
- dstack/_internal/server/routers/backends.py +14 -23
- dstack/_internal/server/routers/instances.py +3 -4
- dstack/_internal/server/routers/metrics.py +10 -8
- dstack/_internal/server/routers/prometheus.py +1 -1
- dstack/_internal/server/routers/repos.py +1 -2
- dstack/_internal/server/routers/runs.py +13 -59
- dstack/_internal/server/schemas/gateways.py +14 -23
- dstack/_internal/server/schemas/projects.py +7 -2
- dstack/_internal/server/schemas/repos.py +2 -38
- dstack/_internal/server/schemas/runner.py +1 -0
- dstack/_internal/server/schemas/runs.py +1 -24
- dstack/_internal/server/services/backends/__init__.py +85 -158
- dstack/_internal/server/services/config.py +52 -576
- dstack/_internal/server/services/fleets.py +8 -103
- dstack/_internal/server/services/gateways/__init__.py +12 -4
- dstack/_internal/server/services/{pools.py → instances.py} +22 -329
- dstack/_internal/server/services/jobs/__init__.py +9 -6
- dstack/_internal/server/services/jobs/configurators/base.py +16 -0
- dstack/_internal/server/services/jobs/configurators/dev.py +9 -1
- dstack/_internal/server/services/jobs/configurators/extensions/cursor.py +42 -0
- dstack/_internal/server/services/metrics.py +39 -13
- dstack/_internal/server/services/offers.py +1 -1
- dstack/_internal/server/services/projects.py +23 -14
- dstack/_internal/server/services/prometheus.py +176 -18
- dstack/_internal/server/services/runs.py +24 -16
- dstack/_internal/server/services/volumes.py +8 -4
- dstack/_internal/server/statics/index.html +1 -1
- dstack/_internal/server/statics/{main-4eb116b97819badd1e2c.js → main-4a0fe83e84574654e397.js} +18 -14
- dstack/_internal/server/statics/{main-4eb116b97819badd1e2c.js.map → main-4a0fe83e84574654e397.js.map} +1 -1
- dstack/_internal/server/testing/common.py +58 -32
- dstack/_internal/utils/json_schema.py +6 -0
- dstack/_internal/utils/ssh.py +2 -1
- dstack/api/__init__.py +4 -0
- dstack/api/_public/__init__.py +16 -20
- dstack/api/_public/backends.py +1 -1
- dstack/api/_public/repos.py +36 -36
- dstack/api/_public/runs.py +167 -83
- dstack/api/server/__init__.py +11 -13
- dstack/api/server/_backends.py +12 -16
- dstack/api/server/_fleets.py +15 -57
- dstack/api/server/_gateways.py +3 -14
- dstack/api/server/_repos.py +1 -4
- dstack/api/server/_runs.py +21 -100
- dstack/api/server/_volumes.py +10 -5
- dstack/version.py +1 -1
- {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/METADATA +1 -1
- {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/RECORD +218 -204
- tests/_internal/cli/services/configurators/test_profile.py +6 -6
- tests/_internal/core/backends/aws/test_configurator.py +35 -0
- tests/_internal/core/backends/aws/test_resources.py +1 -1
- tests/_internal/core/backends/azure/test_configurator.py +61 -0
- tests/_internal/core/backends/cudo/__init__.py +0 -0
- tests/_internal/core/backends/cudo/test_configurator.py +37 -0
- tests/_internal/core/backends/datacrunch/__init__.py +0 -0
- tests/_internal/core/backends/datacrunch/test_configurator.py +17 -0
- tests/_internal/core/backends/gcp/test_configurator.py +42 -0
- tests/_internal/core/backends/kubernetes/test_configurator.py +43 -0
- tests/_internal/core/backends/lambdalabs/__init__.py +0 -0
- tests/_internal/core/backends/lambdalabs/test_configurator.py +38 -0
- tests/_internal/core/backends/oci/test_configurator.py +55 -0
- tests/_internal/core/backends/runpod/__init__.py +0 -0
- tests/_internal/core/backends/runpod/test_configurator.py +33 -0
- tests/_internal/core/backends/tensordock/__init__.py +0 -0
- tests/_internal/core/backends/tensordock/test_configurator.py +38 -0
- tests/_internal/core/backends/vastai/__init__.py +0 -0
- tests/_internal/core/backends/vastai/test_configurator.py +33 -0
- tests/_internal/core/backends/vultr/__init__.py +0 -0
- tests/_internal/core/backends/vultr/test_configurator.py +33 -0
- tests/_internal/server/background/tasks/test_process_gateways.py +4 -0
- tests/_internal/server/background/tasks/test_process_instances.py +49 -48
- tests/_internal/server/background/tasks/test_process_metrics.py +0 -3
- tests/_internal/server/background/tasks/test_process_placement_groups.py +2 -0
- tests/_internal/server/background/tasks/test_process_prometheus_metrics.py +0 -3
- tests/_internal/server/background/tasks/test_process_running_jobs.py +0 -21
- tests/_internal/server/background/tasks/test_process_runs.py +8 -22
- tests/_internal/server/background/tasks/test_process_submitted_jobs.py +3 -40
- tests/_internal/server/background/tasks/test_process_submitted_volumes.py +2 -0
- tests/_internal/server/background/tasks/test_process_terminating_jobs.py +10 -15
- tests/_internal/server/routers/test_backends.py +6 -764
- tests/_internal/server/routers/test_fleets.py +0 -26
- tests/_internal/server/routers/test_gateways.py +27 -3
- tests/_internal/server/routers/test_instances.py +0 -10
- tests/_internal/server/routers/test_metrics.py +27 -0
- tests/_internal/server/routers/test_projects.py +56 -0
- tests/_internal/server/routers/test_prometheus.py +116 -27
- tests/_internal/server/routers/test_repos.py +0 -15
- tests/_internal/server/routers/test_runs.py +4 -219
- tests/_internal/server/routers/test_volumes.py +2 -3
- tests/_internal/server/services/backends/__init__.py +0 -0
- tests/_internal/server/services/jobs/configurators/test_task.py +35 -0
- tests/_internal/server/services/test_config.py +7 -4
- tests/_internal/server/services/test_fleets.py +1 -4
- tests/_internal/server/services/{test_pools.py → test_instances.py} +11 -49
- tests/_internal/server/services/test_metrics.py +9 -5
- tests/_internal/server/services/test_repos.py +1 -14
- tests/_internal/server/services/test_runs.py +0 -4
- dstack/_internal/cli/commands/pool.py +0 -581
- dstack/_internal/cli/commands/run.py +0 -75
- dstack/_internal/core/backends/aws/config.py +0 -18
- dstack/_internal/core/backends/azure/config.py +0 -12
- dstack/_internal/core/backends/base/config.py +0 -5
- dstack/_internal/core/backends/cudo/config.py +0 -9
- dstack/_internal/core/backends/datacrunch/config.py +0 -9
- dstack/_internal/core/backends/gcp/config.py +0 -22
- dstack/_internal/core/backends/kubernetes/config.py +0 -6
- dstack/_internal/core/backends/lambdalabs/config.py +0 -9
- dstack/_internal/core/backends/nebius/__init__.py +0 -15
- dstack/_internal/core/backends/nebius/api_client.py +0 -319
- dstack/_internal/core/backends/nebius/compute.py +0 -220
- dstack/_internal/core/backends/nebius/config.py +0 -6
- dstack/_internal/core/backends/nebius/types.py +0 -37
- dstack/_internal/core/backends/oci/config.py +0 -6
- dstack/_internal/core/backends/runpod/config.py +0 -17
- dstack/_internal/core/backends/tensordock/config.py +0 -9
- dstack/_internal/core/backends/vastai/config.py +0 -6
- dstack/_internal/core/backends/vultr/config.py +0 -9
- dstack/_internal/core/models/backends/aws.py +0 -86
- dstack/_internal/core/models/backends/azure.py +0 -68
- dstack/_internal/core/models/backends/cudo.py +0 -43
- dstack/_internal/core/models/backends/datacrunch.py +0 -44
- dstack/_internal/core/models/backends/gcp.py +0 -67
- dstack/_internal/core/models/backends/kubernetes.py +0 -40
- dstack/_internal/core/models/backends/lambdalabs.py +0 -43
- dstack/_internal/core/models/backends/nebius.py +0 -54
- dstack/_internal/core/models/backends/runpod.py +0 -42
- dstack/_internal/core/models/backends/tensordock.py +0 -44
- dstack/_internal/core/models/backends/vastai.py +0 -43
- dstack/_internal/core/models/backends/vultr.py +0 -40
- dstack/_internal/core/models/pools.py +0 -43
- dstack/_internal/server/routers/pools.py +0 -142
- dstack/_internal/server/schemas/pools.py +0 -38
- dstack/_internal/server/services/backends/configurators/base.py +0 -72
- dstack/_internal/server/services/backends/configurators/cudo.py +0 -87
- dstack/_internal/server/services/backends/configurators/datacrunch.py +0 -79
- dstack/_internal/server/services/backends/configurators/kubernetes.py +0 -63
- dstack/_internal/server/services/backends/configurators/lambdalabs.py +0 -98
- dstack/_internal/server/services/backends/configurators/nebius.py +0 -85
- dstack/_internal/server/services/backends/configurators/runpod.py +0 -67
- dstack/_internal/server/services/backends/configurators/tensordock.py +0 -82
- dstack/_internal/server/services/backends/configurators/vastai.py +0 -80
- dstack/_internal/server/services/backends/configurators/vultr.py +0 -80
- dstack/api/_public/pools.py +0 -41
- dstack/api/_public/resources.py +0 -105
- dstack/api/server/_pools.py +0 -63
- tests/_internal/server/routers/test_pools.py +0 -612
- /dstack/_internal/{server/services/backends/configurators → core/backends/dstack}/__init__.py +0 -0
- {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/LICENSE.md +0 -0
- {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/WHEEL +0 -0
- {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/entry_points.txt +0 -0
- {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/top_level.txt +0 -0
|
@@ -27,7 +27,6 @@ from dstack._internal.server.testing.common import (
|
|
|
27
27
|
create_fleet,
|
|
28
28
|
create_instance,
|
|
29
29
|
create_job,
|
|
30
|
-
create_pool,
|
|
31
30
|
create_project,
|
|
32
31
|
create_repo,
|
|
33
32
|
create_run,
|
|
@@ -346,8 +345,6 @@ class TestCreateFleet:
|
|
|
346
345
|
"retry": None,
|
|
347
346
|
"max_price": None,
|
|
348
347
|
"idle_duration": None,
|
|
349
|
-
"termination_policy": None,
|
|
350
|
-
"termination_idle_time": None,
|
|
351
348
|
"type": "fleet",
|
|
352
349
|
"name": "test-fleet",
|
|
353
350
|
"reservation": None,
|
|
@@ -360,16 +357,11 @@ class TestCreateFleet:
|
|
|
360
357
|
"instance_types": None,
|
|
361
358
|
"spot_policy": None,
|
|
362
359
|
"retry": None,
|
|
363
|
-
"retry_policy": None,
|
|
364
360
|
"max_duration": None,
|
|
365
361
|
"stop_duration": None,
|
|
366
362
|
"max_price": None,
|
|
367
|
-
"pool_name": None,
|
|
368
|
-
"instance_name": None,
|
|
369
363
|
"creation_policy": None,
|
|
370
364
|
"idle_duration": None,
|
|
371
|
-
"termination_policy": None,
|
|
372
|
-
"termination_idle_time": None,
|
|
373
365
|
"utilization_policy": None,
|
|
374
366
|
"name": "",
|
|
375
367
|
"default": False,
|
|
@@ -394,7 +386,6 @@ class TestCreateFleet:
|
|
|
394
386
|
"unreachable": False,
|
|
395
387
|
"termination_reason": None,
|
|
396
388
|
"created": "2023-01-02T03:04:00+00:00",
|
|
397
|
-
"pool_name": None,
|
|
398
389
|
"backend": None,
|
|
399
390
|
"region": None,
|
|
400
391
|
"availability_zone": None,
|
|
@@ -472,8 +463,6 @@ class TestCreateFleet:
|
|
|
472
463
|
"retry": None,
|
|
473
464
|
"max_price": None,
|
|
474
465
|
"idle_duration": None,
|
|
475
|
-
"termination_policy": None,
|
|
476
|
-
"termination_idle_time": None,
|
|
477
466
|
"type": "fleet",
|
|
478
467
|
"name": spec.configuration.name,
|
|
479
468
|
"reservation": None,
|
|
@@ -486,16 +475,11 @@ class TestCreateFleet:
|
|
|
486
475
|
"instance_types": None,
|
|
487
476
|
"spot_policy": None,
|
|
488
477
|
"retry": None,
|
|
489
|
-
"retry_policy": None,
|
|
490
478
|
"max_duration": None,
|
|
491
479
|
"stop_duration": None,
|
|
492
480
|
"max_price": None,
|
|
493
|
-
"pool_name": None,
|
|
494
|
-
"instance_name": None,
|
|
495
481
|
"creation_policy": None,
|
|
496
482
|
"idle_duration": None,
|
|
497
|
-
"termination_policy": None,
|
|
498
|
-
"termination_idle_time": None,
|
|
499
483
|
"utilization_policy": None,
|
|
500
484
|
"name": "",
|
|
501
485
|
"default": False,
|
|
@@ -526,7 +510,6 @@ class TestCreateFleet:
|
|
|
526
510
|
"fleet_id": "1b0e1b45-2f8c-4ab6-8010-a0d1a3e44e0e",
|
|
527
511
|
"fleet_name": spec.configuration.name,
|
|
528
512
|
"instance_num": 0,
|
|
529
|
-
"pool_name": None,
|
|
530
513
|
"job_name": None,
|
|
531
514
|
"hostname": "1.1.1.1",
|
|
532
515
|
"status": "pending",
|
|
@@ -627,12 +610,10 @@ class TestDeleteFleets:
|
|
|
627
610
|
await add_project_member(
|
|
628
611
|
session=session, project=project, user=user, project_role=ProjectRole.USER
|
|
629
612
|
)
|
|
630
|
-
pool = await create_pool(session=session, project=project)
|
|
631
613
|
fleet = await create_fleet(session=session, project=project)
|
|
632
614
|
instance = await create_instance(
|
|
633
615
|
session=session,
|
|
634
616
|
project=project,
|
|
635
|
-
pool=pool,
|
|
636
617
|
)
|
|
637
618
|
fleet.instances.append(instance)
|
|
638
619
|
await session.commit()
|
|
@@ -654,7 +635,6 @@ class TestDeleteFleets:
|
|
|
654
635
|
):
|
|
655
636
|
user = await create_user(session, global_role=GlobalRole.USER)
|
|
656
637
|
project = await create_project(session)
|
|
657
|
-
pool = await create_pool(session=session, project=project)
|
|
658
638
|
await add_project_member(
|
|
659
639
|
session=session, project=project, user=user, project_role=ProjectRole.USER
|
|
660
640
|
)
|
|
@@ -676,7 +656,6 @@ class TestDeleteFleets:
|
|
|
676
656
|
instance = await create_instance(
|
|
677
657
|
session=session,
|
|
678
658
|
project=project,
|
|
679
|
-
pool=pool,
|
|
680
659
|
status=InstanceStatus.BUSY,
|
|
681
660
|
job=job,
|
|
682
661
|
)
|
|
@@ -744,18 +723,15 @@ class TestDeleteFleetInstances:
|
|
|
744
723
|
await add_project_member(
|
|
745
724
|
session=session, project=project, user=user, project_role=ProjectRole.USER
|
|
746
725
|
)
|
|
747
|
-
pool = await create_pool(session=session, project=project)
|
|
748
726
|
fleet = await create_fleet(session=session, project=project)
|
|
749
727
|
instance1 = await create_instance(
|
|
750
728
|
session=session,
|
|
751
729
|
project=project,
|
|
752
|
-
pool=pool,
|
|
753
730
|
instance_num=1,
|
|
754
731
|
)
|
|
755
732
|
instance2 = await create_instance(
|
|
756
733
|
session=session,
|
|
757
734
|
project=project,
|
|
758
|
-
pool=pool,
|
|
759
735
|
instance_num=2,
|
|
760
736
|
)
|
|
761
737
|
fleet.instances.append(instance1)
|
|
@@ -785,7 +761,6 @@ class TestDeleteFleetInstances:
|
|
|
785
761
|
await add_project_member(
|
|
786
762
|
session=session, project=project, user=user, project_role=ProjectRole.USER
|
|
787
763
|
)
|
|
788
|
-
pool = await create_pool(session=session, project=project)
|
|
789
764
|
fleet = await create_fleet(session=session, project=project)
|
|
790
765
|
repo = await create_repo(
|
|
791
766
|
session=session,
|
|
@@ -804,7 +779,6 @@ class TestDeleteFleetInstances:
|
|
|
804
779
|
instance = await create_instance(
|
|
805
780
|
session=session,
|
|
806
781
|
project=project,
|
|
807
|
-
pool=pool,
|
|
808
782
|
instance_num=1,
|
|
809
783
|
status=InstanceStatus.BUSY,
|
|
810
784
|
job=job,
|
|
@@ -13,6 +13,7 @@ from dstack._internal.server.services.gateways import (
|
|
|
13
13
|
)
|
|
14
14
|
from dstack._internal.server.services.projects import add_project_member
|
|
15
15
|
from dstack._internal.server.testing.common import (
|
|
16
|
+
ComputeMockSpec,
|
|
16
17
|
create_backend,
|
|
17
18
|
create_gateway,
|
|
18
19
|
create_gateway_compute,
|
|
@@ -174,7 +175,14 @@ class TestCreateGateway:
|
|
|
174
175
|
backend = await create_backend(session, project.id, backend_type=BackendType.AWS)
|
|
175
176
|
response = await client.post(
|
|
176
177
|
f"/api/project/{project.name}/gateways/create",
|
|
177
|
-
json={
|
|
178
|
+
json={
|
|
179
|
+
"configuration": {
|
|
180
|
+
"type": "gateway",
|
|
181
|
+
"name": "test",
|
|
182
|
+
"backend": "aws",
|
|
183
|
+
"region": "us",
|
|
184
|
+
},
|
|
185
|
+
},
|
|
178
186
|
headers=get_auth_headers(user.token),
|
|
179
187
|
)
|
|
180
188
|
assert response.status_code == 200
|
|
@@ -217,7 +225,14 @@ class TestCreateGateway:
|
|
|
217
225
|
g.return_value = "random-name"
|
|
218
226
|
response = await client.post(
|
|
219
227
|
f"/api/project/{project.name}/gateways/create",
|
|
220
|
-
json={
|
|
228
|
+
json={
|
|
229
|
+
"configuration": {
|
|
230
|
+
"type": "gateway",
|
|
231
|
+
"name": None,
|
|
232
|
+
"backend": "aws",
|
|
233
|
+
"region": "us",
|
|
234
|
+
},
|
|
235
|
+
},
|
|
221
236
|
headers=get_auth_headers(user.token),
|
|
222
237
|
)
|
|
223
238
|
g.assert_called_once()
|
|
@@ -258,7 +273,14 @@ class TestCreateGateway:
|
|
|
258
273
|
)
|
|
259
274
|
response = await client.post(
|
|
260
275
|
f"/api/project/{project.name}/gateways/create",
|
|
261
|
-
json={
|
|
276
|
+
json={
|
|
277
|
+
"configuration": {
|
|
278
|
+
"type": "gateway",
|
|
279
|
+
"name": "test",
|
|
280
|
+
"backend": "aws",
|
|
281
|
+
"region": "us",
|
|
282
|
+
},
|
|
283
|
+
},
|
|
262
284
|
headers=get_auth_headers(user.token),
|
|
263
285
|
)
|
|
264
286
|
assert response.status_code == 400
|
|
@@ -437,8 +459,10 @@ class TestDeleteGateway:
|
|
|
437
459
|
"dstack._internal.server.services.gateways.get_project_backend_by_type_or_error"
|
|
438
460
|
) as m:
|
|
439
461
|
aws = Mock()
|
|
462
|
+
aws.compute.return_value = Mock(spec=ComputeMockSpec)
|
|
440
463
|
aws.compute.return_value.terminate_gateway.return_value = None # success
|
|
441
464
|
gcp = Mock()
|
|
465
|
+
gcp.compute.return_value = Mock(spec=ComputeMockSpec)
|
|
442
466
|
gcp.compute.return_value.terminate_gateway.side_effect = DstackError() # fail
|
|
443
467
|
|
|
444
468
|
def get_backend(project, backend_type):
|
|
@@ -15,7 +15,6 @@ from dstack._internal.server.services.projects import add_project_member
|
|
|
15
15
|
from dstack._internal.server.testing.common import (
|
|
16
16
|
create_fleet,
|
|
17
17
|
create_instance,
|
|
18
|
-
create_pool,
|
|
19
18
|
create_project,
|
|
20
19
|
create_user,
|
|
21
20
|
get_auth_headers,
|
|
@@ -59,11 +58,6 @@ class TestListInstances:
|
|
|
59
58
|
await add_project_member(
|
|
60
59
|
session, project=projects[2], user=users[1], project_role=ProjectRole.USER
|
|
61
60
|
)
|
|
62
|
-
pools = [
|
|
63
|
-
await create_pool(session, projects[0]),
|
|
64
|
-
await create_pool(session, projects[1]),
|
|
65
|
-
await create_pool(session, projects[2]),
|
|
66
|
-
]
|
|
67
61
|
fleets = [
|
|
68
62
|
await create_fleet(
|
|
69
63
|
session,
|
|
@@ -88,7 +82,6 @@ class TestListInstances:
|
|
|
88
82
|
await create_instance(
|
|
89
83
|
session=session,
|
|
90
84
|
project=projects[0],
|
|
91
|
-
pool=pools[0],
|
|
92
85
|
fleet=fleets[0],
|
|
93
86
|
created_at=dt.datetime(2024, 1, 1, tzinfo=dt.timezone.utc),
|
|
94
87
|
name="fleet0-0",
|
|
@@ -96,7 +89,6 @@ class TestListInstances:
|
|
|
96
89
|
await create_instance(
|
|
97
90
|
session=session,
|
|
98
91
|
project=projects[1],
|
|
99
|
-
pool=pools[1],
|
|
100
92
|
fleet=fleets[1],
|
|
101
93
|
created_at=dt.datetime(2024, 1, 2, tzinfo=dt.timezone.utc),
|
|
102
94
|
name="fleet1-0",
|
|
@@ -104,7 +96,6 @@ class TestListInstances:
|
|
|
104
96
|
await create_instance(
|
|
105
97
|
session=session,
|
|
106
98
|
project=projects[2],
|
|
107
|
-
pool=pools[2],
|
|
108
99
|
fleet=fleets[2],
|
|
109
100
|
created_at=dt.datetime(2024, 1, 3, tzinfo=dt.timezone.utc),
|
|
110
101
|
name="fleet2-0",
|
|
@@ -112,7 +103,6 @@ class TestListInstances:
|
|
|
112
103
|
await create_instance(
|
|
113
104
|
session=session,
|
|
114
105
|
project=projects[2],
|
|
115
|
-
pool=pools[2],
|
|
116
106
|
fleet=fleets[2],
|
|
117
107
|
created_at=dt.datetime(2024, 1, 4, tzinfo=dt.timezone.utc),
|
|
118
108
|
instance_num=1,
|
|
@@ -14,6 +14,9 @@ from dstack._internal.server.testing.common import (
|
|
|
14
14
|
create_run,
|
|
15
15
|
create_user,
|
|
16
16
|
get_auth_headers,
|
|
17
|
+
get_instance_offer_with_availability,
|
|
18
|
+
get_job_provisioning_data,
|
|
19
|
+
get_job_runtime_data,
|
|
17
20
|
)
|
|
18
21
|
|
|
19
22
|
pytestmark = pytest.mark.usefixtures("image_config_mock")
|
|
@@ -51,9 +54,18 @@ class TestGetJobMetrics:
|
|
|
51
54
|
repo=repo,
|
|
52
55
|
user=user,
|
|
53
56
|
)
|
|
57
|
+
jpd = get_job_provisioning_data(
|
|
58
|
+
cpu_count=128, memory_gib=256, gpu_count=2, gpu_memory_gib=32
|
|
59
|
+
)
|
|
60
|
+
offer = get_instance_offer_with_availability(
|
|
61
|
+
cpu_count=64, memory_gib=128, gpu_count=1, gpu_memory_gib=32
|
|
62
|
+
)
|
|
63
|
+
jrd = get_job_runtime_data(offer=offer)
|
|
54
64
|
job = await create_job(
|
|
55
65
|
session=session,
|
|
56
66
|
run=run,
|
|
67
|
+
job_provisioning_data=jpd,
|
|
68
|
+
job_runtime_data=jrd,
|
|
57
69
|
)
|
|
58
70
|
await create_job_metrics_point(
|
|
59
71
|
session=session,
|
|
@@ -108,11 +120,26 @@ class TestGetJobMetrics:
|
|
|
108
120
|
"timestamps": ["2023-01-02T03:04:25+00:00"],
|
|
109
121
|
"values": [512],
|
|
110
122
|
},
|
|
123
|
+
{
|
|
124
|
+
"name": "cpus_detected_num",
|
|
125
|
+
"timestamps": ["2023-01-02T03:04:25+00:00"],
|
|
126
|
+
"values": [64],
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"name": "memory_total_bytes",
|
|
130
|
+
"timestamps": ["2023-01-02T03:04:25+00:00"],
|
|
131
|
+
"values": [137438953472],
|
|
132
|
+
},
|
|
111
133
|
{
|
|
112
134
|
"name": "gpus_detected_num",
|
|
113
135
|
"timestamps": ["2023-01-02T03:04:25+00:00"],
|
|
114
136
|
"values": [1],
|
|
115
137
|
},
|
|
138
|
+
{
|
|
139
|
+
"name": "gpu_memory_total_bytes",
|
|
140
|
+
"timestamps": ["2023-01-02T03:04:25+00:00"],
|
|
141
|
+
"values": [34359738368],
|
|
142
|
+
},
|
|
116
143
|
{
|
|
117
144
|
"name": "gpu_memory_usage_bytes_gpu0",
|
|
118
145
|
"timestamps": ["2023-01-02T03:04:25+00:00"],
|
|
@@ -503,6 +503,62 @@ class TestSetProjectMembers:
|
|
|
503
503
|
members = res.scalars().all()
|
|
504
504
|
assert len(members) == 3
|
|
505
505
|
|
|
506
|
+
@pytest.mark.asyncio
|
|
507
|
+
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
508
|
+
async def test_sets_project_members_by_email(
|
|
509
|
+
self, test_db, session: AsyncSession, client: AsyncClient
|
|
510
|
+
):
|
|
511
|
+
project = await create_project(
|
|
512
|
+
session=session,
|
|
513
|
+
created_at=datetime(2023, 1, 2, 3, 4, tzinfo=timezone.utc),
|
|
514
|
+
)
|
|
515
|
+
admin = await create_user(
|
|
516
|
+
session=session,
|
|
517
|
+
created_at=datetime(2023, 1, 2, 3, 4, tzinfo=timezone.utc),
|
|
518
|
+
global_role=GlobalRole.ADMIN,
|
|
519
|
+
)
|
|
520
|
+
user1 = await create_user(
|
|
521
|
+
session=session,
|
|
522
|
+
name="user1",
|
|
523
|
+
created_at=datetime(2023, 1, 2, 3, 4, tzinfo=timezone.utc),
|
|
524
|
+
email="testemail@example.com",
|
|
525
|
+
)
|
|
526
|
+
members = [
|
|
527
|
+
{
|
|
528
|
+
"username": user1.email,
|
|
529
|
+
"project_role": ProjectRole.ADMIN,
|
|
530
|
+
},
|
|
531
|
+
]
|
|
532
|
+
body = {"members": members}
|
|
533
|
+
response = await client.post(
|
|
534
|
+
f"/api/projects/{project.name}/set_members",
|
|
535
|
+
headers=get_auth_headers(admin.token),
|
|
536
|
+
json=body,
|
|
537
|
+
)
|
|
538
|
+
assert response.status_code == 200, response.json()
|
|
539
|
+
assert response.json()["members"] == [
|
|
540
|
+
{
|
|
541
|
+
"user": {
|
|
542
|
+
"id": str(user1.id),
|
|
543
|
+
"username": user1.name,
|
|
544
|
+
"created_at": "2023-01-02T03:04:00+00:00",
|
|
545
|
+
"global_role": user1.global_role,
|
|
546
|
+
"email": user1.email,
|
|
547
|
+
"active": True,
|
|
548
|
+
"permissions": {
|
|
549
|
+
"can_create_projects": True,
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
"project_role": ProjectRole.ADMIN,
|
|
553
|
+
"permissions": {
|
|
554
|
+
"can_manage_ssh_fleets": True,
|
|
555
|
+
},
|
|
556
|
+
},
|
|
557
|
+
]
|
|
558
|
+
res = await session.execute(select(MemberModel))
|
|
559
|
+
members = res.scalars().all()
|
|
560
|
+
assert len(members) == 1
|
|
561
|
+
|
|
506
562
|
@pytest.mark.asyncio
|
|
507
563
|
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
508
564
|
async def test_manager_cannot_set_project_admins(
|
|
@@ -1,20 +1,31 @@
|
|
|
1
|
+
from datetime import datetime, timedelta, timezone
|
|
1
2
|
from textwrap import dedent
|
|
3
|
+
from typing import Optional
|
|
2
4
|
|
|
3
5
|
import pytest
|
|
6
|
+
from freezegun import freeze_time
|
|
4
7
|
from httpx import AsyncClient
|
|
5
8
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
6
9
|
|
|
7
|
-
from dstack._internal.core.models.
|
|
10
|
+
from dstack._internal.core.models.backends.base import BackendType
|
|
11
|
+
from dstack._internal.core.models.configurations import DevEnvironmentConfiguration
|
|
12
|
+
from dstack._internal.core.models.runs import JobProvisioningData, JobRuntimeData, JobStatus
|
|
8
13
|
from dstack._internal.core.models.users import GlobalRole, ProjectRole
|
|
9
14
|
from dstack._internal.server.models import JobModel, ProjectModel, UserModel
|
|
10
15
|
from dstack._internal.server.services.projects import add_project_member
|
|
11
16
|
from dstack._internal.server.testing.common import (
|
|
17
|
+
create_fleet,
|
|
18
|
+
create_instance,
|
|
12
19
|
create_job,
|
|
13
20
|
create_job_prometheus_metrics,
|
|
14
21
|
create_project,
|
|
15
22
|
create_repo,
|
|
16
23
|
create_run,
|
|
17
24
|
create_user,
|
|
25
|
+
get_instance_offer_with_availability,
|
|
26
|
+
get_job_provisioning_data,
|
|
27
|
+
get_job_runtime_data,
|
|
28
|
+
get_run_spec,
|
|
18
29
|
)
|
|
19
30
|
|
|
20
31
|
|
|
@@ -23,14 +34,32 @@ def enable_metrics(monkeypatch: pytest.MonkeyPatch):
|
|
|
23
34
|
monkeypatch.setattr("dstack._internal.server.settings.ENABLE_PROMETHEUS_METRICS", True)
|
|
24
35
|
|
|
25
36
|
|
|
37
|
+
FAKE_NOW = datetime(2023, 1, 2, 3, 4, tzinfo=timezone.utc)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@freeze_time(FAKE_NOW)
|
|
26
41
|
@pytest.mark.asyncio
|
|
27
42
|
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
28
43
|
@pytest.mark.usefixtures("image_config_mock", "test_db", "enable_metrics")
|
|
29
44
|
class TestGetPrometheusMetrics:
|
|
30
45
|
async def test_returns_metrics(self, session: AsyncSession, client: AsyncClient):
|
|
31
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
46
|
+
user = await create_user(session=session, name="test-user", global_role=GlobalRole.USER)
|
|
47
|
+
offer = get_instance_offer_with_availability(
|
|
48
|
+
instance_type="test-type", gpu_count=2, gpu_name="V4", price=12
|
|
49
|
+
)
|
|
32
50
|
project_2 = await _create_project(session, "project-2", user)
|
|
33
|
-
|
|
51
|
+
jpd_2_1 = get_job_provisioning_data(
|
|
52
|
+
backend=BackendType.AWS, gpu_name="T4", gpu_count=2, price=16
|
|
53
|
+
)
|
|
54
|
+
job_2_1 = await _create_job(
|
|
55
|
+
session=session,
|
|
56
|
+
run_name="run-1",
|
|
57
|
+
project=project_2,
|
|
58
|
+
user=user,
|
|
59
|
+
status=JobStatus.RUNNING,
|
|
60
|
+
job_provisioning_data=jpd_2_1,
|
|
61
|
+
submitted_at=FAKE_NOW - timedelta(seconds=100),
|
|
62
|
+
)
|
|
34
63
|
await create_job_prometheus_metrics(
|
|
35
64
|
session=session,
|
|
36
65
|
job=job_2_1,
|
|
@@ -42,7 +71,18 @@ class TestGetPrometheusMetrics:
|
|
|
42
71
|
"""),
|
|
43
72
|
)
|
|
44
73
|
project_1 = await _create_project(session, "project-1", user)
|
|
45
|
-
|
|
74
|
+
jpd_1_1 = get_job_provisioning_data(backend=BackendType.AWS, gpu_count=4, gpu_name="T4")
|
|
75
|
+
jrd_1_1 = get_job_runtime_data(offer=offer)
|
|
76
|
+
job_1_1 = await _create_job(
|
|
77
|
+
session=session,
|
|
78
|
+
run_name="run-1",
|
|
79
|
+
project=project_1,
|
|
80
|
+
user=user,
|
|
81
|
+
status=JobStatus.RUNNING,
|
|
82
|
+
job_provisioning_data=jpd_1_1,
|
|
83
|
+
job_runtime_data=jrd_1_1,
|
|
84
|
+
submitted_at=FAKE_NOW - timedelta(seconds=120),
|
|
85
|
+
)
|
|
46
86
|
await create_job_prometheus_metrics(
|
|
47
87
|
session=session,
|
|
48
88
|
job=job_1_1,
|
|
@@ -84,30 +124,62 @@ class TestGetPrometheusMetrics:
|
|
|
84
124
|
FIELD_1{gpu="1"} 20
|
|
85
125
|
"""),
|
|
86
126
|
)
|
|
127
|
+
fleet = await create_fleet(session=session, project=project_1, name="test-fleet")
|
|
128
|
+
instance = await create_instance(
|
|
129
|
+
session=session,
|
|
130
|
+
project=project_1,
|
|
131
|
+
fleet=fleet,
|
|
132
|
+
backend=BackendType.AWS,
|
|
133
|
+
offer=offer,
|
|
134
|
+
price=14,
|
|
135
|
+
created_at=FAKE_NOW - timedelta(hours=1),
|
|
136
|
+
name="test-instance",
|
|
137
|
+
)
|
|
87
138
|
|
|
88
139
|
response = await client.get("/metrics")
|
|
89
140
|
|
|
90
141
|
assert response.status_code == 200
|
|
91
|
-
assert response.text == dedent("""\
|
|
142
|
+
assert response.text == dedent(f"""\
|
|
143
|
+
# HELP dstack_instance_duration_seconds_total Total seconds the instance is running
|
|
144
|
+
# TYPE dstack_instance_duration_seconds_total counter
|
|
145
|
+
dstack_instance_duration_seconds_total{{dstack_project_name="project-1",dstack_fleet_name="test-fleet",dstack_fleet_id="{fleet.id}",dstack_instance_name="test-instance",dstack_instance_id="{instance.id}",dstack_instance_type="test-type",dstack_backend="aws",dstack_gpu="V4"}} 3600.0
|
|
146
|
+
# HELP dstack_instance_price_dollars_per_hour Instance price, USD/hour
|
|
147
|
+
# TYPE dstack_instance_price_dollars_per_hour gauge
|
|
148
|
+
dstack_instance_price_dollars_per_hour{{dstack_project_name="project-1",dstack_fleet_name="test-fleet",dstack_fleet_id="{fleet.id}",dstack_instance_name="test-instance",dstack_instance_id="{instance.id}",dstack_instance_type="test-type",dstack_backend="aws",dstack_gpu="V4"}} 14.0
|
|
149
|
+
# HELP dstack_instance_gpu_count Instance GPU count
|
|
150
|
+
# TYPE dstack_instance_gpu_count gauge
|
|
151
|
+
dstack_instance_gpu_count{{dstack_project_name="project-1",dstack_fleet_name="test-fleet",dstack_fleet_id="{fleet.id}",dstack_instance_name="test-instance",dstack_instance_id="{instance.id}",dstack_instance_type="test-type",dstack_backend="aws",dstack_gpu="V4"}} 2.0
|
|
152
|
+
# HELP dstack_job_duration_seconds_total Total seconds the job is running
|
|
153
|
+
# TYPE dstack_job_duration_seconds_total counter
|
|
154
|
+
dstack_job_duration_seconds_total{{dstack_project_name="project-1",dstack_user_name="test-user",dstack_run_name="run-1",dstack_run_id="{job_1_1.run_id}",dstack_job_name="run-1-0-0",dstack_job_id="{job_1_1.id}",dstack_job_num="0",dstack_replica_num="0",dstack_run_type="dev-environment",dstack_backend="aws",dstack_gpu="V4"}} 120.0
|
|
155
|
+
dstack_job_duration_seconds_total{{dstack_project_name="project-2",dstack_user_name="test-user",dstack_run_name="run-1",dstack_run_id="{job_2_1.run_id}",dstack_job_name="run-1-0-0",dstack_job_id="{job_2_1.id}",dstack_job_num="0",dstack_replica_num="0",dstack_run_type="dev-environment",dstack_backend="aws",dstack_gpu="T4"}} 100.0
|
|
156
|
+
# HELP dstack_job_price_dollars_per_hour Job instance price, USD/hour
|
|
157
|
+
# TYPE dstack_job_price_dollars_per_hour gauge
|
|
158
|
+
dstack_job_price_dollars_per_hour{{dstack_project_name="project-1",dstack_user_name="test-user",dstack_run_name="run-1",dstack_run_id="{job_1_1.run_id}",dstack_job_name="run-1-0-0",dstack_job_id="{job_1_1.id}",dstack_job_num="0",dstack_replica_num="0",dstack_run_type="dev-environment",dstack_backend="aws",dstack_gpu="V4"}} 12.0
|
|
159
|
+
dstack_job_price_dollars_per_hour{{dstack_project_name="project-2",dstack_user_name="test-user",dstack_run_name="run-1",dstack_run_id="{job_2_1.run_id}",dstack_job_name="run-1-0-0",dstack_job_id="{job_2_1.id}",dstack_job_num="0",dstack_replica_num="0",dstack_run_type="dev-environment",dstack_backend="aws",dstack_gpu="T4"}} 16.0
|
|
160
|
+
# HELP dstack_job_gpu_count Job GPU count
|
|
161
|
+
# TYPE dstack_job_gpu_count gauge
|
|
162
|
+
dstack_job_gpu_count{{dstack_project_name="project-1",dstack_user_name="test-user",dstack_run_name="run-1",dstack_run_id="{job_1_1.run_id}",dstack_job_name="run-1-0-0",dstack_job_id="{job_1_1.id}",dstack_job_num="0",dstack_replica_num="0",dstack_run_type="dev-environment",dstack_backend="aws",dstack_gpu="V4"}} 2.0
|
|
163
|
+
dstack_job_gpu_count{{dstack_project_name="project-2",dstack_user_name="test-user",dstack_run_name="run-1",dstack_run_id="{job_2_1.run_id}",dstack_job_name="run-1-0-0",dstack_job_id="{job_2_1.id}",dstack_job_num="0",dstack_replica_num="0",dstack_run_type="dev-environment",dstack_backend="aws",dstack_gpu="T4"}} 2.0
|
|
92
164
|
# HELP FIELD_1 Test field 1
|
|
93
165
|
# TYPE FIELD_1 gauge
|
|
94
|
-
FIELD_1{gpu="0",dstack_project_name="project-1",dstack_run_name="run-1",dstack_job_name="run-1-0-0",dstack_job_num="0",dstack_replica_num="0"} 350.0
|
|
95
|
-
FIELD_1{gpu="1",dstack_project_name="project-1",dstack_run_name="run-1",dstack_job_name="run-1-0-0",dstack_job_num="0",dstack_replica_num="0"} 400.0
|
|
96
|
-
FIELD_1{gpu="0",dstack_project_name="project-1",dstack_run_name="run-2",dstack_job_name="run-2-0-0",dstack_job_num="0",dstack_replica_num="0"} 1200.0
|
|
97
|
-
FIELD_1{gpu="1",dstack_project_name="project-1",dstack_run_name="run-2",dstack_job_name="run-2-0-0",dstack_job_num="0",dstack_replica_num="0"} 1600.0
|
|
98
|
-
FIELD_1{gpu="2",dstack_project_name="project-1",dstack_run_name="run-2",dstack_job_name="run-2-0-0",dstack_job_num="0",dstack_replica_num="0"} 2400.0
|
|
99
|
-
FIELD_1{gpu="0",dstack_project_name="project-2",dstack_run_name="run-1",dstack_job_name="run-1-0-0",dstack_job_num="0",dstack_replica_num="0"} 100.0
|
|
100
|
-
FIELD_1{gpu="1",dstack_project_name="project-2",dstack_run_name="run-1",dstack_job_name="run-1-0-0",dstack_job_num="0",dstack_replica_num="0"} 200.0
|
|
166
|
+
FIELD_1{{gpu="0",dstack_project_name="project-1",dstack_user_name="test-user",dstack_run_name="run-1",dstack_run_id="{job_1_1.run_id}",dstack_job_name="run-1-0-0",dstack_job_id="{job_1_1.id}",dstack_job_num="0",dstack_replica_num="0"}} 350.0
|
|
167
|
+
FIELD_1{{gpu="1",dstack_project_name="project-1",dstack_user_name="test-user",dstack_run_name="run-1",dstack_run_id="{job_1_1.run_id}",dstack_job_name="run-1-0-0",dstack_job_id="{job_1_1.id}",dstack_job_num="0",dstack_replica_num="0"}} 400.0
|
|
168
|
+
FIELD_1{{gpu="0",dstack_project_name="project-1",dstack_user_name="test-user",dstack_run_name="run-2",dstack_run_id="{job_1_2.run_id}",dstack_job_name="run-2-0-0",dstack_job_id="{job_1_2.id}",dstack_job_num="0",dstack_replica_num="0"}} 1200.0
|
|
169
|
+
FIELD_1{{gpu="1",dstack_project_name="project-1",dstack_user_name="test-user",dstack_run_name="run-2",dstack_run_id="{job_1_2.run_id}",dstack_job_name="run-2-0-0",dstack_job_id="{job_1_2.id}",dstack_job_num="0",dstack_replica_num="0"}} 1600.0
|
|
170
|
+
FIELD_1{{gpu="2",dstack_project_name="project-1",dstack_user_name="test-user",dstack_run_name="run-2",dstack_run_id="{job_1_2.run_id}",dstack_job_name="run-2-0-0",dstack_job_id="{job_1_2.id}",dstack_job_num="0",dstack_replica_num="0"}} 2400.0
|
|
171
|
+
FIELD_1{{gpu="0",dstack_project_name="project-2",dstack_user_name="test-user",dstack_run_name="run-1",dstack_run_id="{job_2_1.run_id}",dstack_job_name="run-1-0-0",dstack_job_id="{job_2_1.id}",dstack_job_num="0",dstack_replica_num="0"}} 100.0
|
|
172
|
+
FIELD_1{{gpu="1",dstack_project_name="project-2",dstack_user_name="test-user",dstack_run_name="run-1",dstack_run_id="{job_2_1.run_id}",dstack_job_name="run-1-0-0",dstack_job_id="{job_2_1.id}",dstack_job_num="0",dstack_replica_num="0"}} 200.0
|
|
101
173
|
# HELP FIELD_2 Test field 2
|
|
102
174
|
# TYPE FIELD_2 counter
|
|
103
|
-
FIELD_2{gpu="0",dstack_project_name="project-1",dstack_run_name="run-1",dstack_job_name="run-1-0-0",dstack_job_num="0",dstack_replica_num="0"} 337325.0 1395066363000
|
|
104
|
-
FIELD_2{gpu="1",dstack_project_name="project-1",dstack_run_name="run-1",dstack_job_name="run-1-0-0",dstack_job_num="0",dstack_replica_num="0"} 987169.0 1395066363010
|
|
175
|
+
FIELD_2{{gpu="0",dstack_project_name="project-1",dstack_user_name="test-user",dstack_run_name="run-1",dstack_run_id="{job_1_1.run_id}",dstack_job_name="run-1-0-0",dstack_job_id="{job_1_1.id}",dstack_job_num="0",dstack_replica_num="0"}} 337325.0 1395066363000
|
|
176
|
+
FIELD_2{{gpu="1",dstack_project_name="project-1",dstack_user_name="test-user",dstack_run_name="run-1",dstack_run_id="{job_1_1.run_id}",dstack_job_name="run-1-0-0",dstack_job_id="{job_1_1.id}",dstack_job_num="0",dstack_replica_num="0"}} 987169.0 1395066363010
|
|
105
177
|
""")
|
|
106
178
|
|
|
107
179
|
async def test_returns_empty_response_if_no_runs(self, client: AsyncClient):
|
|
108
180
|
response = await client.get("/metrics")
|
|
109
181
|
assert response.status_code == 200
|
|
110
|
-
assert response.text == ""
|
|
182
|
+
assert response.text == "\n"
|
|
111
183
|
|
|
112
184
|
async def test_returns_404_if_not_enabled(
|
|
113
185
|
self, monkeypatch: pytest.MonkeyPatch, client: AsyncClient
|
|
@@ -122,7 +194,7 @@ class TestGetPrometheusMetrics:
|
|
|
122
194
|
@pytest.mark.usefixtures("image_config_mock", "test_db", "enable_metrics")
|
|
123
195
|
class TestGetPrometheusProjectMetrics:
|
|
124
196
|
async def test_returns_metrics(self, session: AsyncSession, client: AsyncClient):
|
|
125
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
197
|
+
user = await create_user(session=session, name="test-user", global_role=GlobalRole.USER)
|
|
126
198
|
project = await _create_project(session, "project-1", user)
|
|
127
199
|
job_1 = await _create_job(session, "run-1", project, user, JobStatus.RUNNING)
|
|
128
200
|
await create_job_prometheus_metrics(
|
|
@@ -184,18 +256,18 @@ class TestGetPrometheusProjectMetrics:
|
|
|
184
256
|
response = await client.get("/metrics/project/project-1")
|
|
185
257
|
|
|
186
258
|
assert response.status_code == 200
|
|
187
|
-
assert response.text == dedent("""\
|
|
259
|
+
assert response.text == dedent(f"""\
|
|
188
260
|
# HELP FIELD_1 Test field 1
|
|
189
261
|
# TYPE FIELD_1 gauge
|
|
190
|
-
FIELD_1{gpu="0",dstack_project_name="project-1",dstack_run_name="run-1",dstack_job_name="run-1-0-0",dstack_job_num="0",dstack_replica_num="0"} 350.0
|
|
191
|
-
FIELD_1{gpu="1",dstack_project_name="project-1",dstack_run_name="run-1",dstack_job_name="run-1-0-0",dstack_job_num="0",dstack_replica_num="0"} 400.0
|
|
192
|
-
FIELD_1{gpu="0",dstack_project_name="project-1",dstack_run_name="run-2",dstack_job_name="run-2-0-0",dstack_job_num="0",dstack_replica_num="0"} 1200.0
|
|
193
|
-
FIELD_1{gpu="1",dstack_project_name="project-1",dstack_run_name="run-2",dstack_job_name="run-2-0-0",dstack_job_num="0",dstack_replica_num="0"} 1600.0
|
|
194
|
-
FIELD_1{gpu="2",dstack_project_name="project-1",dstack_run_name="run-2",dstack_job_name="run-2-0-0",dstack_job_num="0",dstack_replica_num="0"} 2400.0
|
|
262
|
+
FIELD_1{{gpu="0",dstack_project_name="project-1",dstack_user_name="test-user",dstack_run_name="run-1",dstack_run_id="{job_1.run_id}",dstack_job_name="run-1-0-0",dstack_job_id="{job_1.id}",dstack_job_num="0",dstack_replica_num="0"}} 350.0
|
|
263
|
+
FIELD_1{{gpu="1",dstack_project_name="project-1",dstack_user_name="test-user",dstack_run_name="run-1",dstack_run_id="{job_1.run_id}",dstack_job_name="run-1-0-0",dstack_job_id="{job_1.id}",dstack_job_num="0",dstack_replica_num="0"}} 400.0
|
|
264
|
+
FIELD_1{{gpu="0",dstack_project_name="project-1",dstack_user_name="test-user",dstack_run_name="run-2",dstack_run_id="{job_2.run_id}",dstack_job_name="run-2-0-0",dstack_job_id="{job_2.id}",dstack_job_num="0",dstack_replica_num="0"}} 1200.0
|
|
265
|
+
FIELD_1{{gpu="1",dstack_project_name="project-1",dstack_user_name="test-user",dstack_run_name="run-2",dstack_run_id="{job_2.run_id}",dstack_job_name="run-2-0-0",dstack_job_id="{job_2.id}",dstack_job_num="0",dstack_replica_num="0"}} 1600.0
|
|
266
|
+
FIELD_1{{gpu="2",dstack_project_name="project-1",dstack_user_name="test-user",dstack_run_name="run-2",dstack_run_id="{job_2.run_id}",dstack_job_name="run-2-0-0",dstack_job_id="{job_2.id}",dstack_job_num="0",dstack_replica_num="0"}} 2400.0
|
|
195
267
|
# HELP FIELD_2 Test field 2
|
|
196
268
|
# TYPE FIELD_2 counter
|
|
197
|
-
FIELD_2{gpu="0",dstack_project_name="project-1",dstack_run_name="run-1",dstack_job_name="run-1-0-0",dstack_job_num="0",dstack_replica_num="0"} 337325.0 1395066363000
|
|
198
|
-
FIELD_2{gpu="1",dstack_project_name="project-1",dstack_run_name="run-1",dstack_job_name="run-1-0-0",dstack_job_num="0",dstack_replica_num="0"} 987169.0 1395066363010
|
|
269
|
+
FIELD_2{{gpu="0",dstack_project_name="project-1",dstack_user_name="test-user",dstack_run_name="run-1",dstack_run_id="{job_1.run_id}",dstack_job_name="run-1-0-0",dstack_job_id="{job_1.id}",dstack_job_num="0",dstack_replica_num="0"}} 337325.0 1395066363000
|
|
270
|
+
FIELD_2{{gpu="1",dstack_project_name="project-1",dstack_user_name="test-user",dstack_run_name="run-1",dstack_run_id="{job_1.run_id}",dstack_job_name="run-1-0-0",dstack_job_id="{job_1.id}",dstack_job_num="0",dstack_replica_num="0"}} 987169.0 1395066363010
|
|
199
271
|
""")
|
|
200
272
|
|
|
201
273
|
async def test_returns_empty_response_if_no_runs(
|
|
@@ -205,7 +277,7 @@ class TestGetPrometheusProjectMetrics:
|
|
|
205
277
|
await create_project(session=session, owner=user, name="test-project")
|
|
206
278
|
response = await client.get("/metrics/project/test-project")
|
|
207
279
|
assert response.status_code == 200
|
|
208
|
-
assert response.text == ""
|
|
280
|
+
assert response.text == "\n"
|
|
209
281
|
|
|
210
282
|
async def test_returns_404_if_project_doesnt_exist(self, client: AsyncClient):
|
|
211
283
|
response = await client.get("/metrics/project/nonexistent")
|
|
@@ -230,15 +302,32 @@ async def _create_project(session: AsyncSession, name: str, user: UserModel) ->
|
|
|
230
302
|
|
|
231
303
|
|
|
232
304
|
async def _create_job(
|
|
233
|
-
session: AsyncSession,
|
|
305
|
+
session: AsyncSession,
|
|
306
|
+
run_name: str,
|
|
307
|
+
project: ProjectModel,
|
|
308
|
+
user: UserModel,
|
|
309
|
+
status: JobStatus,
|
|
310
|
+
job_provisioning_data: Optional[JobProvisioningData] = None,
|
|
311
|
+
job_runtime_data: Optional[JobRuntimeData] = None,
|
|
312
|
+
submitted_at: datetime = FAKE_NOW,
|
|
234
313
|
) -> JobModel:
|
|
235
314
|
repo = await create_repo(session=session, project_id=project.id, repo_name=f"{run_name}-repo")
|
|
315
|
+
configuration = DevEnvironmentConfiguration(ide="vscode")
|
|
316
|
+
run_spec = get_run_spec(run_name=run_name, repo_id=repo.name, configuration=configuration)
|
|
236
317
|
run = await create_run(
|
|
237
318
|
session=session,
|
|
238
319
|
project=project,
|
|
239
320
|
repo=repo,
|
|
240
321
|
user=user,
|
|
241
322
|
run_name=run_name,
|
|
323
|
+
run_spec=run_spec,
|
|
324
|
+
)
|
|
325
|
+
job = await create_job(
|
|
326
|
+
session=session,
|
|
327
|
+
run=run,
|
|
328
|
+
status=status,
|
|
329
|
+
job_provisioning_data=job_provisioning_data,
|
|
330
|
+
job_runtime_data=job_runtime_data,
|
|
331
|
+
submitted_at=submitted_at,
|
|
242
332
|
)
|
|
243
|
-
job = await create_job(session=session, run=run, status=status)
|
|
244
333
|
return job
|