Skip to content

Commit ed2d206

Browse files
author
Andrei Neagu
committed
refactor
1 parent 0fb8613 commit ed2d206

File tree

7 files changed

+154
-73
lines changed

7 files changed

+154
-73
lines changed

packages/service-library/src/servicelib/fastapi/docker.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import logging
33
from collections.abc import AsyncIterator
44
from contextlib import AsyncExitStack
5-
from typing import Final
5+
from typing import Any, Final
66

77
import aiodocker
88
import aiohttp
@@ -58,6 +58,52 @@ async def _(app: FastAPI) -> AsyncIterator[State]:
5858
return _
5959

6060

61+
_DOCKER_API_PROXY_SETTINGS: Final[str] = "docker_api_proxy_settings"
62+
63+
64+
def get_remote_docker_client_main_lifespan(
65+
settings: DockerApiProxysettings,
66+
) -> dict[str, Any]:
67+
return {_DOCKER_API_PROXY_SETTINGS: settings}
68+
69+
70+
async def lifespan_remote_docker_client(
71+
app: FastAPI, state: State
72+
) -> AsyncIterator[State]:
73+
settings: DockerApiProxysettings = state[_DOCKER_API_PROXY_SETTINGS]
74+
75+
session: ClientSession | None = None
76+
if settings.DOCKER_API_PROXY_USER and settings.DOCKER_API_PROXY_PASSWORD:
77+
session = ClientSession(
78+
auth=aiohttp.BasicAuth(
79+
login=settings.DOCKER_API_PROXY_USER,
80+
password=settings.DOCKER_API_PROXY_PASSWORD.get_secret_value(),
81+
)
82+
)
83+
84+
async with AsyncExitStack() as exit_stack:
85+
if settings.DOCKER_API_PROXY_USER and settings.DOCKER_API_PROXY_PASSWORD:
86+
await exit_stack.enter_async_context(
87+
ClientSession(
88+
auth=aiohttp.BasicAuth(
89+
login=settings.DOCKER_API_PROXY_USER,
90+
password=settings.DOCKER_API_PROXY_PASSWORD.get_secret_value(),
91+
)
92+
)
93+
)
94+
95+
client = await exit_stack.enter_async_context(
96+
aiodocker.Docker(url=settings.base_url, session=session)
97+
)
98+
99+
app.state.remote_docker_client = client
100+
101+
await wait_till_docker_api_proxy_is_responsive(app)
102+
103+
# NOTE this has to be inside exit_stack scope
104+
yield {}
105+
106+
61107
@tenacity.retry(
62108
wait=tenacity.wait_fixed(5),
63109
stop=tenacity.stop_after_delay(60),

packages/service-library/src/servicelib/fastapi/postgres_lifespan.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class PostgresConfigurationError(LifespanOnStartupError):
2323
msg_template = "Invalid postgres settings [={settings}] on startup. Note that postgres cannot be disabled using settings"
2424

2525

26-
def create_input_state(settings: PostgresSettings) -> State:
26+
def get_postgres_database_main_lifespan(settings: PostgresSettings) -> State:
2727
return {PostgresLifespanState.POSTGRES_SETTINGS: settings}
2828

2929

packages/service-library/src/servicelib/fastapi/prometheus_instrumentation.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# pylint: disable=protected-access
22

3-
43
from collections.abc import AsyncIterator
4+
from typing import Any, Final
55

66
from fastapi import FastAPI
77
from fastapi_lifespan_manager import State
@@ -54,9 +54,25 @@ def _on_shutdown() -> None:
5454
return get_prometheus_instrumentator(app)
5555

5656

57-
async def lifespan_prometheus_instrumentation(app: FastAPI) -> AsyncIterator[State]:
57+
_PROMETHEUS_INSTRUMENTATION_ENABLED: Final[str] = "prometheus_instrumentation_enabled"
58+
59+
60+
def get_prometheus_instrumentationmain_main_lifespan(
61+
*, enabled: bool
62+
) -> dict[str, Any]:
63+
return {_PROMETHEUS_INSTRUMENTATION_ENABLED: enabled}
64+
65+
66+
async def lifespan_prometheus_instrumentation(
67+
app: FastAPI, state: State
68+
) -> AsyncIterator[State]:
5869
# NOTE: requires ``initialize_prometheus_instrumentation`` to be called before the
5970
# lifespan of the applicaiton runs, usually rigth after the ``FastAPI`` instance is created
60-
_startup(app)
71+
72+
instrumentaiton_enabled = state.get(_PROMETHEUS_INSTRUMENTATION_ENABLED, False)
73+
if instrumentaiton_enabled:
74+
75+
_startup(app)
6176
yield {}
62-
_shutdown(app)
77+
if instrumentaiton_enabled:
78+
_shutdown(app)

services/catalog/src/simcore_service_catalog/core/events.py

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
from fastapi import FastAPI
55
from fastapi_lifespan_manager import LifespanManager, State
66
from servicelib.fastapi.postgres_lifespan import (
7-
PostgresLifespanState,
7+
get_postgres_database_main_lifespan,
88
postgres_database_lifespan,
99
)
1010
from servicelib.fastapi.prometheus_instrumentation import (
11+
get_prometheus_instrumentationmain_main_lifespan,
1112
lifespan_prometheus_instrumentation,
1213
)
1314

@@ -43,20 +44,14 @@ async def _main_lifespan(app: FastAPI) -> AsyncIterator[State]:
4344
settings: ApplicationSettings = app.state.settings
4445

4546
yield {
46-
PostgresLifespanState.POSTGRES_SETTINGS: settings.CATALOG_POSTGRES,
47-
"prometheus_instrumentation_enabled": settings.CATALOG_PROMETHEUS_INSTRUMENTATION_ENABLED,
47+
**get_postgres_database_main_lifespan(settings.CATALOG_POSTGRES),
48+
**get_prometheus_instrumentationmain_main_lifespan(
49+
enabled=settings.CATALOG_PROMETHEUS_INSTRUMENTATION_ENABLED
50+
),
4851
}
4952

5053

51-
async def _prometheus_instrumentation_lifespan(
52-
app: FastAPI, state: State
53-
) -> AsyncIterator[State]:
54-
if state.get("prometheus_instrumentation_enabled", False):
55-
async for prometheus_state in lifespan_prometheus_instrumentation(app):
56-
yield prometheus_state
57-
58-
59-
def create_app_lifespan():
54+
def create_app_lifespan() -> LifespanManager:
6055
# WARNING: order matters
6156
app_lifespan = LifespanManager()
6257
app_lifespan.add(_main_lifespan)
@@ -81,7 +76,7 @@ def create_app_lifespan():
8176
app_lifespan.add(background_task_lifespan)
8277

8378
# - prometheus instrumentation
84-
app_lifespan.add(_prometheus_instrumentation_lifespan)
79+
app_lifespan.add(lifespan_prometheus_instrumentation)
8580

8681
app_lifespan.add(_banners_lifespan)
8782

services/dynamic-scheduler/src/simcore_service_dynamic_scheduler/core/application.py

Lines changed: 5 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,21 @@
1-
from collections.abc import AsyncIterator
2-
31
from fastapi import FastAPI
4-
from fastapi_lifespan_manager import State
5-
from servicelib.fastapi.docker import get_lifespan_remote_docker_client
6-
from servicelib.fastapi.lifespan_utils import LifespanGenerator, combine_lifespans
72
from servicelib.fastapi.openapi import override_fastapi_openapi_method
83
from servicelib.fastapi.profiler import initialize_profiler
94
from servicelib.fastapi.prometheus_instrumentation import (
105
initialize_prometheus_instrumentation,
11-
lifespan_prometheus_instrumentation,
126
)
137
from servicelib.fastapi.tracing import initialize_tracing
148

15-
from .._meta import (
16-
API_VERSION,
17-
API_VTAG,
18-
APP_FINISHED_BANNER_MSG,
19-
APP_NAME,
20-
APP_STARTED_BANNER_MSG,
21-
PROJECT_NAME,
22-
SUMMARY,
23-
)
9+
from .._meta import API_VERSION, API_VTAG, APP_NAME, PROJECT_NAME, SUMMARY
2410
from ..api.frontend import initialize_frontend
2511
from ..api.rest.routes import initialize_rest_api
26-
from ..api.rpc.routes import lifespan_rpc_api_routes
27-
from ..services.catalog import lifespan_catalog
28-
from ..services.deferred_manager import lifespan_deferred_manager
29-
from ..services.director_v0 import lifespan_director_v0
30-
from ..services.director_v2 import lifespan_director_v2
31-
from ..services.notifier import get_lifespans_notifier
32-
from ..services.rabbitmq import lifespan_rabbitmq
33-
from ..services.redis import lifespan_redis
34-
from ..services.service_tracker import lifespan_service_tracker
35-
from ..services.status_monitor import lifespan_status_monitor
12+
from . import events
3613
from .settings import ApplicationSettings
3714

3815

39-
async def _lifespan_banner(app: FastAPI) -> AsyncIterator[State]:
40-
_ = app
41-
print(APP_STARTED_BANNER_MSG, flush=True) # noqa: T201
42-
yield {}
43-
print(APP_FINISHED_BANNER_MSG, flush=True) # noqa: T201
44-
45-
4616
def create_app(settings: ApplicationSettings | None = None) -> FastAPI:
4717
app_settings = settings or ApplicationSettings.create_from_envs()
4818

49-
lifespans: list[LifespanGenerator] = [
50-
lifespan_director_v2,
51-
lifespan_director_v0,
52-
lifespan_catalog,
53-
lifespan_rabbitmq,
54-
lifespan_rpc_api_routes,
55-
lifespan_redis,
56-
*get_lifespans_notifier(),
57-
lifespan_service_tracker,
58-
lifespan_deferred_manager,
59-
lifespan_status_monitor,
60-
get_lifespan_remote_docker_client(
61-
app_settings.DYNAMIC_SCHEDULER_DOCKER_API_PROXY
62-
),
63-
]
64-
65-
if app_settings.DYNAMIC_SCHEDULER_PROMETHEUS_INSTRUMENTATION_ENABLED:
66-
lifespans.append(lifespan_prometheus_instrumentation)
67-
6819
app = FastAPI(
6920
title=f"{PROJECT_NAME} web API",
7021
description=SUMMARY,
@@ -74,7 +25,7 @@ def create_app(settings: ApplicationSettings | None = None) -> FastAPI:
7425
"/doc" if app_settings.DYNAMIC_SCHEDULER_SWAGGER_API_DOC_ENABLED else None
7526
),
7627
redoc_url=None,
77-
lifespan=combine_lifespans(*lifespans, _lifespan_banner),
28+
lifespan=events.create_app_lifespan(),
7829
)
7930
override_fastapi_openapi_method(app)
8031

