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.
- 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 -21
- dstack/_internal/cli/services/profile.py +34 -83
- dstack/_internal/cli/utils/gateway.py +1 -1
- 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 +20 -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 +13 -27
- 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 +13 -43
- 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 +7 -3
- 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 +20 -15
- 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 +41 -46
- 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 +17 -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 +0 -3
- 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 +4 -1
- dstack/_internal/server/background/tasks/process_prometheus_metrics.py +1 -1
- dstack/_internal/server/background/tasks/process_running_jobs.py +14 -5
- dstack/_internal/server/background/tasks/process_submitted_jobs.py +16 -37
- dstack/_internal/server/background/tasks/process_volumes.py +5 -2
- dstack/_internal/server/migrations/versions/7bc2586e8b9e_make_instancemodel_pool_id_optional.py +36 -0
- dstack/_internal/server/migrations/versions/bc8ca4a505c6_store_backendtype_as_string.py +171 -0
- dstack/_internal/server/models.py +48 -9
- dstack/_internal/server/routers/backends.py +14 -23
- dstack/_internal/server/routers/instances.py +3 -4
- dstack/_internal/server/routers/metrics.py +10 -8
- dstack/_internal/server/routers/prometheus.py +1 -1
- 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/services/backends/__init__.py +85 -158
- dstack/_internal/server/services/config.py +52 -576
- dstack/_internal/server/services/fleets.py +8 -103
- dstack/_internal/server/services/gateways/__init__.py +12 -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 +16 -0
- 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 +39 -13
- dstack/_internal/server/services/offers.py +1 -1
- dstack/_internal/server/services/projects.py +23 -14
- dstack/_internal/server/services/prometheus.py +176 -18
- dstack/_internal/server/services/runs.py +24 -16
- dstack/_internal/server/services/volumes.py +8 -4
- dstack/_internal/server/statics/index.html +1 -1
- dstack/_internal/server/statics/{main-4eb116b97819badd1e2c.js → main-4a0fe83e84574654e397.js} +18 -14
- dstack/_internal/server/statics/{main-4eb116b97819badd1e2c.js.map → main-4a0fe83e84574654e397.js.map} +1 -1
- dstack/_internal/server/testing/common.py +58 -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 +167 -83
- dstack/api/server/__init__.py +11 -13
- dstack/api/server/_backends.py +12 -16
- dstack/api/server/_fleets.py +15 -57
- dstack/api/server/_gateways.py +3 -14
- dstack/api/server/_repos.py +1 -4
- dstack/api/server/_runs.py +21 -100
- dstack/api/server/_volumes.py +10 -5
- dstack/version.py +1 -1
- {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/METADATA +1 -1
- {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/RECORD +218 -204
- 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 +0 -3
- tests/_internal/server/background/tasks/test_process_running_jobs.py +0 -21
- 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 +0 -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 +27 -0
- tests/_internal/server/routers/test_projects.py +56 -0
- tests/_internal/server/routers/test_prometheus.py +116 -27
- tests/_internal/server/routers/test_repos.py +0 -15
- tests/_internal/server/routers/test_runs.py +4 -219
- 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 +9 -5
- 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 -17
- 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 -42
- 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 -67
- 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.44.dist-info → dstack-0.19.0.dist-info}/LICENSE.md +0 -0
- {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/WHEEL +0 -0
- {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/entry_points.txt +0 -0
- {dstack-0.18.44.dist-info → dstack-0.19.0.dist-info}/top_level.txt +0 -0
|
@@ -1,612 +0,0 @@
|
|
|
1
|
-
import datetime as dt
|
|
2
|
-
|
|
3
|
-
import pytest
|
|
4
|
-
from freezegun import freeze_time
|
|
5
|
-
from httpx import AsyncClient
|
|
6
|
-
from sqlalchemy import select
|
|
7
|
-
from sqlalchemy.ext.asyncio import AsyncSession
|
|
8
|
-
|
|
9
|
-
from dstack._internal.core.models.backends.base import BackendType
|
|
10
|
-
from dstack._internal.core.models.instances import SSHKey
|
|
11
|
-
from dstack._internal.core.models.profiles import DEFAULT_POOL_NAME
|
|
12
|
-
from dstack._internal.core.models.users import GlobalRole, ProjectRole
|
|
13
|
-
from dstack._internal.server.models import PoolModel
|
|
14
|
-
from dstack._internal.server.schemas.pools import (
|
|
15
|
-
CreatePoolRequest,
|
|
16
|
-
DeletePoolRequest,
|
|
17
|
-
RemoveInstanceRequest,
|
|
18
|
-
SetDefaultPoolRequest,
|
|
19
|
-
ShowPoolRequest,
|
|
20
|
-
)
|
|
21
|
-
from dstack._internal.server.schemas.runs import AddRemoteInstanceRequest
|
|
22
|
-
from dstack._internal.server.services.projects import add_project_member
|
|
23
|
-
from dstack._internal.server.testing.common import (
|
|
24
|
-
create_instance,
|
|
25
|
-
create_pool,
|
|
26
|
-
create_project,
|
|
27
|
-
create_user,
|
|
28
|
-
get_auth_headers,
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
TEST_POOL_NAME = "test_router_pool_name"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class TestListPools:
|
|
35
|
-
@pytest.mark.asyncio
|
|
36
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
37
|
-
async def test_returns_403_if_not_authenticated(
|
|
38
|
-
self, test_db, session: AsyncSession, client: AsyncClient
|
|
39
|
-
):
|
|
40
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
41
|
-
project = await create_project(session=session, owner=user)
|
|
42
|
-
response = await client.post(
|
|
43
|
-
f"/api/project/{project.name}/pool/list",
|
|
44
|
-
json={},
|
|
45
|
-
)
|
|
46
|
-
assert response.status_code == 403
|
|
47
|
-
|
|
48
|
-
@pytest.mark.asyncio
|
|
49
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
50
|
-
@freeze_time(dt.datetime(2023, 10, 4, 12, 0, tzinfo=dt.timezone.utc))
|
|
51
|
-
async def test_creates_and_lists_default_pool(
|
|
52
|
-
self, test_db, session: AsyncSession, client: AsyncClient
|
|
53
|
-
):
|
|
54
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
55
|
-
project = await create_project(session=session, owner=user)
|
|
56
|
-
await add_project_member(
|
|
57
|
-
session=session, project=project, user=user, project_role=ProjectRole.USER
|
|
58
|
-
)
|
|
59
|
-
response = await client.post(
|
|
60
|
-
f"/api/project/{project.name}/pool/list",
|
|
61
|
-
headers=get_auth_headers(user.token),
|
|
62
|
-
json={},
|
|
63
|
-
)
|
|
64
|
-
assert response.status_code == 200
|
|
65
|
-
result = response.json()
|
|
66
|
-
expected = [
|
|
67
|
-
{
|
|
68
|
-
"name": "default-pool",
|
|
69
|
-
"default": True,
|
|
70
|
-
"created_at": "2023-10-04T12:00:00+00:00",
|
|
71
|
-
"total_instances": 0,
|
|
72
|
-
"available_instances": 0,
|
|
73
|
-
}
|
|
74
|
-
]
|
|
75
|
-
assert result == expected
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
class TestDeletePool:
|
|
79
|
-
@pytest.mark.asyncio
|
|
80
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
81
|
-
async def test_returns_403_if_not_authenticated(
|
|
82
|
-
self, test_db, session: AsyncSession, client: AsyncClient
|
|
83
|
-
):
|
|
84
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
85
|
-
project = await create_project(session=session, owner=user)
|
|
86
|
-
response = await client.post(
|
|
87
|
-
f"/api/project/{project.name}/pool/delete",
|
|
88
|
-
json=DeletePoolRequest(name=TEST_POOL_NAME, force=False).dict(),
|
|
89
|
-
)
|
|
90
|
-
assert response.status_code == 403
|
|
91
|
-
|
|
92
|
-
@pytest.mark.asyncio
|
|
93
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
94
|
-
async def test_delete_last_pool(self, test_db, session: AsyncSession, client: AsyncClient):
|
|
95
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
96
|
-
project = await create_project(session=session, owner=user)
|
|
97
|
-
await add_project_member(
|
|
98
|
-
session=session, project=project, user=user, project_role=ProjectRole.ADMIN
|
|
99
|
-
)
|
|
100
|
-
pool = await create_pool(session, project, pool_name=TEST_POOL_NAME)
|
|
101
|
-
response = await client.post(
|
|
102
|
-
f"/api/project/{project.name}/pool/delete",
|
|
103
|
-
headers=get_auth_headers(user.token),
|
|
104
|
-
json=DeletePoolRequest(name=TEST_POOL_NAME, force=False).dict(),
|
|
105
|
-
)
|
|
106
|
-
assert response.status_code == 200
|
|
107
|
-
assert response.json() is None
|
|
108
|
-
|
|
109
|
-
response = await client.post(
|
|
110
|
-
f"/api/project/{project.name}/pool/list",
|
|
111
|
-
headers=get_auth_headers(user.token),
|
|
112
|
-
json={},
|
|
113
|
-
)
|
|
114
|
-
assert response.status_code == 200
|
|
115
|
-
|
|
116
|
-
result = response.json()
|
|
117
|
-
assert len(result) == 1
|
|
118
|
-
|
|
119
|
-
default_pool = result[0]
|
|
120
|
-
assert default_pool["name"] == DEFAULT_POOL_NAME
|
|
121
|
-
assert dt.datetime.fromisoformat(default_pool["created_at"]) > pool.created_at
|
|
122
|
-
|
|
123
|
-
@pytest.mark.asyncio
|
|
124
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
125
|
-
async def test_deletes_pool(self, test_db, session: AsyncSession, client: AsyncClient):
|
|
126
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
127
|
-
project = await create_project(session=session, owner=user)
|
|
128
|
-
await add_project_member(
|
|
129
|
-
session=session, project=project, user=user, project_role=ProjectRole.ADMIN
|
|
130
|
-
)
|
|
131
|
-
pool1 = await create_pool(session, project, pool_name=f"{TEST_POOL_NAME}-left")
|
|
132
|
-
pool2 = await create_pool(session, project, pool_name=f"{TEST_POOL_NAME}-right")
|
|
133
|
-
response = await client.post(
|
|
134
|
-
f"/api/project/{project.name}/pool/delete",
|
|
135
|
-
headers=get_auth_headers(user.token),
|
|
136
|
-
json=DeletePoolRequest(name=pool1.name, force=False).dict(),
|
|
137
|
-
)
|
|
138
|
-
assert response.status_code == 200
|
|
139
|
-
assert response.json() is None
|
|
140
|
-
res = await session.execute(select(PoolModel).where(PoolModel.deleted == False))
|
|
141
|
-
pool = res.scalar_one()
|
|
142
|
-
assert pool.name == pool2.name
|
|
143
|
-
|
|
144
|
-
@pytest.mark.asyncio
|
|
145
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
146
|
-
async def test_returns_400_if_pool_missing(
|
|
147
|
-
self, test_db, session: AsyncSession, client: AsyncClient
|
|
148
|
-
):
|
|
149
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
150
|
-
project = await create_project(session=session, owner=user)
|
|
151
|
-
await add_project_member(
|
|
152
|
-
session=session, project=project, user=user, project_role=ProjectRole.ADMIN
|
|
153
|
-
)
|
|
154
|
-
response = await client.post(
|
|
155
|
-
f"/api/project/{project.name}/pool/delete",
|
|
156
|
-
headers=get_auth_headers(user.token),
|
|
157
|
-
json=DeletePoolRequest(name="missing name", force=False).dict(),
|
|
158
|
-
)
|
|
159
|
-
assert response.status_code == 400
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
class TestSetDefaultPool:
|
|
163
|
-
@pytest.mark.asyncio
|
|
164
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
165
|
-
async def test_returns_403_if_not_authenticated(
|
|
166
|
-
self, test_db, session: AsyncSession, client: AsyncClient
|
|
167
|
-
):
|
|
168
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
169
|
-
project = await create_project(session=session, owner=user)
|
|
170
|
-
response = await client.post(
|
|
171
|
-
f"/api/project/{project.name}/pool/set_default",
|
|
172
|
-
json=SetDefaultPoolRequest(pool_name=TEST_POOL_NAME).dict(),
|
|
173
|
-
)
|
|
174
|
-
assert response.status_code == 403
|
|
175
|
-
|
|
176
|
-
@pytest.mark.asyncio
|
|
177
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
178
|
-
async def test_sets_default(self, test_db, session: AsyncSession, client: AsyncClient):
|
|
179
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
180
|
-
project = await create_project(session=session, owner=user)
|
|
181
|
-
await add_project_member(
|
|
182
|
-
session=session, project=project, user=user, project_role=ProjectRole.ADMIN
|
|
183
|
-
)
|
|
184
|
-
pool = await create_pool(session, project, pool_name=f"{TEST_POOL_NAME}-right")
|
|
185
|
-
response = await client.post(
|
|
186
|
-
f"/api/project/{project.name}/pool/set_default",
|
|
187
|
-
headers=get_auth_headers(user.token),
|
|
188
|
-
json=SetDefaultPoolRequest(pool_name=pool.name).dict(),
|
|
189
|
-
)
|
|
190
|
-
assert response.status_code == 200
|
|
191
|
-
await session.refresh(project)
|
|
192
|
-
assert project.default_pool_id == pool.id
|
|
193
|
-
|
|
194
|
-
@pytest.mark.asyncio
|
|
195
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
196
|
-
async def test_returns_400_if_pool_missing(
|
|
197
|
-
self, test_db, session: AsyncSession, client: AsyncClient
|
|
198
|
-
):
|
|
199
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
200
|
-
project = await create_project(session=session, owner=user)
|
|
201
|
-
await add_project_member(
|
|
202
|
-
session=session, project=project, user=user, project_role=ProjectRole.ADMIN
|
|
203
|
-
)
|
|
204
|
-
response = await client.post(
|
|
205
|
-
f"/api/project/{project.name}/pool/set_default",
|
|
206
|
-
headers=get_auth_headers(user.token),
|
|
207
|
-
json=SetDefaultPoolRequest(pool_name="missing pool").dict(),
|
|
208
|
-
)
|
|
209
|
-
assert response.status_code == 400
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
class TestCreatePool:
|
|
213
|
-
@pytest.mark.asyncio
|
|
214
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
215
|
-
async def test_returns_403_if_not_authenticated(
|
|
216
|
-
self, test_db, session: AsyncSession, client: AsyncClient
|
|
217
|
-
):
|
|
218
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
219
|
-
project = await create_project(session=session, owner=user)
|
|
220
|
-
response = await client.post(
|
|
221
|
-
f"/api/project/{project.name}/pool/create",
|
|
222
|
-
json=CreatePoolRequest(name=TEST_POOL_NAME).dict(),
|
|
223
|
-
)
|
|
224
|
-
assert response.status_code == 403
|
|
225
|
-
|
|
226
|
-
@pytest.mark.asyncio
|
|
227
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
228
|
-
async def test_create_pool(self, test_db, session: AsyncSession, client: AsyncClient):
|
|
229
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
230
|
-
project = await create_project(session=session, owner=user)
|
|
231
|
-
await add_project_member(
|
|
232
|
-
session=session, project=project, user=user, project_role=ProjectRole.ADMIN
|
|
233
|
-
)
|
|
234
|
-
response = await client.post(
|
|
235
|
-
f"/api/project/{project.name}/pool/create",
|
|
236
|
-
headers=get_auth_headers(user.token),
|
|
237
|
-
json=CreatePoolRequest(name=TEST_POOL_NAME).dict(),
|
|
238
|
-
)
|
|
239
|
-
assert response.status_code == 200
|
|
240
|
-
assert response.json() is None
|
|
241
|
-
res = await session.execute(select(PoolModel).where(PoolModel.deleted == False))
|
|
242
|
-
res.scalar_one()
|
|
243
|
-
|
|
244
|
-
@pytest.mark.asyncio
|
|
245
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
246
|
-
async def test_returns_400_on_duplicate_name(
|
|
247
|
-
self, test_db, session: AsyncSession, client: AsyncClient
|
|
248
|
-
):
|
|
249
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
250
|
-
project = await create_project(session=session, owner=user)
|
|
251
|
-
await add_project_member(
|
|
252
|
-
session=session, project=project, user=user, project_role=ProjectRole.ADMIN
|
|
253
|
-
)
|
|
254
|
-
response = await client.post(
|
|
255
|
-
f"/api/project/{project.name}/pool/create",
|
|
256
|
-
headers=get_auth_headers(user.token),
|
|
257
|
-
json=CreatePoolRequest(name=TEST_POOL_NAME).dict(),
|
|
258
|
-
)
|
|
259
|
-
assert response.status_code == 200
|
|
260
|
-
assert response.json() is None
|
|
261
|
-
response = await client.post(
|
|
262
|
-
f"/api/project/{project.name}/pool/create",
|
|
263
|
-
headers=get_auth_headers(user.token),
|
|
264
|
-
json=CreatePoolRequest(name=TEST_POOL_NAME).dict(),
|
|
265
|
-
)
|
|
266
|
-
assert response.status_code == 400
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
class TestShowPool:
|
|
270
|
-
@pytest.mark.asyncio
|
|
271
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
272
|
-
async def test_returns_403_if_not_authenticated(
|
|
273
|
-
self, test_db, session: AsyncSession, client: AsyncClient
|
|
274
|
-
):
|
|
275
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
276
|
-
project = await create_project(session=session, owner=user)
|
|
277
|
-
response = await client.post(
|
|
278
|
-
f"/api/project/{project.name}/pool/show",
|
|
279
|
-
json=CreatePoolRequest(name=TEST_POOL_NAME).dict(),
|
|
280
|
-
)
|
|
281
|
-
assert response.status_code == 403
|
|
282
|
-
|
|
283
|
-
@pytest.mark.asyncio
|
|
284
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
285
|
-
async def test_show_pool(self, test_db, session: AsyncSession, client: AsyncClient):
|
|
286
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
287
|
-
project = await create_project(session=session, owner=user)
|
|
288
|
-
await add_project_member(
|
|
289
|
-
session=session, project=project, user=user, project_role=ProjectRole.ADMIN
|
|
290
|
-
)
|
|
291
|
-
pool = await create_pool(session, project, pool_name=TEST_POOL_NAME)
|
|
292
|
-
instance = await create_instance(
|
|
293
|
-
session=session,
|
|
294
|
-
project=project,
|
|
295
|
-
pool=pool,
|
|
296
|
-
backend=BackendType.DATACRUNCH,
|
|
297
|
-
region="en",
|
|
298
|
-
)
|
|
299
|
-
response = await client.post(
|
|
300
|
-
f"/api/project/{project.name}/pool/show",
|
|
301
|
-
headers=get_auth_headers(user.token),
|
|
302
|
-
json=ShowPoolRequest(name=TEST_POOL_NAME).dict(),
|
|
303
|
-
)
|
|
304
|
-
assert response.status_code == 200
|
|
305
|
-
assert response.json() == {
|
|
306
|
-
"name": "test_router_pool_name",
|
|
307
|
-
"instances": [
|
|
308
|
-
{
|
|
309
|
-
"backend": "datacrunch",
|
|
310
|
-
"instance_type": {
|
|
311
|
-
"name": "instance",
|
|
312
|
-
"resources": {
|
|
313
|
-
"cpus": 1,
|
|
314
|
-
"memory_mib": 512,
|
|
315
|
-
"gpus": [],
|
|
316
|
-
"spot": False,
|
|
317
|
-
"disk": {"size_mib": 102400},
|
|
318
|
-
"description": "",
|
|
319
|
-
},
|
|
320
|
-
},
|
|
321
|
-
"id": str(instance.id),
|
|
322
|
-
"project_name": project.name,
|
|
323
|
-
"name": "test_instance",
|
|
324
|
-
"fleet_id": None,
|
|
325
|
-
"fleet_name": None,
|
|
326
|
-
"instance_num": 0,
|
|
327
|
-
"job_name": None,
|
|
328
|
-
"hostname": "running_instance.ip",
|
|
329
|
-
"status": "idle",
|
|
330
|
-
"unreachable": False,
|
|
331
|
-
"termination_reason": None,
|
|
332
|
-
"created": "2023-01-02T03:04:00+00:00",
|
|
333
|
-
"pool_name": None,
|
|
334
|
-
"region": "en",
|
|
335
|
-
"availability_zone": None,
|
|
336
|
-
"price": 1,
|
|
337
|
-
"total_blocks": 1,
|
|
338
|
-
"busy_blocks": 0,
|
|
339
|
-
}
|
|
340
|
-
],
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
@pytest.mark.asyncio
|
|
344
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
345
|
-
async def test_show_missing_pool(self, test_db, session: AsyncSession, client: AsyncClient):
|
|
346
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
347
|
-
project = await create_project(session=session, owner=user)
|
|
348
|
-
await add_project_member(
|
|
349
|
-
session=session, project=project, user=user, project_role=ProjectRole.ADMIN
|
|
350
|
-
)
|
|
351
|
-
pool = await create_pool(session, project, pool_name=TEST_POOL_NAME)
|
|
352
|
-
await create_instance(
|
|
353
|
-
session=session,
|
|
354
|
-
project=project,
|
|
355
|
-
pool=pool,
|
|
356
|
-
)
|
|
357
|
-
response = await client.post(
|
|
358
|
-
f"/api/project/{project.name}/pool/show",
|
|
359
|
-
headers=get_auth_headers(user.token),
|
|
360
|
-
json=ShowPoolRequest(name="missing_pool").dict(),
|
|
361
|
-
)
|
|
362
|
-
assert response.status_code == 400
|
|
363
|
-
assert response.json() == {
|
|
364
|
-
"detail": [{"msg": "Pool not found", "code": "resource_not_exists"}]
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
class TestAddRemote:
|
|
369
|
-
@pytest.mark.asyncio
|
|
370
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
371
|
-
async def test_returns_403_if_not_authenticated(
|
|
372
|
-
self, test_db, session: AsyncSession, client: AsyncClient
|
|
373
|
-
):
|
|
374
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
375
|
-
project = await create_project(session=session, owner=user)
|
|
376
|
-
remote = AddRemoteInstanceRequest(
|
|
377
|
-
instance_name="test_instance_name",
|
|
378
|
-
instance_network=None,
|
|
379
|
-
region="",
|
|
380
|
-
host="localhost",
|
|
381
|
-
port=22,
|
|
382
|
-
pool_name="pool_name",
|
|
383
|
-
ssh_user="user",
|
|
384
|
-
ssh_keys=[SSHKey(public="abc")],
|
|
385
|
-
)
|
|
386
|
-
response = await client.post(
|
|
387
|
-
f"/api/project/{project.name}/pool/add_remote",
|
|
388
|
-
json=remote.dict(),
|
|
389
|
-
)
|
|
390
|
-
assert response.status_code == 403
|
|
391
|
-
|
|
392
|
-
@pytest.mark.asyncio
|
|
393
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
394
|
-
async def test_add_remote(self, test_db, session: AsyncSession, client: AsyncClient):
|
|
395
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
396
|
-
project = await create_project(session=session, owner=user)
|
|
397
|
-
await add_project_member(
|
|
398
|
-
session=session, project=project, user=user, project_role=ProjectRole.ADMIN
|
|
399
|
-
)
|
|
400
|
-
remote = AddRemoteInstanceRequest(
|
|
401
|
-
instance_name="test_instance_name",
|
|
402
|
-
instance_network=None,
|
|
403
|
-
region="",
|
|
404
|
-
host="localhost",
|
|
405
|
-
port=22,
|
|
406
|
-
pool_name="pool_name",
|
|
407
|
-
ssh_user="user",
|
|
408
|
-
ssh_keys=[SSHKey(public="abc")],
|
|
409
|
-
)
|
|
410
|
-
response = await client.post(
|
|
411
|
-
f"/api/project/{project.name}/pool/add_remote",
|
|
412
|
-
headers=get_auth_headers(user.token),
|
|
413
|
-
json=remote.dict(),
|
|
414
|
-
)
|
|
415
|
-
assert response.status_code == 200
|
|
416
|
-
|
|
417
|
-
data = response.json()
|
|
418
|
-
assert data["status"] == "pending"
|
|
419
|
-
assert data["name"] == "test_instance_name"
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
class TestRemoveInstance:
|
|
423
|
-
@pytest.mark.asyncio
|
|
424
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
425
|
-
async def test_returns_403_if_not_authenticated(
|
|
426
|
-
self, test_db, session: AsyncSession, client: AsyncClient
|
|
427
|
-
):
|
|
428
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
429
|
-
project = await create_project(session=session, owner=user)
|
|
430
|
-
remote = AddRemoteInstanceRequest(
|
|
431
|
-
instance_name="test_instance_name",
|
|
432
|
-
instance_network=None,
|
|
433
|
-
region="",
|
|
434
|
-
host="localhost",
|
|
435
|
-
port=22,
|
|
436
|
-
pool_name="pool_name",
|
|
437
|
-
ssh_user="user",
|
|
438
|
-
ssh_keys=[SSHKey(public="abc")],
|
|
439
|
-
)
|
|
440
|
-
response = await client.post(
|
|
441
|
-
f"/api/project/{project.name}/pool/add_remote",
|
|
442
|
-
json=remote.dict(),
|
|
443
|
-
)
|
|
444
|
-
assert response.status_code == 403
|
|
445
|
-
|
|
446
|
-
@pytest.mark.asyncio
|
|
447
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
448
|
-
async def test_remove_instance(self, test_db, session: AsyncSession, client: AsyncClient):
|
|
449
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
450
|
-
project = await create_project(session=session, owner=user)
|
|
451
|
-
await add_project_member(
|
|
452
|
-
session=session, project=project, user=user, project_role=ProjectRole.ADMIN
|
|
453
|
-
)
|
|
454
|
-
pool = await create_pool(session, project, pool_name=TEST_POOL_NAME)
|
|
455
|
-
instance = await create_instance(
|
|
456
|
-
session=session,
|
|
457
|
-
project=project,
|
|
458
|
-
pool=pool,
|
|
459
|
-
backend=BackendType.DATACRUNCH,
|
|
460
|
-
region="en",
|
|
461
|
-
)
|
|
462
|
-
response = await client.post(
|
|
463
|
-
f"/api/project/{project.name}/pool/remove",
|
|
464
|
-
headers=get_auth_headers(user.token),
|
|
465
|
-
json=RemoveInstanceRequest(
|
|
466
|
-
pool_name=TEST_POOL_NAME,
|
|
467
|
-
instance_name=instance.name,
|
|
468
|
-
).dict(),
|
|
469
|
-
)
|
|
470
|
-
assert response.status_code == 200
|
|
471
|
-
assert response.json() is None
|
|
472
|
-
|
|
473
|
-
response = await client.post(
|
|
474
|
-
f"/api/project/{project.name}/pool/show",
|
|
475
|
-
headers=get_auth_headers(user.token),
|
|
476
|
-
json=ShowPoolRequest(name=TEST_POOL_NAME).dict(),
|
|
477
|
-
)
|
|
478
|
-
assert response.status_code == 200
|
|
479
|
-
assert response.json() == {
|
|
480
|
-
"name": "test_router_pool_name",
|
|
481
|
-
"instances": [
|
|
482
|
-
{
|
|
483
|
-
"backend": "datacrunch",
|
|
484
|
-
"instance_type": {
|
|
485
|
-
"name": "instance",
|
|
486
|
-
"resources": {
|
|
487
|
-
"cpus": 1,
|
|
488
|
-
"memory_mib": 512,
|
|
489
|
-
"gpus": [],
|
|
490
|
-
"spot": False,
|
|
491
|
-
"disk": {"size_mib": 102400},
|
|
492
|
-
"description": "",
|
|
493
|
-
},
|
|
494
|
-
},
|
|
495
|
-
"id": str(instance.id),
|
|
496
|
-
"project_name": project.name,
|
|
497
|
-
"name": "test_instance",
|
|
498
|
-
"fleet_id": None,
|
|
499
|
-
"fleet_name": None,
|
|
500
|
-
"instance_num": 0,
|
|
501
|
-
"job_name": None,
|
|
502
|
-
"hostname": "running_instance.ip",
|
|
503
|
-
"status": "terminating",
|
|
504
|
-
"unreachable": False,
|
|
505
|
-
"termination_reason": None,
|
|
506
|
-
"created": "2023-01-02T03:04:00+00:00",
|
|
507
|
-
"pool_name": None,
|
|
508
|
-
"region": "en",
|
|
509
|
-
"availability_zone": None,
|
|
510
|
-
"price": 1,
|
|
511
|
-
"total_blocks": 1,
|
|
512
|
-
"busy_blocks": 0,
|
|
513
|
-
}
|
|
514
|
-
],
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
class TestListInstances:
|
|
519
|
-
@pytest.mark.asyncio
|
|
520
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
521
|
-
async def test_returns_403_if_not_authenticated(
|
|
522
|
-
self, test_db, session: AsyncSession, client: AsyncClient
|
|
523
|
-
):
|
|
524
|
-
response = await client.post(
|
|
525
|
-
"/api/pools/list_instances",
|
|
526
|
-
json={},
|
|
527
|
-
)
|
|
528
|
-
assert response.status_code == 403
|
|
529
|
-
|
|
530
|
-
@pytest.mark.asyncio
|
|
531
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
532
|
-
async def test_lists_instances(self, test_db, session: AsyncSession, client: AsyncClient):
|
|
533
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
534
|
-
project = await create_project(session=session, owner=user)
|
|
535
|
-
await add_project_member(
|
|
536
|
-
session=session, project=project, user=user, project_role=ProjectRole.ADMIN
|
|
537
|
-
)
|
|
538
|
-
pool = await create_pool(session, project, pool_name=TEST_POOL_NAME)
|
|
539
|
-
instance1 = await create_instance(
|
|
540
|
-
session=session,
|
|
541
|
-
project=project,
|
|
542
|
-
pool=pool,
|
|
543
|
-
created_at=dt.datetime(2023, 10, 4, 12, 0, tzinfo=dt.timezone.utc),
|
|
544
|
-
)
|
|
545
|
-
instance2 = await create_instance(
|
|
546
|
-
session=session,
|
|
547
|
-
project=project,
|
|
548
|
-
pool=pool,
|
|
549
|
-
created_at=dt.datetime(2023, 10, 5, 12, 0, tzinfo=dt.timezone.utc),
|
|
550
|
-
)
|
|
551
|
-
response = await client.post(
|
|
552
|
-
"/api/pools/list_instances",
|
|
553
|
-
headers=get_auth_headers(user.token),
|
|
554
|
-
json={},
|
|
555
|
-
)
|
|
556
|
-
assert response.status_code == 200
|
|
557
|
-
response_json = response.json()
|
|
558
|
-
assert len(response_json) == 2
|
|
559
|
-
assert response_json[0]["id"] == str(instance2.id)
|
|
560
|
-
assert response_json[1]["id"] == str(instance1.id)
|
|
561
|
-
|
|
562
|
-
@pytest.mark.asyncio
|
|
563
|
-
@pytest.mark.parametrize("test_db", ["sqlite", "postgres"], indirect=True)
|
|
564
|
-
async def test_lists_paginated_instances(
|
|
565
|
-
self, test_db, session: AsyncSession, client: AsyncClient
|
|
566
|
-
):
|
|
567
|
-
user = await create_user(session=session, global_role=GlobalRole.USER)
|
|
568
|
-
project = await create_project(session=session, owner=user)
|
|
569
|
-
await add_project_member(
|
|
570
|
-
session=session, project=project, user=user, project_role=ProjectRole.ADMIN
|
|
571
|
-
)
|
|
572
|
-
pool = await create_pool(session, project, pool_name=TEST_POOL_NAME)
|
|
573
|
-
instance1 = await create_instance(
|
|
574
|
-
session=session,
|
|
575
|
-
project=project,
|
|
576
|
-
pool=pool,
|
|
577
|
-
created_at=dt.datetime(2023, 10, 5, 12, 0, tzinfo=dt.timezone.utc),
|
|
578
|
-
)
|
|
579
|
-
instance2 = await create_instance(
|
|
580
|
-
session=session,
|
|
581
|
-
project=project,
|
|
582
|
-
pool=pool,
|
|
583
|
-
created_at=dt.datetime(2023, 10, 3, 12, 0, tzinfo=dt.timezone.utc),
|
|
584
|
-
)
|
|
585
|
-
instance3 = await create_instance(
|
|
586
|
-
session=session,
|
|
587
|
-
project=project,
|
|
588
|
-
pool=pool,
|
|
589
|
-
created_at=dt.datetime(2023, 10, 6, 12, 0, tzinfo=dt.timezone.utc),
|
|
590
|
-
)
|
|
591
|
-
response = await client.post(
|
|
592
|
-
"/api/pools/list_instances",
|
|
593
|
-
headers=get_auth_headers(user.token),
|
|
594
|
-
json={"limit": 2},
|
|
595
|
-
)
|
|
596
|
-
assert response.status_code == 200
|
|
597
|
-
response_json = response.json()
|
|
598
|
-
assert len(response_json) == 2
|
|
599
|
-
assert response_json[0]["id"] == str(instance3.id)
|
|
600
|
-
assert response_json[1]["id"] == str(instance1.id)
|
|
601
|
-
response = await client.post(
|
|
602
|
-
"/api/pools/list_instances",
|
|
603
|
-
headers=get_auth_headers(user.token),
|
|
604
|
-
json={
|
|
605
|
-
"prev_id": response_json[1]["id"],
|
|
606
|
-
"prev_created_at": response_json[1]["created"],
|
|
607
|
-
},
|
|
608
|
-
)
|
|
609
|
-
assert response.status_code == 200
|
|
610
|
-
response_json = response.json()
|
|
611
|
-
assert len(response_json) == 1
|
|
612
|
-
assert response_json[0]["id"] == str(instance2.id)
|
/dstack/_internal/{server/services/backends/configurators → core/backends/dstack}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|