Skip to content

Commit 0b4577b

Browse files
author
Andrei Neagu
committed
added listing endpoint to dynamic-scheduler
1 parent 59aeb99 commit 0b4577b

File tree

6 files changed

+100
-28
lines changed

6 files changed

+100
-28
lines changed

packages/models-library/src/models_library/api_schemas_directorv2/dynamic_services_service.py

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -92,32 +92,33 @@ class RunningDynamicServiceDetails(ServiceDetails):
9292
ignored_types=(cached_property,),
9393
json_schema_extra={
9494
"examples": [
95+
# legacy
9596
{
96-
"boot_type": "V0",
97-
"key": "simcore/services/dynamic/3dviewer",
98-
"version": "2.4.5",
99-
"user_id": 234,
100-
"project_id": "dd1d04d9-d704-4f7e-8f0f-1ca60cc771fe",
101-
"uuid": "75c7f3f4-18f9-4678-8610-54a2ade78eaa",
102-
"basepath": "/x/75c7f3f4-18f9-4678-8610-54a2ade78eaa",
103-
"host": "3dviewer_75c7f3f4-18f9-4678-8610-54a2ade78eaa",
104-
"internal_port": 8888,
105-
"state": "running",
106-
"message": "",
107-
"node_uuid": "75c7f3f4-18f9-4678-8610-54a2ade78eaa",
97+
"service_key": "simcore/services/dynamic/raw-graphs",
98+
"service_version": "2.10.6",
99+
"user_id": 1,
100+
"project_id": "32fb4eb6-ab30-11ef-9ee4-0242ac140008",
101+
"service_uuid": "0cd049ba-cd6b-4a12-b416-a50c9bc8e7bb",
102+
"service_basepath": "/x/0cd049ba-cd6b-4a12-b416-a50c9bc8e7bb",
103+
"service_host": "raw-graphs_0cd049ba-cd6b-4a12-b416-a50c9bc8e7bb",
104+
"service_port": 4000,
105+
"published_port": None,
106+
"entry_point": "",
107+
"service_state": "running",
108+
"service_message": "",
108109
},
110+
# new style
109111
{
112+
"service_key": "simcore/services/dynamic/jupyter-math",
113+
"service_version": "3.0.3",
114+
"user_id": 1,
115+
"project_id": "32fb4eb6-ab30-11ef-9ee4-0242ac140008",
116+
"service_uuid": "6e3cad3a-eb64-43de-b476-9ac3c413fd9c",
110117
"boot_type": "V2",
111-
"key": "simcore/services/dynamic/dy-static-file-viewer-dynamic-sidecar",
112-
"version": "1.0.0",
113-
"user_id": 234,
114-
"project_id": "dd1d04d9-d704-4f7e-8f0f-1ca60cc771fe",
115-
"uuid": "75c7f3f4-18f9-4678-8610-54a2ade78eaa",
116-
"host": "dy-sidecar_75c7f3f4-18f9-4678-8610-54a2ade78eaa",
117-
"internal_port": 80,
118-
"state": "running",
119-
"message": "",
120-
"node_uuid": "75c7f3f4-18f9-4678-8610-54a2ade78eaa",
118+
"service_host": "dy-sidecar_6e3cad3a-eb64-43de-b476-9ac3c413fd9c",
119+
"service_port": 8888,
120+
"service_state": "running",
121+
"service_message": "",
121122
},
122123
]
123124
},

packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/dynamic_scheduler/services.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
DynamicServiceStop,
99
)
1010
from models_library.api_schemas_webserver.projects_nodes import NodeGet, NodeGetIdle
11+
from models_library.projects import ProjectID
1112
from models_library.projects_nodes_io import NodeID
1213
from models_library.rabbitmq_basic_types import RPCMethodName
14+
from models_library.users import UserID
1315
from pydantic import NonNegativeInt, TypeAdapter
1416
from servicelib.logging_utils import log_decorator
1517
from servicelib.rabbitmq import RabbitMQRPCClient
@@ -29,6 +31,24 @@
2931
_RPC_METHOD_NAME_ADAPTER: TypeAdapter[RPCMethodName] = TypeAdapter(RPCMethodName)
3032

3133