@@ -84,7 +35,8 @@ def create_app(settings: ApplicationSettings | None = None) -> FastAPI:
8435

8536
initialize_rest_api(app)
8637

87-
initialize_prometheus_instrumentation(app)
38+
if app_settings.DYNAMIC_SCHEDULER_PROMETHEUS_INSTRUMENTATION_ENABLED:
39+
initialize_prometheus_instrumentation(app)
8840

8941
initialize_frontend(app)
9042

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
from collections.abc import AsyncIterator
2+
3+
from fastapi import FastAPI
4+
from fastapi_lifespan_manager import LifespanManager, State
5+
from servicelib.fastapi.docker import (
6+
get_remote_docker_client_main_lifespan,
7+
lifespan_remote_docker_client,
8+
)
9+
from servicelib.fastapi.prometheus_instrumentation import (
10+
get_prometheus_instrumentationmain_main_lifespan,
11+
lifespan_prometheus_instrumentation,
12+
)
13+
14+
from .._meta import APP_FINISHED_BANNER_MSG, APP_STARTED_BANNER_MSG
15+
from ..api.rpc.routes import lifespan_rpc_api_routes
16+
from ..services.catalog import lifespan_catalog
17+
from ..services.deferred_manager import lifespan_deferred_manager
18+
from ..services.director_v0 import lifespan_director_v0
19+
from ..services.director_v2 import lifespan_director_v2
20+
from ..services.notifier import get_lifespans_notifier
21+
from ..services.rabbitmq import lifespan_rabbitmq
22+
from ..services.redis import lifespan_redis
23+
from ..services.service_tracker import lifespan_service_tracker
24+
from ..services.status_monitor import lifespan_status_monitor
25+
from .settings import ApplicationSettings
26+
27+
28+
async def _banner_lifespan(app: FastAPI) -> AsyncIterator[State]:
29+
_ = app
30+
print(APP_STARTED_BANNER_MSG, flush=True) # noqa: T201
31+
yield {}
32+
print(APP_FINISHED_BANNER_MSG, flush=True) # noqa: T201
33+
34+
35+
async def _main_lifespan(app: FastAPI) -> AsyncIterator[State]:
36+
settings: ApplicationSettings = app.state.settings
37+
38+
yield {
39+
**get_prometheus_instrumentationmain_main_lifespan(
40+
enabled=settings.DYNAMIC_SCHEDULER_PROMETHEUS_INSTRUMENTATION_ENABLED
41+
),
42+
**get_remote_docker_client_main_lifespan(
43+
settings.DYNAMIC_SCHEDULER_DOCKER_API_PROXY
44+
),
45+
}
46+
47+
48+
def create_app_lifespan() -> LifespanManager:
49+
app_lifespan = LifespanManager()
50+
app_lifespan.add(_main_lifespan)
51+
52+
app_lifespan.add(lifespan_director_v2)
53+
app_lifespan.add(lifespan_director_v0)
54+
app_lifespan.add(lifespan_catalog)
55+
app_lifespan.add(lifespan_rabbitmq)
56+
app_lifespan.add(lifespan_rpc_api_routes)
57+
app_lifespan.add(lifespan_redis)
58+
59+
for lifespan in get_lifespans_notifier():
60+
app_lifespan.add(lifespan)
61+
62+
app_lifespan.add(lifespan_service_tracker)
63+
app_lifespan.add(lifespan_deferred_manager)
64+
app_lifespan.add(lifespan_status_monitor)
65+
66+
app_lifespan.add(lifespan_remote_docker_client)
67+
68+
app_lifespan.add(lifespan_prometheus_instrumentation)
69+
70+
app_lifespan.add(_banner_lifespan)
71+
72+
return app_lifespan

services/dynamic-scheduler/tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def app_environment(
8383
)
8484

8585

86-
_PATH_APPLICATION: Final[str] = "simcore_service_dynamic_scheduler.core.application"
86+
_PATH_APPLICATION: Final[str] = "simcore_service_dynamic_scheduler.core.events"
8787

8888

8989
@pytest.fixture

0 commit comments

Comments
 (0)