dstack 0.18.41__py3-none-any.whl → 0.18.42__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. dstack/_internal/cli/utils/volume.py +9 -0
  2. dstack/_internal/core/backends/aws/compute.py +2 -1
  3. dstack/_internal/core/backends/gcp/compute.py +2 -1
  4. dstack/_internal/core/models/runs.py +3 -3
  5. dstack/_internal/core/models/volumes.py +23 -0
  6. dstack/_internal/server/background/tasks/process_instances.py +2 -3
  7. dstack/_internal/server/background/tasks/process_running_jobs.py +4 -0
  8. dstack/_internal/server/background/tasks/process_submitted_jobs.py +12 -7
  9. dstack/_internal/server/background/tasks/process_terminating_jobs.py +13 -2
  10. dstack/_internal/server/background/tasks/process_volumes.py +11 -1
  11. dstack/_internal/server/migrations/versions/a751ef183f27_move_attachment_data_to_volumes_.py +34 -0
  12. dstack/_internal/server/models.py +17 -19
  13. dstack/_internal/server/services/fleets.py +5 -1
  14. dstack/_internal/server/services/jobs/__init__.py +4 -4
  15. dstack/_internal/server/services/offers.py +7 -7
  16. dstack/_internal/server/services/pools.py +3 -3
  17. dstack/_internal/server/services/runner/client.py +8 -5
  18. dstack/_internal/server/services/volumes.py +68 -9
  19. dstack/_internal/server/testing/common.py +13 -9
  20. dstack/version.py +1 -1
  21. {dstack-0.18.41.dist-info → dstack-0.18.42.dist-info}/METADATA +1 -1
  22. {dstack-0.18.41.dist-info → dstack-0.18.42.dist-info}/RECORD +33 -31
  23. tests/_internal/server/background/tasks/test_process_running_jobs.py +1 -0
  24. tests/_internal/server/background/tasks/test_process_submitted_jobs.py +5 -3
  25. tests/_internal/server/background/tasks/test_process_terminating_jobs.py +11 -6
  26. tests/_internal/server/routers/test_volumes.py +9 -2
  27. tests/_internal/server/services/runner/test_client.py +22 -3
  28. tests/_internal/server/services/test_offers.py +167 -0
  29. tests/_internal/server/services/test_pools.py +105 -1
  30. {dstack-0.18.41.dist-info → dstack-0.18.42.dist-info}/LICENSE.md +0 -0
  31. {dstack-0.18.41.dist-info → dstack-0.18.42.dist-info}/WHEEL +0 -0
  32. {dstack-0.18.41.dist-info → dstack-0.18.42.dist-info}/entry_points.txt +0 -0
  33. {dstack-0.18.41.dist-info → dstack-0.18.42.dist-info}/top_level.txt +0 -0
