Skip to content

Commit e22d6c9

Browse files
Merge branch 'master' into maintenance/removing-old-folders
2 parents 140ecc5 + ec000db commit e22d6c9

File tree

76 files changed

+1153
-383
lines changed

Some content is hidden

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

76 files changed

+1153
-383
lines changed

.env-devel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ CLUSTERS_KEEPER_COMPUTATIONAL_BACKEND_DOCKER_IMAGE_TAG=master-github-latest
5050
CLUSTERS_KEEPER_DASK_NTHREADS=0
5151
CLUSTERS_KEEPER_DASK_WORKER_SATURATION=inf
5252
CLUSTERS_KEEPER_EC2_ACCESS=null
53+
CLUSTERS_KEEPER_SSM_ACCESS=null
5354
CLUSTERS_KEEPER_EC2_INSTANCES_PREFIX=""
5455
CLUSTERS_KEEPER_LOGLEVEL=WARNING
5556
CLUSTERS_KEEPER_MAX_MISSED_HEARTBEATS_BEFORE_CLUSTER_TERMINATION=5

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,7 @@ jobs:
590590
unit-test-autoscaling:
591591
needs: changes
592592
if: ${{ needs.changes.outputs.autoscaling == 'true' || github.event_name == 'push' }}
593-
timeout-minutes: 19 # if this timeout gets too small, then split the tests
593+
timeout-minutes: 22 # temporary: mypy takes a huge amount of time to run here, maybe we should cache it
594594
name: "[unit] autoscaling"
595595
runs-on: ${{ matrix.os }}
596596
strategy:
@@ -1288,7 +1288,7 @@ jobs:
12881288
uses: docker/setup-buildx-action@v3
12891289
with:
12901290
driver: docker-container
1291-
- uses: actions/[email protected].3
1291+
- uses: actions/[email protected].4
12921292
with:
12931293
node-version: ${{ matrix.node }}
12941294
cache: "npm"
@@ -2359,7 +2359,7 @@ jobs:
23592359
uses: actions/setup-python@v5
23602360
with:
23612361
python-version: ${{ matrix.python }}
2362-
- uses: actions/[email protected].3
2362+
- uses: actions/[email protected].4
23632363
with:
23642364
node-version: ${{ matrix.node }}
23652365
cache: "npm"

packages/models-library/src/models_library/clusters.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ class Config(BaseAuthentication.Config):
9696
class NoAuthentication(BaseAuthentication):
9797
type: Literal["none"] = "none"
9898

99+
class Config(BaseAuthentication.Config):
100+
schema_extra: ClassVar[dict[str, Any]] = {"examples": [{"type": "none"}]}
101+
99102

100103
class TLSAuthentication(BaseAuthentication):
101104
type: Literal["tls"] = "tls"

packages/models-library/src/models_library/users.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class UserBillingDetails(BaseModel):
2222
address: str | None
2323
city: str | None
2424
state: str | None = Field(description="State, province, canton, ...")
25-
country: str
25+
country: str # Required for taxes
2626
postal_code: str | None
2727
phone: str | None
2828

