Skip to content

✨ Integrate locust dashboards #6047

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tests/performance/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ RUN pip3 --version && \
pip3 install \
faker \
locust-plugins \
locust-plugins[dashboards] \
pydantic \
pydantic-settings \
tenacity &&\
Expand Down
37 changes: 30 additions & 7 deletions tests/performance/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ include ../../scripts/common.Makefile
LOCUST_VERSION=2.29.1
export LOCUST_VERSION

ENV_FILE=$(shell pwd)/.env
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)

Expand Down Expand Up @@ -36,14 +41,32 @@ down: ## stops and removes osparc locust containers
docker compose --file docker-compose.yml down

.PHONY: test
test: ## runs osparc locust. locust and test confiuration are specified in .env file next to target file
@$(call check_defined, target, please define target file when calling $@ - e.g. ```make $@ target=MY_LOCUST_FILE.py```)
@export LOCUST_FILE=$(target); \
export ENV_FILE=$$(realpath .env); \
if [ ! -f $${ENV_FILE} ]; then echo "You must generate a .env file before running tests!!! See the README..." && exit 1; fi; \
test: ## runs osparc locust. Locust and test configuration are specified in ENV_FILE
@if [ ! -f $${ENV_FILE} ]; then echo "You must generate a .env file before running tests!!! See the README..." && exit 1; fi;
@if ! docker network ls | grep -q $(NETWORK_NAME); then \
docker network create $(NETWORK_NAME); \
echo "Created docker network $(NETWORK_NAME)"; \
fi
docker compose --file docker-compose.yml up --scale worker=4 --exit-code-from=master

.PHONY: install-ci install-dev install

.PHONY: install-dev
install-dev: _check_venv_active ## install python tools
install-dev: install
install-ci: install

install: _check_venv_active ## install python tools
@uv pip install -r requirements.txt


.PHONY: dashboards-up dashboards-down

dashboards-up: ## Create Grafana dashboard for inspecting locust results. See dashboard on localhost:3000
@echo "View your dashboard on localhost:3000"
@if docker network ls | grep -q $(NETWORK_NAME); then \
docker network rm $(NETWORK_NAME); \
echo "Removed docker network $(NETWORK_NAME)"; \
fi
@locust-compose up

dashboards-down:
@locust-compose down
16 changes: 13 additions & 3 deletions tests/performance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,22 @@ In the [locust_files] folder are located the test files.

## Usage

1. Generate a `.env` file with setting for your test. After running `make install-dev` you can execute your test script in python and set settings as arguments. Once your settings are validated you pipe them to `.env`, e.g.
1. Generate a `.env` file with setting for your test. After running `make install-dev` you can execute your test script in python and set settings as arguments. Once your settings are validated you pipe them to `.env`. E.g. if your testscript is `locust_files/platform_ping_test.py` you could run
```bash
python locust_files/platform_ping_test.py --LOCUST_HOST=https://api.osparc-master.speag.com \
--LOCUST_USERS=100 --LOCUST_RUN_TIME=0:10:00 --SC_USER_NAME=myname --SC_PASSWORD=mypassword > .env
```
2. Run your test script using the Make `test` recipe, setting the correct `target`, e.g.
2. Run your test script using the Make `test` recipe, e.g.
```
make test target=locust_files/platform_ping_test.py
make test
```

## Dashboards for visualization
- 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).
- 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))
- When you are done you run `make dashboards-down` to clean up.
- If you are using VPN you will need to forward port 300 to your local machine to view the dashboard.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

port 300?? that is legal?



