Skip to content

Commit d7b2ad8

Browse files
committed
Cleanup
- joined and named mockers from directory-api app module - renaming - retry for background
1 parent 8d5d844 commit d7b2ad8

File tree

6 files changed

+94
-93
lines changed

6 files changed

+94
-93
lines changed

services/web/server/src/simcore_service_webserver/projects/projects_api.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,11 +178,11 @@ async def start_project_interactive_services(
178178
async def delete_project(request: web.Request, project_uuid: str, user_id: int) -> None:
179179
await delete_project_from_db(request.app, project_uuid, user_id)
180180

181-
async def remove_services_and_data():
181+
async def _remove_services_and_data():
182182
await remove_project_interactive_services(user_id, project_uuid, request.app)
183183
await delete_project_data(request, project_uuid, user_id)
184184

185-
fire_and_forget_task(remove_services_and_data())
185+
fire_and_forget_task(_remove_services_and_data())
186186

187187

188188
## PROJECT NODES -----------------------------------------------------

services/web/server/src/simcore_service_webserver/resource_manager/garbage_collector.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,25 +51,25 @@
5151
logger = logging.getLogger(__name__)
5252

5353

54-
async def _setup_garbage_collector_task(app: web.Application):
55-
56-
app[APP_GARBAGE_COLLECTOR_KEY] = asyncio.create_task(
57-
garbage_collector_task(app), name="garbage-collector"
58-
)
59-
60-
yield
54+
def setup_garbage_collector(app: web.Application):
55+
async def _setup_background_task(app: web.Application):
56+
# on_startup
57+
app[APP_GARBAGE_COLLECTOR_KEY] = asyncio.create_task(
58+
collect_garbage_periodically(app), name="garbage-collector"
59+
)
6160

62-
with suppress(asyncio.CancelledError):
63-
task = app[APP_GARBAGE_COLLECTOR_KEY]
64-
task.cancel()
65-
await task
61+
yield
6662

63+
# on_cleanup
64+
with suppress(asyncio.CancelledError):
65+
task = app[APP_GARBAGE_COLLECTOR_KEY]
66+
task.cancel()
67+
await task
6768

68-
def setup_garbage_collector(app: web.Application):
69-
app.cleanup_ctx.append(_setup_garbage_collector_task)
69+
app.cleanup_ctx.append(_setup_background_task)
7070

7171

72-
async def garbage_collector_task(app: web.Application):
72+
async def collect_garbage_periodically(app: web.Application):
7373

7474
while True:
7575
logger.info("Starting garbage collector...")
@@ -358,8 +358,8 @@ async def remove_orphaned_services(
358358
logger.info("Will remove service %s", interactive_service["service_host"])
359359
try:
360360
await stop_service(app, node_id)
361-
except (ServiceNotFoundError, DirectorException) as e:
362-
logger.warning("Error while stopping service: %s", e)
361+
except (ServiceNotFoundError, DirectorException) as err:
362+
logger.warning("Error while stopping service: %s", err)
363363

364364
logger.info("Finished orphaned services removal")
365365

services/web/server/src/simcore_service_webserver/socketio/handlers.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@
1111
from typing import Any, Dict, List, Optional
1212

1313
from aiohttp import web
14-
from socketio.exceptions import ConnectionRefusedError as SocketIOConnectionError
15-
1614
from servicelib.observer import observe
1715
from servicelib.utils import fire_and_forget_task, logged_gather
16+
from socketio.exceptions import ConnectionRefusedError as SocketIOConnectionError
1817

1918
from ..groups_api import list_user_groups
2019
from ..login.decorators import RQT_USERID_KEY, login_required
@@ -68,8 +67,7 @@ async def connect(sid: str, environ: Dict, app: web.Application) -> bool:
6867
async def authenticate_user(
6968
sid: str, app: web.Application, request: web.Request
7069
) -> None:
71-
"""throws web.HTTPUnauthorized when the user is not recognized. Keeps the original request.
72-
"""
70+
"""throws web.HTTPUnauthorized when the user is not recognized. Keeps the original request."""
7371
user_id = request.get(RQT_USERID_KEY, ANONYMOUS_USER_ID)
7472
log.debug("client %s authenticated", user_id)
7573
client_session_id = request.query.get("client_session_id", None)
@@ -116,7 +114,7 @@ async def disconnect_other_sockets(sio, sockets: List[str]) -> None:
116114

117115

118116
@observe(event="SIGNAL_USER_LOGOUT")
119-
async def user_logged_out(
117+
async def on_user_logout(
120118
user_id: str, client_session_id: Optional[str], app: web.Application
121119
) -> None:
122120
log.debug("user %s must be disconnected", user_id)

services/web/server/tests/unit/with_dbs/conftest.py

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -229,40 +229,25 @@ def asyncpg_storage_system_mock(mocker):
229229
return mocked_method
230230

231231

232-
@pytest.fixture
233-
def mocked_director_subsystem(mocker):
234-
mock_director_api = {
235-
"get_running_interactive_services": mocker.patch(
236-
"simcore_service_webserver.director.director_api.get_running_interactive_services",
237-
return_value="",
238-
),
239-
"start_service": mocker.patch(
240-
"simcore_service_webserver.director.director_api.start_service",
241-
return_value="",
242-
),
243-
"stop_service": mocker.patch(
244-
"simcore_service_webserver.director.director_api.stop_service",
245-
return_value="",
246-
),
247-
}
248-
return mock_director_api
249-
250-
251232
@pytest.fixture
252233
async def mocked_director_api(loop, mocker):
234+
# NOTE: patches are done at 'simcore_service_webserver.director.director_api'
235+
#
236+
# Read carefully "where to patch" in https://docs.python.org/3/library/unittest.mock.html#id6
237+
#
253238
mocks = {}
254-
mocked_running_services = mocker.patch(
255-
"simcore_service_webserver.director.director_api.get_running_interactive_services",
256-
return_value="",
257-
)
258-
mocks["get_running_interactive_services"] = mocked_running_services
259-
mocked_stop_service = mocker.patch(
260-
"simcore_service_webserver.director.director_api.stop_service",
261-
return_value="",
262-
)
263-
mocks["stop_service"] = mocked_stop_service
239+
for func_name, fake_return in [
240+
("get_running_interactive_services", []),
241+
("start_service", []),
242+
("stop_service", None),
243+
]:
244+
mocks[func_name] = mocker.patch(
245+
f"simcore_service_webserver.director.director_api.{func_name}",
246+
return_value=fake_return,
247+
name=f"{__name__}.mocked_director_api::director_api.{func_name}",
248+
)
264249

265-
yield mocks
250+
return mocks
266251

267252

268253
@pytest.fixture

services/web/server/tests/unit/with_dbs/medium/test_resource_manager.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# pylint:disable=redefined-outer-name
44

55

6+
import logging
67
from asyncio import sleep
78
from copy import deepcopy
89
from typing import Callable
@@ -30,10 +31,27 @@
3031
from simcore_service_webserver.session import setup_session
3132
from simcore_service_webserver.socketio import setup_socketio
3233
from simcore_service_webserver.users import setup_users
34+
from tenacity import (
35+
AsyncRetrying,
36+
after_log,
37+
retry_if_exception_type,
38+
stop_after_attempt,
39+
wait_fixed,
40+
)
41+
42+
logger = logging.getLogger(__name__)
43+
3344

3445
API_VERSION = "v0"
3546
GARBAGE_COLLECTOR_INTERVAL = 1
3647
SERVICE_DELETION_DELAY = 1
48+
CHECK_BACKGROUND_RETRY_POLICY = dict(
49+
stop=stop_after_attempt(2),
50+
wait=wait_fixed(SERVICE_DELETION_DELAY + GARBAGE_COLLECTOR_INTERVAL),
51+
retry=retry_if_exception_type(AssertionError),
52+
after=after_log(logger, logging.INFO),
53+
reraise=True,
54+
)
3755

3856

3957
@pytest.fixture
@@ -352,11 +370,15 @@ async def test_interactive_services_removed_after_logout(
352370
r = await client.post(logout_url, json={"client_session_id": client_session_id1})
353371
assert r.url_obj.path == logout_url.path
354372
await assert_status(r, web.HTTPOk)
355-
# ensure sufficient time is wasted here
356-
await sleep(SERVICE_DELETION_DELAY + GARBAGE_COLLECTOR_INTERVAL + 1)
357-
# assert dynamic service is removed
358-
calls = [call(client.server.app, service["service_uuid"])]
359-
mocked_director_api["stop_service"].assert_has_calls(calls)
373+
374+
# check result perfomed by background task
375+
async for attempt in AsyncRetrying(**CHECK_BACKGROUND_RETRY_POLICY):
376+
with attempt:
377+
print(".")
378+
# assert dynamic service is removed
379+
mocked_director_api["stop_service"].assert_awaited_with(
380+
client.server.app, service["service_uuid"]
381+
)
360382

361383

362384
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)