|
16 | 16 | InstanceStatus, |
17 | 17 | ) |
18 | 18 | from dstack._internal.core.models.profiles import Profile |
| 19 | +from dstack._internal.core.models.resources import Range, ResourcesSpec |
19 | 20 | from dstack._internal.core.models.runs import ( |
20 | 21 | JobStatus, |
21 | 22 | JobTerminationReason, |
@@ -744,7 +745,7 @@ async def test_assigns_no_fleet_when_all_fleets_occupied(self, test_db, session: |
744 | 745 |
|
745 | 746 | @pytest.mark.asyncio |
746 | 747 | @pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True) |
747 | | - async def test_does_not_assign_job_to_elastic_empty_fleet_if_fleets_unspecified( |
| 748 | + async def test_does_not_assign_job_to_elastic_empty_fleet_without_backend_offers_if_fleets_unspecified( |
748 | 749 | self, test_db, session: AsyncSession |
749 | 750 | ): |
750 | 751 | project = await create_project(session) |
@@ -782,6 +783,58 @@ async def test_does_not_assign_job_to_elastic_empty_fleet_if_fleets_unspecified( |
782 | 783 | assert job.instance_id is None |
783 | 784 | assert job.fleet_id is None |
784 | 785 |
|
| 786 | + @pytest.mark.asyncio |
| 787 | + @pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True) |
| 788 | + async def test_assigns_job_to_elastic_empty_fleet_with_backend_offers_if_fleets_unspecified( |
| 789 | + self, test_db, session: AsyncSession |
| 790 | + ): |
| 791 | + project = await create_project(session) |
| 792 | + user = await create_user(session) |
| 793 | + repo = await create_repo(session=session, project_id=project.id) |
| 794 | + fleet_spec1 = get_fleet_spec() |
| 795 | + fleet_spec1.configuration.nodes = FleetNodesSpec(min=0, target=0, max=1) |
| 796 | + fleet1 = await create_fleet( |
| 797 | + session=session, project=project, spec=fleet_spec1, name="fleet" |
| 798 | + ) |
| 799 | + # Need a second non-empty fleet to have two-stage processing |
| 800 | + fleet_spec2 = get_fleet_spec() |
| 801 | + # Empty resources intersection to return no backend offers |
| 802 | + fleet_spec2.configuration.resources = ResourcesSpec(cpu=Range(min=0, max=0)) |
| 803 | + fleet2 = await create_fleet( |
| 804 | + session=session, project=project, spec=fleet_spec2, name="fleet2" |
| 805 | + ) |
| 806 | + await create_instance( |
| 807 | + session=session, |
| 808 | + project=project, |
| 809 | + fleet=fleet2, |
| 810 | + instance_num=0, |
| 811 | + status=InstanceStatus.BUSY, |
| 812 | + ) |
| 813 | + run = await create_run( |
| 814 | + session=session, |
| 815 | + project=project, |
| 816 | + repo=repo, |
| 817 | + user=user, |
| 818 | + ) |
| 819 | + job = await create_job( |
| 820 | + session=session, |
| 821 | + run=run, |
| 822 | + instance_assigned=False, |
| 823 | + ) |
| 824 | + aws_mock = Mock() |
| 825 | + aws_mock.TYPE = BackendType.AWS |
| 826 | + offer = get_instance_offer_with_availability(backend=BackendType.AWS, price=1.0) |
| 827 | + aws_mock.compute.return_value = Mock(spec=ComputeMockSpec) |
| 828 | + aws_mock.compute.return_value.get_offers.return_value = [offer] |
| 829 | + with patch("dstack._internal.server.services.backends.get_project_backends") as m: |
| 830 | + m.return_value = [aws_mock] |
| 831 | + await process_submitted_jobs() |
| 832 | + await session.refresh(job) |
| 833 | + assert job.status == JobStatus.SUBMITTED |
| 834 | + assert job.instance_assigned |
| 835 | + assert job.instance_id is None |
| 836 | + assert job.fleet_id == fleet1.id |
| 837 | + |
785 | 838 | @pytest.mark.asyncio |
786 | 839 | @pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True) |
787 | 840 | async def test_assigns_job_to_elastic_empty_fleet_if_fleets_specified( |
|
0 commit comments