Skip to content

Commit 3cf3a29

Browse files
committed
Merge branch 'feature/node-in-use' of github.com:odeimaiz/osparc-simcore into feature/node-in-use
2 parents 1ed0152 + 5d085e0 commit 3cf3a29

File tree

127 files changed

+2716
-1167
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

127 files changed

+2716
-1167
lines changed

.env-devel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ LICENSES_ITIS_VIP_API_URL=https://replace-with-itis-api/{category}
147147
LICENSES_ITIS_VIP_CATEGORIES='{"HumanWholeBody": "Humans", "HumanBodyRegion": "Humans (Region)", "AnimalWholeBody": "Animal"}'
148148
LICENSES_SPEAG_PHANTOMS_API_URL=https://replace-with-speag-api/{category}
149149
LICENSES_SPEAG_PHANTOMS_CATEGORIES='{"ComputationalPhantom": "Phantom of the Opera"}'
150+
LONG_RUNNING_TASKS_NAMESPACE_SUFFIX=development
150151

151152
# Can use 'docker run -it itisfoundation/invitations:latest simcore-service-invitations generate-dotenv --auto-password'
152153
INVITATIONS_DEFAULT_PRODUCT=osparc

.github/workflows/ci-testing-deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1252,7 +1252,7 @@ jobs:
12521252
unit-test-service-library:
12531253
needs: changes
12541254
if: ${{ needs.changes.outputs.service-library == 'true' || github.event_name == 'push' || github.event.inputs.force_all_builds == 'true' }}
1255-
timeout-minutes: 18 # if this timeout gets too small, then split the tests
1255+
timeout-minutes: 20 # if this timeout gets too small, then split the tests
12561256
name: "[unit] service-library"
12571257
runs-on: ${{ matrix.os }}
12581258
strategy:

api/specs/web-server/_conversations.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
)
3333
from simcore_service_webserver.conversations._controller._conversations_rest import (
3434
_ConversationsCreateBodyParams,
35-
_GetConversationsQueryParams,
3635
_ListConversationsQueryParams,
3736
)
3837

@@ -56,7 +55,6 @@
5655
)
5756
async def create_conversation(
5857
_body: _ConversationsCreateBodyParams,
59-
_query: Annotated[_GetConversationsQueryParams, Depends()],
6058
): ...
6159

6260

@@ -76,7 +74,6 @@ async def list_conversations(
7674
async def update_conversation(
7775
_params: Annotated[ConversationPathParams, Depends()],
7876
_body: ConversationPatch,
79-
_query: Annotated[as_query(_GetConversationsQueryParams), Depends()],
8077
): ...
8178

8279

@@ -86,7 +83,6 @@ async def update_conversation(
8683
)
8784
async def delete_conversation(
8885
_params: Annotated[ConversationPathParams, Depends()],
89-
_query: Annotated[as_query(_GetConversationsQueryParams), Depends()],
9086
): ...
9187

9288

@@ -96,7 +92,6 @@ async def delete_conversation(
9692
)
9793
async def get_conversation(
9894
_params: Annotated[ConversationPathParams, Depends()],
99-
_query: Annotated[as_query(_GetConversationsQueryParams), Depends()],
10095
): ...
10196

10297

api/specs/web-server/_long_running_tasks.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,44 +32,40 @@
3232
@router.get(
3333
"/tasks",
3434
response_model=Envelope[list[TaskGet]],
35-
name="list_tasks",
36-
description="Lists all long running tasks",
3735
responses=_export_data_responses,
3836
)
39-
def get_async_jobs(): ...
37+
def get_async_jobs():
38+
"""Lists all long running tasks"""
4039

4140

4241
@router.get(
4342
"/tasks/{task_id}",
4443
response_model=Envelope[TaskStatus],
45-
name="get_task_status",
46-
description="Retrieves the status of a task",
4744
responses=_export_data_responses,
4845
)
4946
def get_async_job_status(
5047
_path_params: Annotated[_PathParam, Depends()],
51-
): ...
48+
):
49+
"""Retrieves the status of a task"""
5250

5351

