Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

♻️ redirect list_dynamic_services via dynamic-scheduler #6893

Merged
Show file tree
Hide file tree
Changes from 16 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
Original file line number Diff line number Diff line change
Expand Up @@ -92,32 +92,33 @@ class RunningDynamicServiceDetails(ServiceDetails):
ignored_types=(cached_property,),
json_schema_extra={
"examples": [
# legacy
{
"boot_type": "V0",
"key": "simcore/services/dynamic/3dviewer",
"version": "2.4.5",
"user_id": 234,
"project_id": "dd1d04d9-d704-4f7e-8f0f-1ca60cc771fe",
"uuid": "75c7f3f4-18f9-4678-8610-54a2ade78eaa",
"basepath": "/x/75c7f3f4-18f9-4678-8610-54a2ade78eaa",
"host": "3dviewer_75c7f3f4-18f9-4678-8610-54a2ade78eaa",
"internal_port": 8888,
"state": "running",
"message": "",
"node_uuid": "75c7f3f4-18f9-4678-8610-54a2ade78eaa",
"service_key": "simcore/services/dynamic/raw-graphs",
"service_version": "2.10.6",
"user_id": 1,
"project_id": "32fb4eb6-ab30-11ef-9ee4-0242ac140008",
"service_uuid": "0cd049ba-cd6b-4a12-b416-a50c9bc8e7bb",
"service_basepath": "/x/0cd049ba-cd6b-4a12-b416-a50c9bc8e7bb",
"service_host": "raw-graphs_0cd049ba-cd6b-4a12-b416-a50c9bc8e7bb",
"service_port": 4000,
"published_port": None,
"entry_point": "",
"service_state": "running",
"service_message": "",
},
GitHK marked this conversation as resolved.
Show resolved Hide resolved
# new style
{
"service_key": "simcore/services/dynamic/jupyter-math",
"service_version": "3.0.3",
"user_id": 1,
"project_id": "32fb4eb6-ab30-11ef-9ee4-0242ac140008",
"service_uuid": "6e3cad3a-eb64-43de-b476-9ac3c413fd9c",
"boot_type": "V2",
"key": "simcore/services/dynamic/dy-static-file-viewer-dynamic-sidecar",
"version": "1.0.0",
"user_id": 234,
"project_id": "dd1d04d9-d704-4f7e-8f0f-1ca60cc771fe",
"uuid": "75c7f3f4-18f9-4678-8610-54a2ade78eaa",
"host": "dy-sidecar_75c7f3f4-18f9-4678-8610-54a2ade78eaa",
"internal_port": 80,
"state": "running",
"message": "",
"node_uuid": "75c7f3f4-18f9-4678-8610-54a2ade78eaa",
"service_host": "dy-sidecar_6e3cad3a-eb64-43de-b476-9ac3c413fd9c",
"service_port": 8888,
"service_state": "running",
"service_message": "",
},
]
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
DynamicServiceStop,
)
from models_library.api_schemas_webserver.projects_nodes import NodeGet, NodeGetIdle
from models_library.projects import ProjectID
from models_library.projects_nodes_io import NodeID
from models_library.rabbitmq_basic_types import RPCMethodName
from models_library.users import UserID
from pydantic import NonNegativeInt, TypeAdapter
from servicelib.logging_utils import log_decorator
from servicelib.rabbitmq import RabbitMQRPCClient
Expand All @@ -29,6 +31,24 @@
_RPC_METHOD_NAME_ADAPTER: TypeAdapter[RPCMethodName] = TypeAdapter(RPCMethodName)


@log_decorator(_logger, level=logging.DEBUG)
async def list_tracked_dynamic_services(
rabbitmq_rpc_client: RabbitMQRPCClient,
*,
user_id: UserID | None = None,
project_id: ProjectID | None = None,
) -> list[DynamicServiceGet]:
result = await rabbitmq_rpc_client.request(
DYNAMIC_SCHEDULER_RPC_NAMESPACE,
_RPC_METHOD_NAME_ADAPTER.validate_python("list_tracked_dynamic_services"),
user_id=user_id,
project_id=project_id,
timeout_s=_RPC_DEFAULT_TIMEOUT_S,
)
assert isinstance(result, list) # nosec
return result


