Skip to content

Commit

Permalink
🐛 webserver now replies with unknown service state (ITISFoundation#…
Browse files Browse the repository at this point in the history
…5230)

Co-authored-by: Andrei Neagu <[email protected]>
  • Loading branch information
GitHK and Andrei Neagu authored Jan 18, 2024
1 parent 7fe457d commit b4088ad
Show file tree
Hide file tree
Showing 15 changed files with 235 additions and 41 deletions.
49 changes: 28 additions & 21 deletions api/specs/web-server/_projects_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@

from _common import assert_handler_signature_against_model
from fastapi import APIRouter, status
from models_library.api_schemas_directorv2.dynamic_services import DynamicServiceGet
from models_library.api_schemas_long_running_tasks.tasks import TaskGet
from models_library.api_schemas_webserver.projects_nodes import (
NodeCreate,
NodeCreated,
NodeGet,
NodeGetIdle,
NodeGetUnknown,
NodeRetrieve,
NodeRetrieved,
ServiceResourcesDict,
Expand Down Expand Up @@ -44,36 +46,35 @@
response_model=Envelope[NodeCreated],
status_code=status.HTTP_201_CREATED,
)
def create_node(project_id: str, body: NodeCreate):
def create_node(project_id: str, body: NodeCreate): # noqa: ARG001
...


@router.get(
# issues with this endpoint https://github.com/ITISFoundation/osparc-simcore/issues/5245
"/projects/{project_id}/nodes/{node_id}",
response_model=Envelope[NodeGet | NodeGetIdle],
# responses={"idle": {"model": NodeGetIdle}}, TODO: check this variant
response_model=Envelope[NodeGetIdle | NodeGetUnknown | DynamicServiceGet | NodeGet],
)
def get_node(
project_id: str,
node_id: str,
):
pass
def get_node(project_id: str, node_id: str): # noqa: ARG001
...


@router.delete(
"/projects/{project_id}/nodes/{node_id}",
response_model=None,
status_code=status.HTTP_204_NO_CONTENT,
)
def delete_node(project_id: str, node_id: str):
pass
def delete_node(project_id: str, node_id: str): # noqa: ARG001
...


@router.post(
"/projects/{project_id}/nodes/{node_id}:retrieve",
response_model=Envelope[NodeRetrieved],
)
def retrieve_node(project_id: str, node_id: str, _retrieve: NodeRetrieve):
def retrieve_node(
project_id: str, node_id: str, _retrieve: NodeRetrieve # noqa: ARG001
):
...


Expand All @@ -82,15 +83,15 @@ def retrieve_node(project_id: str, node_id: str, _retrieve: NodeRetrieve):
status_code=status.HTTP_204_NO_CONTENT,
response_model=None,
)
def start_node(project_id: str, node_id: str):
def start_node(project_id: str, node_id: str): # noqa: ARG001
...


@router.post(
"/projects/{project_id}/nodes/{node_id}:stop",
response_model=Envelope[TaskGet],
)
def stop_node(project_id: str, node_id: str):
def stop_node(project_id: str, node_id: str): # noqa: ARG001
...


Expand All @@ -99,7 +100,7 @@ def stop_node(project_id: str, node_id: str):
response_model=None,
status_code=status.HTTP_204_NO_CONTENT,
)
def restart_node(project_id: str, node_id: str):
def restart_node(project_id: str, node_id: str): # noqa: ARG001
"""Note that it has only effect on nodes associated to dynamic services"""


Expand All @@ -112,16 +113,18 @@ def restart_node(project_id: str, node_id: str):
"/projects/{project_id}/nodes/{node_id}/resources",
response_model=Envelope[ServiceResourcesDict],
)
def get_node_resources(project_id: str, node_id: str):
pass
def get_node_resources(project_id: str, node_id: str): # noqa: ARG001
...


@router.put(
"/projects/{project_id}/nodes/{node_id}/resources",
response_model=Envelope[ServiceResourcesDict],
)
def replace_node_resources(project_id: str, node_id: str, _new: ServiceResourcesDict):
pass
def replace_node_resources(
project_id: str, node_id: str, _new: ServiceResourcesDict # noqa: ARG001
):
...


#
Expand All @@ -134,7 +137,9 @@ def replace_node_resources(project_id: str, node_id: str, _new: ServiceResources
response_model=Envelope[_ProjectGroupAccess],
summary="Check whether provided group has access to the project services",
)
async def get_project_services_access_for_gid(project_id: ProjectID, for_gid: GroupID):
async def get_project_services_access_for_gid(
project_id: ProjectID, for_gid: GroupID # noqa: ARG001
):
...


