Skip to content

Commit db5a1a2

Browse files
author
Andrei Neagu
committed
finished adding RPC interface
1 parent 57efa22 commit db5a1a2

File tree

5 files changed

+490
-20
lines changed

5 files changed

+490
-20
lines changed

packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/dynamic_sidecar/containers.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import logging
2+
from typing import Any
23

34
from models_library.api_schemas_directorv2.dynamic_services import ContainersComposeSpec
5+
from models_library.api_schemas_dynamic_sidecar.containers import ActivityInfoOrNone
46
from models_library.projects_nodes_io import NodeID
57
from models_library.rabbitmq_basic_types import RPCMethodName
68
from pydantic import TypeAdapter
@@ -26,3 +28,60 @@ async def create_compose_spec(
2628
containers_compose_spec=containers_compose_spec,
2729
)
2830
assert result is None # nosec
31+
32+
33+
@log_decorator(_logger, level=logging.DEBUG)
34+
async def containers_docker_inspect(
35+
rabbitmq_rpc_client: RabbitMQRPCClient,
36+
*,
37+
node_id: NodeID,
38+
only_status: bool,
39+
) -> dict[str, Any]:
40+
rpc_namespace = get_rpc_namespace(node_id)
41+
result = await rabbitmq_rpc_client.request(
42+
rpc_namespace,
43+
TypeAdapter(RPCMethodName).validate_python("containers_docker_inspect"),
44+
only_status=only_status,
45+
)
46+
assert isinstance(result, dict) # nosec
47+
return result
48+
49+
50+
@log_decorator(_logger, level=logging.DEBUG)
51+
async def get_containers_activity(
52+
rabbitmq_rpc_client: RabbitMQRPCClient, *, node_id: NodeID
53+
) -> ActivityInfoOrNone:
54+
rpc_namespace = get_rpc_namespace(node_id)
55+
result = await rabbitmq_rpc_client.request(
56+
rpc_namespace,
57+
TypeAdapter(RPCMethodName).validate_python("get_containers_activity"),
58+
)
59+
return TypeAdapter(ActivityInfoOrNone).validate_python(result) if result else None
60+
61+
62+
@log_decorator(_logger, level=logging.DEBUG)
63+
async def get_containers_name(
64+
rabbitmq_rpc_client: RabbitMQRPCClient, *, node_id: NodeID, filters: str
65+
) -> str:
66+
rpc_namespace = get_rpc_namespace(node_id)
67+
result = await rabbitmq_rpc_client.request(
68+
rpc_namespace,
69+
TypeAdapter(RPCMethodName).validate_python("get_containers_name"),
70+
filters=filters,
71+
)
72+
assert isinstance(result, str)
73+
return result
74+
75+
76+
@log_decorator(_logger, level=logging.DEBUG)
77+
async def inspect_container(
78+
rabbitmq_rpc_client: RabbitMQRPCClient, *, node_id: NodeID, container_id: str
79+
) -> dict[str, Any]:
80+
rpc_namespace = get_rpc_namespace(node_id)
81+
result = await rabbitmq_rpc_client.request(
82+
rpc_namespace,
83+
TypeAdapter(RPCMethodName).validate_python("inspect_container"),
84+
container_id=container_id,
85+
)
86+
assert isinstance(result, dict)
87+
return result

services/dynamic-sidecar/src/simcore_service_dynamic_sidecar/api/rest/containers.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@
1313
from servicelib.fastapi.requests_decorators import cancel_on_disconnect
1414

1515
from ...services import containers
16-
from ...services.containers import ContainerIsMissingError
16+
from ...services.containers import (
17+
ContainerIsMissingError,
18+
ContainerNotFoundError,
19+
InvalidFilterFormatError,
20+
MissingDockerComposeDownSpecError,
21+
)
1722

1823
_logger = logging.getLogger(__name__)
1924

