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.

Files changed (267) hide show
  1. dstack/_internal/cli/commands/gateway.py +15 -3
  2. dstack/_internal/cli/commands/logs.py +0 -22
  3. dstack/_internal/cli/commands/stats.py +8 -17
  4. dstack/_internal/cli/main.py +1 -5
  5. dstack/_internal/cli/services/configurators/fleet.py +4 -39
  6. dstack/_internal/cli/services/configurators/run.py +22 -21
  7. dstack/_internal/cli/services/profile.py +34 -83
  8. dstack/_internal/cli/utils/gateway.py +1 -1
  9. dstack/_internal/core/backends/__init__.py +56 -39
  10. dstack/_internal/core/backends/aws/__init__.py +0 -25
  11. dstack/_internal/core/backends/aws/auth.py +1 -10
  12. dstack/_internal/core/backends/aws/backend.py +26 -0
  13. dstack/_internal/core/backends/aws/compute.py +20 -45
  14. dstack/_internal/{server/services/backends/configurators/aws.py → core/backends/aws/configurator.py} +46 -85
  15. dstack/_internal/core/backends/aws/models.py +135 -0
  16. dstack/_internal/core/backends/aws/resources.py +1 -1
  17. dstack/_internal/core/backends/azure/__init__.py +0 -20
  18. dstack/_internal/core/backends/azure/auth.py +2 -11
  19. dstack/_internal/core/backends/azure/backend.py +21 -0
  20. dstack/_internal/core/backends/azure/compute.py +13 -27
  21. dstack/_internal/{server/services/backends/configurators/azure.py → core/backends/azure/configurator.py} +141 -210
  22. dstack/_internal/core/backends/azure/models.py +89 -0
  23. dstack/_internal/core/backends/base/__init__.py +0 -12
  24. dstack/_internal/core/backends/base/backend.py +18 -0
  25. dstack/_internal/core/backends/base/compute.py +153 -33
  26. dstack/_internal/core/backends/base/configurator.py +105 -0
  27. dstack/_internal/core/backends/base/models.py +14 -0
  28. dstack/_internal/core/backends/configurators.py +138 -0
  29. dstack/_internal/core/backends/cudo/__init__.py +0 -15
  30. dstack/_internal/core/backends/cudo/backend.py +16 -0
  31. dstack/_internal/core/backends/cudo/compute.py +8 -26
  32. dstack/_internal/core/backends/cudo/configurator.py +72 -0
  33. dstack/_internal/core/backends/cudo/models.py +37 -0
  34. dstack/_internal/core/backends/datacrunch/__init__.py +0 -15
  35. dstack/_internal/core/backends/datacrunch/backend.py +16 -0
  36. dstack/_internal/core/backends/datacrunch/compute.py +8 -25
  37. dstack/_internal/core/backends/datacrunch/configurator.py +66 -0
  38. dstack/_internal/core/backends/datacrunch/models.py +38 -0
  39. dstack/_internal/core/{models/backends/dstack.py → backends/dstack/models.py} +7 -7
  40. dstack/_internal/core/backends/gcp/__init__.py +0 -16
  41. dstack/_internal/core/backends/gcp/auth.py +2 -11
  42. dstack/_internal/core/backends/gcp/backend.py +17 -0
  43. dstack/_internal/core/backends/gcp/compute.py +13 -43
  44. dstack/_internal/{server/services/backends/configurators/gcp.py → core/backends/gcp/configurator.py} +46 -103
  45. dstack/_internal/core/backends/gcp/models.py +125 -0
  46. dstack/_internal/core/backends/kubernetes/__init__.py +0 -15
  47. dstack/_internal/core/backends/kubernetes/backend.py +16 -0
  48. dstack/_internal/core/backends/kubernetes/compute.py +16 -5
  49. dstack/_internal/core/backends/kubernetes/configurator.py +55 -0
  50. dstack/_internal/core/backends/kubernetes/models.py +72 -0
  51. dstack/_internal/core/backends/lambdalabs/__init__.py +0 -16
  52. dstack/_internal/core/backends/lambdalabs/backend.py +17 -0
  53. dstack/_internal/core/backends/lambdalabs/compute.py +7 -28
  54. dstack/_internal/core/backends/lambdalabs/configurator.py +82 -0
  55. dstack/_internal/core/backends/lambdalabs/models.py +37 -0
  56. dstack/_internal/core/backends/local/__init__.py +0 -13
  57. dstack/_internal/core/backends/local/backend.py +14 -0
  58. dstack/_internal/core/backends/local/compute.py +16 -2
  59. dstack/_internal/core/backends/models.py +128 -0
  60. dstack/_internal/core/backends/oci/__init__.py +0 -15
  61. dstack/_internal/core/backends/oci/auth.py +1 -5
  62. dstack/_internal/core/backends/oci/backend.py +16 -0
  63. dstack/_internal/core/backends/oci/compute.py +9 -23
  64. dstack/_internal/{server/services/backends/configurators/oci.py → core/backends/oci/configurator.py} +40 -85
  65. dstack/_internal/core/{models/backends/oci.py → backends/oci/models.py} +24 -25
  66. dstack/_internal/core/backends/oci/region.py +1 -1
  67. dstack/_internal/core/backends/runpod/__init__.py +0 -15
  68. dstack/_internal/core/backends/runpod/backend.py +16 -0
  69. dstack/_internal/core/backends/runpod/compute.py +7 -3
  70. dstack/_internal/core/backends/runpod/configurator.py +59 -0
  71. dstack/_internal/core/backends/runpod/models.py +54 -0
  72. dstack/_internal/core/backends/template/__init__.py +0 -0
  73. dstack/_internal/core/backends/tensordock/__init__.py +0 -15
  74. dstack/_internal/core/backends/tensordock/backend.py +16 -0
  75. dstack/_internal/core/backends/tensordock/compute.py +8 -27
  76. dstack/_internal/core/backends/tensordock/configurator.py +68 -0
  77. dstack/_internal/core/backends/tensordock/models.py +38 -0
  78. dstack/_internal/core/backends/vastai/__init__.py +0 -15
  79. dstack/_internal/core/backends/vastai/backend.py +16 -0
  80. dstack/_internal/core/backends/vastai/compute.py +2 -2
  81. dstack/_internal/core/backends/vastai/configurator.py +66 -0
  82. dstack/_internal/core/backends/vastai/models.py +37 -0
  83. dstack/_internal/core/backends/vultr/__init__.py +0 -15
  84. dstack/_internal/core/backends/vultr/backend.py +16 -0
  85. dstack/_internal/core/backends/vultr/compute.py +10 -24
  86. dstack/_internal/core/backends/vultr/configurator.py +64 -0
  87. dstack/_internal/core/backends/vultr/models.py +34 -0
  88. dstack/_internal/core/models/backends/__init__.py +0 -184
  89. dstack/_internal/core/models/backends/base.py +0 -19
  90. dstack/_internal/core/models/configurations.py +20 -15
  91. dstack/_internal/core/models/envs.py +4 -3
  92. dstack/_internal/core/models/fleets.py +17 -22
  93. dstack/_internal/core/models/gateways.py +3 -3
  94. dstack/_internal/core/models/instances.py +24 -0
  95. dstack/_internal/core/models/profiles.py +41 -46
  96. dstack/_internal/core/models/projects.py +1 -1
  97. dstack/_internal/core/models/repos/base.py +0 -5
  98. dstack/_internal/core/models/repos/local.py +3 -3
  99. dstack/_internal/core/models/repos/remote.py +26 -12
  100. dstack/_internal/core/models/repos/virtual.py +1 -1
  101. dstack/_internal/core/models/resources.py +45 -76
  102. dstack/_internal/core/models/runs.py +17 -19
  103. dstack/_internal/core/models/volumes.py +1 -3
  104. dstack/_internal/core/services/profiles.py +7 -16
  105. dstack/_internal/core/services/repos.py +0 -4
  106. dstack/_internal/server/app.py +0 -3
  107. dstack/_internal/server/background/tasks/process_gateways.py +4 -8
  108. dstack/_internal/server/background/tasks/process_instances.py +14 -9
  109. dstack/_internal/server/background/tasks/process_metrics.py +1 -1
  110. dstack/_internal/server/background/tasks/process_placement_groups.py +4 -1
  111. dstack/_internal/server/background/tasks/process_prometheus_metrics.py +1 -1
  112. dstack/_internal/server/background/tasks/process_running_jobs.py +14 -5
  113. dstack/_internal/server/background/tasks/process_submitted_jobs.py +16 -37
  114. dstack/_internal/server/background/tasks/process_volumes.py +5 -2
  115. dstack/_internal/server/migrations/versions/7bc2586e8b9e_make_instancemodel_pool_id_optional.py +36 -0
  116. dstack/_internal/server/migrations/versions/bc8ca4a505c6_store_backendtype_as_string.py +171 -0
  117. dstack/_internal/server/models.py +48 -9
  118. dstack/_internal/server/routers/backends.py +14 -23
  119. dstack/_internal/server/routers/instances.py +3 -4
  120. dstack/_internal/server/routers/metrics.py +10 -8
  121. dstack/_internal/server/routers/prometheus.py +1 -1
  122. dstack/_internal/server/routers/repos.py +1 -2
  123. dstack/_internal/server/routers/runs.py +13 -59
  124. dstack/_internal/server/schemas/gateways.py +14 -23
  125. dstack/_internal/server/schemas/projects.py +7 -2
  126. dstack/_internal/server/schemas/repos.py +2 -38
  127. dstack/_internal/server/schemas/runner.py +1 -0
  128. dstack/_internal/server/schemas/runs.py +1 -24
  129. dstack/_internal/server/services/backends/__init__.py +85 -158
  130. dstack/_internal/server/services/config.py +52 -576
  131. dstack/_internal/server/services/fleets.py +8 -103
  132. dstack/_internal/server/services/gateways/__init__.py +12 -4
  133. dstack/_internal/server/services/{pools.py → instances.py} +22 -329
  134. dstack/_internal/server/services/jobs/__init__.py +9 -6
  135. dstack/_internal/server/services/jobs/configurators/base.py +16 -0
  136. dstack/_internal/server/services/jobs/configurators/dev.py +9 -1
  137. dstack/_internal/server/services/jobs/configurators/extensions/cursor.py +42 -0
  138. dstack/_internal/server/services/metrics.py +39 -13
  139. dstack/_internal/server/services/offers.py +1 -1
  140. dstack/_internal/server/services/projects.py +23 -14
  141. dstack/_internal/server/services/prometheus.py +176 -18
  142. dstack/_internal/server/services/runs.py +24 -16
  143. dstack/_internal/server/services/volumes.py +8 -4
  144. dstack/_internal/server/statics/index.html +1 -1
  145. dstack/_internal/server/statics/{main-4eb116b97819badd1e2c.js → main-4a0fe83e84574654e397.js} +18 -14
  146. dstack/_internal/server/statics/{main-4eb116b97819badd1e2c.js.map → main-4a0fe83e84574654e397.js.map} +1 -1
  147. dstack/_internal/server/testing/common.py +58 -32
  148. dstack/_internal/utils/json_schema.py +6 -0
  149. dstack/_internal/utils/ssh.py +2 -1
  150. dstack/api/__init__.py +4 -0
  151. dstack/api/_public/__init__.py +16 -20
  152. dstack/api/_public/backends.py +1 -1
  153. dstack/api/_public/repos.py +36 -36
  154. dstack/api/_public/runs.py +167 -83
  155. dstack/api/server/__init__.py +11 -13
  156. dstack/api/server/_backends.py +12 -16
  157. dstack/api/server/_fleets.py +15 -57
  158. dstack/api/server/_gateways.py +3 -14
  159. dstack/api/server/_repos.py +1 -4
  160. dstack/api/server/_runs.py +21 -100
  161. dstack/api/server/_volumes.py +10 -5
  162. dstack/version.py +1 -1
  163. {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/METADATA +1 -1
  164. {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/RECORD +218 -204
  165. tests/_internal/cli/services/configurators/test_profile.py +6 -6
  166. tests/_internal/core/backends/aws/test_configurator.py +35 -0
  167. tests/_internal/core/backends/aws/test_resources.py +1 -1
  168. tests/_internal/core/backends/azure/test_configurator.py +61 -0
  169. tests/_internal/core/backends/cudo/__init__.py +0 -0
  170. tests/_internal/core/backends/cudo/test_configurator.py +37 -0
  171. tests/_internal/core/backends/datacrunch/__init__.py +0 -0
  172. tests/_internal/core/backends/datacrunch/test_configurator.py +17 -0
  173. tests/_internal/core/backends/gcp/test_configurator.py +42 -0
  174. tests/_internal/core/backends/kubernetes/test_configurator.py +43 -0
  175. tests/_internal/core/backends/lambdalabs/__init__.py +0 -0
  176. tests/_internal/core/backends/lambdalabs/test_configurator.py +38 -0
  177. tests/_internal/core/backends/oci/test_configurator.py +55 -0
  178. tests/_internal/core/backends/runpod/__init__.py +0 -0
  179. tests/_internal/core/backends/runpod/test_configurator.py +33 -0
  180. tests/_internal/core/backends/tensordock/__init__.py +0 -0
  181. tests/_internal/core/backends/tensordock/test_configurator.py +38 -0
  182. tests/_internal/core/backends/vastai/__init__.py +0 -0
  183. tests/_internal/core/backends/vastai/test_configurator.py +33 -0
  184. tests/_internal/core/backends/vultr/__init__.py +0 -0
  185. tests/_internal/core/backends/vultr/test_configurator.py +33 -0
  186. tests/_internal/server/background/tasks/test_process_gateways.py +4 -0
  187. tests/_internal/server/background/tasks/test_process_instances.py +49 -48
  188. tests/_internal/server/background/tasks/test_process_metrics.py +0 -3
  189. tests/_internal/server/background/tasks/test_process_placement_groups.py +2 -0
  190. tests/_internal/server/background/tasks/test_process_prometheus_metrics.py +0 -3
  191. tests/_internal/server/background/tasks/test_process_running_jobs.py +0 -21
  192. tests/_internal/server/background/tasks/test_process_runs.py +8 -22
  193. tests/_internal/server/background/tasks/test_process_submitted_jobs.py +3 -40
  194. tests/_internal/server/background/tasks/test_process_submitted_volumes.py +2 -0
  195. tests/_internal/server/background/tasks/test_process_terminating_jobs.py +10 -15
  196. tests/_internal/server/routers/test_backends.py +6 -764
  197. tests/_internal/server/routers/test_fleets.py +0 -26
  198. tests/_internal/server/routers/test_gateways.py +27 -3
  199. tests/_internal/server/routers/test_instances.py +0 -10
  200. tests/_internal/server/routers/test_metrics.py +27 -0
  201. tests/_internal/server/routers/test_projects.py +56 -0
  202. tests/_internal/server/routers/test_prometheus.py +116 -27
  203. tests/_internal/server/routers/test_repos.py +0 -15
  204. tests/_internal/server/routers/test_runs.py +4 -219
  205. tests/_internal/server/routers/test_volumes.py +2 -3
  206. tests/_internal/server/services/backends/__init__.py +0 -0
  207. tests/_internal/server/services/jobs/configurators/test_task.py +35 -0
  208. tests/_internal/server/services/test_config.py +7 -4
  209. tests/_internal/server/services/test_fleets.py +1 -4
  210. tests/_internal/server/services/{test_pools.py → test_instances.py} +11 -49
  211. tests/_internal/server/services/test_metrics.py +9 -5
  212. tests/_internal/server/services/test_repos.py +1 -14
  213. tests/_internal/server/services/test_runs.py +0 -4
  214. dstack/_internal/cli/commands/pool.py +0 -581
  215. dstack/_internal/cli/commands/run.py +0 -75
  216. dstack/_internal/core/backends/aws/config.py +0 -18
  217. dstack/_internal/core/backends/azure/config.py +0 -12
  218. dstack/_internal/core/backends/base/config.py +0 -5
  219. dstack/_internal/core/backends/cudo/config.py +0 -9
  220. dstack/_internal/core/backends/datacrunch/config.py +0 -9
  221. dstack/_internal/core/backends/gcp/config.py +0 -22
  222. dstack/_internal/core/backends/kubernetes/config.py +0 -6
  223. dstack/_internal/core/backends/lambdalabs/config.py +0 -9
  224. dstack/_internal/core/backends/nebius/__init__.py +0 -15
  225. dstack/_internal/core/backends/nebius/api_client.py +0 -319
  226. dstack/_internal/core/backends/nebius/compute.py +0 -220
  227. dstack/_internal/core/backends/nebius/config.py +0 -6
  228. dstack/_internal/core/backends/nebius/types.py +0 -37
  229. dstack/_internal/core/backends/oci/config.py +0 -6
  230. dstack/_internal/core/backends/runpod/config.py +0 -17
  231. dstack/_internal/core/backends/tensordock/config.py +0 -9
  232. dstack/_internal/core/backends/vastai/config.py +0 -6
  233. dstack/_internal/core/backends/vultr/config.py +0 -9
  234. dstack/_internal/core/models/backends/aws.py +0 -86
  235. dstack/_internal/core/models/backends/azure.py +0 -68
  236. dstack/_internal/core/models/backends/cudo.py +0 -43
  237. dstack/_internal/core/models/backends/datacrunch.py +0 -44
  238. dstack/_internal/core/models/backends/gcp.py +0 -67
  239. dstack/_internal/core/models/backends/kubernetes.py +0 -40
  240. dstack/_internal/core/models/backends/lambdalabs.py +0 -43
  241. dstack/_internal/core/models/backends/nebius.py +0 -54
  242. dstack/_internal/core/models/backends/runpod.py +0 -42
  243. dstack/_internal/core/models/backends/tensordock.py +0 -44
  244. dstack/_internal/core/models/backends/vastai.py +0 -43
  245. dstack/_internal/core/models/backends/vultr.py +0 -40
  246. dstack/_internal/core/models/pools.py +0 -43
  247. dstack/_internal/server/routers/pools.py +0 -142
  248. dstack/_internal/server/schemas/pools.py +0 -38
  249. dstack/_internal/server/services/backends/configurators/base.py +0 -72
  250. dstack/_internal/server/services/backends/configurators/cudo.py +0 -87
  251. dstack/_internal/server/services/backends/configurators/datacrunch.py +0 -79
  252. dstack/_internal/server/services/backends/configurators/kubernetes.py +0 -63
  253. dstack/_internal/server/services/backends/configurators/lambdalabs.py +0 -98
  254. dstack/_internal/server/services/backends/configurators/nebius.py +0 -85
  255. dstack/_internal/server/services/backends/configurators/runpod.py +0 -67
  256. dstack/_internal/server/services/backends/configurators/tensordock.py +0 -82
  257. dstack/_internal/server/services/backends/configurators/vastai.py +0 -80
  258. dstack/_internal/server/services/backends/configurators/vultr.py +0 -80
  259. dstack/api/_public/pools.py +0 -41
  260. dstack/api/_public/resources.py +0 -105
  261. dstack/api/server/_pools.py +0 -63
  262. tests/_internal/server/routers/test_pools.py +0 -612
  263. /dstack/_internal/{server/services/backends/configurators → core/backends/dstack}/__init__.py +0 -0
  264. {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/LICENSE.md +0 -0
  265. {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/WHEEL +0 -0
  266. {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/entry_points.txt +0 -0
  267. {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/top_level.txt +0 -0
@@ -128,7 +128,6 @@ class TestGetRepo:
128
128
  user = await create_user(session=session, global_role=GlobalRole.USER)
129
129
  project = await create_project(session=session, owner=user)
130
130
  legacy_creds = {
131
- "protocol": "https",
132
131
  "clone_url": "https://github.com/dstackai/dstack.git",
133
132
  "private_key": None,
134
133
  "oauth_token": "test_token",
@@ -157,14 +156,12 @@ class TestGetRepo:
157
156
  user = await create_user(session=session, global_role=GlobalRole.USER)
158
157
  project = await create_project(session=session, owner=user)
159
158
  legacy_creds = {
160
- "protocol": "https",
161
159
  "clone_url": "https://github.com/dstackai/dstack.git",
162
160
  "private_key": None,
163
161
  "oauth_token": "legacy_creds",
164
162
  }
165
163
  repo = await create_repo(session=session, project_id=project.id, creds=legacy_creds)
166
164
  user_creds = {
167
- "protocol": "https",
168
165
  "clone_url": "https://github.com/dstackai/dstack.git",
169
166
  "private_key": None,
170
167
  "oauth_token": "user_creds",
@@ -214,13 +211,9 @@ class TestInitRepo:
214
211
  "repo_id": "test_repo",
215
212
  "repo_info": {
216
213
  "repo_type": "remote",
217
- "repo_host_name": "",
218
- "repo_port": None,
219
- "repo_user_name": "",
220
214
  "repo_name": "dstack",
221
215
  },
222
216
  "repo_creds": {
223
- "protocol": "https",
224
217
  "clone_url": "https://github.com/dstackai/dstack.git",
225
218
  "private_key": None,
226
219
  "oauth_token": "test_token",
@@ -254,13 +247,9 @@ class TestInitRepo:
254
247
  "repo_id": "test_repo",
255
248
  "repo_info": {
256
249
  "repo_type": "remote",
257
- "repo_host_name": "",
258
- "repo_port": None,
259
- "repo_user_name": "",
260
250
  "repo_name": "dstack",
261
251
  },
262
252
  "repo_creds": {
263
- "protocol": "https",
264
253
  "clone_url": "https://github.com/dstackai/dstack.git",
265
254
  "private_key": None,
266
255
  "oauth_token": "test_token",
@@ -276,13 +265,9 @@ class TestInitRepo:
276
265
  "repo_id": "test_repo",
277
266
  "repo_info": {
278
267
  "repo_type": "remote",
279
- "repo_host_name": "",
280
- "repo_port": None,
281
- "repo_user_name": "",
282
268
  "repo_name": "dstack",
283
269
  },
284
270
  "repo_creds": {
285
- "protocol": "https",
286
271
  "clone_url": "https://github.com/dstackai/dstack.git",
287
272
  "private_key": None,
288
273
  "oauth_token": "test_token_updated",
@@ -28,15 +28,12 @@ from dstack._internal.core.models.instances import (
28
28
  InstanceType,
29
29
  Resources,
30
30
  )
31
- from dstack._internal.core.models.profiles import DEFAULT_POOL_NAME, Profile
32
- from dstack._internal.core.models.resources import Range, ResourcesSpec
31
+ from dstack._internal.core.models.resources import Range
33
32
  from dstack._internal.core.models.runs import (
34
33
  ApplyRunPlanInput,
35
- JobProvisioningData,
36
34
  JobSpec,
37
35
  JobStatus,
38
36
  JobTerminationReason,
39
- Requirements,
40
37
  Run,
41
38
  RunSpec,
42
39
  RunStatus,
@@ -44,10 +41,9 @@ from dstack._internal.core.models.runs import (
44
41
  )
45
42
  from dstack._internal.core.models.users import GlobalRole, ProjectRole
46
43
  from dstack._internal.core.models.volumes import InstanceMountPoint, MountPoint
47
- from dstack._internal.server.background.tasks.process_instances import process_instances
48
44
  from dstack._internal.server.main import app
49
45
  from dstack._internal.server.models import JobModel, RunModel
50
- from dstack._internal.server.schemas.runs import ApplyRunPlanRequest, CreateInstanceRequest
46
+ from dstack._internal.server.schemas.runs import ApplyRunPlanRequest
51
47
  from dstack._internal.server.services.projects import add_project_member
52
48
  from dstack._internal.server.services.runs import run_model_to_run
53
49
  from dstack._internal.server.testing.common import (
@@ -56,7 +52,6 @@ from dstack._internal.server.testing.common import (
56
52
  create_gateway_compute,
57
53
  create_instance,
58
54
  create_job,
59
- create_pool,
60
55
  create_project,
61
56
  create_repo,
62
57
  create_run,
@@ -120,18 +115,13 @@ def get_dev_env_run_plan_dict(
120
115
  "availability_zones": None,
121
116
  "instance_types": None,
122
117
  "creation_policy": None,
123
- "instance_name": None,
124
118
  "single_branch": None,
125
119
  "max_duration": "off",
126
120
  "stop_duration": None,
127
121
  "max_price": None,
128
- "pool_name": DEFAULT_POOL_NAME,
129
122
  "retry": None,
130
- "retry_policy": None,
131
123
  "spot_policy": "spot",
132
124
  "idle_duration": None,
133
- "termination_idle_time": 300,
134
- "termination_policy": None,
135
125
  "utilization_policy": None,
136
126
  "reservation": None,
137
127
  },
@@ -143,18 +133,13 @@ def get_dev_env_run_plan_dict(
143
133
  "instance_types": None,
144
134
  "creation_policy": None,
145
135
  "default": False,
146
- "instance_name": None,
147
136
  "max_duration": "off",
148
137
  "stop_duration": None,
149
138
  "max_price": None,
150
139
  "name": "string",
151
- "pool_name": DEFAULT_POOL_NAME,
152
140
  "retry": None,
153
- "retry_policy": None,
154
141
  "spot_policy": "spot",
155
142
  "idle_duration": None,
156
- "termination_idle_time": 300,
157
- "termination_policy": None,
158
143
  "utilization_policy": None,
159
144
  "reservation": None,
160
145
  },
@@ -214,7 +199,7 @@ def get_dev_env_run_plan_dict(
214
199
  },
215
200
  "retry": None,
216
201
  "volumes": volumes,
217
- "retry_policy": {"retry": False, "duration": None},
202
+ "ssh_key": None,
218
203
  "working_dir": ".",
219
204
  },
220
205
  "offers": [json.loads(o.json()) for o in offers],
@@ -280,18 +265,13 @@ def get_dev_env_run_dict(
280
265
  "availability_zones": None,
281
266
  "instance_types": None,
282
267
  "creation_policy": None,
283
- "instance_name": None,
284
268
  "single_branch": None,
285
269
  "max_duration": "off",
286
270
  "stop_duration": None,
287
271
  "max_price": None,
288
- "pool_name": DEFAULT_POOL_NAME,
289
272
  "retry": None,
290
- "retry_policy": None,
291
273
  "spot_policy": "spot",
292
274
  "idle_duration": None,
293
- "termination_idle_time": 300,
294
- "termination_policy": None,
295
275
  "utilization_policy": None,
296
276
  "reservation": None,
297
277
  },
@@ -303,18 +283,13 @@ def get_dev_env_run_dict(
303
283
  "instance_types": None,
304
284
  "creation_policy": None,
305
285
  "default": False,
306
- "instance_name": None,
307
286
  "max_duration": "off",
308
287
  "stop_duration": None,
309
288
  "max_price": None,
310
289
  "name": "string",
311
- "pool_name": DEFAULT_POOL_NAME,
312
290
  "retry": None,
313
- "retry_policy": None,
314
291
  "spot_policy": "spot",
315
292
  "idle_duration": None,
316
- "termination_idle_time": 300,
317
- "termination_policy": None,
318
293
  "utilization_policy": None,
319
294
  "reservation": None,
320
295
  },
@@ -374,7 +349,7 @@ def get_dev_env_run_dict(
374
349
  },
375
350
  "retry": None,
376
351
  "volumes": [],
377
- "retry_policy": {"retry": False, "duration": None},
352
+ "ssh_key": None,
378
353
  "working_dir": ".",
379
354
  },
380
355
  "job_submissions": [
@@ -1341,11 +1316,9 @@ class TestStopRuns:
1341
1316
  user=user,
1342
1317
  status=RunStatus.RUNNING,
1343
1318
  )
1344
- pool = await create_pool(session=session, project=project)
1345
1319
  instance = await create_instance(
1346
1320
  session=session,
1347
1321
  project=project,
1348
- pool=pool,
1349
1322
  status=InstanceStatus.BUSY,
1350
1323
  )
1351
1324
  job = await create_job(
@@ -1494,194 +1467,6 @@ class TestDeleteRuns:
1494
1467
  assert len(res.scalars().all()) == 1
1495
1468
 
1496
1469
 
1497
- class TestCreateInstance:
1498
- @pytest.mark.asyncio
1499
- @pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
1500
- async def test_returns_403_if_not_project_member(
1501
- self, test_db, session: AsyncSession, client: AsyncClient
1502
- ):
1503
- user = await create_user(session=session, global_role=GlobalRole.USER)
1504
- project = await create_project(session=session, owner=user)
1505
- response = await client.post(
1506
- f"/api/project/{project.name}/runs/create_instance",
1507
- headers=get_auth_headers(user.token),
1508
- )
1509
- assert response.status_code == 403
1510
-
1511
- @pytest.mark.asyncio
1512
- @pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
1513
- async def test_creates_instance(self, test_db, session: AsyncSession, client: AsyncClient):
1514
- user = await create_user(session=session, global_role=GlobalRole.USER)
1515
- project = await create_project(session=session, owner=user)
1516
- await add_project_member(
1517
- session=session, project=project, user=user, project_role=ProjectRole.USER
1518
- )
1519
- request = CreateInstanceRequest(
1520
- profile=Profile(name="test_profile"),
1521
- requirements=Requirements(resources=ResourcesSpec(cpu=1)),
1522
- )
1523
- instance_id = UUID("1b0e1b45-2f8c-4ab6-8010-a0d1a3e44e0e")
1524
- with (
1525
- patch(
1526
- "dstack._internal.server.services.offers.get_offers_by_requirements"
1527
- ) as run_plan_by_req,
1528
- patch("uuid.uuid4") as uuid_mock,
1529
- ):
1530
- uuid_mock.return_value = instance_id
1531
- offer = InstanceOfferWithAvailability(
1532
- backend=BackendType.AWS,
1533
- instance=InstanceType(
1534
- name="instance",
1535
- resources=Resources(cpus=1, memory_mib=512, spot=False, gpus=[]),
1536
- ),
1537
- region="eu",
1538
- price=1.0,
1539
- availability=InstanceAvailability.AVAILABLE,
1540
- )
1541
- backend = Mock()
1542
- backend.compute.return_value.get_offers_cached.return_value = [offer]
1543
- backend.compute.return_value.create_instance.return_value = JobProvisioningData(
1544
- backend=offer.backend,
1545
- instance_type=offer.instance,
1546
- instance_id="test_instance",
1547
- hostname="1.1.1.1",
1548
- internal_ip=None,
1549
- region=offer.region,
1550
- price=offer.price,
1551
- username="ubuntu",
1552
- ssh_port=22,
1553
- ssh_proxy=None,
1554
- dockerized=True,
1555
- backend_data=None,
1556
- )
1557
- backend.TYPE = BackendType.AWS
1558
- run_plan_by_req.return_value = [(backend, offer)]
1559
- response = await client.post(
1560
- f"/api/project/{project.name}/runs/create_instance",
1561
- headers=get_auth_headers(user.token),
1562
- json=request.dict(),
1563
- )
1564
- assert response.status_code == 200
1565
- result = response.json()
1566
- expected = {
1567
- "id": str(instance_id),
1568
- "project_name": project.name,
1569
- "backend": None,
1570
- "instance_type": None,
1571
- "name": result["name"],
1572
- "fleet_id": None,
1573
- "fleet_name": None,
1574
- "instance_num": 0,
1575
- "job_name": None,
1576
- "hostname": None,
1577
- "status": "pending",
1578
- "unreachable": False,
1579
- "termination_reason": None,
1580
- "created": result["created"],
1581
- "pool_name": None,
1582
- "region": None,
1583
- "availability_zone": None,
1584
- "price": None,
1585
- "total_blocks": 1,
1586
- "busy_blocks": 0,
1587
- }
1588
- assert result == expected
1589
-
1590
- @pytest.mark.asyncio
1591
- @pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
1592
- async def test_error_if_backends_do_not_support_create_instance(
1593
- self, test_db, session: AsyncSession, client: AsyncClient
1594
- ):
1595
- user = await create_user(session=session, global_role=GlobalRole.USER)
1596
- project = await create_project(session=session, owner=user)
1597
- await add_project_member(
1598
- session=session, project=project, user=user, project_role=ProjectRole.USER
1599
- )
1600
- request = CreateInstanceRequest(
1601
- profile=Profile(name="test_profile"),
1602
- requirements=Requirements(resources=ResourcesSpec(cpu=1)),
1603
- )
1604
- with patch(
1605
- "dstack._internal.server.services.offers.get_offers_by_requirements"
1606
- ) as run_plan_by_req:
1607
- offer = InstanceOfferWithAvailability(
1608
- backend=BackendType.AZURE,
1609
- instance=InstanceType(
1610
- name="instance",
1611
- resources=Resources(cpus=1, memory_mib=512, spot=False, gpus=[]),
1612
- ),
1613
- region="eu",
1614
- price=1.0,
1615
- availability=InstanceAvailability.AVAILABLE,
1616
- )
1617
- backend = Mock()
1618
- backend.TYPE = BackendType.AZURE
1619
- backend.compute.return_value.get_offers_cached.return_value = [offer]
1620
- backend.compute.return_value.create_instance.side_effect = NotImplementedError()
1621
- run_plan_by_req.return_value = [(backend, offer)]
1622
- response = await client.post(
1623
- f"/api/project/{project.name}/runs/create_instance",
1624
- headers=get_auth_headers(user.token),
1625
- json=request.dict(),
1626
- )
1627
- assert response.status_code == 200
1628
- await process_instances()
1629
-
1630
- @pytest.mark.asyncio
1631
- @pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
1632
- async def test_backend_does_not_support_create_instance(
1633
- self, test_db, session: AsyncSession, client: AsyncClient
1634
- ):
1635
- user = await create_user(session=session, global_role=GlobalRole.USER)
1636
- project = await create_project(session=session, owner=user)
1637
- await add_project_member(
1638
- session=session, project=project, user=user, project_role=ProjectRole.USER
1639
- )
1640
- request = CreateInstanceRequest(
1641
- profile=Profile(name="test_profile"),
1642
- requirements=Requirements(resources=ResourcesSpec(cpu=1)),
1643
- )
1644
-
1645
- with patch(
1646
- "dstack._internal.server.services.offers.get_offers_by_requirements"
1647
- ) as run_plan_by_req:
1648
- offers = InstanceOfferWithAvailability(
1649
- backend=BackendType.VASTAI,
1650
- instance=InstanceType(
1651
- name="instance",
1652
- resources=Resources(cpus=1, memory_mib=512, spot=False, gpus=[]),
1653
- ),
1654
- region="eu",
1655
- price=1.0,
1656
- availability=InstanceAvailability.AVAILABLE,
1657
- )
1658
-
1659
- backend = Mock()
1660
- backend.TYPE = BackendType.VASTAI
1661
- backend.compute.return_value.get_offers_cached.return_value = [offers]
1662
- backend.compute.return_value.create_instance.side_effect = NotImplementedError()
1663
- run_plan_by_req.return_value = [(backend, offers)]
1664
-
1665
- response = await client.post(
1666
- f"/api/project/{project.name}/runs/create_instance",
1667
- headers=get_auth_headers(user.token),
1668
- json=request.dict(),
1669
- )
1670
-
1671
- assert response.status_code == 400
1672
-
1673
- result = response.json()
1674
- expected = {
1675
- "detail": [
1676
- {
1677
- "msg": "Backends do not support create_instance. Try to select other backends.",
1678
- "code": "error",
1679
- }
1680
- ]
1681
- }
1682
- assert result == expected
1683
-
1684
-
1685
1470
  @pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
1686
1471
  class TestSubmitService:
1687
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.models.backends.azure import AzureConfigInfoWithCreds, AzureDefaultCreds
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 AzureConfig, ServerConfigManager
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 = AzureConfigInfoWithCreds(
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] == AzureConfig(
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,