@log_decorator(_logger, level=logging.DEBUG)
async def get_service_status(
rabbitmq_rpc_client: RabbitMQRPCClient, *, node_id: NodeID
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
DynamicServiceStop,
)
from models_library.api_schemas_webserver.projects_nodes import NodeGet, NodeGetIdle
from models_library.projects import ProjectID
from models_library.projects_nodes_io import NodeID
from models_library.users import UserID
from servicelib.rabbitmq import RPCRouter
from servicelib.rabbitmq.rpc_interfaces.dynamic_scheduler.errors import (
ServiceWaitingForManualInterventionError,
Expand All @@ -19,15 +21,22 @@
router = RPCRouter()


@router.expose()
async def list_tracked_dynamic_services(
app: FastAPI, *, user_id: UserID | None = None, project_id: ProjectID | None = None
) -> list[DynamicServiceGet]:
director_v2_client = DirectorV2Client.get_from_app_state(app)
return await director_v2_client.list_tracked_dynamic_services(
user_id=user_id, project_id=project_id
)


@router.expose()
async def get_service_status(
app: FastAPI, *, node_id: NodeID
) -> NodeGet | DynamicServiceGet | NodeGetIdle:
director_v2_client = DirectorV2Client.get_from_app_state(app)
response: NodeGet | DynamicServiceGet | NodeGetIdle = (
await director_v2_client.get_status(node_id)
)
return response
return await director_v2_client.get_status(node_id)


@router.expose()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
DynamicServiceStart,
)
from models_library.api_schemas_webserver.projects_nodes import NodeGet, NodeGetIdle
from models_library.projects import ProjectID
from models_library.projects_nodes_io import NodeID
from models_library.users import UserID
from pydantic import TypeAdapter
from servicelib.fastapi.app_state import SingletonInAppStateMixin
from servicelib.fastapi.http_client import AttachLifespanMixin, HasClientSetupInterface
Expand Down Expand Up @@ -73,7 +75,7 @@ async def stop_dynamic_service(
node_id: NodeID,
simcore_user_agent: str,
save_state: bool,
timeout: datetime.timedelta
timeout: datetime.timedelta, # noqa: ASYNC109
) -> None:
try:
await self.thin_client.delete_dynamic_service(
Expand All @@ -98,6 +100,14 @@ async def stop_dynamic_service(

raise

async def list_tracked_dynamic_services(
self, *, user_id: UserID | None = None, project_id: ProjectID | None = None
) -> list[DynamicServiceGet]:
response = await self.thin_client.get_dynamic_services(
user_id=user_id, project_id=project_id
)
return TypeAdapter(list[DynamicServiceGet]).validate_python(response.json())


def setup_director_v2(app: FastAPI) -> None:
public_client = DirectorV2Client(app)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import datetime
from typing import cast
from typing import Any, cast

from common_library.json_serialization import json_dumps
from fastapi import FastAPI, status
from httpx import Response, Timeout
from models_library.api_schemas_dynamic_scheduler.dynamic_services import (
DynamicServiceStart,
)
from models_library.projects import ProjectID
from models_library.projects_nodes_io import NodeID
from models_library.services_resources import ServiceResourcesDictHelpers
from models_library.users import UserID
from servicelib.common_headers import (
X_DYNAMIC_SIDECAR_REQUEST_DNS,
X_DYNAMIC_SIDECAR_REQUEST_SCHEME,
Expand Down Expand Up @@ -108,3 +110,15 @@ async def _(
)

return await _(self)

@retry_on_errors()
@expect_status(status.HTTP_200_OK)
async def get_dynamic_services(
self, *, user_id: UserID | None = None, project_id: ProjectID | None = None
GitHK marked this conversation as resolved.
Show resolved Hide resolved
) -> Response:
params: dict[str, Any] = {}
if user_id:
params["user_id"] = user_id
if project_id:
params["project_id"] = project_id
return await self.client.get("/dynamic_services", params=params)
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@ def mock_director_v2_service_state(
assert_all_called=False,
assert_all_mocked=True, # IMPORTANT: KEEP always True!
) as mock:
mock.get("/dynamic_services").respond(
status.HTTP_200_OK,
text=json.dumps(
jsonable_encoder(
DynamicServiceGet.model_config["json_schema_extra"]["examples"]
)
),
)

mock.get(f"/dynamic_services/{node_id_new_style}").respond(
status.HTTP_200_OK, text=service_status_new_style.model_dump_json()
)
Expand Down Expand Up @@ -153,6 +162,17 @@ async def rpc_client(
return await rabbitmq_rpc_client("client")


async def test_list_tracked_dynamic_services(rpc_client: RabbitMQRPCClient):
results = await services.list_tracked_dynamic_services(
rpc_client, user_id=None, project_id=None
)
assert len(results) == 2
assert results == [
TypeAdapter(DynamicServiceGet).validate_python(x)
for x in DynamicServiceGet.model_config["json_schema_extra"]["examples"]
]


async def test_get_state(
rpc_client: RabbitMQRPCClient,
node_id_new_style: NodeID,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,8 @@ def _get_dynamic_service_get_from(
service_state: ServiceState,
) -> DynamicServiceGet:
dict_data = DynamicServiceGet.model_config["json_schema_extra"]["examples"][1]
assert "state" in dict_data
dict_data["state"] = service_state
assert "service_state" in dict_data
dict_data["service_state"] = service_state
return TypeAdapter(DynamicServiceGet).validate_python(dict_data)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,8 @@ def _get_dynamic_service_get_legacy_with(
_add_to_dict(
dict_data,
[
("state", state),
("uuid", f"{node_id}"),
("node_uuid", f"{node_id}"),
("service_state", state),
("service_uuid", f"{node_id}"),
],
)
return TypeAdapter(DynamicServiceGet).validate_python(dict_data)
Expand All @@ -116,9 +115,8 @@ def _get_dynamic_service_get_new_style_with(
_add_to_dict(
dict_data,
[
("state", state),
("uuid", f"{node_id}"),
("node_uuid", f"{node_id}"),
("service_state", state),
("service_uuid", f"{node_id}"),
],
)
return TypeAdapter(DynamicServiceGet).validate_python(dict_data)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@
import logging

from aiohttp import web
from models_library.api_schemas_directorv2.dynamic_services import DynamicServiceGet
from models_library.projects import ProjectID
from models_library.services import ServicePortKey
from pydantic import BaseModel, NonNegativeInt, TypeAdapter
from pydantic.types import PositiveInt
from pydantic import NonNegativeInt
from servicelib.logging_utils import log_decorator
from yarl import URL

Expand All @@ -22,36 +20,6 @@
_log = logging.getLogger(__name__)


class _Params(BaseModel):
user_id: PositiveInt | None = None
project_id: str | None = None


async def list_dynamic_services(
app: web.Application,
user_id: PositiveInt | None = None,
project_id: str | None = None,
) -> list[DynamicServiceGet]:
params = _Params(user_id=user_id, project_id=project_id)
params_dict = params.model_dump(exclude_none=True)
settings: DirectorV2Settings = get_plugin_settings(app)
if params_dict: # Update query doesnt work with no params to unwrap
backend_url = (settings.base_url / "dynamic_services").update_query(
**params_dict
)
else:
backend_url = settings.base_url / "dynamic_services"

services = await request_director_v2(
app, "GET", backend_url, expected_status=web.HTTPOk
)

if services is None:
services = []
assert isinstance(services, list) # nosec
return TypeAdapter(list[DynamicServiceGet]).validate_python(services)


# NOTE: ANE https://github.com/ITISFoundation/osparc-simcore/issues/3191
@log_decorator(logger=_log)
async def retrieve(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
)
from ._core_dynamic_services import (
get_project_inactivity,
list_dynamic_services,
request_retrieve_dyn_service,
restart_dynamic_service,
retrieve,
Expand All @@ -39,7 +38,6 @@
"get_project_run_policy",
"is_healthy",
"is_pipeline_running",
"list_dynamic_services",
"request_retrieve_dyn_service",
"restart_dynamic_service",
"retrieve",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,30 @@
from models_library.projects import ProjectID
from models_library.projects_nodes_io import NodeID
from models_library.rabbitmq_messages import ProgressRabbitMessageProject, ProgressType
from models_library.users import UserID
from pydantic.types import PositiveInt
from servicelib.progress_bar import ProgressBarData
from servicelib.rabbitmq import RabbitMQClient, RPCServerError
from servicelib.rabbitmq.rpc_interfaces.dynamic_scheduler import services
from servicelib.utils import logged_gather

from ..director_v2.api import list_dynamic_services
from ..rabbitmq import get_rabbitmq_client, get_rabbitmq_rpc_client
from .settings import DynamicSchedulerSettings, get_plugin_settings

_logger = logging.getLogger(__name__)


async def list_dynamic_services(
app: web.Application,
*,
user_id: UserID | None = None,
GitHK marked this conversation as resolved.
Show resolved Hide resolved
project_id: ProjectID | None = None,
) -> list[DynamicServiceGet]:
return await services.list_tracked_dynamic_services(
get_rabbitmq_rpc_client(app), user_id=user_id, project_id=project_id
)


async def get_dynamic_service(
app: web.Application, *, node_id: NodeID
) -> NodeGetIdle | NodeGetUnknown | DynamicServiceGet | NodeGet:
Expand Down Expand Up @@ -98,7 +109,7 @@ async def stop_dynamic_services_in_project(
) -> None:
"""Stops all dynamic services in the project"""
running_dynamic_services = await list_dynamic_services(
app, user_id=user_id, project_id=project_id
app, user_id=user_id, project_id=ProjectID(project_id)
)

async with AsyncExitStack() as stack:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
from servicelib.utils import logged_gather
from simcore_postgres_database.models.users import UserRole

from ..director_v2 import api as director_v2_api
from ..dynamic_scheduler import api as dynamic_scheduler_api
from ..projects.api import has_user_project_access_rights
from ..projects.projects_api import (
Expand Down Expand Up @@ -90,7 +89,7 @@ async def remove_orphaned_services(
# in between and the GC would remove services that actually should be running.

with log_catch(_logger, reraise=False):
running_services = await director_v2_api.list_dynamic_services(app)
running_services = await dynamic_scheduler_api.list_dynamic_services(app)
if not running_services:
# nothing to do
return
Expand Down
Loading
Loading