Skip to content

Commit 74c3690

Browse files
🎨🔨 Improve workflow for performance tests (#6815)
Co-authored-by: Sylvain <[email protected]>
1 parent 0aeae77 commit 74c3690

File tree

5 files changed

+46
-36
lines changed

5 files changed

+46
-36
lines changed

tests/performance/Makefile

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ export LOCUST_VERSION
99
ENV_FILE=$(shell pwd)/.env
1010
export ENV_FILE
1111

12+
KERNEL_NAME=$(shell uname -s)
13+
1214
NETWORK_NAME=dashboards_timenet
1315

1416
# UTILS
@@ -44,22 +46,17 @@ build: ## builds distributed osparc locust docker image
4446
push:
4547
docker push itisfoundation/locust:$(LOCUST_VERSION)
4648

47-
48-
49-
.PHONY: down
50-
down: ## stops and removes osparc locust containers
51-
docker compose --file docker-compose.yml down
52-
53-
.PHONY: test
54-
test: ## runs osparc locust. Locust and test configuration are specified in ENV_FILE
49+
.PHONY: test-up test-down
50+
test-up: ## runs osparc locust. Locust and test configuration are specified in ENV_FILE
5551
@if [ ! -f $${ENV_FILE} ]; then echo "You must generate a .env file before running tests!!! See the README..." && exit 1; fi;
5652
@if ! docker network ls | grep -q $(NETWORK_NAME); then \
5753
docker network create $(NETWORK_NAME); \
5854
echo "Created docker network $(NETWORK_NAME)"; \
5955
fi
6056
docker compose --file docker-compose.yml up --scale worker=4 --exit-code-from=master
6157

62-
58+
test-down: ## stops and removes osparc locust containers
59+
@docker compose --file docker-compose.yml down
6360

6461
.PHONY: dashboards-up dashboards-down
6562

@@ -69,12 +66,14 @@ dashboards-up: ## Create Grafana dashboard for inspecting locust results. See da
6966
docker network rm $(NETWORK_NAME); \
7067
echo "Removed docker network $(NETWORK_NAME)"; \
7168
fi
69+
@if [[ "$(KERNEL_NAME)" == "Linux" ]]; then \
70+
( sleep 3 && xdg-open http://localhost:3000 ) & \
71+
fi
7272
@locust-compose up
7373

74-
dashboards-down:
75-
@locust-compose down
76-
7774

75+
dashboards-down: ## stops and removes Grafana dashboard and Timescale postgress containers
76+
@locust-compose down
7877

7978
.PHONY: install-ci install-dev
8079

@@ -86,6 +85,6 @@ install-ci:
8685

8786

8887
.PHONY: config
89-
config:
88+
config: ## Create config for your locust tests
9089
@$(call check_defined, input, please define inputs when calling $@ - e.g. ```make $@ input="--help"```)
9190
@uv run locust_settings.py $(input) | tee "${ENV_FILE}"

tests/performance/README.md

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,18 @@ make config input="--LOCUST_HOST=https://api.osparc-master.speag.com
1515
```
1616
This will validate your settings and you should be good to go once you see a the settings printed in your terminal.
1717

18-
2. Add settings related to your locust file. E.g. if your file expects to find an environment variable `MYENVVAR` you add it to `.env`:
18+
2. Once you have all settings setup you run your test script using the Make `test` recipe:
1919
```bash
20-
echo "MYENVVAR=thisismyenvvar" >> .env
20+
make test-up
2121
```
2222

23-
3. Once you have all settings setup you uun your test script using the Make `test` recipe:
24-
```bash
25-
make test
26-
```
23+
3. If you want to clean up after your tests (remove docker containers) you run `make test-down`
2724

2825
## Dashboards for visualization
2926
- You can visualize the results of your tests (in real time) in a collection of beautiful [Grafana dashboards](https://github.com/SvenskaSpel/locust-plugins/tree/master/locust_plugins/dashboards).
30-
- To do this, run `make dashboards-up` and go to `localhost:3000` to view the dashboards. The way you tell locust to send test results to the database/grafana is by ensuring `LOCUST_TIMESCALE=1` (see how to generate settings in [usage](#usage))
27+
- To do this, run `make dashboards-up`. If you are on linux you should see your browser opening `localhost:3000`, where you can view the dashboards. If the browser doesn't open automatically, do it manually and navigate to `localhost:3000`.The way you tell locust to send test results to the database/grafana is by ensuring `LOCUST_TIMESCALE=1` (see how to generate settings in [usage](#usage))
3128
- When you are done you run `make dashboards-down` to clean up.
32-
- If you are using VPN you will need to forward port 300 to your local machine to view the dashboard.
29+
- If you are using VPN you will need to forward port 3000 to your local machine to view the dashboard.
3330

3431

3532
## Tricky settings 🚨

tests/performance/locust_files/metamodeling/workflow.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from locust import HttpUser, task
88
from pydantic import Field
9-
from pydantic_settings import BaseSettings
9+
from pydantic_settings import BaseSettings, SettingsConfigDict
1010
from requests.auth import HTTPBasicAuth
1111
from tenacity import (
1212
Retrying,
@@ -27,6 +27,7 @@
2727

2828

2929
class UserSettings(BaseSettings):
30+
model_config = SettingsConfigDict(extra="ignore")
3031
OSPARC_API_KEY: str = Field(default=...)
3132
OSPARC_API_SECRET: str = Field(default=...)
3233

tests/performance/locust_files/platform_ping_test.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from locust import task
99
from locust.contrib.fasthttp import FastHttpUser
1010
from pydantic import Field
11-
from pydantic_settings import BaseSettings
11+
from pydantic_settings import BaseSettings, SettingsConfigDict
1212

1313
logging.basicConfig(level=logging.INFO)
1414

@@ -20,6 +20,7 @@
2020

2121

2222
class MonitoringBasicAuth(BaseSettings):
23+
model_config = SettingsConfigDict(extra="ignore")
2324
SC_USER_NAME: str = Field(default=..., examples=["<your username>"])
2425
SC_PASSWORD: str = Field(default=..., examples=["<your password>"])
2526

tests/performance/locust_settings.py

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
# /// script
22
# requires-python = ">=3.11"
33
# dependencies = [
4+
# "faker",
5+
# "locust",
6+
# "locust-plugins",
47
# "parse",
58
# "pydantic",
69
# "pydantic-settings",
10+
# "tenacity"
711
# ]
812
# ///
913
# pylint: disable=unused-argument
@@ -13,6 +17,7 @@
1317
import importlib.util
1418
import inspect
1519
import json
20+
import sys
1621
from datetime import timedelta
1722
from pathlib import Path
1823
from types import ModuleType
@@ -37,8 +42,9 @@
3742
assert _LOCUST_FILES_DIR.is_dir()
3843

3944

40-
def _check_load_and_instantiate_settings_classes(file_path: str):
41-
module_name = Path(file_path).stem
45+
def _get_settings_classes(file_path: Path) -> list[type[BaseSettings]]:
46+
assert file_path.is_file()
47+
module_name = file_path.stem
4248
spec = importlib.util.spec_from_file_location(module_name, file_path)
4349
if spec is None or spec.loader is None:
4450
msg = f"Invalid {file_path=}"
@@ -60,16 +66,11 @@ def _check_load_and_instantiate_settings_classes(file_path: str):
6066
if issubclass(obj, BaseSettings) and obj is not BaseSettings
6167
]
6268

63-
for settings_class in settings_classes:
64-
try:
65-
settings_class()
66-
except Exception as e:
67-
msg = f"Missing env vars for {settings_class.__name__} in {file_path=}: {e}"
68-
raise ValueError(msg) from e
69+
return settings_classes
6970

7071

7172
class LocustSettings(BaseSettings):
72-
model_config = SettingsConfigDict(cli_parse_args=True)
73+
model_config = SettingsConfigDict(cli_parse_args=True, cli_ignore_unknown_args=True)
7374

7475
LOCUST_CHECK_AVG_RESPONSE_TIME: PositiveInt = Field(default=200)
7576
LOCUST_CHECK_FAIL_RATIO: PositiveFloat = Field(default=0.01, ge=0.0, le=1.0)
@@ -150,8 +151,19 @@ def _serialize_host(self, url: AnyHttpUrl, info: SerializationInfo) -> str:
150151

151152

152153
if __name__ == "__main__":
153-
settings = LocustSettings()
154-
env_vars = [
155-
f"{key}={val}" for key, val in json.loads(settings.model_dump_json()).items()
156-
]
154+
locust_settings = LocustSettings()
155+
156+
arguments = dict(
157+
arg.removeprefix("--").split("=") for arg in sys.argv if arg.startswith("--")
158+
)
159+
160+
settings_objects = [locust_settings]
161+
for sclass in _get_settings_classes(locust_settings.LOCUST_LOCUSTFILE):
162+
settings_objects.append(sclass(**arguments))
163+
164+
env_vars = []
165+
for obj in settings_objects:
166+
env_vars += [
167+
f"{key}={val}" for key, val in json.loads(obj.model_dump_json()).items()
168+
]
157169
print("\n".join(env_vars))

0 commit comments

Comments
 (0)