5452
@router.delete(
5553
"/tasks/{task_id}",
56-
name="cancel_and_delete_task",
57-
description="Cancels and deletes a task",
5854
responses=_export_data_responses,
5955
status_code=status.HTTP_204_NO_CONTENT,
6056
)
6157
def cancel_async_job(
6258
_path_params: Annotated[_PathParam, Depends()],
63-
): ...
59+
):
60+
"""Cancels and removes a task"""
6461

6562

6663
@router.get(
6764
"/tasks/{task_id}/result",
6865
response_model=Any,
69-
name="get_task_result",
70-
description="Retrieves the result of a task",
7166
responses=_export_data_responses,
7267
)
7368
def get_async_job_result(
7469
_path_params: Annotated[_PathParam, Depends()],
75-
): ...
70+
):
71+
"""Retrieves the result of a task"""

api/specs/web-server/_long_running_tasks_legacy.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ async def get_task_status(
4242

4343
@router.delete(
4444
"/{task_id}",
45-
name="cancel_and_delete_task",
46-
description="Cancels and deletes a task",
45+
name="remove_task",
46+
description="Cancels and removes a task",
4747
status_code=status.HTTP_204_NO_CONTENT,
4848
)
49-
async def cancel_and_delete_task(
49+
async def remove_task(
5050
_path_params: Annotated[_PathParam, Depends()],
5151
): ...
5252

packages/models-library/src/models_library/api_schemas_directorv2/dynamic_services_service.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from functools import cached_property
22
from pathlib import Path
3+
from typing import Annotated
34

45
from pydantic import BaseModel, ConfigDict, Field
56
from pydantic.config import JsonDict
@@ -89,6 +90,11 @@ class RunningDynamicServiceDetails(ServiceDetails):
8990
alias="service_message",
9091
)
9192

93+
is_collaborative: Annotated[
94+
bool,
95+
Field(description="True if service allows collaboration (multi-tenant access)"),
96+
] = False
97+
9298
@staticmethod
9399
def _update_json_schema_extra(schema: JsonDict) -> None:
94100
schema.update(

packages/models-library/tests/test_docker.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,9 @@ def test_docker_generic_tag(image_name: str, valid: bool):
112112
def test_simcore_service_docker_label_keys(obj_data: dict[str, Any]):
113113
simcore_service_docker_label_keys = SimcoreContainerLabels.model_validate(obj_data)
114114
exported_dict = simcore_service_docker_label_keys.to_simcore_runtime_docker_labels()
115-
assert all(
116-
isinstance(v, str) for v in exported_dict.values()
117-
), "docker labels must be strings!"
115+
assert all(isinstance(v, str) for v in exported_dict.values()), (
116+
"docker labels must be strings!"
117+
)
118118
assert all(
119119
key.startswith(_SIMCORE_RUNTIME_DOCKER_LABEL_PREFIX) for key in exported_dict
120120
)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from datetime import timedelta
2+
3+
import pytest
4+
from pytest_mock import MockerFixture
5+
6+
7+
@pytest.fixture
8+
async def fast_long_running_tasks_cancellation(
9+
mocker: MockerFixture,
10+
) -> None:
11+
mocker.patch(
12+
"servicelib.long_running_tasks.task._CANCEL_TASKS_CHECK_INTERVAL",
13+
new=timedelta(seconds=1),
14+
)

packages/service-library/src/servicelib/aiohttp/long_running_tasks/_manager.py

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,12 @@
1-
import datetime
2-
31
from aiohttp import web
4-
from settings_library.redis import RedisSettings
52

63
from ...long_running_tasks.base_long_running_manager import BaseLongRunningManager
74
from ...long_running_tasks.models import TaskContext
8-
from ...long_running_tasks.task import RedisNamespace, TasksManager
95
from ._constants import APP_LONG_RUNNING_MANAGER_KEY
106
from ._request import get_task_context
117

128

139
class AiohttpLongRunningManager(BaseLongRunningManager):
14-
def __init__(
15-
self,
16-
app: web.Application,
17-
stale_task_check_interval: datetime.timedelta,
18-
stale_task_detect_timeout: datetime.timedelta,
19-
redis_settings: RedisSettings,
20-
redis_namespace: RedisNamespace,
21-
):
22-
self._app = app
23-
self._tasks_manager = TasksManager(
24-
stale_task_check_interval=stale_task_check_interval,
25-
stale_task_detect_timeout=stale_task_detect_timeout,
26-
redis_settings=redis_settings,
27-
redis_namespace=redis_namespace,
28-
)
29-
30-
@property
31-
def tasks_manager(self) -> TasksManager:
32-
return self._tasks_manager
33-
34-
async def setup(self) -> None:
35-
await self._tasks_manager.setup()
36-
37-
async def teardown(self) -> None:
38-
await self._tasks_manager.teardown()
3910

4011
@staticmethod
4112
def get_task_context(request: web.Request) -> TaskContext:

packages/service-library/src/servicelib/aiohttp/long_running_tasks/_routes.py

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
from typing import Any
1+
from typing import Annotated, Any
22

33
from aiohttp import web
4-
from pydantic import BaseModel
4+
from models_library.rest_base import RequestParameters
5+
from pydantic import BaseModel, Field
56

67
from ...aiohttp import status
78
from ...long_running_tasks import lrt_api
89
from ...long_running_tasks.models import TaskGet, TaskId
9-
from ..requests_validation import parse_request_path_parameters_as
10+
from ..requests_validation import (
11+
parse_request_path_parameters_as,
12+
parse_request_query_parameters_as,
13+
)
1014
from ..rest_responses import create_data_response
1115
from ._manager import get_long_running_manager
1216

@@ -26,10 +30,11 @@ async def list_tasks(request: web.Request) -> web.Response:
2630
task_id=t.task_id,
2731
status_href=f"{request.app.router['get_task_status'].url_for(task_id=t.task_id)}",
2832
result_href=f"{request.app.router['get_task_result'].url_for(task_id=t.task_id)}",
29-
abort_href=f"{request.app.router['cancel_and_delete_task'].url_for(task_id=t.task_id)}",
33+
abort_href=f"{request.app.router['remove_task'].url_for(task_id=t.task_id)}",
3034
)
3135
for t in await lrt_api.list_tasks(
32-
long_running_manager.tasks_manager,
36+
long_running_manager.rpc_client,
37+
long_running_manager.lrt_namespace,
3338
long_running_manager.get_task_context(request),
3439
)
3540
]
@@ -42,7 +47,8 @@ async def get_task_status(request: web.Request) -> web.Response:
4247
long_running_manager = get_long_running_manager(request.app)
4348

