From 2c64fe26e7a1aa84cd34439baa8c4bc8b7f478ab Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Tue, 12 Nov 2024 09:37:53 +0100 Subject: [PATCH 1/4] fixes cut issue with busybox --- tests/performance/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/performance/Makefile b/tests/performance/Makefile index ead1e417d39..4cd8abf90a8 100644 --- a/tests/performance/Makefile +++ b/tests/performance/Makefile @@ -12,7 +12,8 @@ export ENV_FILE NETWORK_NAME=dashboards_timenet # UTILS -get_my_ip := $(shell (hostname --all-ip-addresses || hostname -i) 2>/dev/null | cut --delimiter=" " --fields=1) +# NOTE: keep short arguments for `cut` so it works in both BusyBox (alpine) AND Ubuntu +get_my_ip := $(shell (hostname --all-ip-addresses || hostname -i) 2>/dev/null | cut -d " " -f 1) # Check that given variables are set and all have non-empty values, # die with an error otherwise. From 7e45fff84871ca4151ef97392db902327e17256b Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Tue, 12 Nov 2024 10:05:27 +0100 Subject: [PATCH 2/4] doc --- tests/performance/locust_settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/performance/locust_settings.py b/tests/performance/locust_settings.py index 24f896180fd..eef5b301168 100644 --- a/tests/performance/locust_settings.py +++ b/tests/performance/locust_settings.py @@ -44,8 +44,8 @@ class LocustSettings(BaseSettings): LOCUST_RUN_TIME: timedelta LOCUST_SPAWN_RATE: PositiveInt = Field(default=20) - # Options for Timescale + Grafana Dashboards - # SEE https://github.com/SvenskaSpel/locust-plugins/blob/master/locust_plugins/timescale/ + # Timescale: Log and graph results using TimescaleDB and Grafana dashboards + # SEE https://github.com/SvenskaSpel/locust-plugins/tree/master/locust_plugins/dashboards # LOCUST_TIMESCALE: NonNegativeInt = Field( default=1, From 29884a7c2797e7e25dfe669ed6e3ac3abaf4b4e8 Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Tue, 12 Nov 2024 10:49:25 +0100 Subject: [PATCH 3/4] rename --- tests/performance/locust_files/platform_ping_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/performance/locust_files/platform_ping_test.py b/tests/performance/locust_files/platform_ping_test.py index 61cb0733458..c8839bb8c2b 100644 --- a/tests/performance/locust_files/platform_ping_test.py +++ b/tests/performance/locust_files/platform_ping_test.py @@ -19,7 +19,7 @@ assert locust_plugins # nosec -class LocustAuth(BaseSettings): +class MonitoringBasicAuth(BaseSettings): SC_USER_NAME: str = Field(default=..., examples=[""]) SC_PASSWORD: str = Field(default=..., examples=[""]) @@ -27,7 +27,7 @@ class LocustAuth(BaseSettings): class WebApiUser(FastHttpUser): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - _auth = LocustAuth() + _auth = MonitoringBasicAuth() self.auth = ( _auth.SC_USER_NAME, _auth.SC_PASSWORD, From 4bee9b358f62fc98f235891241bf78e2968d7f94 Mon Sep 17 00:00:00 2001 From: Pedro Crespo-Valero <32402063+pcrespov@users.noreply.github.com> Date: Tue, 12 Nov 2024 10:49:50 +0100 Subject: [PATCH 4/4] uv add config --- tests/performance/Makefile | 9 +++++- tests/performance/locust_settings.py | 46 ++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/tests/performance/Makefile b/tests/performance/Makefile index 4cd8abf90a8..d41a60d7af8 100644 --- a/tests/performance/Makefile +++ b/tests/performance/Makefile @@ -29,6 +29,7 @@ __check_defined = \ $(error Undefined $1$(if $2, ($2)))) + .PHONY: build build: ## builds distributed osparc locust docker image docker \ @@ -43,6 +44,8 @@ build: ## builds distributed osparc locust docker image push: docker push itisfoundation/locust:$(LOCUST_VERSION) + + .PHONY: down down: ## stops and removes osparc locust containers docker compose --file docker-compose.yml down @@ -56,6 +59,8 @@ test: ## runs osparc locust. Locust and test configuration are specified in ENV_ fi docker compose --file docker-compose.yml up --scale worker=4 --exit-code-from=master + + .PHONY: dashboards-up dashboards-down dashboards-up: ## Create Grafana dashboard for inspecting locust results. See dashboard on localhost:3000 @@ -69,6 +74,8 @@ dashboards-up: ## Create Grafana dashboard for inspecting locust results. See da dashboards-down: @locust-compose down + + .PHONY: install-ci install-dev install-dev: @@ -81,4 +88,4 @@ install-ci: .PHONY: config config: @$(call check_defined, input, please define inputs when calling $@ - e.g. ```make $@ input="--help"```) - @uv run locust_settings.py $(input) | tee .env + @uv run locust_settings.py $(input) | tee "${ENV_FILE}" diff --git a/tests/performance/locust_settings.py b/tests/performance/locust_settings.py index eef5b301168..48c219871fe 100644 --- a/tests/performance/locust_settings.py +++ b/tests/performance/locust_settings.py @@ -1,10 +1,21 @@ +# /// script +# requires-python = ">=3.11" +# dependencies = [ +# "parse", +# "pydantic", +# "pydantic-settings", +# ] +# /// # pylint: disable=unused-argument # pylint: disable=no-self-use # pylint: disable=no-name-in-module +import importlib.util +import inspect import json from datetime import timedelta from pathlib import Path +from types import ModuleType from typing import Final from parse import Result, parse @@ -26,6 +37,37 @@ assert _LOCUST_FILES_DIR.is_dir() +def _check_load_and_instantiate_settings_classes(file_path: str): + module_name = Path(file_path).stem + spec = importlib.util.spec_from_file_location(module_name, file_path) + if spec is None or spec.loader is None: + msg = f"Invalid {file_path=}" + raise ValueError(msg) + + module: ModuleType = importlib.util.module_from_spec(spec) + + # Execute the module in its own namespace + try: + spec.loader.exec_module(module) + except Exception as e: + msg = f"Failed to load module {module_name} from {file_path}" + raise ValueError(msg) from e + + # Filter subclasses of BaseSettings + settings_classes = [ + obj + for _, obj in inspect.getmembers(module, inspect.isclass) + if issubclass(obj, BaseSettings) and obj is not BaseSettings + ] + + for settings_class in settings_classes: + try: + settings_class() + except Exception as e: + msg = f"Missing env vars for {settings_class.__name__} in {file_path=}: {e}" + raise ValueError(msg) from e + + class LocustSettings(BaseSettings): model_config = SettingsConfigDict(cli_parse_args=True) @@ -87,6 +129,10 @@ def _validate_locust_file(cls, v: Path) -> Path: if not v.is_relative_to(_LOCUST_FILES_DIR): msg = f"{v} must be a test file relative to {_LOCUST_FILES_DIR}" raise ValueError(msg) + + # NOTE: CHECK that all the env-vars are defined for this test + # _check_load_and_instantiate_settings_classes(f"{v}") + return v.relative_to(_TEST_DIR) @field_serializer("LOCUST_RUN_TIME")