Skip to content

Commit 0651c06

Browse files
GitHKAndrei Neagu
andauthored
♻️ redirect list_dynamic_services via dynamic-scheduler (#6893)
Co-authored-by: Andrei Neagu <[email protected]>
1 parent 1ab9b99 commit 0651c06

File tree

33 files changed

+389
-280
lines changed

33 files changed

+389
-280
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from typing import Any
2+
3+
4+
class UnSet:
5+
VALUE: "UnSet"
6+
7+
8+
UnSet.VALUE = UnSet()
9+
10+
11+
def as_dict_exclude_unset(**params) -> dict[str, Any]:
12+
return {k: v for k, v in params.items() if not isinstance(v, UnSet)}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from typing import Any
2+
3+
from common_library.unset import UnSet, as_dict_exclude_unset
4+
5+
6+
def test_as_dict_exclude_unset():
7+
def f(
8+
par1: str | UnSet = UnSet.VALUE, par2: int | UnSet = UnSet.VALUE
9+
) -> dict[str, Any]:
10+
return as_dict_exclude_unset(par1=par1, par2=par2)
11+
12+
assert f() == {}
13+
assert f(par1="hi") == {"par1": "hi"}
14+
assert f(par2=4) == {"par2": 4}
15+
assert f(par1="hi", par2=4) == {"par1": "hi", "par2": 4}

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: 11 additions & 0 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,
@@ -17,6 +19,15 @@
1719
router = RPCRouter()
1820

1921

22+
@router.expose()
23+
async def list_tracked_dynamic_services(
24+
app: FastAPI, *, user_id: UserID | None = None, project_id: ProjectID | None = None
25+
) -> list[DynamicServiceGet]:
26+
return await scheduler_interface.list_tracked_dynamic_services(
27+
app, user_id=user_id, project_id=project_id
28+
)
29+
30+
2031
@router.expose()
2132
async def get_service_status(
2233
app: FastAPI, *, node_id: NodeID

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: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22
from typing import cast
33

44
from common_library.json_serialization import json_dumps
5+
from common_library.unset import UnSet, as_dict_exclude_unset
56
from fastapi import FastAPI, status
67
from httpx import Response, Timeout
78
from models_library.api_schemas_dynamic_scheduler.dynamic_services import (
89
DynamicServiceStart,
910
)
11+
from models_library.projects import ProjectID
1012
from models_library.projects_nodes_io import NodeID
1113
from models_library.services_resources import ServiceResourcesDictHelpers
14+
from models_library.users import UserID
1215
from servicelib.common_headers import (
1316
X_DYNAMIC_SIDECAR_REQUEST_DNS,
1417
X_DYNAMIC_SIDECAR_REQUEST_SCHEME,
@@ -108,3 +111,16 @@ async def _(
108111
)
109112

110113
return await _(self)
114+
115+
@retry_on_errors()
116+
@expect_status(status.HTTP_200_OK)
117+
async def get_dynamic_services(
118+
self,
119+
*,
120+
user_id: UserID | None | UnSet = UnSet.VALUE,
121+
project_id: ProjectID | None | UnSet = UnSet.VALUE,
122+
) -> Response:
123+
return await self.client.get(
124+
"/dynamic_services",
125+
params=as_dict_exclude_unset(user_id=user_id, project_id=project_id),
126+
)

services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/services/scheduler_interface.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,28 @@
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

1012
from ..core.settings import ApplicationSettings
1113
from .director_v2 import DirectorV2Client
1214
from .service_tracker import set_request_as_running, set_request_as_stopped
1315

1416

17+
async def list_tracked_dynamic_services(
18+
app: FastAPI, *, user_id: UserID | None = None, project_id: ProjectID | None = None
19+
) -> list[DynamicServiceGet]:
20+
settings: ApplicationSettings = app.state.settings
21+
if settings.DYNAMIC_SCHEDULER_USE_INTERNAL_SCHEDULER:
22+
raise NotImplementedError
23+
24+
director_v2_client = DirectorV2Client.get_from_app_state(app)
25+
return await director_v2_client.list_tracked_dynamic_services(
26+
user_id=user_id, project_id=project_id
27+
)
28+
29+
1530
async def get_service_status(
1631
app: FastAPI, *, node_id: NodeID
1732
) -> NodeGet | DynamicServiceGet | NodeGetIdle:

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,15 @@ def mock_director_v2_service_state(
109109
assert_all_called=False,
110110
assert_all_mocked=True, # IMPORTANT: KEEP always True!
111111
) as mock:
112+
mock.get("/dynamic_services").respond(
113+
status.HTTP_200_OK,
114+
text=json.dumps(
115+
jsonable_encoder(
116+
DynamicServiceGet.model_config["json_schema_extra"]["examples"]
117+
)
118+
),
119+
)
120+
112121
mock.get(f"/dynamic_services/{node_id_new_style}").respond(
113122
status.HTTP_200_OK, text=service_status_new_style.model_dump_json()
114123
)
@@ -177,6 +186,17 @@ async def rpc_client(
177186
return await rabbitmq_rpc_client("client")
178187

179188

189+
async def test_list_tracked_dynamic_services(rpc_client: RabbitMQRPCClient):
190+
results = await services.list_tracked_dynamic_services(
191+
rpc_client, user_id=None, project_id=None
192+
)
193+
assert len(results) == 2
194+
assert results == [
195+
TypeAdapter(DynamicServiceGet).validate_python(x)
196+
for x in DynamicServiceGet.model_config["json_schema_extra"]["examples"]
197+
]
198+
199+
180200
async def test_get_state(
181201
rpc_client: RabbitMQRPCClient,
182202
node_id_new_style: NodeID,

services/dynamic-scheduler/tests/unit/service_tracker/test__api.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,8 @@ def _get_dynamic_service_get_from(
208208
service_state: ServiceState,
209209
) -> DynamicServiceGet:
210210
dict_data = DynamicServiceGet.model_config["json_schema_extra"]["examples"][1]
211-
assert "state" in dict_data
212-
dict_data["state"] = service_state
211+
assert "service_state" in dict_data
212+
dict_data["service_state"] = service_state
213213
return TypeAdapter(DynamicServiceGet).validate_python(dict_data)
214214

215215

0 commit comments

Comments
 (0)