packages/pytest-simcore/src/pytest_simcore/helpers/logging_tools.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,14 @@ def log_context(
133133
else:
134134
ctx_msg = msg
135135

136-
started_time = datetime.datetime.now(tz=datetime.timezone.utc)
136+
started_time = datetime.datetime.now(tz=datetime.UTC)
137137
try:
138138
DynamicIndentFormatter.cls_increase_indent()
139139

140140
logger.log(level, ctx_msg.starting, *args, **kwargs)
141141
with _increased_logger_indent(logger):
142142
yield SimpleNamespace(logger=logger, messages=ctx_msg)
143-
elapsed_time = datetime.datetime.now(tz=datetime.timezone.utc) - started_time
143+
elapsed_time = datetime.datetime.now(tz=datetime.UTC) - started_time
144144
done_message = (
145145
f"{ctx_msg.done} ({_timedelta_as_minute_second_ms(elapsed_time)})"
146146
)
@@ -152,7 +152,7 @@ def log_context(
152152
)
153153

154154
except:
155-
elapsed_time = datetime.datetime.now(tz=datetime.timezone.utc) - started_time
155+
elapsed_time = datetime.datetime.now(tz=datetime.UTC) - started_time
156156
error_message = (
157157
f"{ctx_msg.raised} ({_timedelta_as_minute_second_ms(elapsed_time)})"
158158
)

packages/pytest-simcore/src/pytest_simcore/helpers/playwright.py

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
import logging
44
import re
55
from collections import defaultdict
6-
from contextlib import ExitStack
6+
from collections.abc import Generator, Iterator
77
from dataclasses import dataclass, field
88
from enum import Enum, unique
9-
from typing import Any, Final, Generator
9+
from typing import Any, Final
1010

1111
from playwright.sync_api import FrameLocator, Page, Request, WebSocket
1212
from pytest_simcore.helpers.logging_tools import log_context
@@ -263,28 +263,37 @@ def wait_for_pipeline_state(
263263
return current_state
264264

265265

266-
def on_web_socket_default_handler(ws) -> None:
267-
"""Usage
268-
269-
from pytest_simcore.playwright_utils import on_web_socket_default_handler
270-
271-
page.on("websocket", on_web_socket_default_handler)
272-
273-
"""
274-
stack = ExitStack()
275-
ctx = stack.enter_context(
276-
log_context(
277-
logging.INFO,
278-
(
279-
f"WebSocket opened: {ws.url}",
280-
"WebSocket closed",
281-
),
282-
)
283-
)
266+
@contextlib.contextmanager
267+
def web_socket_default_log_handler(web_socket: WebSocket) -> Iterator[None]:
284268

285-
ws.on("framesent", lambda payload: ctx.logger.info("⬇️ %s", payload))
286-
ws.on("framereceived", lambda payload: ctx.logger.info("⬆️ %s", payload))
287-
ws.on("close", lambda payload: stack.close()) # noqa: ARG005
269+
try:
270+
with log_context(
271+
logging.DEBUG,
272+
msg="handle websocket message (set to --log-cli-level=DEBUG level if you wanna see all of them)",
273+
) as ctx:
274+
275+
def on_framesent(payload: str | bytes) -> None:
276+
ctx.logger.debug("⬇️ Frame sent: %s", payload)
277+
278+
def on_framereceived(payload: str | bytes) -> None:
279+
ctx.logger.debug("⬆️ Frame received: %s", payload)
280+
281+
def on_close(payload: WebSocket) -> None:
282+
ctx.logger.warning("⚠️ Websocket closed: %s", payload)
283+
284+
def on_socketerror(error_msg: str) -> None:
285+
ctx.logger.error("❌ Websocket error: %s", error_msg)
286+
287+
web_socket.on("framesent", on_framesent)
288+
web_socket.on("framereceived", on_framereceived)
289+
web_socket.on("close", on_close)
290+
web_socket.on("socketerror", on_socketerror)
291+
yield
292+
finally:
293+
web_socket.remove_listener("framesent", on_framesent)
294+
web_socket.remove_listener("framereceived", on_framereceived)
295+
web_socket.remove_listener("close", on_close)
296+
web_socket.remove_listener("socketerror", on_socketerror)
288297

289298

290299
def _node_started_predicate(request: Request) -> bool:

packages/pytest-simcore/src/pytest_simcore/helpers/playwright_sim4life.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
import arrow
88
from playwright.sync_api import FrameLocator, Page, WebSocket, expect
9+
from pydantic import TypeAdapter # pylint: disable=no-name-in-module
10+
from pydantic import ByteSize
911

1012
from .logging_tools import log_context
1113
from .playwright import (
@@ -17,7 +19,7 @@
1719
wait_for_service_running,
1820
)
1921

20-
_S4L_STREAMING_ESTABLISHMENT_MAX_TIME: Final[int] = 15 * SECOND
22+
_S4L_STREAMING_ESTABLISHMENT_MAX_TIME: Final[int] = 30 * SECOND
2123
_S4L_SOCKETIO_REGEX: Final[re.Pattern] = re.compile(
2224
r"^(?P<protocol>[^:]+)://(?P<node_id>[^\.]+)\.services\.(?P<hostname>[^\/]+)\/socket\.io\/.+$"
2325
)
@@ -63,7 +65,7 @@ def __call__(self, message: str) -> bool:
6365
self._initial_bit_rate_time = arrow.utcnow().datetime
6466
self.logger.info(
6567
"%s",
66-
f"{self._initial_bit_rate=} at {self._initial_bit_rate_time.isoformat()}",
68+
f"{TypeAdapter(ByteSize).validate_python(self._initial_bit_rate).human_readable()}/s at {self._initial_bit_rate_time.isoformat()}",
6769
)
6870
return False
6971

@@ -78,7 +80,7 @@ def __call__(self, message: str) -> bool:
7880
bitrate_test = bool(self._initial_bit_rate != current_bitrate)
7981
self.logger.info(
8082
"%s",
81-
f"{current_bitrate=} after {elapsed_time=}: {'good!' if bitrate_test else 'failed! bitrate did not change! TIP: talk with MaG about underwater cables!'}",
83+
f"{TypeAdapter(ByteSize).validate_python(current_bitrate).human_readable()}/s after {elapsed_time=}: {'good!' if bitrate_test else 'failed! bitrate did not change! TIP: talk with MaG about underwater cables!'}",
8284
)
8385
return bitrate_test
8486

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

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,27 @@
22

33

44
from fastapi import FastAPI
5+
from prometheus_client import CollectorRegistry
56
from prometheus_fastapi_instrumentator import Instrumentator
67

78

89
def setup_prometheus_instrumentation(app: FastAPI) -> Instrumentator:
10+
# NOTE: use that registry to prevent having a global one
11+
app.state.prometheus_registry = registry = CollectorRegistry(auto_describe=True)
12+
instrumentator = Instrumentator(
13+
should_instrument_requests_inprogress=False, # bug in https://github.com/trallnag/prometheus-fastapi-instrumentator/issues/317
14+
inprogress_labels=False,
15+
registry=registry,
16+
).instrument(app)
917

10-
instrumentator = (
11-
Instrumentator(
12-
should_instrument_requests_inprogress=True, inprogress_labels=False
13-
)
14-
.instrument(app)
15-
.expose(app, include_in_schema=False)
16-
)
18+
async def _on_startup() -> None:
19+
instrumentator.expose(app, include_in_schema=False)
1720

18-
def _unregister():
19-
for collector in list(instrumentator.registry._collector_to_names.keys()):
20-
instrumentator.registry.unregister(collector)
21+
def _unregister() -> None:
22+
# NOTE: avoid registering collectors multiple times when running unittests consecutively (https://stackoverflow.com/a/62489287)
23+
for collector in list(registry._collector_to_names.keys()): # noqa: SLF001
24+
registry.unregister(collector)
2125

22-
# avoid registering collectors multiple times when running unittests consecutively (https://stackoverflow.com/a/62489287)
26+
app.add_event_handler("startup", _on_startup)
2327
app.add_event_handler("shutdown", _unregister)
2428
return instrumentator
Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
""" Adds fastapi middleware for tracing using opentelemetry instrumentation.
22
33
"""
4+
45
import logging
56

67
from fastapi import FastAPI
78
from opentelemetry import trace
89
from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
910
OTLPSpanExporter as OTLPSpanExporterHTTP,
1011
)
11-
from opentelemetry.instrumentation.fastapi import (
12-
FastAPIInstrumentor, # pylint: disable=no-name-in-module
13-
)
12+
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
1413
from opentelemetry.sdk.resources import Resource
1514
from opentelemetry.sdk.trace import TracerProvider
1615
from opentelemetry.sdk.trace.export import BatchSpanProcessor
@@ -21,24 +20,19 @@
2120

2221
def setup_tracing(
2322
app: FastAPI, tracing_settings: TracingSettings, service_name: str
24-
) -> FastAPIInstrumentor | None:
23+
) -> None:
2524
if (
2625
not tracing_settings.TRACING_OPENTELEMETRY_COLLECTOR_ENDPOINT
2726
and not tracing_settings.TRACING_OPENTELEMETRY_COLLECTOR_PORT
2827
):
2928
log.warning("Skipping opentelemetry tracing setup")
30-
return None
31-
if (
32-
not tracing_settings.TRACING_OPENTELEMETRY_COLLECTOR_ENDPOINT
33-
or not tracing_settings.TRACING_OPENTELEMETRY_COLLECTOR_PORT
34-
):
35-
raise RuntimeError(
36-
f"Variable opentelemetry_collector_endpoint [{tracing_settings.TRACING_OPENTELEMETRY_COLLECTOR_ENDPOINT}] or opentelemetry_collector_port [{tracing_settings.TRACING_OPENTELEMETRY_COLLECTOR_PORT}] unset. Tracing options incomplete."
37-
)
29+
return
30+
3831
# Set up the tracer provider
3932
resource = Resource(attributes={"service.name": service_name})
4033
trace.set_tracer_provider(TracerProvider(resource=resource))
41-
tracer_provider = trace.get_tracer_provider()
34+
global_tracer_provider = trace.get_tracer_provider()
35+
assert isinstance(global_tracer_provider, TracerProvider) # nosec
4236
tracing_destination: str = f"{tracing_settings.TRACING_OPENTELEMETRY_COLLECTOR_ENDPOINT}:{tracing_settings.TRACING_OPENTELEMETRY_COLLECTOR_PORT}/v1/traces"
4337
log.info(
4438
"Trying to connect service %s to tracing collector at %s.",
@@ -48,7 +42,6 @@ def setup_tracing(
4842
# Configure OTLP exporter to send spans to the collector
4943
otlp_exporter = OTLPSpanExporterHTTP(endpoint=tracing_destination)
5044
span_processor = BatchSpanProcessor(otlp_exporter)
51-
# Mypy bug --> https://github.com/open-telemetry/opentelemetry-python/issues/3713
52-
tracer_provider.add_span_processor(span_processor) # type: ignore[attr-defined]
45+
global_tracer_provider.add_span_processor(span_processor)
5346
# Instrument FastAPI
54-
return FastAPIInstrumentor().instrument_app(app) # type: ignore[no-any-return]
47+
FastAPIInstrumentor().instrument_app(app)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,13 @@
1+
from dataclasses import dataclass
2+
3+
from prometheus_client import CollectorRegistry
4+
5+
6+
@dataclass(slots=True, kw_only=True)
7+
class MetricsBase:
8+
subsystem: str
9+
registry: CollectorRegistry
10+
11+
112
def get_metrics_namespace(application_name: str) -> str:
213
return application_name.replace("-", "_")

0 commit comments

Comments
 (0)