@@ -15,19 +15,28 @@ from dstack._internal.core.errors import (
15
15
  from dstack._internal.core.models.users import GlobalRole
16
16
  from dstack._internal.core.models.volumes import (
17
17
  Volume,
18
+ VolumeAttachment,
18
19
  VolumeAttachmentData,
19
20
  VolumeConfiguration,
21
+ VolumeInstance,
20
22
  VolumeProvisioningData,
21
23
  VolumeStatus,
22
24
  )
23
25
  from dstack._internal.core.services import validate_dstack_resource_name
24
26
  from dstack._internal.server.db import get_db
25
- from dstack._internal.server.models import ProjectModel, UserModel, VolumeModel
27
+ from dstack._internal.server.models import (
28
+ InstanceModel,
29
+ ProjectModel,
30
+ UserModel,
31
+ VolumeAttachmentModel,
32
+ VolumeModel,
33
+ )
26
34
  from dstack._internal.server.services import backends as backends_services
27
35
  from dstack._internal.server.services.locking import (
28
36
  get_locker,
29
37
  string_to_lock_id,
30
38
  )
39
+ from dstack._internal.server.services.pools import get_instance_provisioning_data
31
40
  from dstack._internal.server.services.projects import list_project_models, list_user_project_models
32
41
  from dstack._internal.utils import common, random_names
33
42
  from dstack._internal.utils.logging import get_logger
@@ -106,8 +115,13 @@ async def list_projects_volume_models(
106
115
  .order_by(*order_by)
107
116
  .limit(limit)
108
117
  .options(joinedload(VolumeModel.user))
118
+ .options(
119
+ joinedload(VolumeModel.attachments)
120
+ .joinedload(VolumeAttachmentModel.instance)
121
+ .joinedload(InstanceModel.fleet)
122
+ )
109
123
  )
110
- volume_models = list(res.scalars().all())
124
+ volume_models = list(res.unique().scalars().all())
111
125
  return volume_models
112
126
 
113
127
 
@@ -134,9 +148,16 @@ async def list_project_volume_models(
134
148
  if not include_deleted:
135
149
  filters.append(VolumeModel.deleted == False)
136
150
  res = await session.execute(
137
- select(VolumeModel).where(*filters).options(joinedload(VolumeModel.user))
151
+ select(VolumeModel)
152
+ .where(*filters)
153
+ .options(joinedload(VolumeModel.user))
154
+ .options(
155
+ joinedload(VolumeModel.attachments)
156
+ .joinedload(VolumeAttachmentModel.instance)
157
+ .joinedload(InstanceModel.fleet)
158
+ )
138
159
  )
139
- return list(res.scalars().all())
160
+ return list(res.unique().scalars().all())
140
161
 
141
162
 
142
163
  async def get_volume_by_name(
@@ -163,9 +184,16 @@ async def get_project_volume_model_by_name(
163
184
  if not include_deleted:
164
185
  filters.append(VolumeModel.deleted == False)
165
186
  res = await session.execute(
166
- select(VolumeModel).where(*filters).options(joinedload(VolumeModel.user))
187
+ select(VolumeModel)
188
+ .where(*filters)
189
+ .options(joinedload(VolumeModel.user))
190
+ .options(
191
+ joinedload(VolumeModel.attachments)
192
+ .joinedload(VolumeAttachmentModel.instance)
193
+ .joinedload(InstanceModel.fleet)
194
+ )
167
195
  )
168
- return res.scalar_one_or_none()
196
+ return res.unique().scalar_one_or_none()
169
197
 
170
198
 
171
199
  async def create_volume(
@@ -205,10 +233,10 @@ async def create_volume(
205
233
  project=project,
206
234
  status=VolumeStatus.SUBMITTED,
207
235
  configuration=configuration.json(),
236
+ attachments=[],
208
237
  )
209
238
  session.add(volume_model)
210
239
  await session.commit()
211
- await session.refresh(volume_model)
212
240
  return volume_model_to_volume(volume_model)
213
241
 
214
242
 
@@ -234,13 +262,13 @@ async def delete_volumes(session: AsyncSession, project: ProjectModel, names: Li
234
262
  VolumeModel.deleted == False,
235
263
  )
236
264
  .options(selectinload(VolumeModel.user))
237
- .options(selectinload(VolumeModel.instances))
265
+ .options(selectinload(VolumeModel.attachments))
238
266
  .execution_options(populate_existing=True)
239
267
  .with_for_update()
240
268
  )
241
269
  volume_models = res.scalars().unique().all()
242
270
  for volume_model in volume_models:
243
- if len(volume_model.instances) > 0:
271
+ if len(volume_model.attachments) > 0:
244
272
  raise ServerClientError(
245
273
  f"Failed to delete volume {volume_model.name}. Volume is in use."
246
274
  )
@@ -270,6 +298,15 @@ def volume_model_to_volume(volume_model: VolumeModel) -> Volume:
270
298
  # Initially VolumeProvisionigData lacked backend
271
299
  if vpd is not None and vpd.backend is None:
272
300
  vpd.backend = configuration.backend
301
+ attachments = []
302
+ for volume_attachment_model in volume_model.attachments:
303
+ instance = volume_attachment_model.instance
304
+ attachments.append(
305
+ VolumeAttachment(
306
+ instance=instance_model_to_volume_instance(instance),
307
+ attachment_data=get_attachment_data(volume_attachment_model),
308
+ )
309
+ )
273
310
  return Volume(
274
311
  name=volume_model.name,
275
312
  project_name=volume_model.project.name,
@@ -282,6 +319,7 @@ def volume_model_to_volume(volume_model: VolumeModel) -> Volume:
282
319
  deleted=volume_model.deleted,
283
320
  volume_id=vpd.volume_id if vpd is not None else None,
284
321
  provisioning_data=vpd,
322
+ attachments=attachments,
285
323
  attachment_data=vad,
286
324
  id=volume_model.id,
287
325
  )
@@ -303,6 +341,27 @@ def get_volume_attachment_data(volume_model: VolumeModel) -> Optional[VolumeAtta
303
341
  return VolumeAttachmentData.__response__.parse_raw(volume_model.volume_attachment_data)
304
342
 
305
343
 
344
+ def get_attachment_data(
345
+ volume_attachment_model: VolumeAttachmentModel,
346
+ ) -> Optional[VolumeAttachmentData]:
347
+ if volume_attachment_model.attachment_data is None:
348
+ return None
349
+ return VolumeAttachmentData.__response__.parse_raw(volume_attachment_model.attachment_data)
350
+
351
+
352
+ def instance_model_to_volume_instance(instance_model: InstanceModel) -> VolumeInstance:
353
+ instance_id = None
354
+ jpd = get_instance_provisioning_data(instance_model)
355
+ if jpd is not None:
356
+ instance_id = jpd.instance_id
357
+ return VolumeInstance(
358
+ name=instance_model.name,
359
+ fleet_name=instance_model.fleet.name if instance_model.fleet else None,
360
+ instance_num=instance_model.instance_num,
361
+ instance_id=instance_id,
362
+ )
363
+
364
+
306
365
  async def generate_volume_name(session: AsyncSession, project: ProjectModel) -> str:
307
366
  volume_models = await list_project_volume_models(session=session, project=project)
308
367
  names = {v.name for v in volume_models}
@@ -55,7 +55,7 @@ from dstack._internal.core.models.runs import (
55
55
  from dstack._internal.core.models.users import GlobalRole
56
56
  from dstack._internal.core.models.volumes import (
57
57
  Volume,
58
- VolumeAttachmentData,
58
+ VolumeAttachment,
59
59
  VolumeConfiguration,
60
60
  VolumeProvisioningData,
61
61
  VolumeStatus,
@@ -76,6 +76,7 @@ from dstack._internal.server.models import (
76
76
  RepoModel,
77
77
  RunModel,
78
78
  UserModel,
79
+ VolumeAttachmentModel,
79
80
  VolumeModel,
80
81
  )
81
82
  from dstack._internal.server.services.jobs import get_job_specs_from_run_spec
@@ -542,6 +543,9 @@ async def create_instance(
542
543
 
543
544
  if volumes is None:
544
545
  volumes = []
546
+ volume_attachments = []
547
+ for volume in volumes:
548
+ volume_attachments.append(VolumeAttachmentModel(volume=volume))
545
549
 
546
550
  im = InstanceModel(
547
551
  id=instance_id,
@@ -566,7 +570,7 @@ async def create_instance(
566
570
  requirements=requirements.json(),
567
571
  instance_configuration=instance_configuration.json(),
568
572
  remote_connection_info=remote_connection_info.json() if remote_connection_info else None,
569
- volumes=volumes,
573
+ volume_attachments=volume_attachments,
570
574
  total_blocks=total_blocks,
571
575
  busy_blocks=busy_blocks,
572
576
  )
@@ -587,6 +591,7 @@ def get_instance_offer_with_availability(
587
591
  spot: bool = False,
588
592
  blocks: int = 1,
589
593
  total_blocks: int = 1,
594
+ availability_zones: Optional[List[str]] = None,
590
595
  ):
591
596
  gpus = [Gpu(name="T4", memory_mib=16384, vendor=gpuhunt.AcceleratorVendor.NVIDIA)] * gpu_count
592
597
  return InstanceOfferWithAvailability(
@@ -605,6 +610,7 @@ def get_instance_offer_with_availability(
605
610
  region=region,
606
611
  price=1,
607
612
  availability=InstanceAvailability.AVAILABLE,
613
+ availability_zones=availability_zones,
608
614
  blocks=blocks,
609
615
  total_blocks=total_blocks,
610
616
  )
@@ -669,7 +675,7 @@ async def create_volume(
669
675
  volume_provisioning_data=volume_provisioning_data.json()
670
676
  if volume_provisioning_data
671
677
  else None,
672
- instances=[],
678
+ attachments=[],
673
679
  deleted_at=deleted_at,
674
680
  deleted=True if deleted_at else False,
675
681
  )
@@ -691,16 +697,14 @@ def get_volume(
691
697
  deleted: bool = False,
692
698
  volume_id: Optional[str] = None,
693
699
  provisioning_data: Optional[VolumeProvisioningData] = None,
694
- attachment_data: Optional[VolumeAttachmentData] = None,
695
- device_name: Optional[str] = None,
700
+ attachments: Optional[List[VolumeAttachment]] = None,
696
701
  ) -> Volume:
697
702
  if id_ is None:
698
703
  id_ = uuid.uuid4()
699
704
  if configuration is None:
700
705
  configuration = get_volume_configuration()
701
- if device_name is not None:
702
- assert attachment_data is None, "attachment_data and device_name are mutually exclusive"
703
- attachment_data = VolumeAttachmentData(device_name=device_name)
706
+ if attachments is None:
707
+ attachments = []
704
708
  return Volume(
705
709
  id=id_,
706
710
  name=name,
@@ -714,7 +718,7 @@ def get_volume(
714
718
  deleted=deleted,
715
719
  volume_id=volume_id,
716
720
  provisioning_data=provisioning_data,
717
- attachment_data=attachment_data,
721
+ attachments=attachments,
718
722
  )
719
723
 
720
724
 
dstack/version.py CHANGED
@@ -1,3 +1,3 @@
1
- __version__ = "0.18.41"
1
+ __version__ = "0.18.42"
2
2
  __is_release__ = True
3
3
  base_image = "0.6"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dstack
3
- Version: 0.18.41
3
+ Version: 0.18.42
4
4
  Summary: dstack is an open-source orchestration engine for running AI workloads on any cloud or on-premises.
5
5
  Home-page: https://dstack.ai
6
6
  Author: Andrey Cheptsov
@@ -1,5 +1,5 @@
1
1
  dstack/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- dstack/version.py,sha256=_y8GsLLZfsEC3VdxzEbtitoyheXaElaOvO21SlEehwY,65
2
+ dstack/version.py,sha256=2vyD60hf6Ar7fKjyGNRtiogwJDCRrkoglz7oYuLU-uY,65
3
3
  dstack/_internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  dstack/_internal/compat.py,sha256=bF9U9fTMfL8UVhCouedoUSTYFl7UAOiU0WXrnRoByxw,40
5
5
  dstack/_internal/settings.py,sha256=8XODoSW2joaEndvZxuHUPSFK85sGgJ7fVL976isYeJM,557
@@ -38,14 +38,14 @@ dstack/_internal/cli/utils/gateway.py,sha256=jMytH6u3x8hctMhm9bcmXLJxSgTXmpW8M9a
38
38
  dstack/_internal/cli/utils/rich.py,sha256=Gx1MJU929kMKsbdo9qF7XHARNta2426Ssb-xMLVhwbQ,5710
39
39
  dstack/_internal/cli/utils/run.py,sha256=4NDhlcdE4bccQ4gWzhZ2EP8tHryZt4eI3BBho425VOA,7737
40
40
  dstack/_internal/cli/utils/updates.py,sha256=9KQcm_TEE_PiU0yc5WH4xRse0WIeHOubi9Et55odgvk,2601
41
- dstack/_internal/cli/utils/volume.py,sha256=dAdIBejLh-5vz34XcB4z2JkqQ3rrzmX5jKY0q7MyjNE,1639
41
+ dstack/_internal/cli/utils/volume.py,sha256=mU9I06dVMFbpjfkefxrZNoSWadKLoib3U14rHudNQN4,1975
42
42
  dstack/_internal/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
43
  dstack/_internal/core/consts.py,sha256=c1Yd5UY6Qx7KeuYgloXWncWhMsYj6TqwlElda7NtB98,254
44
44
  dstack/_internal/core/errors.py,sha256=UvqMIBVHG1NIUP0w0g5dWjnxOz37sfFi4lh4yuYK0vw,2793
45
45
  dstack/_internal/core/backends/__init__.py,sha256=P7uX9QYj-aWSHOub1vIwr-MVERVCwTi8pcG4MQVuEYw,958
46
46
  dstack/_internal/core/backends/aws/__init__.py,sha256=gGXM9op3UJ3-OGDjNr4ywa_fNf--oywRbrOGUH2tTDA,850
47
47
  dstack/_internal/core/backends/aws/auth.py,sha256=lRBwNUdEWMWlblDnFSGlIcNkZMY2tSjapdq1OjwollU,1253
48
- dstack/_internal/core/backends/aws/compute.py,sha256=UgRb2S5TQ0dbrXIU1jauElNgpvU4OPuj1Nrxa7gg3G8,35236
48
+ dstack/_internal/core/backends/aws/compute.py,sha256=I7DyBNc9wFqMQ9-9IxX3rmIyNzq4hCS5hT8uorSn9_E,35308
49
49
  dstack/_internal/core/backends/aws/config.py,sha256=bd4juOaiqsUsWeorIgqCW0xNigCFIRQIvaRxEHyhpwg,533
50
50
  dstack/_internal/core/backends/aws/resources.py,sha256=yKsMMLO4HMRTWero8cLDxtavWvqcne_y9VDXVH9G1lE,22551
51
51
  dstack/_internal/core/backends/azure/__init__.py,sha256=J-mWp413v0DQFaTochlvMJXlGf8ukQCH_6oGuYFR31U,718
@@ -68,7 +68,7 @@ dstack/_internal/core/backends/datacrunch/compute.py,sha256=gyOftmUdmS9fMqpyew0P
68
68
  dstack/_internal/core/backends/datacrunch/config.py,sha256=bShWTAb02PYP8fuBz7H7r-YhRkhy6_yfD0nuaKd9xd4,281
69
69
  dstack/_internal/core/backends/gcp/__init__.py,sha256=nxhPNTGd1pPag2DwBRKg3X5gEKlh32p7a9Yfyi7-d5k,546
70
70
  dstack/_internal/core/backends/gcp/auth.py,sha256=AL5i7VtXvMboTCk-dzvNykaogYV31xAYfEivXxzOD_k,1832
71
- dstack/_internal/core/backends/gcp/compute.py,sha256=VEAeaKYa-gIBNJ8MYBCnPf0xoELzlhoAZREHPupnqgQ,35929
71
+ dstack/_internal/core/backends/gcp/compute.py,sha256=ksGatfOjPp78kTIpuxbEX3awMv9Wy2ErLX4FbGlrOI4,36001
72
72
  dstack/_internal/core/backends/gcp/config.py,sha256=AjNHgIsDqB5aKdyFXTKG9wNO2d3lgNyJW4Dk5Yb-LL4,722
73
73
  dstack/_internal/core/backends/gcp/resources.py,sha256=6khFYRZwMrjRxVAoHav3OsKFSVw-tuLVkJ_FzQCY8EI,14741
74
74
  dstack/_internal/core/backends/kubernetes/__init__.py,sha256=MpuT3Dxt2ZwZpAQomE_fXUDmgHBmfP8xvUP1YMAKTJQ,573
@@ -126,13 +126,13 @@ dstack/_internal/core/models/pools.py,sha256=SmxNQsSYolUMkraLi0BvEXDacuhp1cvbGQn
126
126
  dstack/_internal/core/models/profiles.py,sha256=Y0qsIV0DvvI-YxtcMLqmsrqulGKzJsE_0SdWU2Wusv8,8208
127
127
  dstack/_internal/core/models/projects.py,sha256=hY3rhLWkuDRsavAdNvQaK6IhnTr7yBHTmNwy9k7zjVE,643
128
128
  dstack/_internal/core/models/resources.py,sha256=AzBqGEZLHmg-HpjQtcb_W4pJQRxRtDyeuiHO4berdBA,12408
129
- dstack/_internal/core/models/runs.py,sha256=y41U2RLnLU9HZkXUcKi0c7-2CxXYKuOAlt9EtBogi3w,17919
129
+ dstack/_internal/core/models/runs.py,sha256=ziWnqFRL1hORWJ10evwxLySAe9umiP7kTc3IUvEniSc,17940
130
130
  dstack/_internal/core/models/secrets.py,sha256=OlnBI8ESBnpjSqB0-Vr3z8JcqB2Ydfiwo8IJUuM5jAc,219
131
131
  dstack/_internal/core/models/server.py,sha256=Hkc1v2s3KOiwslsWVmhUOAzcSeREoG-HD1SzSX9WUGg,152
132
132
  dstack/_internal/core/models/services.py,sha256=2Hpi7j0Q1shaf_0wd0C0044AJAmuYi-D3qx3PH849oI,3076
133
133
  dstack/_internal/core/models/unix.py,sha256=KxnSQELnkAjjuUgYcQKVkf-UAbYREBD8WCWDvHfOkuA,1915
134
134
  dstack/_internal/core/models/users.py,sha256=o_rd0GAmd6jufypVUs9P12NRri3rgAPDt-KxnqNNsGw,703
135
- dstack/_internal/core/models/volumes.py,sha256=ox2HTqpDi5J1hHh-R-hPrgBQ9Ku_Vas3fPSVfj3nPpo,5335
135
+ dstack/_internal/core/models/volumes.py,sha256=ZebkSybGXmnPQHjsDCWVofHavjwjLIovssmpXnLW6nU,6207
136
136
  dstack/_internal/core/models/backends/__init__.py,sha256=iPMAv4j7gpHc0cjt3SxzQGb-sywms8LUXt7IjtKNPnM,5589
137
137
  dstack/_internal/core/models/backends/aws.py,sha256=H0RB7pTVb7vjW_-MDpBBwPLbAoKDRtYFHe_n4ppXz6w,2463
138
138
  dstack/_internal/core/models/backends/azure.py,sha256=OhK3HBvI_ZfLmKVxlnm7AowWOlarp6QXb3tbcbFoACw,2076
@@ -223,20 +223,20 @@ dstack/_internal/server/app.py,sha256=pPto7JaBEkwvPF2FR8sqDzwIyMovI_85xk6WXfnnCY
223
223
  dstack/_internal/server/db.py,sha256=WjuqmjG3QAZmSMCeUaJ_ynbowlHuNAvYCZO649cTPHc,3210
224
224
  dstack/_internal/server/deps.py,sha256=31e8SU_ogPJWHIDLkgl7cuC_5V91xbJoLyAj17VanfM,670
225
225
  dstack/_internal/server/main.py,sha256=kztKhCYNoHSDyJJQScWfZXE0naNleJOCQULW6dd8SGw,109
226
- dstack/_internal/server/models.py,sha256=4DsizdUw1tOEpUKp9NQ_w9YKbjoJSc8SEGcaz5el5CI,27090
226
+ dstack/_internal/server/models.py,sha256=VKh_lfgGUlc0Xy_CGo02IDgwPRp3membAJg07jm27j4,27405
227
227
  dstack/_internal/server/settings.py,sha256=tEPw0xIltvW4TpLBA-OzKRM_PmvDuD4vuXCo7ITyzpo,3067
228
228
  dstack/_internal/server/background/__init__.py,sha256=jNDwTgQwKhK1NYl0RJaKU4mwKIMOSIxPTKVyhizU1PM,3436
229
229
  dstack/_internal/server/background/tasks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
230
230
  dstack/_internal/server/background/tasks/process_fleets.py,sha256=lKXUvN_b7DNjD3psHzyCt_JYsTxPFuQ86iXi8fj8GkM,3202
231
231
  dstack/_internal/server/background/tasks/process_gateways.py,sha256=KN5n9r0cKizJ3oGpeLsv3euZKZOMf-02X0Sv09GE8N8,7141
232
- dstack/_internal/server/background/tasks/process_instances.py,sha256=gryg6-eKv6bQAtaX6v-GTi-Sc_g9Lk0CWauwYv7lWDA,37402
232
+ dstack/_internal/server/background/tasks/process_instances.py,sha256=HaOTfcBcrwlRhHHbpeRflCcf3VYW-OkE6SB74SxvwXQ,37369
233
233
  dstack/_internal/server/background/tasks/process_metrics.py,sha256=BPv0S7pmMX91vObZiW51AJiNR0ULIj-OQlo5n97KISY,5328
234
234
  dstack/_internal/server/background/tasks/process_placement_groups.py,sha256=DZctiMF2TXKbYeygoz--ZiC9MXVL1sQJgq0oqRIc7hg,3858
235
- dstack/_internal/server/background/tasks/process_running_jobs.py,sha256=Y1-1CVwZ4q_R2Vqsb7BeRJFWsYMEKyBCp9siQfpGSm0,29899
235
+ dstack/_internal/server/background/tasks/process_running_jobs.py,sha256=FAIwXKIOUbVM-svKo7aoAF9DdxIhcejQJfg6gs0z83k,30108
236
236
  dstack/_internal/server/background/tasks/process_runs.py,sha256=JWJqMzXXqvwQSxyKs4bKH2aBP8YQbiyO4tuwyMPVduk,16673
237
- dstack/_internal/server/background/tasks/process_submitted_jobs.py,sha256=QFV5sruEEswjprAukeF1rgzQSRtV5nvAFRdkc4deqLc,26785
238
- dstack/_internal/server/background/tasks/process_terminating_jobs.py,sha256=Tq6oZP6nMHLhtdKnWmC11VHNWaO409F6IwzajzE-kJs,3746
239
- dstack/_internal/server/background/tasks/process_volumes.py,sha256=UoMtYjX6MbSoMXOOuBhA0FVIlQMT_wZWsRNeYVV3cSI,4703
237
+ dstack/_internal/server/background/tasks/process_submitted_jobs.py,sha256=xv1aW2umlOdbdZn4TOvlhl6td3Uam378yIwosfSO9ks,27022
238
+ dstack/_internal/server/background/tasks/process_terminating_jobs.py,sha256=A8J6JabVih1nHl3Kgyaoh7eaKPywicZe7vx-4V_uvgk,4033
239
+ dstack/_internal/server/background/tasks/process_volumes.py,sha256=YafZml-b-bD7M6S0UmY-rLV-pIIX-SdzuyaErqxrnyk,4939
240
240
  dstack/_internal/server/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
241
241
  dstack/_internal/server/migrations/env.py,sha256=RuhcN-CST86VzMatjqBMdYcG0fGGMZl_o1YgZTL-WDw,2831
242
242
  dstack/_internal/server/migrations/versions/065588ec72b8_add_vultr_to_backendtype_enum.py,sha256=wSdI_HWGUFgczhOPyoP6BTPmDbN3KmjokHVpZJGsq4Q,2112
@@ -277,6 +277,7 @@ dstack/_internal/server/migrations/versions/99b4c8c954ea_add_termination_reason_
277
277
  dstack/_internal/server/migrations/versions/9eea6af28e10_added_fail_reason_for_instancemodel.py,sha256=n0iWQ3L1DWQqPIT-IRs9TR74psf-ZDjcYvSIfn80etY,1027
278
278
  dstack/_internal/server/migrations/versions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
279
279
  dstack/_internal/server/migrations/versions/a060e2440936_.py,sha256=4wXIln1yzEowe-1_xFRJFGfaVIwkwlpAO9RlLeVQ-uY,8016
280
+ dstack/_internal/server/migrations/versions/a751ef183f27_move_attachment_data_to_volumes_.py,sha256=befGXlRV7vP6u3VIyf7nMslyBHVAZuFbomQpmc7phRs,1023
280
281
  dstack/_internal/server/migrations/versions/a7b46c073fa1_add_placementgroupmodel.py,sha256=fXYI21V43UmHcIpq7KfD8Ggcjy_l4I_hfXWhB0SnSBM,2078
281
282
  dstack/_internal/server/migrations/versions/afbc600ff2b2_add_created_at_to_usermodel_and_.py,sha256=qRDvCdBaTjL82sdZN2GTWq0JCF2PZU-jFANzCJnM4Bw,3705
282
283
  dstack/_internal/server/migrations/versions/b4d6ad60db08_add_instancemodel_unreachable.py,sha256=rnmLqt_pzNtqMqcmzXIPZxs5WxDE7ieAdI6SnKUFjqM,1038
@@ -333,21 +334,21 @@ dstack/_internal/server/security/permissions.py,sha256=K756I8fnBLBrq4PZwz1ttt74H
333
334
  dstack/_internal/server/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
334
335
  dstack/_internal/server/services/config.py,sha256=b7m2wkVjtXDR46WMgYLw2uun9orM9lxXjnDp53oeASM,29525
335
336
  dstack/_internal/server/services/docker.py,sha256=3EcYPiVsrNBGDQYOb60QJ241mzTT6lJROYQXIwt-8dk,5351
336
- dstack/_internal/server/services/fleets.py,sha256=1b63NYpx4QMa1TaMZDEQxawpdrRLstFN5xUCPbm2_m8,28779
337
+ dstack/_internal/server/services/fleets.py,sha256=h5LL8FGOZMOl0kzLycP36U6k3SWl_bSnSn7oFg8ubQY,28980
337
338
  dstack/_internal/server/services/locking.py,sha256=UV5kc-BgROBuNDpBrp8jkcf-fHPOpHLc8JufL-8mbN8,2453
338
339
  dstack/_internal/server/services/logging.py,sha256=Nu1628kW2hqB__N0Eyr07wGWjVWxfyJnczonTJ72kSM,417
339
340
  dstack/_internal/server/services/logs.py,sha256=53pymPDaM9-xXHFzCyEHdvM49JPTpsLI2aPTSP5zaPo,20090
340
341
  dstack/_internal/server/services/metrics.py,sha256=_YmAQ_oCzzklgk_O5iSJDZJhRk9rBqx8REkPcRQUTAo,3490
341
- dstack/_internal/server/services/offers.py,sha256=gHE7HM_6EdOkAVL35Eclh5INbtLRtaanw1kTd9is48A,6926
342
+ dstack/_internal/server/services/offers.py,sha256=GIaICztEu4zbcpIGHvNsfMVYmPvoYg5zcQ0apHNdgBw,6954
342
343
  dstack/_internal/server/services/permissions.py,sha256=l7Ngdelmn65vjw13NcOdaC6lBYMRuSw6FbHzYwdK3nE,1005
343
344
  dstack/_internal/server/services/placement.py,sha256=DWZ8-iAE3o0J0xaHikuJYZzpuBiq7lj41LiAP1PfoEs,1773
344
- dstack/_internal/server/services/pools.py,sha256=ZJGZT2AtOxgm0m0BaSVdynelGXzr4Y0rRcH3BdyG_w4,28874
345
+ dstack/_internal/server/services/pools.py,sha256=yDlNJn219satSuoxRmlxDG-BPrK0j7MdmGNa-HMyCsk,28886
345
346
  dstack/_internal/server/services/projects.py,sha256=HWOnFOC6LmvbjDzRKADv7tXKIDWthbO11zrsHU7vkew,14709
346
347
  dstack/_internal/server/services/repos.py,sha256=f9ztN7jz_2gvD9hXF5sJwWDVyG2-NHRfjIdSukowPh8,9342
347
348
  dstack/_internal/server/services/runs.py,sha256=3m9oYKq08QQF1GlaOa6_2nYp9SMYoqvfjVnO3-fpTGY,36045
348
349
  dstack/_internal/server/services/storage.py,sha256=6I0xI_3_RpJNbKZwHjDnjrEwXGdHfiaeb5li15T-M1I,1884
349
350
  dstack/_internal/server/services/users.py,sha256=L-exfxHdhj3TKX-gSjezHrYK6tnrt5qsQs-zZng1tUI,7123
350
- dstack/_internal/server/services/volumes.py,sha256=GMHUlSjeiIc9LSWKes-ZPRKgyvWD6y1VT8w6mH-URtg,12635
351
+ dstack/_internal/server/services/volumes.py,sha256=UX-fqC3c240aG6EbdtVsXrJQbkF0O06IFZNtg6bA_tE,14551
351
352
  dstack/_internal/server/services/backends/__init__.py,sha256=Q8Ju0Shf2RAlIdi1CapJNUKOdUAFeRnxhNYti6GZiww,14923
352
353
  dstack/_internal/server/services/backends/handlers.py,sha256=j-MhBxrpdepoDG7f2tApjFnE23RVO5I15-hxHyOWnew,3251
353
354
  dstack/_internal/server/services/backends/configurators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -374,7 +375,7 @@ dstack/_internal/server/services/gateways/__init__.py,sha256=ceTzXRWu-pVghze9New
374
375
  dstack/_internal/server/services/gateways/client.py,sha256=1PH9gUjRkHegp37B27pjBVoPFk64KjxcE30eEPBTLM0,7321
375
376
  dstack/_internal/server/services/gateways/connection.py,sha256=ot3lV85XdmCT45vBWeyj57nLPcLPNm316zu3jMyeWjA,5625
376
377
  dstack/_internal/server/services/gateways/pool.py,sha256=0LclTl1tyx-doS78LeaAKjr-SMp98zuwh5f9s06JSd0,1914
377
- dstack/_internal/server/services/jobs/__init__.py,sha256=17TOvwep41_LrvaemVU8v5LK07vpjGDqbSNsRVBZz8E,25185
378
+ dstack/_internal/server/services/jobs/__init__.py,sha256=zxKGDUo_6Sjoh3VGMQVrxQn9OkwgJQJXeKo3DZWkRI8,25283
378
379
  dstack/_internal/server/services/jobs/configurators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
379
380
  dstack/_internal/server/services/jobs/configurators/base.py,sha256=I21dp1P9XC49NNJL_bfihwXfhFd6G-yQXYFYk-EJPEI,10306
380
381
  dstack/_internal/server/services/jobs/configurators/dev.py,sha256=2EEtfOpNXP5d9b3ZErE3dA3Co8aAAv1fbx9wAjRZpGY,2018
@@ -392,7 +393,7 @@ dstack/_internal/server/services/proxy/routers/service_proxy.py,sha256=8rsuenv9L
392
393
  dstack/_internal/server/services/proxy/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
393
394
  dstack/_internal/server/services/proxy/services/service_proxy.py,sha256=4JrSxHqhBYqU1oENii89Db-bzkFWExYrOy-0mNEhWBs,4879
394
395
  dstack/_internal/server/services/runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
395
- dstack/_internal/server/services/runner/client.py,sha256=eIMQkyB-2RBA220zBfif74-lHYSQ5nA5WAdvW9dcvuE,14804
396
+ dstack/_internal/server/services/runner/client.py,sha256=lQBQoUIMtKviSSQV9rx8VKfWXlRswGJOEI0TF7QV_oA,14961
396
397
  dstack/_internal/server/services/runner/ssh.py,sha256=H-X0015ZPwYq5tc31ytFF1uNaUAr9itAsABI2oPJWrk,5017
397
398
  dstack/_internal/server/services/services/__init__.py,sha256=-kbk6Qb0mGImhNtYhG7JRsmyc0rKGl0A4rRuN1Wq2bA,10617
398
399
  dstack/_internal/server/services/services/autoscalers.py,sha256=0o_w9La-ex_P3VKG88w_XN3hkLkzryv5l1cH3pkZyAE,4315
@@ -502,7 +503,7 @@ dstack/_internal/server/statics/static/media/logo.f602feeb138844eda97c8cb6414614
502
503
  dstack/_internal/server/statics/static/media/okta.12f178e6873a1100965f2a4dbd18fcec.svg,sha256=KqFI05gQM135zC1plF1WBRF2F7CyKL7km97WKsZjAHI,319
503
504
  dstack/_internal/server/statics/static/media/theme.3994c817bb7dda191c1c9640dee0bf42.svg,sha256=ZxFFBVZWuRLqmWH4zhwGLNtKjOzHj-5MGJRunFAtu1I,561
504
505
  dstack/_internal/server/testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
505
- dstack/_internal/server/testing/common.py,sha256=lEvvOOZbKhxKHI1EZGEz9p_aGj3von2NLiHmaQd1cDk,28873
506
+ dstack/_internal/server/testing/common.py,sha256=xwIk9mNec_9OBvJYw_RH9kdOou0Hs8S0qxMor9SH2x4,28949
506
507
  dstack/_internal/server/testing/conf.py,sha256=-zhujfFjTHNfQDOK-hBck32By11c_kC0OeinB3esQGg,1902
507
508
  dstack/_internal/server/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
508
509
  dstack/_internal/server/utils/common.py,sha256=PbjXtqYy1taKXpyG5ys8cIrz9MXqc9dBAsR_9D1brrk,1414
@@ -613,11 +614,11 @@ tests/_internal/server/background/tasks/test_process_gateways.py,sha256=h7jzDFQp
613
614
  tests/_internal/server/background/tasks/test_process_instances.py,sha256=-ATbgOT8XAeqGmPn4XPlSJzxnrm0haAJhX13nWOBOtM,24816
614
615
  tests/_internal/server/background/tasks/test_process_metrics.py,sha256=eirq386mVZqX_PqctuX7CxO38Prwy8RH2UesYXo3CnQ,5083
615
616
  tests/_internal/server/background/tasks/test_process_placement_groups.py,sha256=shHqZvS8QoNwQe8J29-aHk2X2HqX-gsxcQiZevU8yuY,1528
616
- tests/_internal/server/background/tasks/test_process_running_jobs.py,sha256=HrFPqsKJDTxaxj5Gounpqsx1Vr91-OrQ3-1c4PnfKNI,25483
617
+ tests/_internal/server/background/tasks/test_process_running_jobs.py,sha256=zm0eGL3MY5H7YAZuKO0fTqyU0Tb-W2lGniHN6XwOXoE,25542
617
618
  tests/_internal/server/background/tasks/test_process_runs.py,sha256=Yv6f2CDZ-ejDGo7uOKyyAGxYLsvqyjNo4Zw1cCQbjgM,14489
618
- tests/_internal/server/background/tasks/test_process_submitted_jobs.py,sha256=XBgVD2DEzjh2dVj18qAYmRP6DDNnx6YINUbZOtVQT8M,24325
619
+ tests/_internal/server/background/tasks/test_process_submitted_jobs.py,sha256=Sf4aDa1ZYw5Sz3xAw8K0hoI-VDesv7Ew3gZdAzQvj_4,24451
619
620
  tests/_internal/server/background/tasks/test_process_submitted_volumes.py,sha256=njfRDdDzVtDL8rech_004Wf5z5PpoQVWFufKh1yUyYY,2171
620
- tests/_internal/server/background/tasks/test_process_terminating_jobs.py,sha256=85X-HD9HHBduTxOBfiTDgwVOOwoMDxjfioT8SfWUrmA,13899
621
+ tests/_internal/server/background/tasks/test_process_terminating_jobs.py,sha256=VZsfP1NVxkcLSNWfEXDFe_vQZUQfuG0B5i-krXKP_IQ,14147
621
622
  tests/_internal/server/routers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
622
623
  tests/_internal/server/routers/test_backends.py,sha256=4bY8oMMKB3xIRWUfVIUcI4dhDz1ukcpEeWOLllxmPUw,65231
623
624
  tests/_internal/server/routers/test_fleets.py,sha256=ap3Jjg2iiwuklosu2_-xx-cJBlM6uZN5CQfGudORPf0,35105
@@ -631,13 +632,14 @@ tests/_internal/server/routers/test_repos.py,sha256=4O4mCDlAPh8xJU_XWtwLhFWRkoTx
631
632
  tests/_internal/server/routers/test_runs.py,sha256=t6tuvN8JHKSKgd_PRTlPo3VzsJ3aE_EmW9f8cczxQ4M,71547
632
633
  tests/_internal/server/routers/test_server.py,sha256=ROkuRNNJEkMQuK8guZ3Qy3iRRfiWvPIJJJDc09BI0D4,489
633
634
  tests/_internal/server/routers/test_users.py,sha256=5QSLvfn9SroGsZoBGmSTEaaqJcdEO8EUUy_YuvLL8ss,12787
634
- tests/_internal/server/routers/test_volumes.py,sha256=4nv7ba9VkjbYFyYyQmO0coyav_Z9x5t_ayoiyKmvXVw,15947
635
+ tests/_internal/server/routers/test_volumes.py,sha256=fVv7sVo9POFImal9Mlkaw8CJOAR67S_eYHxIReHebBU,16241
635
636
  tests/_internal/server/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
636
637
  tests/_internal/server/services/test_config.py,sha256=KfPyYSSsLaQszBAgqYeM2Tt5YkRiV-at0ik86ecdrtE,5168
637
638
  tests/_internal/server/services/test_docker.py,sha256=QsnoVJUnvzcd1Tpw0UtKXtDSCr9nh_jmtVSkOi7J4ug,5628
638
639
  tests/_internal/server/services/test_fleets.py,sha256=WqK9Bs3Nfew45n4WunZhvIvCw0VHJ2w5t0-4CakxFgU,8222
639
640
  tests/_internal/server/services/test_logs.py,sha256=QmaI07u9O0czwH8J2eZO3mOYnutAtIrDTUrF9CJLdwU,29647
640
- tests/_internal/server/services/test_pools.py,sha256=xbmrLoLlCxahnRhNUxvZNv2-u_JOOUXs5v1UKLJ_xlo,3719
641
+ tests/_internal/server/services/test_offers.py,sha256=t0nQNjjm-fvIqHIOA_99hokrdTXwpYrgzjaQjqJXs8w,7814
642
+ tests/_internal/server/services/test_pools.py,sha256=aPF4R6_VCeDR4AcjQ0H2J1BKivoVs-JUNQBJZ-IYdPg,7436
641
643
  tests/_internal/server/services/test_repos.py,sha256=o2D0xmOPYaiwDq6nNJNtqYMSuSz_xKJ_ZChVOvWIhJY,14289
642
644
  tests/_internal/server/services/test_runs.py,sha256=8frDxa3NTJzXir6fJTXRLm_0B_jUd36qV66DgHvRdDU,9289
643
645
  tests/_internal/server/services/encryption/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -651,7 +653,7 @@ tests/_internal/server/services/proxy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCe
651
653
  tests/_internal/server/services/proxy/routers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
652
654
  tests/_internal/server/services/proxy/routers/test_service_proxy.py,sha256=cGyBwfSgT268CRhkgpTEIM3O1U9z8tSNTYjHQrn3e0E,9843
653
655
  tests/_internal/server/services/runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
654
- tests/_internal/server/services/runner/test_client.py,sha256=LRJzmyMOGtURc-S4uICfhHjO2K0fxvCa--yXflnoTDM,16212
656
+ tests/_internal/server/services/runner/test_client.py,sha256=azCFqMipDCEhoTy4pjq3L5me4SD-mk0ldHTEQ6N6uu8,16855
655
657
  tests/_internal/server/services/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
656
658
  tests/_internal/server/services/services/test_autoscalers.py,sha256=YMi4W5gKFOpEdutc2Fli1L1DIUxCJLHE7ji-CJoiVac,3986
657
659
  tests/_internal/server/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -667,9 +669,9 @@ tests/_internal/utils/test_path.py,sha256=rzS-1YCxsFUocBe42dghLOMFNymPruGrA7bqFZ
667
669
  tests/_internal/utils/test_ssh.py,sha256=V-cBFPhD--9eM9d1uQQgpj2gnYLA3c43f4cX9uJ6E-U,1743
668
670
  tests/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
669
671
  tests/api/test_utils.py,sha256=SSSqHcNE5cZVqDq4n2sKZthRoXaZ_Bx7z1AAN5xTM9s,391
670
- dstack-0.18.41.dist-info/LICENSE.md,sha256=qDABaRGjSKVOib1U8viw2P_96sIK7Puo426784oD9f8,15976
671
- dstack-0.18.41.dist-info/METADATA,sha256=DyUee2y4XQJ8iDBDiDonQQIaqp-VfU9s_IV2Jfg4F2s,17690
672
- dstack-0.18.41.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
673
- dstack-0.18.41.dist-info/entry_points.txt,sha256=GnLrMS8hx3rWAySQjA7tPNhtixV6a-brRkmal1PKoHc,58
674
- dstack-0.18.41.dist-info/top_level.txt,sha256=3BrIO1zrqxT9P20ymhRM6k15meZXzbPL6ykBlDZG2_k,13
675
- dstack-0.18.41.dist-info/RECORD,,
672
+ dstack-0.18.42.dist-info/LICENSE.md,sha256=qDABaRGjSKVOib1U8viw2P_96sIK7Puo426784oD9f8,15976
673
+ dstack-0.18.42.dist-info/METADATA,sha256=vSyb16zzc0XeDvb0drJPn91GJqIajnyIKNc8RY3THtE,17690
674
+ dstack-0.18.42.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
675
+ dstack-0.18.42.dist-info/entry_points.txt,sha256=GnLrMS8hx3rWAySQjA7tPNhtixV6a-brRkmal1PKoHc,58
676
+ dstack-0.18.42.dist-info/top_level.txt,sha256=3BrIO1zrqxT9P20ymhRM6k15meZXzbPL6ykBlDZG2_k,13
677
+ dstack-0.18.42.dist-info/RECORD,,
@@ -349,6 +349,7 @@ class TestProcessRunningJobs:
349
349
  host_ssh_user="ubuntu",
350
350
  host_ssh_keys=["user_ssh_key"],
351
351
  container_ssh_keys=[project_ssh_pub_key, "user_ssh_key"],
352
+ instance_id=job_provisioning_data.instance_id,
352
353
  )
353
354
  await session.refresh(job)
354
355
  assert job is not None
@@ -27,7 +27,7 @@ from dstack._internal.core.models.volumes import (
27
27
  VolumeStatus,
28
28
  )
29
29
  from dstack._internal.server.background.tasks.process_submitted_jobs import process_submitted_jobs
30
- from dstack._internal.server.models import InstanceModel, JobModel
30
+ from dstack._internal.server.models import InstanceModel, JobModel, VolumeAttachmentModel
31
31
  from dstack._internal.server.testing.common import (
32
32
  create_fleet,
33
33
  create_instance,
@@ -515,7 +515,9 @@ class TestProcessSubmittedJobs:
515
515
  await session.refresh(instance)
516
516
  res = await session.execute(
517
517
  select(JobModel).options(
518
- joinedload(JobModel.instance).selectinload(InstanceModel.volumes)
518
+ joinedload(JobModel.instance)
519
+ .joinedload(InstanceModel.volume_attachments)
520
+ .joinedload(VolumeAttachmentModel.volume)
519
521
  )
520
522
  )
521
523
  job = res.unique().scalar_one()
@@ -523,7 +525,7 @@ class TestProcessSubmittedJobs:
523
525
  assert (
524
526
  job.instance_assigned and job.instance is not None and job.instance.id == instance.id
525
527
  )
526
- assert job.instance.volumes == [volume]
528
+ assert job.instance.volume_attachments[0].volume == volume
527
529
 
528
530
  @pytest.mark.asyncio
529
531
  @pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
@@ -13,7 +13,7 @@ from dstack._internal.core.models.volumes import VolumeStatus
13
13
  from dstack._internal.server.background.tasks.process_terminating_jobs import (
14
14
  process_terminating_jobs,
15
15
  )
16
- from dstack._internal.server.models import InstanceModel, JobModel
16
+ from dstack._internal.server.models import InstanceModel, JobModel, VolumeAttachmentModel
17
17
  from dstack._internal.server.services.volumes import volume_model_to_volume
18
18
  from dstack._internal.server.testing.common import (
19
19
  create_instance,
@@ -176,7 +176,7 @@ class TestProcessTerminatingJobs:
176
176
  # so that stuck volumes don't prevent the instance from terminating.
177
177
  assert job.instance is None
178
178
  assert job.volumes_detached_at is not None
179
- assert len(instance.volumes) == 1
179
+ assert len(instance.volume_attachments) == 1
180
180
 
181
181
  # Force detach called
182
182
  with (
@@ -214,11 +214,11 @@ class TestProcessTerminatingJobs:
214
214
  res = await session.execute(select(JobModel).options(joinedload(JobModel.instance)))
215
215
  job = res.unique().scalar_one()
216
216
  res = await session.execute(
217
- select(InstanceModel).options(joinedload(InstanceModel.volumes))
217
+ select(InstanceModel).options(joinedload(InstanceModel.volume_attachments))
218
218
  )
219
219
  instance = res.unique().scalar_one()
220
220
  assert job.status == JobStatus.TERMINATED
221
- assert len(instance.volumes) == 0
221
+ assert len(instance.volume_attachments) == 0
222
222
 
223
223
  async def test_terminates_job_on_shared_instance(self, session: AsyncSession):
224
224
  project = await create_project(session)
@@ -330,7 +330,12 @@ class TestProcessTerminatingJobs:
330
330
  await session.refresh(instance)
331
331
  assert job.status == JobStatus.TERMINATED
332
332
  res = await session.execute(
333
- select(InstanceModel).options(joinedload(InstanceModel.volumes))
333
+ select(InstanceModel).options(
334
+ joinedload(InstanceModel.volume_attachments).joinedload(
335
+ VolumeAttachmentModel.volume
336
+ )
337
+ )
334
338
  )
335
339
  instance = res.unique().scalar_one()
336
- assert instance.volumes == [volume_2]
340
+ assert len(instance.volume_attachments) == 1
341
+ assert instance.volume_attachments[0].volume == volume_2
@@ -11,7 +11,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
11
11
 
12
12
  from dstack._internal.core.models.backends.base import BackendType
13
13
  from dstack._internal.core.models.users import GlobalRole, ProjectRole
14
- from dstack._internal.server.models import VolumeModel
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
17
  create_instance,
@@ -76,6 +76,7 @@ class TestListVolumes:
76
76
  "deleted": False,
77
77
  "volume_id": None,
78
78
  "provisioning_data": None,
79
+ "attachments": [],
79
80
  "attachment_data": None,
80
81
  },
81
82
  {
@@ -91,6 +92,7 @@ class TestListVolumes:
91
92
  "deleted": False,
92
93
  "volume_id": None,
93
94
  "provisioning_data": None,
95
+ "attachments": [],
94
96
  "attachment_data": None,
95
97
  },
96
98
  ]
@@ -117,6 +119,7 @@ class TestListVolumes:
117
119
  "deleted": False,
118
120
  "volume_id": None,
119
121
  "provisioning_data": None,
122
+ "attachments": [],
120
123
  "attachment_data": None,
121
124
  },
122
125
  ]
@@ -170,6 +173,7 @@ class TestListVolumes:
170
173
  "deleted": False,
171
174
  "volume_id": None,
172
175
  "provisioning_data": None,
176
+ "attachments": [],
173
177
  "attachment_data": None,
174
178
  },
175
179
  ]
@@ -217,6 +221,7 @@ class TestListProjectVolumes:
217
221
  "deleted": False,
218
222
  "volume_id": None,
219
223
  "provisioning_data": None,
224
+ "attachments": [],
220
225
  "attachment_data": None,
221
226
  }
222
227
  ]
@@ -264,6 +269,7 @@ class TestGetVolume:
264
269
  "deleted": False,
265
270
  "volume_id": None,
266
271
  "provisioning_data": None,
272
+ "attachments": [],
267
273
  "attachment_data": None,
268
274
  }
269
275
 
@@ -325,6 +331,7 @@ class TestCreateVolume:
325
331
  "deleted": False,
326
332
  "volume_id": None,
327
333
  "provisioning_data": None,
334
+ "attachments": [],
328
335
  "attachment_data": None,
329
336
  }
330
337
  res = await session.execute(select(VolumeModel))
@@ -391,7 +398,7 @@ class TestDeleteVolumes:
391
398
  project=project,
392
399
  pool=pool,
393
400
  )
394
- volume.instances.append(instance)
401
+ volume.attachments.append(VolumeAttachmentModel(instance=instance))
395
402
  await session.commit()
396
403
  response = await client.post(
397
404
  f"/api/project/{project.name}/volumes/delete",