Skip to content

Commit

Permalink
add verify_latency_percentile_from_load_test probe
Browse files Browse the repository at this point in the history
Signed-off-by: Sylvain Hellegouarch <sh@defuze.org>
  • Loading branch information
Lawouach committed Dec 18, 2023
1 parent 76cf3f7 commit 1ff26a5
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 9 deletions.
13 changes: 11 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,24 @@

## [Unreleased][]

[Unreleased]: https://github.com/chaostoolkit-incubator/chaostoolkit-reliably/compare/0.74.0...HEAD
[Unreleased]: https://github.com/chaostoolkit-incubator/chaostoolkit-reliably/compare/0.75.0...HEAD

## [0.75.0][]

[0.75.0]: https://github.com/chaostoolkit-incubator/chaostoolkit-reliably/compare/0.74.0...0.75.0

### Added

* The `verify_latency_percentile_from_load_test` probeto perform a verification of a
load test from `run_load_test`

## [0.74.0][]

[0.74.0]: https://github.com/chaostoolkit-incubator/chaostoolkit-reliably/compare/0.73.0...0.74.0

### Added

* The `run_load_test` to perform a load test using the excellent [oha](https://github.com/hatoo/oha)
* The `run_load_test` action to perform a load test using the excellent [oha](https://github.com/hatoo/oha)

### Changed

Expand Down
15 changes: 15 additions & 0 deletions chaosreliably/activities/load/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import threading
from typing import Any, Dict

lock = threading.Lock()
RESULTS = {}


def store_results(name: str, results: Dict[str, Any]) -> None:
with lock():
RESULTS[name] = results


def get_results(name: str) -> Dict[str, Any]:
with lock():
RESULTS.get("name", {})
29 changes: 22 additions & 7 deletions chaosreliably/activities/load/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import shutil
import subprocess # nosec
import tempfile
from threading import Lock
from typing import Any, Dict, Optional, cast
from urllib.parse import urlparse

Expand All @@ -13,6 +14,8 @@
from chaoslib.types import Configuration, Secrets
from logzero import logger

from chaosreliably.activities.load import store_results

try:
from opentelemetry.context import get_current
from opentelemetry.propagate import inject
Expand All @@ -22,6 +25,8 @@
HAS_OTEL = False

__all__ = ["inject_gradual_traffic_into_endpoint", "run_load_test"]
lock = Lock()
RESULTS = dict()


def inject_gradual_traffic_into_endpoint(
Expand Down Expand Up @@ -166,13 +171,14 @@ def run_load_test(
url: str,
duration: int = 30,
qps: int = 5,
use_dns_servers: str = "",
connect_to: str = "",
insecure: bool = False,
host: str = "None",
method: str = "GET",
headers: str = "",
body: str = "",
content_type: str = "",
test_name: str = "load test",
) -> Dict[str, Any]:
"""
Run a load test against the given URL.
Expand All @@ -181,21 +187,27 @@ def run_load_test(
It produces a different set of results. Please make sure to have it
installed in your `PATH`.
Set the `test_name` so you can use one of the probe against this action
to retrieve its results.
Use the following parameters to adjust the default:
* `use_dns_servers` pass a comma-separated list of DNS servers to connect
to instead of the default ones
* `connect_to` pass a column seperated list of addresses `host:port`
to connect to instead of the DNS values for the domain
* `insecure` set to False to communicate with a non-secure TLS server
* `host` set a different `HOST` header
* `method` the HTTP method to use
* `headers` a comma-separated list of headers "foo: bar,other: thing"
* `body` the content of the request to send if any
* `content_type` the content-type of the request to send if any
"""
oha_path = shutil.which("oha")
if not oha_path:
raise ActivityFailed("missing load test dependency")

results = {} # Dict[str, Any]

cmd = [
oha_path,
"--json",
Expand All @@ -209,8 +221,8 @@ def run_load_test(
f"{qps}",
]

if use_dns_servers:
cmd.extend(["--connect-to", use_dns_servers])
if connect_to:
cmd.extend(["--connect-to", connect_to])

if insecure:
cmd.extend(
Expand Down Expand Up @@ -261,9 +273,12 @@ def run_load_test(
logger.debug(f"locust stderr: {stderr}")

try:
results = json.loads(stdout)
results = cast(Dict[str, Any], json.loads(stdout))
except json.decoder.JSONDecodeError:
logger.error("failed to parse oha results")
raise ActivityFailed("failed to parse oha results")

return cast(Dict[str, Any], results)
if test_name:
store_results(test_name, results)

return results
21 changes: 21 additions & 0 deletions chaosreliably/activities/load/probes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
import os.path
from typing import Optional, cast

from chaosreliably.activities.load import get_results

__all__ = [
"load_test_result_field_should_be",
"load_test_result_field_should_be_less_than",
"load_test_result_field_should_be_greater_than",
"verify_latency_percentile_from_load_test",
]


Expand Down Expand Up @@ -103,3 +106,21 @@ def load_test_result_field_should_be_greater_than(
return cast(bool, data[0][field] > int(expect))

return False


def verify_latency_percentile_from_load_test(
lower_than: float, percentile: str = "p99", test_name: str = "load test"
) -> bool:
"""
Verify that the percentile of a load test result, generated with the
`run_load_test` action, is lower than the expected value.
"""
results = get_results("test_name")
if not results:
return False

p = results.get("latencyPercentilesSuccessful", {}).get(percentile)
if not p:
return False

return p <= lower_than

0 comments on commit 1ff26a5

Please sign in to comment.