## Tricky settings 🚨
- `LOCUST_TIMESCALE` tells locust whether or not to send data to the database associated with visualizing the results. If you are not using the Grafana [dashboards](#dashboards-for-visualization) you should set `LOCUST_TIMESCALE=0`.
19 changes: 14 additions & 5 deletions tests/performance/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,27 @@ services:
ports:
- "8089:8089"
volumes:
- ./locust_files:/mnt/locust_files
- ./locust_report:/mnt/locust_report
- ./locust_files:/home/locust/locust_files
- ./locust_report:/home/locust/locust_report
command: >
-f /mnt/${LOCUST_FILE} --html /mnt/locust_report/locust_html.html --master
--master --html=locust_report/locust_html.html
env_file:
- ${ENV_FILE}
networks:
- dashboards_timenet

worker:
image: itisfoundation/locust:${LOCUST_VERSION}
volumes:
- ./locust_files/:/mnt/locust_files
- ./locust_files/:/home/locust/locust_files
command: >
-f /mnt/${LOCUST_FILE} --worker --master-host master
--worker --master-host master
env_file:
- ${ENV_FILE}
networks:
- dashboards_timenet

networks:
dashboards_timenet :
external: true
name: dashboards_timenet
7 changes: 6 additions & 1 deletion tests/performance/locust_files/catalog_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#

import logging
from pathlib import Path
from time import time

import faker
Expand Down Expand Up @@ -60,4 +61,8 @@ def on_stop(self):
if __name__ == "__main__":
from locust_settings import LocustSettings, dump_dotenv

dump_dotenv(LocustSettings())
dump_dotenv(
LocustSettings(
LOCUST_LOCUSTFILE=Path(__file__).relative_to(Path(__file__).parent.parent)
)
)
7 changes: 6 additions & 1 deletion tests/performance/locust_files/director_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#

import logging
from pathlib import Path

from locust import task
from locust.contrib.fasthttp import FastHttpUser
Expand Down Expand Up @@ -34,4 +35,8 @@ def on_stop(self): # pylint: disable=no-self-use
if __name__ == "__main__":
from locust_settings import LocustSettings, dump_dotenv

dump_dotenv(LocustSettings())
dump_dotenv(
LocustSettings(
LOCUST_LOCUSTFILE=Path(__file__).relative_to(Path(__file__).parent.parent)
)
)
7 changes: 6 additions & 1 deletion tests/performance/locust_files/locustfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#

import logging
from pathlib import Path
from uuid import UUID

import faker
Expand Down Expand Up @@ -82,4 +83,8 @@ def on_stop(self):
class LoadTestSettings(TemplateSettings, LocustSettings):
pass

dump_dotenv(LoadTestSettings())
dump_dotenv(
LoadTestSettings(
LOCUST_LOCUSTFILE=Path(__file__).relative_to(Path(__file__).parent.parent)
)
)
8 changes: 7 additions & 1 deletion tests/performance/locust_files/metamodeling/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,10 @@ def upload_file(self, file: Path) -> UUID:
class MetaModelingSettings(UserSettings, LocustSettings):
pass

dump_dotenv(MetaModelingSettings())
dump_dotenv(
MetaModelingSettings(
LOCUST_LOCUSTFILE=Path(__file__).relative_to(
Path(__file__).parent.parent.parent
)
)
)
7 changes: 6 additions & 1 deletion tests/performance/locust_files/platform_ping_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#

import logging
from pathlib import Path

import locust_plugins
from locust import task
Expand Down Expand Up @@ -58,4 +59,8 @@ def on_stop(self): # pylint: disable=no-self-use
class LoadTestSettings(LocustAuth, LocustSettings):
pass

dump_dotenv(LoadTestSettings())
dump_dotenv(
LoadTestSettings(
LOCUST_LOCUSTFILE=Path(__file__).relative_to(Path(__file__).parent.parent)
)
)
7 changes: 6 additions & 1 deletion tests/performance/locust_files/user_basic_calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import logging
import time
from pathlib import Path

import faker
from locust import task
Expand Down Expand Up @@ -96,4 +97,8 @@ def on_stop(self):
if __name__ == "__main__":
from locust_settings import LocustSettings, dump_dotenv

dump_dotenv(LocustSettings())
dump_dotenv(
LocustSettings(
LOCUST_LOCUSTFILE=Path(__file__).relative_to(Path(__file__).parent.parent)
)
)
7 changes: 6 additions & 1 deletion tests/performance/locust_files/webserver_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#

import logging
from pathlib import Path

import faker
from dotenv import load_dotenv
Expand Down Expand Up @@ -54,4 +55,8 @@ def on_stop(self):
if __name__ == "__main__":
from locust_settings import LocustSettings, dump_dotenv

dump_dotenv(LocustSettings())
dump_dotenv(
LocustSettings(
LOCUST_LOCUSTFILE=Path(__file__).relative_to(Path(__file__).parent.parent)
)
)
19 changes: 14 additions & 5 deletions tests/performance/settings/locust_settings/_locust_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
# pylint: disable=no-name-in-module

from datetime import timedelta
from pathlib import Path

from parse import Result, parse
from pydantic import (
AnyHttpUrl,
Field,
NonNegativeInt,
PositiveFloat,
PositiveInt,
SerializationInfo,
Expand All @@ -22,16 +24,23 @@
class LocustSettings(BaseSettings):
model_config = SettingsConfigDict(cli_parse_args=True)

LOCUST_CHECK_AVG_RESPONSE_TIME: PositiveInt = Field(default=200)
LOCUST_CHECK_FAIL_RATIO: PositiveFloat = Field(default=0.01, ge=0.0, le=1.0)
LOCUST_HEADLESS: bool = Field(default=True)
LOCUST_HOST: AnyHttpUrl = Field(
default=..., examples=["https://api.osparc-master.speag.com"]
)
LOCUST_USERS: PositiveInt = Field(default=...)
LOCUST_HEADLESS: bool = Field(default=True)
LOCUST_LOCUSTFILE: Path = Field(default=...)
LOCUST_PRINT_STATS: bool = Field(default=True)
LOCUST_SPAWN_RATE: PositiveInt = Field(default=20)
LOCUST_RUN_TIME: timedelta = Field(default=...)
LOCUST_CHECK_AVG_RESPONSE_TIME: PositiveInt = Field(default=200)
LOCUST_CHECK_FAIL_RATIO: PositiveFloat = Field(default=0.01, ge=0.0, le=1.0)
LOCUST_SPAWN_RATE: PositiveInt = Field(default=20)
LOCUST_TIMESCALE: NonNegativeInt = Field(default=1, ge=0, le=1)
LOCUST_USERS: PositiveInt = Field(default=...)

PGHOST: str = Field(default="postgres")
PGPASSWORD: str = Field(default="password")
PGPORT: int = Field(default=5432)
PGUSER: str = Field(default="postgres")

@field_validator("LOCUST_RUN_TIME", mode="before")
@classmethod
Expand Down
Loading