@@ -29,15 +34,15 @@
2934
},
3035
)
3136
@cancel_on_disconnect
32-
async def store_compose_spec(
37+
async def create_compose_spec(
3338
request: Request, containers_compose_spec: ContainersComposeSpec
3439
):
3540
"""
3641
Validates and stores the docker compose spec for the user services.
3742
"""
3843
_ = request
3944

40-
return await containers.create_compose_spec(
45+
await containers.create_compose_spec(
4146
app=request.app, containers_compose_spec=containers_compose_spec
4247
)
4348

@@ -108,7 +113,12 @@ async def get_containers_name(
108113
"""
109114
_ = request
110115

111-
return await containers.get_containers_name(app=request.app, filters=filters)
116+
try:
117+
return await containers.get_containers_name(app=request.app, filters=filters)
118+
except InvalidFilterFormatError as e:
119+
raise HTTPException(status.HTTP_422_UNPROCESSABLE_ENTITY, detail=f"{e}") from e
120+
except (MissingDockerComposeDownSpecError, ContainerNotFoundError) as e:
121+
raise HTTPException(status.HTTP_404_NOT_FOUND, detail=f"{e}") from e
112122

113123

114124
@router.get(

services/dynamic-sidecar/src/simcore_service_dynamic_sidecar/api/rpc/_containers.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
from typing import Any
2+
13
from fastapi import FastAPI
24
from models_library.api_schemas_directorv2.dynamic_services import ContainersComposeSpec
5+
from models_library.api_schemas_dynamic_sidecar.containers import (
6+
ActivityInfoOrNone,
7+
)
38
from servicelib.rabbitmq import RPCRouter
49

510
from ...core.validation import InvalidComposeSpecError
@@ -17,3 +22,25 @@ async def create_compose_spec(
1722
await containers.create_compose_spec(
1823
app, containers_compose_spec=containers_compose_spec
1924
)
25+
26+
27+
@router.expose()
28+
async def containers_docker_inspect(
29+
app: FastAPI, *, only_status: bool
30+
) -> dict[str, Any]:
31+
return await containers.containers_docker_inspect(app, only_status=only_status)
32+
33+
34+
@router.expose()
35+
async def get_containers_activity(app: FastAPI) -> ActivityInfoOrNone:
36+
return await containers.get_containers_activity(app=app)
37+
38+
39+
@router.expose()
40+
async def get_containers_name(app: FastAPI, *, filters: str) -> str | dict[str, Any]:
41+
return await containers.get_containers_name(app=app, filters=filters)
42+
43+
44+
@router.expose()
45+
async def inspect_container(app: FastAPI, *, container_id: str) -> dict[str, Any]:
46+
return await containers.inspect_container(app=app, container_id=container_id)

services/dynamic-sidecar/src/simcore_service_dynamic_sidecar/services/containers.py

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from aiodocker import DockerError
66
from common_library.errors_classes import OsparcErrorMixin
77
from common_library.json_serialization import json_loads
8-
from fastapi import FastAPI, HTTPException, status
8+
from fastapi import FastAPI
99
from models_library.api_schemas_directorv2.dynamic_services import ContainersComposeSpec
1010
from models_library.api_schemas_dynamic_sidecar.containers import (
1111
ActivityInfo,
@@ -38,7 +38,7 @@ async def create_compose_spec(
3838
app: FastAPI,
3939
*,
4040
containers_compose_spec: ContainersComposeSpec,
41-
):
41+
) -> None:
4242
settings: ApplicationSettings = app.state.settings
4343
shared_store: SharedStore = app.state.shared_store
4444
mounted_volumes: MountedVolumes = app.state.mounted_volumes
@@ -140,6 +140,24 @@ async def get_containers_activity(app: FastAPI) -> ActivityInfoOrNone:
140140
return ActivityInfo(seconds_inactive=_INACTIVE_FOR_LONG_TIME)
141141

142142

143+
class BaseGetNameError(OsparcErrorMixin, RuntimeError):
144+
pass
145+
146+
147+
class InvalidFilterFormatError(BaseGetNameError):
148+
msg_template: str = "Provided filters, could not parsed {filters}"
149+
150+
151+
class MissingDockerComposeDownSpecError(BaseGetNameError):
152+
msg_template: str = "No spec for docker compose down was found"
153+
154+
155+
class ContainerNotFoundError(BaseGetNameError):
156+
msg_template: str = (
157+
"No container found for network={network_name} and exclude={exclude}"
158+
)
159+
160+
143161
async def get_containers_name(app: FastAPI, *, filters: str) -> str | dict[str, Any]:
144162
"""
145163
Searches for the container's name given the network
@@ -155,19 +173,13 @@ async def get_containers_name(app: FastAPI, *, filters: str) -> str | dict[str,
155173

156174
filters_dict: dict[str, str] = json_loads(filters)
157175
if not isinstance(filters_dict, dict):
158-
raise HTTPException(
159-
status.HTTP_422_UNPROCESSABLE_ENTITY,
160-
detail=f"Provided filters, could not parsed {filters_dict}",
161-
)
176+
raise InvalidFilterFormatError(filters=filters_dict)
162177
network_name: str | None = filters_dict.get("network")
163178
exclude: str | None = filters_dict.get("exclude")
164179

165180
stored_compose_content = shared_store.compose_spec
166181
if stored_compose_content is None:
167-
raise HTTPException(
168-
status.HTTP_404_NOT_FOUND,
169-
detail="No spec for docker compose down was found",
170-
)
182+
raise MissingDockerComposeDownSpecError
171183

172184
compose_spec = parse_compose_spec(stored_compose_content)
173185

@@ -184,10 +196,7 @@ async def get_containers_name(app: FastAPI, *, filters: str) -> str | dict[str,
184196
break
185197

186198
if container_name is None:
187-
raise HTTPException(
188-
status.HTTP_404_NOT_FOUND,
189-
detail=f"No container found for network={network_name}",
190-
)
199+
raise ContainerNotFoundError(network_name=network_name, exclude=exclude)
191200

192201
return f"{container_name}"
193202

0 commit comments

Comments
 (0)