dstack 0.18.43__py3-none-any.whl → 0.19.0rc1__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.
- 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 -20
- dstack/_internal/cli/services/profile.py +34 -83
- dstack/_internal/cli/utils/gateway.py +1 -1
- dstack/_internal/cli/utils/run.py +11 -0
- 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 +21 -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 +14 -28
- 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 +14 -44
- 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 +28 -6
- 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 +22 -16
- 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 +85 -45
- 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 +21 -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 +11 -4
- dstack/_internal/server/background/__init__.py +10 -0
- 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 +5 -1
- dstack/_internal/server/background/tasks/process_prometheus_metrics.py +135 -0
- dstack/_internal/server/background/tasks/process_running_jobs.py +80 -24
- dstack/_internal/server/background/tasks/process_runs.py +1 -0
- dstack/_internal/server/background/tasks/process_submitted_jobs.py +20 -38
- dstack/_internal/server/background/tasks/process_volumes.py +5 -2
- dstack/_internal/server/migrations/versions/60e444118b6d_add_jobprometheusmetrics.py +40 -0
- dstack/_internal/server/migrations/versions/7bc2586e8b9e_make_instancemodel_pool_id_optional.py +36 -0
- dstack/_internal/server/migrations/versions/98d1b92988bc_add_jobterminationreason_terminated_due_.py +140 -0
- dstack/_internal/server/migrations/versions/bc8ca4a505c6_store_backendtype_as_string.py +171 -0
- dstack/_internal/server/models.py +59 -9
- dstack/_internal/server/routers/backends.py +14 -23
- dstack/_internal/server/routers/instances.py +3 -4
- dstack/_internal/server/routers/metrics.py +31 -10
- dstack/_internal/server/routers/prometheus.py +36 -0
- 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/security/permissions.py +1 -1
- dstack/_internal/server/services/backends/__init__.py +85 -158
- dstack/_internal/server/services/config.py +53 -567
- dstack/_internal/server/services/fleets.py +9 -103
- dstack/_internal/server/services/gateways/__init__.py +13 -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 +25 -1
- 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 +131 -72
- dstack/_internal/server/services/offers.py +1 -1
- dstack/_internal/server/services/projects.py +23 -14
- dstack/_internal/server/services/prometheus.py +245 -0
- dstack/_internal/server/services/runner/client.py +14 -3
- dstack/_internal/server/services/runs.py +67 -31
- dstack/_internal/server/services/volumes.py +9 -4
- dstack/_internal/server/settings.py +3 -0
- dstack/_internal/server/statics/index.html +1 -1
- dstack/_internal/server/statics/{main-fe8fd9db55df8d10e648.js → main-4fd5a4770eff59325ee3.js} +68 -15
- dstack/_internal/server/statics/{main-fe8fd9db55df8d10e648.js.map → main-4fd5a4770eff59325ee3.js.map} +1 -1
- dstack/_internal/server/statics/{main-7510e71dfa9749a4e70e.css → main-da9f8c06a69c20dac23e.css} +1 -1
- dstack/_internal/server/statics/static/media/entraID.d65d1f3e9486a8e56d24fc07b3230885.svg +9 -0
- dstack/_internal/server/testing/common.py +75 -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 +170 -83
- dstack/api/server/__init__.py +11 -13
- dstack/api/server/_backends.py +12 -16
- dstack/api/server/_fleets.py +15 -55
- dstack/api/server/_gateways.py +3 -14
- dstack/api/server/_repos.py +1 -4
- dstack/api/server/_runs.py +21 -96
- dstack/api/server/_volumes.py +10 -5
- dstack/api/utils.py +3 -0
- dstack/version.py +1 -1
- {dstack-0.18.43.dist-info → dstack-0.19.0rc1.dist-info}/METADATA +10 -1
- {dstack-0.18.43.dist-info → dstack-0.19.0rc1.dist-info}/RECORD +229 -206
- 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 +186 -0
- tests/_internal/server/background/tasks/test_process_running_jobs.py +123 -19
- 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 +2 -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 +42 -0
- tests/_internal/server/routers/test_projects.py +56 -0
- tests/_internal/server/routers/test_prometheus.py +333 -0
- tests/_internal/server/routers/test_repos.py +0 -15
- tests/_internal/server/routers/test_runs.py +83 -275
- 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 +167 -0
- 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 -9
- 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 -40
- 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 -97
- 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.43.dist-info → dstack-0.19.0rc1.dist-info}/LICENSE.md +0 -0
- {dstack-0.18.43.dist-info → dstack-0.19.0rc1.dist-info}/WHEEL +0 -0
- {dstack-0.18.43.dist-info → dstack-0.19.0rc1.dist-info}/entry_points.txt +0 -0
- {dstack-0.18.43.dist-info → dstack-0.19.0rc1.dist-info}/top_level.txt +0 -0
|
@@ -13,7 +13,13 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
13
13
|
|
|
14
14
|
from dstack._internal.core.models.backends.base import BackendType
|
|
15
15
|
from dstack._internal.core.models.common import ApplyAction
|
|
16
|
-
from dstack._internal.core.models.configurations import
|
|
16
|
+
from dstack._internal.core.models.configurations import (
|
|
17
|
+
AnyRunConfiguration,
|
|
18
|
+
DevEnvironmentConfiguration,
|
|
19
|
+
ScalingSpec,
|
|
20
|
+
ServiceConfiguration,
|
|
21
|
+
TaskConfiguration,
|
|
22
|
+
)
|
|
17
23
|
from dstack._internal.core.models.gateways import GatewayStatus
|
|
18
24
|
from dstack._internal.core.models.instances import (
|
|
19
25
|
InstanceAvailability,
|
|
@@ -22,15 +28,12 @@ from dstack._internal.core.models.instances import (
|
|
|
22
28
|
InstanceType,
|
|
23
29
|
Resources,
|
|
24
30
|
)
|
|
25
|
-
from dstack._internal.core.models.
|
|
26
|
-
from dstack._internal.core.models.resources import Range, ResourcesSpec
|
|
31
|
+
from dstack._internal.core.models.resources import Range
|
|
27
32
|
from dstack._internal.core.models.runs import (
|
|
28
33
|
ApplyRunPlanInput,
|
|
29
|
-
JobProvisioningData,
|
|
30
34
|
JobSpec,
|
|
31
35
|
JobStatus,
|
|
32
36
|
JobTerminationReason,
|
|
33
|
-
Requirements,
|
|
34
37
|
Run,
|
|
35
38
|
RunSpec,
|
|
36
39
|
RunStatus,
|
|
@@ -38,10 +41,9 @@ from dstack._internal.core.models.runs import (
|
|
|
38
41
|
)
|
|
39
42
|
from dstack._internal.core.models.users import GlobalRole, ProjectRole
|
|
40
43
|
from dstack._internal.core.models.volumes import InstanceMountPoint, MountPoint
|
|
41
|
-
from dstack._internal.server.background.tasks.process_instances import process_instances
|
|
42
44
|
from dstack._internal.server.main import app
|
|
43
45
|
from dstack._internal.server.models import JobModel, RunModel
|
|
44
|
-
from dstack._internal.server.schemas.runs import ApplyRunPlanRequest
|
|
46
|
+
from dstack._internal.server.schemas.runs import ApplyRunPlanRequest
|
|
45
47
|
from dstack._internal.server.services.projects import add_project_member
|
|
46
48
|
from dstack._internal.server.services.runs import run_model_to_run
|
|
47
49
|
from dstack._internal.server.testing.common import (
|
|
@@ -50,7 +52,6 @@ from dstack._internal.server.testing.common import (
|
|
|
50
52
|
create_gateway_compute,
|
|
51
53
|
create_instance,
|
|
52
54
|
create_job,
|
|
53
|
-
create_pool,
|
|
54
55
|
create_project,
|
|
55
56
|
create_repo,
|
|
56
57
|
create_run,
|
|
@@ -114,18 +115,14 @@ def get_dev_env_run_plan_dict(
|
|
|
114
115
|
"availability_zones": None,
|
|
115
116
|
"instance_types": None,
|
|
116
117
|
"creation_policy": None,
|
|
117
|
-
"instance_name": None,
|
|
118
118
|
"single_branch": None,
|
|
119
119
|
"max_duration": "off",
|
|
120
120
|
"stop_duration": None,
|
|
121
121
|
"max_price": None,
|
|
122
|
-
"pool_name": DEFAULT_POOL_NAME,
|
|
123
122
|
"retry": None,
|
|
124
|
-
"retry_policy": None,
|
|
125
123
|
"spot_policy": "spot",
|
|
126
124
|
"idle_duration": None,
|
|
127
|
-
"
|
|
128
|
-
"termination_policy": None,
|
|
125
|
+
"utilization_policy": None,
|
|
129
126
|
"reservation": None,
|
|
130
127
|
},
|
|
131
128
|
"configuration_path": "dstack.yaml",
|
|
@@ -136,18 +133,14 @@ def get_dev_env_run_plan_dict(
|
|
|
136
133
|
"instance_types": None,
|
|
137
134
|
"creation_policy": None,
|
|
138
135
|
"default": False,
|
|
139
|
-
"instance_name": None,
|
|
140
136
|
"max_duration": "off",
|
|
141
137
|
"stop_duration": None,
|
|
142
138
|
"max_price": None,
|
|
143
139
|
"name": "string",
|
|
144
|
-
"pool_name": DEFAULT_POOL_NAME,
|
|
145
140
|
"retry": None,
|
|
146
|
-
"retry_policy": None,
|
|
147
141
|
"spot_policy": "spot",
|
|
148
142
|
"idle_duration": None,
|
|
149
|
-
"
|
|
150
|
-
"termination_policy": None,
|
|
143
|
+
"utilization_policy": None,
|
|
151
144
|
"reservation": None,
|
|
152
145
|
},
|
|
153
146
|
"repo_code_hash": None,
|
|
@@ -190,6 +183,7 @@ def get_dev_env_run_plan_dict(
|
|
|
190
183
|
"single_branch": False,
|
|
191
184
|
"max_duration": None,
|
|
192
185
|
"stop_duration": 300,
|
|
186
|
+
"utilization_policy": None,
|
|
193
187
|
"registry_auth": None,
|
|
194
188
|
"requirements": {
|
|
195
189
|
"resources": {
|
|
@@ -205,7 +199,7 @@ def get_dev_env_run_plan_dict(
|
|
|
205
199
|
},
|
|
206
200
|
"retry": None,
|
|
207
201
|
"volumes": volumes,
|
|
208
|
-
"
|
|
202
|
+
"ssh_key": None,
|
|
209
203
|
"working_dir": ".",
|
|
210
204
|
},
|
|
211
205
|
"offers": [json.loads(o.json()) for o in offers],
|
|
@@ -271,18 +265,14 @@ def get_dev_env_run_dict(
|
|
|
271
265
|
"availability_zones": None,
|
|
272
266
|
"instance_types": None,
|
|
273
267
|
"creation_policy": None,
|
|
274
|
-
"instance_name": None,
|
|
275
268
|
"single_branch": None,
|
|
276
269
|
"max_duration": "off",
|
|
277
270
|
"stop_duration": None,
|
|
278
271
|
"max_price": None,
|
|
279
|
-
"pool_name": DEFAULT_POOL_NAME,
|
|
280
272
|
"retry": None,
|
|
281
|
-
"retry_policy": None,
|
|
282
273
|
"spot_policy": "spot",
|
|
283
274
|
"idle_duration": None,
|
|
284
|
-
"
|
|
285
|
-
"termination_policy": None,
|
|
275
|
+
"utilization_policy": None,
|
|
286
276
|
"reservation": None,
|
|
287
277
|
},
|
|
288
278
|
"configuration_path": "dstack.yaml",
|
|
@@ -293,18 +283,14 @@ def get_dev_env_run_dict(
|
|
|
293
283
|
"instance_types": None,
|
|
294
284
|
"creation_policy": None,
|
|
295
285
|
"default": False,
|
|
296
|
-
"instance_name": None,
|
|
297
286
|
"max_duration": "off",
|
|
298
287
|
"stop_duration": None,
|
|
299
288
|
"max_price": None,
|
|
300
289
|
"name": "string",
|
|
301
|
-
"pool_name": DEFAULT_POOL_NAME,
|
|
302
290
|
"retry": None,
|
|
303
|
-
"retry_policy": None,
|
|
304
291
|
"spot_policy": "spot",
|
|
305
292
|
"idle_duration": None,
|
|
306
|
-
"
|
|
307
|
-
"termination_policy": None,
|
|
293
|
+
"utilization_policy": None,
|
|
308
294
|
"reservation": None,
|
|
309
295
|
},
|
|
310
296
|
"repo_code_hash": None,
|
|
@@ -347,6 +333,7 @@ def get_dev_env_run_dict(
|
|
|
347
333
|
"single_branch": False,
|
|
348
334
|
"max_duration": None,
|
|
349
335
|
"stop_duration": 300,
|
|
336
|
+
"utilization_policy": None,
|
|
350
337
|
"registry_auth": None,
|
|
351
338
|
"requirements": {
|
|
352
339
|
"resources": {
|
|
@@ -362,7 +349,7 @@ def get_dev_env_run_dict(
|
|
|
362
349
|
},
|
|
363
350
|
"retry": None,
|
|
364
351
|
"volumes": [],
|
|
365
|
-
"
|
|
352
|
+
"ssh_key": None,
|
|
366
353
|
"working_dir": ".",
|
|
367
354
|
},
|
|
368
355
|
"job_submissions": [
|
|
@@ -891,66 +878,77 @@ class TestGetRunPlan:
|
|
|
891
878
|
|
|
892
879
|
@pytest.mark.asyncio
|
|
893
880
|
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
881
|
+
@pytest.mark.parametrize(
|
|
882
|
+
("old_conf", "new_conf", "action"),
|
|
883
|
+
[
|
|
884
|
+
pytest.param(
|
|
885
|
+
ServiceConfiguration(
|
|
886
|
+
commands=["one", "two"],
|
|
887
|
+
port=80,
|
|
888
|
+
replicas=1,
|
|
889
|
+
scaling=None,
|
|
890
|
+
),
|
|
891
|
+
ServiceConfiguration(
|
|
892
|
+
commands=["one", "two"],
|
|
893
|
+
port=80,
|
|
894
|
+
replicas="2..4",
|
|
895
|
+
scaling=ScalingSpec(metric="rps", target=5),
|
|
896
|
+
),
|
|
897
|
+
"update",
|
|
898
|
+
id="update-service",
|
|
911
899
|
),
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
900
|
+
pytest.param(
|
|
901
|
+
ServiceConfiguration(
|
|
902
|
+
commands=["one", "two"],
|
|
903
|
+
port=80,
|
|
904
|
+
replicas=1,
|
|
905
|
+
scaling=None,
|
|
906
|
+
),
|
|
907
|
+
ServiceConfiguration(
|
|
908
|
+
commands=["one", "two"],
|
|
909
|
+
port=8080, # not updatable
|
|
910
|
+
replicas="2..4",
|
|
911
|
+
scaling=ScalingSpec(metric="rps", target=5),
|
|
912
|
+
),
|
|
913
|
+
"create",
|
|
914
|
+
id="no-update-service",
|
|
915
|
+
),
|
|
916
|
+
pytest.param(
|
|
917
|
+
DevEnvironmentConfiguration(ide="vscode", inactivity_duration=False),
|
|
918
|
+
DevEnvironmentConfiguration(ide="vscode", inactivity_duration="30m"),
|
|
919
|
+
"update",
|
|
920
|
+
id="update-dev-env",
|
|
921
|
+
),
|
|
922
|
+
pytest.param(
|
|
923
|
+
TaskConfiguration(image="test-image-1"),
|
|
924
|
+
TaskConfiguration(image="test-image-2"),
|
|
925
|
+
"create",
|
|
926
|
+
id="no-update-task",
|
|
927
|
+
),
|
|
928
|
+
pytest.param(
|
|
929
|
+
DevEnvironmentConfiguration(ide="vscode", image="test-image"),
|
|
930
|
+
TaskConfiguration(image="test-image"),
|
|
931
|
+
"create",
|
|
932
|
+
id="no-update-on-type-change",
|
|
933
|
+
),
|
|
934
|
+
],
|
|
935
|
+
)
|
|
936
|
+
async def test_returns_update_or_create_action_on_conf_change(
|
|
937
|
+
self,
|
|
938
|
+
test_db,
|
|
939
|
+
session: AsyncSession,
|
|
940
|
+
client: AsyncClient,
|
|
941
|
+
old_conf: AnyRunConfiguration,
|
|
942
|
+
new_conf: AnyRunConfiguration,
|
|
943
|
+
action: str,
|
|
944
|
+
) -> None:
|
|
938
945
|
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
939
946
|
project = await create_project(session=session, owner=user)
|
|
940
947
|
await add_project_member(
|
|
941
948
|
session=session, project=project, user=user, project_role=ProjectRole.USER
|
|
942
949
|
)
|
|
943
950
|
repo = await create_repo(session=session, project_id=project.id)
|
|
944
|
-
run_spec = get_run_spec(
|
|
945
|
-
run_name="test-service",
|
|
946
|
-
repo_id=repo.name,
|
|
947
|
-
configuration=ServiceConfiguration(
|
|
948
|
-
type="service",
|
|
949
|
-
commands=["one", "two"],
|
|
950
|
-
port=80,
|
|
951
|
-
replicas=1,
|
|
952
|
-
),
|
|
953
|
-
)
|
|
951
|
+
run_spec = get_run_spec(run_name="test-run", repo_id=repo.name, configuration=old_conf)
|
|
954
952
|
run_model = await create_run(
|
|
955
953
|
session=session,
|
|
956
954
|
project=project,
|
|
@@ -960,7 +958,7 @@ class TestGetRunPlan:
|
|
|
960
958
|
run_spec=run_spec,
|
|
961
959
|
)
|
|
962
960
|
run = run_model_to_run(run_model)
|
|
963
|
-
run_spec.configuration
|
|
961
|
+
run_spec.configuration = new_conf
|
|
964
962
|
response = await client.post(
|
|
965
963
|
f"/api/project/{project.name}/runs/get_plan",
|
|
966
964
|
headers=get_auth_headers(user.token),
|
|
@@ -968,7 +966,7 @@ class TestGetRunPlan:
|
|
|
968
966
|
)
|
|
969
967
|
assert response.status_code == 200
|
|
970
968
|
response_json = response.json()
|
|
971
|
-
assert response_json["action"] ==
|
|
969
|
+
assert response_json["action"] == action
|
|
972
970
|
assert response_json["current_resource"] == json.loads(run.json())
|
|
973
971
|
|
|
974
972
|
|
|
@@ -1318,11 +1316,9 @@ class TestStopRuns:
|
|
|
1318
1316
|
user=user,
|
|
1319
1317
|
status=RunStatus.RUNNING,
|
|
1320
1318
|
)
|
|
1321
|
-
pool = await create_pool(session=session, project=project)
|
|
1322
1319
|
instance = await create_instance(
|
|
1323
1320
|
session=session,
|
|
1324
1321
|
project=project,
|
|
1325
|
-
pool=pool,
|
|
1326
1322
|
status=InstanceStatus.BUSY,
|
|
1327
1323
|
)
|
|
1328
1324
|
job = await create_job(
|
|
@@ -1471,194 +1467,6 @@ class TestDeleteRuns:
|
|
|
1471
1467
|
assert len(res.scalars().all()) == 1
|
|
1472
1468
|
|
|
1473
1469
|
|
|
1474
|
-
class TestCreateInstance:
|
|
1475
|
-
@pytest.mark.asyncio
|
|
1476
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
1477
|
-
async def test_returns_403_if_not_project_member(
|
|
1478
|
-
self, test_db, session: AsyncSession, client: AsyncClient
|
|
1479
|
-
):
|
|
1480
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
1481
|
-
project = await create_project(session=session, owner=user)
|
|
1482
|
-
response = await client.post(
|
|
1483
|
-
f"/api/project/{project.name}/runs/create_instance",
|
|
1484
|
-
headers=get_auth_headers(user.token),
|
|
1485
|
-
)
|
|
1486
|
-
assert response.status_code == 403
|
|
1487
|
-
|
|
1488
|
-
@pytest.mark.asyncio
|
|
1489
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
1490
|
-
async def test_creates_instance(self, test_db, session: AsyncSession, client: AsyncClient):
|
|
1491
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
1492
|
-
project = await create_project(session=session, owner=user)
|
|
1493
|
-
await add_project_member(
|
|
1494
|
-
session=session, project=project, user=user, project_role=ProjectRole.USER
|
|
1495
|
-
)
|
|
1496
|
-
request = CreateInstanceRequest(
|
|
1497
|
-
profile=Profile(name="test_profile"),
|
|
1498
|
-
requirements=Requirements(resources=ResourcesSpec(cpu=1)),
|
|
1499
|
-
)
|
|
1500
|
-
instance_id = UUID("1b0e1b45-2f8c-4ab6-8010-a0d1a3e44e0e")
|
|
1501
|
-
with (
|
|
1502
|
-
patch(
|
|
1503
|
-
"dstack._internal.server.services.offers.get_offers_by_requirements"
|
|
1504
|
-
) as run_plan_by_req,
|
|
1505
|
-
patch("uuid.uuid4") as uuid_mock,
|
|
1506
|
-
):
|
|
1507
|
-
uuid_mock.return_value = instance_id
|
|
1508
|
-
offer = InstanceOfferWithAvailability(
|
|
1509
|
-
backend=BackendType.AWS,
|
|
1510
|
-
instance=InstanceType(
|
|
1511
|
-
name="instance",
|
|
1512
|
-
resources=Resources(cpus=1, memory_mib=512, spot=False, gpus=[]),
|
|
1513
|
-
),
|
|
1514
|
-
region="eu",
|
|
1515
|
-
price=1.0,
|
|
1516
|
-
availability=InstanceAvailability.AVAILABLE,
|
|
1517
|
-
)
|
|
1518
|
-
backend = Mock()
|
|
1519
|
-
backend.compute.return_value.get_offers_cached.return_value = [offer]
|
|
1520
|
-
backend.compute.return_value.create_instance.return_value = JobProvisioningData(
|
|
1521
|
-
backend=offer.backend,
|
|
1522
|
-
instance_type=offer.instance,
|
|
1523
|
-
instance_id="test_instance",
|
|
1524
|
-
hostname="1.1.1.1",
|
|
1525
|
-
internal_ip=None,
|
|
1526
|
-
region=offer.region,
|
|
1527
|
-
price=offer.price,
|
|
1528
|
-
username="ubuntu",
|
|
1529
|
-
ssh_port=22,
|
|
1530
|
-
ssh_proxy=None,
|
|
1531
|
-
dockerized=True,
|
|
1532
|
-
backend_data=None,
|
|
1533
|
-
)
|
|
1534
|
-
backend.TYPE = BackendType.AWS
|
|
1535
|
-
run_plan_by_req.return_value = [(backend, offer)]
|
|
1536
|
-
response = await client.post(
|
|
1537
|
-
f"/api/project/{project.name}/runs/create_instance",
|
|
1538
|
-
headers=get_auth_headers(user.token),
|
|
1539
|
-
json=request.dict(),
|
|
1540
|
-
)
|
|
1541
|
-
assert response.status_code == 200
|
|
1542
|
-
result = response.json()
|
|
1543
|
-
expected = {
|
|
1544
|
-
"id": str(instance_id),
|
|
1545
|
-
"project_name": project.name,
|
|
1546
|
-
"backend": None,
|
|
1547
|
-
"instance_type": None,
|
|
1548
|
-
"name": result["name"],
|
|
1549
|
-
"fleet_id": None,
|
|
1550
|
-
"fleet_name": None,
|
|
1551
|
-
"instance_num": 0,
|
|
1552
|
-
"job_name": None,
|
|
1553
|
-
"hostname": None,
|
|
1554
|
-
"status": "pending",
|
|
1555
|
-
"unreachable": False,
|
|
1556
|
-
"termination_reason": None,
|
|
1557
|
-
"created": result["created"],
|
|
1558
|
-
"pool_name": None,
|
|
1559
|
-
"region": None,
|
|
1560
|
-
"availability_zone": None,
|
|
1561
|
-
"price": None,
|
|
1562
|
-
"total_blocks": 1,
|
|
1563
|
-
"busy_blocks": 0,
|
|
1564
|
-
}
|
|
1565
|
-
assert result == expected
|
|
1566
|
-
|
|
1567
|
-
@pytest.mark.asyncio
|
|
1568
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
1569
|
-
async def test_error_if_backends_do_not_support_create_instance(
|
|
1570
|
-
self, test_db, session: AsyncSession, client: AsyncClient
|
|
1571
|
-
):
|
|
1572
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
1573
|
-
project = await create_project(session=session, owner=user)
|
|
1574
|
-
await add_project_member(
|
|
1575
|
-
session=session, project=project, user=user, project_role=ProjectRole.USER
|
|
1576
|
-
)
|
|
1577
|
-
request = CreateInstanceRequest(
|
|
1578
|
-
profile=Profile(name="test_profile"),
|
|
1579
|
-
requirements=Requirements(resources=ResourcesSpec(cpu=1)),
|
|
1580
|
-
)
|
|
1581
|
-
with patch(
|
|
1582
|
-
"dstack._internal.server.services.offers.get_offers_by_requirements"
|
|
1583
|
-
) as run_plan_by_req:
|
|
1584
|
-
offer = InstanceOfferWithAvailability(
|
|
1585
|
-
backend=BackendType.AZURE,
|
|
1586
|
-
instance=InstanceType(
|
|
1587
|
-
name="instance",
|
|
1588
|
-
resources=Resources(cpus=1, memory_mib=512, spot=False, gpus=[]),
|
|
1589
|
-
),
|
|
1590
|
-
region="eu",
|
|
1591
|
-
price=1.0,
|
|
1592
|
-
availability=InstanceAvailability.AVAILABLE,
|
|
1593
|
-
)
|
|
1594
|
-
backend = Mock()
|
|
1595
|
-
backend.TYPE = BackendType.AZURE
|
|
1596
|
-
backend.compute.return_value.get_offers_cached.return_value = [offer]
|
|
1597
|
-
backend.compute.return_value.create_instance.side_effect = NotImplementedError()
|
|
1598
|
-
run_plan_by_req.return_value = [(backend, offer)]
|
|
1599
|
-
response = await client.post(
|
|
1600
|
-
f"/api/project/{project.name}/runs/create_instance",
|
|
1601
|
-
headers=get_auth_headers(user.token),
|
|
1602
|
-
json=request.dict(),
|
|
1603
|
-
)
|
|
1604
|
-
assert response.status_code == 200
|
|
1605
|
-
await process_instances()
|
|
1606
|
-
|
|
1607
|
-
@pytest.mark.asyncio
|
|
1608
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
1609
|
-
async def test_backend_does_not_support_create_instance(
|
|
1610
|
-
self, test_db, session: AsyncSession, client: AsyncClient
|
|
1611
|
-
):
|
|
1612
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
1613
|
-
project = await create_project(session=session, owner=user)
|
|
1614
|
-
await add_project_member(
|
|
1615
|
-
session=session, project=project, user=user, project_role=ProjectRole.USER
|
|
1616
|
-
)
|
|
1617
|
-
request = CreateInstanceRequest(
|
|
1618
|
-
profile=Profile(name="test_profile"),
|
|
1619
|
-
requirements=Requirements(resources=ResourcesSpec(cpu=1)),
|
|
1620
|
-
)
|
|
1621
|
-
|
|
1622
|
-
with patch(
|
|
1623
|
-
"dstack._internal.server.services.offers.get_offers_by_requirements"
|
|
1624
|
-
) as run_plan_by_req:
|
|
1625
|
-
offers = InstanceOfferWithAvailability(
|
|
1626
|
-
backend=BackendType.VASTAI,
|
|
1627
|
-
instance=InstanceType(
|
|
1628
|
-
name="instance",
|
|
1629
|
-
resources=Resources(cpus=1, memory_mib=512, spot=False, gpus=[]),
|
|
1630
|
-
),
|
|
1631
|
-
region="eu",
|
|
1632
|
-
price=1.0,
|
|
1633
|
-
availability=InstanceAvailability.AVAILABLE,
|
|
1634
|
-
)
|
|
1635
|
-
|
|
1636
|
-
backend = Mock()
|
|
1637
|
-
backend.TYPE = BackendType.VASTAI
|
|
1638
|
-
backend.compute.return_value.get_offers_cached.return_value = [offers]
|
|
1639
|
-
backend.compute.return_value.create_instance.side_effect = NotImplementedError()
|
|
1640
|
-
run_plan_by_req.return_value = [(backend, offers)]
|
|
1641
|
-
|
|
1642
|
-
response = await client.post(
|
|
1643
|
-
f"/api/project/{project.name}/runs/create_instance",
|
|
1644
|
-
headers=get_auth_headers(user.token),
|
|
1645
|
-
json=request.dict(),
|
|
1646
|
-
)
|
|
1647
|
-
|
|
1648
|
-
assert response.status_code == 400
|
|
1649
|
-
|
|
1650
|
-
result = response.json()
|
|
1651
|
-
expected = {
|
|
1652
|
-
"detail": [
|
|
1653
|
-
{
|
|
1654
|
-
"msg": "Backends do not support create_instance. Try to select other backends.",
|
|
1655
|
-
"code": "error",
|
|
1656
|
-
}
|
|
1657
|
-
]
|
|
1658
|
-
}
|
|
1659
|
-
assert result == expected
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
1470
|
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
1663
1471
|
class TestSubmitService:
|
|
1664
1472
|
@pytest.fixture(autouse=True)
|
|
@@ -14,8 +14,8 @@ from dstack._internal.core.models.users import GlobalRole, ProjectRole
|
|
|
14
14
|
from dstack._internal.server.models import VolumeAttachmentModel, VolumeModel
|
|
15
15
|
from dstack._internal.server.services.projects import add_project_member
|
|
16
16
|
from dstack._internal.server.testing.common import (
|
|
17
|
+
ComputeMockSpec,
|
|
17
18
|
create_instance,
|
|
18
|
-
create_pool,
|
|
19
19
|
create_project,
|
|
20
20
|
create_user,
|
|
21
21
|
create_volume,
|
|
@@ -366,6 +366,7 @@ class TestDeleteVolumes:
|
|
|
366
366
|
) as m:
|
|
367
367
|
aws_mock = Mock()
|
|
368
368
|
m.return_value = aws_mock
|
|
369
|
+
aws_mock.compute.return_value = Mock(spec=ComputeMockSpec)
|
|
369
370
|
response = await client.post(
|
|
370
371
|
f"/api/project/{project.name}/volumes/delete",
|
|
371
372
|
headers=get_auth_headers(user.token),
|
|
@@ -383,7 +384,6 @@ class TestDeleteVolumes:
|
|
|
383
384
|
):
|
|
384
385
|
user = await create_user(session, global_role=GlobalRole.USER)
|
|
385
386
|
project = await create_project(session)
|
|
386
|
-
pool = await create_pool(session=session, project=project)
|
|
387
387
|
await add_project_member(
|
|
388
388
|
session=session, project=project, user=user, project_role=ProjectRole.USER
|
|
389
389
|
)
|
|
@@ -396,7 +396,6 @@ class TestDeleteVolumes:
|
|
|
396
396
|
instance = await create_instance(
|
|
397
397
|
session=session,
|
|
398
398
|
project=project,
|
|
399
|
-
pool=pool,
|
|
400
399
|
)
|
|
401
400
|
volume.attachments.append(VolumeAttachmentModel(instance=instance))
|
|
402
401
|
await session.commit()
|
|
File without changes
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from unittest.mock import patch
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from dstack._internal.core.models.configurations import TaskConfiguration
|
|
6
|
+
from dstack._internal.core.models.runs import JobSSHKey
|
|
7
|
+
from dstack._internal.server.services.jobs.configurators.task import TaskJobConfigurator
|
|
8
|
+
from dstack._internal.server.testing.common import get_run_spec
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.mark.asyncio
|
|
12
|
+
@pytest.mark.usefixtures("image_config_mock")
|
|
13
|
+
class TestTaskJobConfigurator:
|
|
14
|
+
async def test_ssh_key_single_node(self):
|
|
15
|
+
configuration = TaskConfiguration(nodes=1, image="debian")
|
|
16
|
+
run_spec = get_run_spec(run_name="run", repo_id="id", configuration=configuration)
|
|
17
|
+
configurator = TaskJobConfigurator(run_spec)
|
|
18
|
+
|
|
19
|
+
job_specs = await configurator.get_job_specs(replica_num=0)
|
|
20
|
+
|
|
21
|
+
assert len(job_specs) == 1
|
|
22
|
+
assert job_specs[0].ssh_key is None
|
|
23
|
+
|
|
24
|
+
async def test_ssh_key_multi_node(self):
|
|
25
|
+
configuration = TaskConfiguration(nodes=2, image="debian")
|
|
26
|
+
run_spec = get_run_spec(run_name="run", repo_id="id", configuration=configuration)
|
|
27
|
+
configurator = TaskJobConfigurator(run_spec)
|
|
28
|
+
|
|
29
|
+
with patch("dstack._internal.utils.crypto.generate_rsa_key_pair_bytes") as gen_mock:
|
|
30
|
+
gen_mock.side_effect = [(b"private1", b"public1"), (b"private2", b"public2")]
|
|
31
|
+
job_specs = await configurator.get_job_specs(replica_num=0)
|
|
32
|
+
|
|
33
|
+
assert len(job_specs) == 2
|
|
34
|
+
assert job_specs[0].ssh_key == JobSSHKey(private="private1", public="public1")
|
|
35
|
+
assert job_specs[1].ssh_key == JobSSHKey(private="private1", public="public1")
|
|
@@ -6,11 +6,14 @@ import yaml
|
|
|
6
6
|
from sqlalchemy import select
|
|
7
7
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
8
8
|
|
|
9
|
-
from dstack._internal.core.
|
|
9
|
+
from dstack._internal.core.backends.azure.models import (
|
|
10
|
+
AzureBackendConfigWithCreds,
|
|
11
|
+
AzureDefaultCreds,
|
|
12
|
+
)
|
|
10
13
|
from dstack._internal.core.models.backends.base import BackendType
|
|
11
14
|
from dstack._internal.server import settings
|
|
12
15
|
from dstack._internal.server.models import BackendModel, ProjectModel
|
|
13
|
-
from dstack._internal.server.services.config import
|
|
16
|
+
from dstack._internal.server.services.config import ServerConfigManager
|
|
14
17
|
from dstack._internal.server.testing.common import (
|
|
15
18
|
create_project,
|
|
16
19
|
create_user,
|
|
@@ -38,7 +41,7 @@ class TestServerConfigManager:
|
|
|
38
41
|
) as create_backend_mock,
|
|
39
42
|
):
|
|
40
43
|
list_available_backend_types_mock.return_value = [BackendType.AZURE]
|
|
41
|
-
default_config =
|
|
44
|
+
default_config = AzureBackendConfigWithCreds(
|
|
42
45
|
tenant_id="test_tenant",
|
|
43
46
|
subscription_id="test_subscription",
|
|
44
47
|
locations=["westeurope"],
|
|
@@ -52,7 +55,7 @@ class TestServerConfigManager:
|
|
|
52
55
|
list_available_backend_types_mock.assert_called()
|
|
53
56
|
get_configurator_mock.assert_called()
|
|
54
57
|
create_backend_mock.assert_called()
|
|
55
|
-
assert manager.config.projects[0].backends[0] ==
|
|
58
|
+
assert manager.config.projects[0].backends[0] == AzureBackendConfigWithCreds(
|
|
56
59
|
tenant_id="test_tenant",
|
|
57
60
|
subscription_id="test_subscription",
|
|
58
61
|
regions=["westeurope"],
|
|
@@ -4,7 +4,7 @@ from unittest.mock import Mock
|
|
|
4
4
|
import pytest
|
|
5
5
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
6
6
|
|
|
7
|
-
from dstack._internal.core.backends.base import Backend
|
|
7
|
+
from dstack._internal.core.backends.base.backend import Backend
|
|
8
8
|
from dstack._internal.core.errors import ServerClientError
|
|
9
9
|
from dstack._internal.core.models.backends.base import BackendType
|
|
10
10
|
from dstack._internal.core.models.fleets import (
|
|
@@ -20,7 +20,6 @@ from dstack._internal.server.services.fleets import get_plan
|
|
|
20
20
|
from dstack._internal.server.testing.common import (
|
|
21
21
|
create_fleet,
|
|
22
22
|
create_instance,
|
|
23
|
-
create_pool,
|
|
24
23
|
create_project,
|
|
25
24
|
create_user,
|
|
26
25
|
get_fleet_spec,
|
|
@@ -45,7 +44,6 @@ class TestGetPlanSSHFleetHostsValidation:
|
|
|
45
44
|
self, session: AsyncSession, project: ProjectModel, spec: FleetSpec
|
|
46
45
|
) -> FleetModel:
|
|
47
46
|
assert spec.configuration.ssh_config is not None, spec.configuration
|
|
48
|
-
pool = await create_pool(session=session, project=project)
|
|
49
47
|
fleet = await create_fleet(session=session, project=project, spec=spec)
|
|
50
48
|
for host in spec.configuration.ssh_config.hosts:
|
|
51
49
|
if isinstance(host, SSHHostParams):
|
|
@@ -56,7 +54,6 @@ class TestGetPlanSSHFleetHostsValidation:
|
|
|
56
54
|
await create_instance(
|
|
57
55
|
session=session,
|
|
58
56
|
project=project,
|
|
59
|
-
pool=pool,
|
|
60
57
|
fleet=fleet,
|
|
61
58
|
backend=BackendType.REMOTE,
|
|
62
59
|
remote_connection_info=rci,
|