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

🐛 webserver now replies with unknown service state #5230

Merged
merged 14 commits into from
Jan 18, 2024
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],
GitHK marked this conversation as resolved.
Show resolved Hide resolved
GitHK marked this conversation as resolved.
Show resolved Hide resolved
)
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
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
Loading