34+
@log_decorator(_logger, level=logging.DEBUG)
35+
async def list_tracked_dynamic_services(
36+
rabbitmq_rpc_client: RabbitMQRPCClient,
37+
*,
38+
user_id: UserID | None = None,
39+
project_id: ProjectID | None = None,
40+
) -> list[DynamicServiceGet]:
41+
result = await rabbitmq_rpc_client.request(
42+
DYNAMIC_SCHEDULER_RPC_NAMESPACE,
43+
_RPC_METHOD_NAME_ADAPTER.validate_python("list_tracked_dynamic_services"),
44+
user_id=user_id,
45+
project_id=project_id,
46+
timeout_s=_RPC_DEFAULT_TIMEOUT_S,
47+
)
48+
assert isinstance(result, list) # nosec
49+
return result
50+
51+
3252
@log_decorator(_logger, level=logging.DEBUG)
3353
async def get_service_status(
3454
rabbitmq_rpc_client: RabbitMQRPCClient, *, node_id: NodeID

services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/api/rpc/_services.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
DynamicServiceStop,
66
)
77
from models_library.api_schemas_webserver.projects_nodes import NodeGet, NodeGetIdle
8+
from models_library.projects import ProjectID
89
from models_library.projects_nodes_io import NodeID
10+
from models_library.users import UserID
911
from servicelib.rabbitmq import RPCRouter
1012
from servicelib.rabbitmq.rpc_interfaces.dynamic_scheduler.errors import (
1113
ServiceWaitingForManualInterventionError,
@@ -19,15 +21,22 @@
1921
router = RPCRouter()
2022

2123

24+
@router.expose()
25+
async def list_tracked_dynamic_services(
26+
app: FastAPI, *, user_id: UserID | None = None, project_id: ProjectID | None = None
27+
) -> list[DynamicServiceGet]:
28+
director_v2_client = DirectorV2Client.get_from_app_state(app)
29+
return await director_v2_client.list_tracked_dynamic_services(
30+
user_id=user_id, project_id=project_id
31+
)
32+
33+
2234
@router.expose()
2335
async def get_service_status(
2436
app: FastAPI, *, node_id: NodeID
2537
) -> NodeGet | DynamicServiceGet | NodeGetIdle:
2638
director_v2_client = DirectorV2Client.get_from_app_state(app)
27-
response: NodeGet | DynamicServiceGet | NodeGetIdle = (
28-
await director_v2_client.get_status(node_id)
29-
)
30-
return response
39+
return await director_v2_client.get_status(node_id)
3140

3241

3342
@router.expose()

services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/services/director_v2/_public_client.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
DynamicServiceStart,
88
)
99
from models_library.api_schemas_webserver.projects_nodes import NodeGet, NodeGetIdle
10+
from models_library.projects import ProjectID
1011
from models_library.projects_nodes_io import NodeID
12+
from models_library.users import UserID
1113
from pydantic import TypeAdapter
1214
from servicelib.fastapi.app_state import SingletonInAppStateMixin
1315
from servicelib.fastapi.http_client import AttachLifespanMixin, HasClientSetupInterface
@@ -73,7 +75,7 @@ async def stop_dynamic_service(
7375
node_id: NodeID,
7476
simcore_user_agent: str,
7577
save_state: bool,
76-
timeout: datetime.timedelta
78+
timeout: datetime.timedelta, # noqa: ASYNC109
7779
) -> None:
7880
try:
7981
await self.thin_client.delete_dynamic_service(
@@ -98,6 +100,14 @@ async def stop_dynamic_service(
98100

99101
raise
100102

103+
async def list_tracked_dynamic_services(
104+
self, *, user_id: UserID | None = None, project_id: ProjectID | None = None
105+
) -> list[DynamicServiceGet]:
106+
response = await self.thin_client.get_dynamic_services(
107+
user_id=user_id, project_id=project_id
108+
)
109+
return TypeAdapter(list[DynamicServiceGet]).validate_python(response.json())
110+
101111

102112
def setup_director_v2(app: FastAPI) -> None:
103113
public_client = DirectorV2Client(app)

services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/services/director_v2/_thin_client.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import datetime
2-
from typing import cast
2+
from typing import Any, cast
33

44
from common_library.json_serialization import json_dumps
55
from fastapi import FastAPI, status
66
from httpx import Response, Timeout
77
from models_library.api_schemas_dynamic_scheduler.dynamic_services import (
88
DynamicServiceStart,
99
)
10+
from models_library.projects import ProjectID
1011
from models_library.projects_nodes_io import NodeID
1112
from models_library.services_resources import ServiceResourcesDictHelpers
13+
from models_library.users import UserID
1214
from servicelib.common_headers import (
1315
X_DYNAMIC_SIDECAR_REQUEST_DNS,
1416
X_DYNAMIC_SIDECAR_REQUEST_SCHEME,
@@ -108,3 +110,15 @@ async def _(
108110
)
109111

110112
return await _(self)
113+
114+
@retry_on_errors()
115+
@expect_status(status.HTTP_200_OK)
116+
async def get_dynamic_services(
117+
self, *, user_id: UserID | None = None, project_id: ProjectID | None = None
118+
) -> Response:
119+
params: dict[str, Any] = {}
120+
if user_id:
121+
params["user_id"] = user_id
122+
if project_id:
123+
params["project_id"] = project_id
124+
return await self.client.get("/dynamic_services", params=params)

services/dynamic-scheduler/tests/unit/api_rpc/test_api_rpc__services.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,13 @@ def mock_director_v2_service_state(
108108
assert_all_called=False,
109109
assert_all_mocked=True, # IMPORTANT: KEEP always True!
110110
) as mock:
111+
mock.get("/dynamic_services").respond(
112+
status.HTTP_200_OK,
113+
text=json.dumps(
114+
DynamicServiceGet.model_config["json_schema_extra"]["examples"]
115+
),
116+
)
117+
111118
mock.get(f"/dynamic_services/{node_id_new_style}").respond(
112119
status.HTTP_200_OK, text=service_status_new_style.model_dump_json()
113120
)
@@ -153,6 +160,17 @@ async def rpc_client(
153160
return await rabbitmq_rpc_client("client")
154161

155162

163+
async def test_list_tracked_dynamic_services(rpc_client: RabbitMQRPCClient):
164+
results = await services.list_tracked_dynamic_services(
165+
rpc_client, user_id=None, project_id=None
166+
)
167+
assert len(results) == 2
168+
assert results == [
169+
TypeAdapter(DynamicServiceGet).validate_python(x)
170+
for x in DynamicServiceGet.model_config["json_schema_extra"]["examples"]
171+
]
172+
173+
156174
async def test_get_state(
157175
rpc_client: RabbitMQRPCClient,
158176
node_id_new_style: NodeID,

0 commit comments

Comments
 (0)