Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/dstack/_internal/server/services/fleets.py
Original file line number Diff line number Diff line change
Expand Up @@ -919,7 +919,9 @@ def is_fleet_in_use(fleet_model: FleetModel, instance_nums: Optional[List[int]]
if instance_nums is not None:
selected_instance_in_use = [i for i in instances_in_use if i.instance_num in instance_nums]
active_runs = [r for r in fleet_model.runs if not r.status.is_finished()]
return len(selected_instance_in_use) > 0 or len(instances_in_use) == 0 and len(active_runs) > 0
return len(selected_instance_in_use) > 0 or (
instance_nums is None and len(instances_in_use) == 0 and len(active_runs) > 0
)


def is_fleet_empty(fleet_model: FleetModel) -> bool:
Expand Down
59 changes: 57 additions & 2 deletions src/tests/_internal/server/routers/test_fleets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1633,7 +1633,38 @@ async def test_terminates_fleet_instances(

@pytest.mark.asyncio
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
async def test_returns_400_when_fleets_in_use(
async def test_returns_400_when_fleet_in_use(
self, test_db, session: AsyncSession, client: AsyncClient
):
user = await create_user(session, global_role=GlobalRole.USER)
project = await create_project(session)
await add_project_member(
session=session, project=project, user=user, project_role=ProjectRole.USER
)
fleet = await create_fleet(session=session, project=project)
repo = await create_repo(
session=session,
project_id=project.id,
)
await create_run(
session=session,
project=project,
repo=repo,
user=user,
fleet=fleet,
)
response = await client.post(
f"/api/project/{project.name}/fleets/delete",
headers=get_auth_headers(user.token),
json={"names": [fleet.name]},
)
assert response.status_code == 400
await session.refresh(fleet)
assert not fleet.deleted

@pytest.mark.asyncio
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
async def test_returns_400_when_fleet_instance_in_use(
self, test_db, session: AsyncSession, client: AsyncClient
):
user = await create_user(session, global_role=GlobalRole.USER)
Expand Down Expand Up @@ -1800,10 +1831,34 @@ async def test_terminates_fleet_instances(
session=session,
project=project,
instance_num=2,
status=InstanceStatus.IDLE,
)
instance3 = await create_instance(
session=session,
project=project,
instance_num=3,
status=InstanceStatus.BUSY,
)
fleet.instances.append(instance1)
fleet.instances.append(instance2)
await session.commit()
fleet.instances.append(instance3)
repo = await create_repo(
session=session,
project_id=project.id,
)
# Run assigned to instance 3. Should not interfere with deleting instance 1.
run = await create_run(
session=session,
project=project,
repo=repo,
user=user,
fleet=fleet,
)
await create_job(
session=session,
run=run,
instance=instance3,
)
response = await client.post(
f"/api/project/{project.name}/fleets/delete_instances",
headers=get_auth_headers(user.token),
Expand Down
Loading