Skip to content

Commit 544f8d4

Browse files
author
Andrei Neagu
committed
extracted common containers interface
1 parent df779bb commit 544f8d4

File tree

4 files changed

+213
-161
lines changed

4 files changed

+213
-161
lines changed
Lines changed: 16 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,19 @@
11
# pylint: disable=too-many-arguments
22

33
import logging
4-
from asyncio import Lock
5-
from typing import Annotated, Any, Final
4+
from typing import Any
65

7-
from aiodocker import DockerError
8-
from common_library.json_serialization import json_loads
9-
from fastapi import APIRouter, Depends, HTTPException
6+
from fastapi import APIRouter, HTTPException
107
from fastapi import Path as PathParam
118
from fastapi import Query, Request, status
129
from models_library.api_schemas_directorv2.dynamic_services import ContainersComposeSpec
1310
from models_library.api_schemas_dynamic_sidecar.containers import (
14-
ActivityInfo,
1511
ActivityInfoOrNone,
1612
)
17-
from pydantic import TypeAdapter, ValidationError
18-
from servicelib.container_utils import (
19-
ContainerExecCommandFailedError,
20-
ContainerExecContainerNotFoundError,
21-
ContainerExecTimeoutError,
22-
run_command_in_container,
23-
)
2413
from servicelib.fastapi.requests_decorators import cancel_on_disconnect
2514

26-
from ...core.docker_utils import docker_client
27-
from ...core.settings import ApplicationSettings
28-
from ...core.validation import parse_compose_spec
29-
from ...models.shared_store import SharedStore
3015
from ...services import containers
31-
from ._dependencies import get_container_restart_lock, get_settings, get_shared_store
32-
33-
_INACTIVE_FOR_LONG_TIME: Final[int] = 2**63 - 1
16+
from ...services.containers import ContainerIsMissingError
3417

3518
_logger = logging.getLogger(__name__)
3619