Expand All @@ -153,7 +158,7 @@ async def get_project_services_access_for_gid(project_id: ProjectID, for_gid: Gr
response_model=Envelope[list[_ProjectNodePreview]],
summary="Lists all previews in the node's project",
)
async def list_project_nodes_previews(project_id: ProjectID):
async def list_project_nodes_previews(project_id: ProjectID): # noqa: ARG001
...


Expand All @@ -166,7 +171,9 @@ async def list_project_nodes_previews(project_id: ProjectID):
summary="Gets a give node's preview",
responses={status.HTTP_404_NOT_FOUND: {"description": "Node has no preview"}},
)
async def get_project_node_preview(project_id: ProjectID, node_id: NodeID):
async def get_project_node_preview(
project_id: ProjectID, node_id: NodeID # noqa: ARG001
):
...


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ class NodeGetIdle(OutputSchema):
service_state: Literal["idle"]
service_uuid: NodeID

@classmethod
def from_node_id(cls, node_id: NodeID) -> "NodeGetIdle":
return cls(service_state="idle", service_uuid=node_id)

class Config:
schema_extra: ClassVar[dict[str, Any]] = {
"example": {
Expand All @@ -100,6 +104,23 @@ class Config:
}


class NodeGetUnknown(OutputSchema):
service_state: Literal["unknown"]
service_uuid: NodeID

@classmethod
def from_node_id(cls, node_id: NodeID) -> "NodeGetUnknown":
return cls(service_state="unknown", service_uuid=node_id)

class Config:
schema_extra: ClassVar[dict[str, Any]] = {
"example": {
"service_uuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"service_state": "unknown",
}
}


class NodeRetrieve(InputSchemaWithoutCameCase):
port_keys: list[ServicePortKey] = []

Expand Down
5 changes: 4 additions & 1 deletion packages/service-library/src/servicelib/rabbitmq/_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,7 @@ class RemoteMethodNotRegisteredError(BaseRPCError):


class RPCServerError(BaseRPCError):
msg_template = "Raised '{exc_type}' while running '{method_name}' method: {msg}"
msg_template = (
"While running method '{method_name}' raised "
"'{exc_type}': '{exc_message}'\n{traceback}"
)
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ async def _wrapper(*args, **kwargs):
raise RPCServerError(
method_name=func.__name__,
exc_type=f"{exc.__class__.__module__}.{exc.__class__.__name__}",
msg=f"{formatted_traceback}",
exc_message=f"{exc}",
traceback=f"{formatted_traceback}",
) from None

self.routes[RPCMethodName(func.__name__)] = _wrapper
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ async def test_exposed_methods(
router_namespace,
RPCMethodName(raising_unexpected_error.__name__),
)
assert "This is not good!" in f"{exc_info.value}"
assert "builtins.ValueError" in f"{exc_info.value}"

# This error was classified int he interface
with pytest.raises(MyBaseError) as exc_info:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ async def get_status(
e.response.status_code # pylint:disable=no-member # type: ignore
== status.HTTP_404_NOT_FOUND
):
return NodeGetIdle(service_state="idle", service_uuid=node_id)
return NodeGetIdle.from_node_id(node_id)
raise

async def run_dynamic_service(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ async def test_get_state(

# node not tracked any of the two directors
result = await services.get_service_status(rpc_client, node_id=node_not_found)
assert result == NodeGetIdle(service_state="idle", service_uuid=node_not_found)
assert result == NodeGetIdle.from_node_id(node_not_found)


@pytest.fixture
Expand Down Expand Up @@ -401,7 +401,9 @@ async def test_stop_dynamic_service_serializes_generic_errors(
simcore_user_agent: str,
save_state: bool,
):
with pytest.raises(RPCServerError, match="while running 'stop_dynamic_service'"):
with pytest.raises(
RPCServerError, match="While running method 'stop_dynamic_service'"
):
await services.stop_dynamic_service(
rpc_client,
node_id=node_id,
Expand Down
3 changes: 2 additions & 1 deletion services/payments/tests/unit/test_rpc_payments.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ async def test_rpc_init_payment_fail(
assert isinstance(error, RPCServerError)
assert error.exc_type == "httpx.ConnectError"
assert error.method_name == "init_payment"
assert error.msg
assert error.exc_message
assert error.traceback


async def test_webserver_one_time_payment_workflow(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1290,6 +1290,7 @@ qx.Class.define("osparc.data.model.Node", {
break;
}
case "stopping":
case "unknown":
case "starting":
case "pulling": {
status.setInteractive(serviceState);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ qx.Class.define("osparc.viewer.NodeViewer", {
qx.event.Timer.once(() => this.__nodeState(), this, interval);
break;
}
case "unknown":
case "starting":
case "connecting":
case "pulling": {
Expand Down
Loading

0 comments on commit b4088ad

Please sign in to comment.