4449
task_status = await lrt_api.get_task_status(
45-
long_running_manager.tasks_manager,
50+
long_running_manager.rpc_client,
51+
long_running_manager.lrt_namespace,
4652
long_running_manager.get_task_context(request),
4753
path_params.task_id,
4854
)
@@ -56,20 +62,36 @@ async def get_task_result(request: web.Request) -> web.Response | Any:
5662

5763
# NOTE: this might raise an exception that will be catched by the _error_handlers
5864
return await lrt_api.get_task_result(
59-
long_running_manager.tasks_manager,
65+
long_running_manager.rpc_client,
66+
long_running_manager.lrt_namespace,
6067
long_running_manager.get_task_context(request),
6168
path_params.task_id,
6269
)
6370

6471

65-
@routes.delete("/{task_id}", name="cancel_and_delete_task")
66-
async def cancel_and_delete_task(request: web.Request) -> web.Response:
72+
class _RemoveTaskQueryParams(RequestParameters):
73+
wait_for_removal: Annotated[
74+
bool,
75+
Field(
76+
description=(
77+
"when True waits for the task to be removed "
78+
"completly instead of returning immediately"
79+
)
80+
),
81+
] = True
82+
83+
84+
@routes.delete("/{task_id}", name="remove_task")
85+
async def remove_task(request: web.Request) -> web.Response:
6786
path_params = parse_request_path_parameters_as(_PathParam, request)
87+
query_params = parse_request_query_parameters_as(_RemoveTaskQueryParams, request)
6888
long_running_manager = get_long_running_manager(request.app)
6989

7090
await lrt_api.remove_task(
71-
long_running_manager.tasks_manager,
91+
long_running_manager.rpc_client,
92+
long_running_manager.lrt_namespace,
7293
long_running_manager.get_task_context(request),
7394
path_params.task_id,
95+
wait_for_removal=query_params.wait_for_removal,
7496
)
7597
return web.json_response(status=status.HTTP_204_NO_CONTENT)

0 commit comments

Comments
 (0)