@@ -77,8 +60,6 @@ async def store_compose_spec(
7760
@cancel_on_disconnect
7861
async def containers_docker_inspect(
7962
request: Request,
80-
shared_store: Annotated[SharedStore, Depends(get_shared_store)],
81-
container_restart_lock: Annotated[Lock, Depends(get_container_restart_lock)],
8263
only_status: bool = Query( # noqa: FBT001
8364
default=False, description="if True only show the status of the container"
8465
),
@@ -88,92 +69,18 @@ async def containers_docker_inspect(
8869
the status of the containers is returned
8970
"""
9071
_ = request
91-
92-
def _format_result(container_inspect: dict[str, Any]) -> dict[str, Any]:
93-
if only_status:
94-
container_state = container_inspect.get("State", {})
95-
96-
# pending is another fake state use to share more information with the frontend
97-
return {
98-
"Status": container_state.get("Status", "pending"),
99-
"Error": container_state.get("Error", ""),
100-
}
101-
102-
return container_inspect
103-
104-
async with container_restart_lock, docker_client() as docker:
105-
container_names = shared_store.container_names
106-
107-
results = {}
108-
109-
for container in container_names:
110-
container_instance = await docker.containers.get(container)
111-
container_inspect = await container_instance.show()
112-
results[container] = _format_result(container_inspect)
113-
114-
return results
72+
return await containers.containers_docker_inspect(
73+
app=request.app, only_status=only_status
74+
)
11575

11676

11777
@router.get(
11878
"/containers/activity",
11979
)
12080
@cancel_on_disconnect
121-
async def get_containers_activity(
122-
request: Request,
123-
settings: Annotated[ApplicationSettings, Depends(get_settings)],
124-
shared_store: Annotated[SharedStore, Depends(get_shared_store)],
125-
) -> ActivityInfoOrNone:
81+
async def get_containers_activity(request: Request) -> ActivityInfoOrNone:
12682
_ = request
127-
inactivity_command = settings.DY_SIDECAR_CALLBACKS_MAPPING.inactivity
128-
if inactivity_command is None:
129-
return None
130-
131-
container_name = inactivity_command.service
132-
133-
try:
134-
inactivity_response = await run_command_in_container(
135-
shared_store.original_to_container_names[inactivity_command.service],
136-
command=inactivity_command.command,
137-
timeout=inactivity_command.timeout,
138-
)
139-
except (
140-
ContainerExecContainerNotFoundError,
141-
ContainerExecCommandFailedError,
142-
ContainerExecTimeoutError,
143-
DockerError,
144-
):
145-
_logger.warning(
146-
"Could not run inactivity command '%s' in container '%s'",
147-
inactivity_command.command,
148-
container_name,
149-
exc_info=True,
150-
)
151-
return ActivityInfo(seconds_inactive=_INACTIVE_FOR_LONG_TIME)
152-
153-
try:
154-
return TypeAdapter(ActivityInfo).validate_json(inactivity_response)
155-
except ValidationError:
156-
_logger.warning(
157-
"Could not parse command result '%s' as '%s'",
158-
inactivity_response,
159-
ActivityInfo.__name__,
160-
exc_info=True,
161-
)
162-
163-
return ActivityInfo(seconds_inactive=_INACTIVE_FOR_LONG_TIME)
164-
165-
166-
# Some of the operations and sub-resources on containers are implemented as long-running tasks.
167-
# Handlers for these operations can be found in:
168-
#
169-
# POST /containers : SEE containers_long_running_tasks::create_service_containers_task
170-
# POST /containers:down : SEE containers_long_running_tasks::runs_docker_compose_down_task
171-
# POST /containers/state:restore : SEE containers_long_running_tasks::state_restore_task
172-
# POST /containers/state:save : SEE containers_long_running_tasks::state_save_task
173-
# POST /containers/ports/inputs:pull : SEE containers_long_running_tasks::ports_inputs_pull_task
174-
# POST /containers/ports/outputs:pull : SEE containers_long_running_tasks::ports_outputs_pull_task
175-
# POST /containers/ports/outputs:push : SEE containers_long_running_tasks::ports_outputs_push_task
176-
#
83+
return await containers.get_containers_activity(app=request.app)
17784

17885

17986
@router.get(
@@ -190,7 +97,6 @@ async def get_containers_activity(
19097
@cancel_on_disconnect
19198
async def get_containers_name(
19299
request: Request,
193-
shared_store: Annotated[SharedStore, Depends(get_shared_store)],
194100
filters: str = Query(
195101
...,
196102
description=(
@@ -211,43 +117,7 @@ async def get_containers_name(
211117
"""
212118
_ = request
213119

214-
filters_dict: dict[str, str] = json_loads(filters)
215-
if not isinstance(filters_dict, dict):
216-
raise HTTPException(
217-
status.HTTP_422_UNPROCESSABLE_ENTITY,
218-
detail=f"Provided filters, could not parsed {filters_dict}",
219-
)
220-
network_name: str | None = filters_dict.get("network", None)
221-
exclude: str | None = filters_dict.get("exclude", None)
222-
223-
stored_compose_content = shared_store.compose_spec
224-
if stored_compose_content is None:
225-
raise HTTPException(
226-
status.HTTP_404_NOT_FOUND,
227-
detail="No spec for docker compose down was found",
228-
)
229-
230-
compose_spec = parse_compose_spec(stored_compose_content)
231-
232-
container_name = None
233-
234-
spec_services = compose_spec["services"]
235-
for service in spec_services:
236-
service_content = spec_services[service]
237-
if network_name in service_content.get("networks", {}):
238-
if exclude is not None and exclude in service_content["container_name"]:
239-
# removing this container from results
240-
continue
241-
container_name = service_content["container_name"]
242-
break
243-
244-
if container_name is None:
245-
raise HTTPException(
246-
status.HTTP_404_NOT_FOUND,
247-
detail=f"No container found for network={network_name}",
248-
)
249-
250-
return f"{container_name}"
120+
return await containers.get_containers_name(app=request.app, filters=filters)
251121

252122

253123
@router.get(
@@ -259,16 +129,14 @@ async def get_containers_name(
259129
)
260130
@cancel_on_disconnect
261131
async def inspect_container(
262-
request: Request,
263-
shared_store: Annotated[SharedStore, Depends(get_shared_store)],
264-
container_id: str = PathParam(..., alias="id"),
132+
request: Request, container_id: str = PathParam(..., alias="id")
265133
) -> dict[str, Any]:
266134
"""Returns information about the container, like docker inspect command"""
267135
_ = request
268136

269-
_raise_if_container_is_missing(container_id, shared_store.container_names)
270-
271-
async with docker_client() as docker:
272-
container_instance = await docker.containers.get(container_id)
273-
inspect_result: dict[str, Any] = await container_instance.show()
274-
return inspect_result
137+
try:
138+
return await containers.inspect_container(
139+
app=request.app, container_id=container_id
140+
)
141+
except ContainerIsMissingError as e:
142+
raise HTTPException(status.HTTP_404_NOT_FOUND, detail=f"{e}") from e

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@ async def create_compose_spec(
1414
*,
1515
containers_compose_spec: ContainersComposeSpec,
1616
) -> None:
17-
await containers.create_compose_spec(app, containers_compose_spec)
17+
await containers.create_compose_spec(
18+
app, containers_compose_spec=containers_compose_spec
19+
)

0 commit comments

Comments
 (0)