Skip to content

Commit 205a6b1

Browse files
[load] support multi-region load | advanced url tests | dashboard reports | debug mode | copy artifacts sas url | high scale test config | convert to jmx | baseline trends | sas download (Azure#8350)
* [load] support multi-region load test configuration * refactor * update recordings * azdev style load * refactor yaml keys to constants file * refactor test load test * update recordings * azdev style load * update help text * update help text * action on comments * refactor test regional-load autostop * check for valid region names client-side * [load] support advanced url tests * action on comments * action on comments * action on comments * update recordings * action on comments * update test and recordings * refactor test exception scenarios * azdev style load * [load] dashboard reports | debug mode | copy artifacts sas url * action on comments * update recordings * set copy artifacts ut live-only * change copy-artifacts to get-artifacts * update recordings * [load] download-files high scale test update console message * [load] download-files high scale test update console message * remove unused import * update texts * update texts * [load] convert test to jmx * update comment * update prompt text * [load] baseline trends | sas download * unit tests and recordings * trends aggregate param,unit tests, recordings * update HISTORY.rst * remove faulty recording * action copilot review Co-authored-by: Copilot <[email protected]> * undo faulty commit by copilot * test instruction * action on comments * azdev mask recordings --------- Co-authored-by: Copilot <[email protected]>
1 parent 1488170 commit 205a6b1

File tree

71 files changed

+321539
-12551
lines changed

Some content is hidden

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

71 files changed

+321539
-12551
lines changed

src/load/HISTORY.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@
22
33
Release History
44
===============
5+
1.4.0
6+
++++++
7+
* Add support for multi-region load test configuration. Multi-region load test configuration can be set using `--regionwise-engines` argument in 'az load test create' and 'az load test update' commands. Multi-region load test configuration set in YAML config file under key `regionalLoadTestConfig` will also be honoured.
8+
* Bug fix for `engineInstances` being reset to 1 and not getting backfilled using test's existing configuration when engine instances are not explicitly specified either in YAML config file or CLI argument.
9+
* Add support for advanced URL test with multiple HTTP request using JSON file. Add `--test-type` argument to 'az load test create' and honor `testType` key in YAML config file.
10+
* Add CLI parameter `--report` to 'az load test-run download-files' to download the dashboard reports.
11+
* Enable debug level logging using `--debug-mode` argument in 'az load test-run create' command .
12+
* Return the SAS URL to copy artifacts to storage accounts using command 'az load test-run get-artifacts-url'.
13+
* Add config for high-scale load tests and extend 'az load test-run download-files' to support download of logs and results from artifacts container for such tests.
14+
* Add command 'az load test convert-to-jmx' to convert URL type tests to JMX tests.
15+
* Add commands 'az load test set-baseline' to set the baseline for a test and 'az load test compare-to-baseline' to compare recent test runs to the baseline test run.
16+
17+
518
1.3.1
619
++++++
720
* Bug fix for `splitAllCSVs` not being honoured from config file due to CLI argument being set as false by default leading to configuration not being selected from the config file.

src/load/azext_load/data_plane/load_test/commands.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
# --------------------------------------------------------------------------------------------
55

66
from azext_load.data_plane.utils import validators
7+
from azext_load.data_plane.utils.constants import LoadCommandsConstants
78
from azure.cli.core.commands import CliCommandType
9+
from .transformers import trends_output_transformer
810

911
admin_custom_sdk = CliCommandType(
1012
operations_tmpl="azext_load.data_plane.load_test.custom#{}"
@@ -25,6 +27,13 @@ def load_test_commands(self, _):
2527
"download_test_files",
2628
validator=validators.validate_download,
2729
)
30+
g.custom_command(
31+
"convert-to-jmx",
32+
"convert_to_jmx",
33+
confirmation=LoadCommandsConstants.CONVERT_TO_JMX_CONFIRM_PROMPT
34+
)
35+
g.custom_command("set-baseline", "set_baseline")
36+
g.custom_command("compare-to-baseline", "compare_to_baseline", table_transformer=trends_output_transformer)
2837

2938
with self.command_group(
3039
"load test app-component",

src/load/azext_load/data_plane/load_test/custom.py

Lines changed: 140 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,22 @@
99

1010
from azext_load.data_plane.utils.utils import (
1111
convert_yaml_to_test,
12+
create_autostop_criteria_from_args,
1213
create_or_update_test_with_config,
1314
create_or_update_test_without_config,
1415
download_file,
16+
generate_trends_row,
1517
get_admin_data_plane_client,
18+
get_testrun_data_plane_client,
1619
load_yaml,
1720
upload_file_to_test,
1821
upload_files_helper,
19-
create_autostop_criteria_from_args,
2022
)
21-
from azure.cli.core.azclierror import InvalidArgumentValueError
23+
from azext_load.data_plane.utils.models import (
24+
AllowedTestTypes,
25+
AllowedTrendsResponseTimeAggregations,
26+
)
27+
from azure.cli.core.azclierror import InvalidArgumentValueError, FileOperationError
2228
from azure.core.exceptions import ResourceNotFoundError
2329
from knack.log import get_logger
2430

@@ -31,6 +37,7 @@ def create_test(
3137
test_id,
3238
display_name=None,
3339
test_plan=None,
40+
test_type=None,
3441
resource_group_name=None,
3542
load_test_config_file=None,
3643
test_description=None,
@@ -46,6 +53,7 @@ def create_test(
4653
autostop=None,
4754
autostop_error_rate=None,
4855
autostop_error_rate_time_window=None,
56+
regionwise_engines=None,
4957
):
5058
client = get_admin_data_plane_client(cmd, load_test_resource, resource_group_name)
5159
logger.info("Create test has started for test ID : %s", test_id)
@@ -68,6 +76,7 @@ def create_test(
6876
body,
6977
display_name=display_name,
7078
test_description=test_description,
79+
test_type=test_type,
7180
engine_instances=engine_instances,
7281
env=env,
7382
secrets=secrets,
@@ -77,15 +86,17 @@ def create_test(
7786
split_csv=split_csv,
7887
disable_public_ip=disable_public_ip,
7988
autostop_criteria=autostop_criteria,
89+
regionwise_engines=regionwise_engines,
8090
)
8191
else:
8292
yaml = load_yaml(load_test_config_file)
83-
yaml_test_body = convert_yaml_to_test(yaml)
93+
yaml_test_body = convert_yaml_to_test(cmd, yaml)
8494
body = create_or_update_test_with_config(
8595
test_id,
8696
body,
8797
yaml_test_body,
8898
display_name=display_name,
99+
test_type=test_type,
89100
test_description=test_description,
90101
engine_instances=engine_instances,
91102
env=env,
@@ -95,16 +106,18 @@ def create_test(
95106
subnet_id=subnet_id,
96107
split_csv=split_csv,
97108
disable_public_ip=disable_public_ip,
98-
autostop_criteria=autostop_criteria
109+
autostop_criteria=autostop_criteria,
110+
regionwise_engines=regionwise_engines,
99111
)
100112
logger.debug("Creating test with test ID: %s and body : %s", test_id, body)
101113
response = client.create_or_update_test(test_id=test_id, body=body)
102114
logger.debug(
103115
"Created test with test ID: %s and response obj is %s", test_id, response
104116
)
105117
logger.info("Uploading files to test %s", test_id)
118+
evaluated_test_type = test_type or yaml_test_body.get("kind") if yaml_test_body else response.get("kind")
106119
upload_files_helper(
107-
client, test_id, yaml, test_plan, load_test_config_file, not custom_no_wait
120+
client, test_id, yaml, test_plan, load_test_config_file, not custom_no_wait, evaluated_test_type
108121
)
109122
response = client.get_test(test_id)
110123
logger.info("Upload files to test %s has completed", test_id)
@@ -133,6 +146,7 @@ def update_test(
133146
autostop=None,
134147
autostop_error_rate=None,
135148
autostop_error_rate_time_window=None,
149+
regionwise_engines=None,
136150
):
137151
client = get_admin_data_plane_client(cmd, load_test_resource, resource_group_name)
138152
logger.info("Update test has started for test ID : %s", test_id)
@@ -149,7 +163,7 @@ def update_test(
149163
autostop=autostop, error_rate=autostop_error_rate, time_window=autostop_error_rate_time_window)
150164
if load_test_config_file is not None:
151165
yaml = load_yaml(load_test_config_file)
152-
yaml_test_body = convert_yaml_to_test(yaml)
166+
yaml_test_body = convert_yaml_to_test(cmd, yaml)
153167
body = create_or_update_test_with_config(
154168
test_id,
155169
body,
@@ -164,7 +178,8 @@ def update_test(
164178
subnet_id=subnet_id,
165179
split_csv=split_csv,
166180
disable_public_ip=disable_public_ip,
167-
autostop_criteria=autostop_criteria
181+
autostop_criteria=autostop_criteria,
182+
regionwise_engines=regionwise_engines,
168183
)
169184
else:
170185
body = create_or_update_test_without_config(
@@ -180,7 +195,8 @@ def update_test(
180195
subnet_id=subnet_id,
181196
split_csv=split_csv,
182197
disable_public_ip=disable_public_ip,
183-
autostop_criteria=autostop_criteria
198+
autostop_criteria=autostop_criteria,
199+
regionwise_engines=regionwise_engines,
184200
)
185201
logger.info("Updating test with test ID: %s", test_id)
186202
response = client.create_or_update_test(test_id=test_id, body=body)
@@ -189,7 +205,7 @@ def update_test(
189205
)
190206
logger.info("Uploading files to test %s", test_id)
191207
upload_files_helper(
192-
client, test_id, yaml, test_plan, load_test_config_file, not custom_no_wait
208+
client, test_id, yaml, test_plan, load_test_config_file, not custom_no_wait, body.get("kind")
193209
)
194210
response = client.get_test(test_id)
195211
logger.info("Upload files to test %s has completed", test_id)
@@ -262,6 +278,117 @@ def delete_test(
262278
logger.info("Deleted test with test ID: %s", test_id)
263279

264280

281+
def convert_to_jmx(
282+
cmd,
283+
load_test_resource,
284+
test_id,
285+
resource_group_name=None,
286+
):
287+
logger.info("Converting test with test ID: %s to JMX", test_id)
288+
client = get_admin_data_plane_client(cmd, load_test_resource, resource_group_name)
289+
existing_test = client.get_test(test_id)
290+
if (
291+
existing_test is not None
292+
and existing_test.get("kind") != AllowedTestTypes.URL.value
293+
):
294+
raise InvalidArgumentValueError(
295+
f"Test with test ID: {test_id} is not of type URL. "
296+
"Only URL type tests can be converted to JMX."
297+
)
298+
body = create_or_update_test_without_config(
299+
test_id=test_id,
300+
body=existing_test,
301+
test_type=AllowedTestTypes.JMX.value,
302+
)
303+
response = client.create_or_update_test(test_id=test_id, body=body)
304+
logger.debug("Converted test with test ID: %s to JMX", test_id)
305+
return response.as_dict()
306+
307+
308+
def set_baseline(
309+
cmd,
310+
load_test_resource,
311+
test_id,
312+
test_run_id,
313+
resource_group_name=None,
314+
):
315+
logger.info("Setting baseline for test with test ID: %s", test_id)
316+
test_client = get_admin_data_plane_client(cmd, load_test_resource, resource_group_name)
317+
test_run_client = get_testrun_data_plane_client(cmd, load_test_resource, resource_group_name)
318+
existing_test = test_client.get_test(test_id)
319+
existing_test_run = test_run_client.get_test_run(test_run_id)
320+
if existing_test_run.get("testId") != test_id:
321+
raise InvalidArgumentValueError(
322+
f"Test run with ID: {test_run_id} is not associated with test ID: {test_id}"
323+
)
324+
if existing_test_run.get("status") not in ["CANCELLED", "DONE"]:
325+
raise InvalidArgumentValueError(
326+
f"Test run with ID: {test_run_id} does not have a valid "
327+
f"test run status {existing_test_run.get('status')}. "
328+
"Valid test run status are: CANCELLED, DONE"
329+
)
330+
if existing_test_run.get("testRunStatistics", {}).get("Total") is None:
331+
raise InvalidArgumentValueError(
332+
f"Sampler statistics are not yet available for test run ID {test_run_id}. "
333+
"Please try again later."
334+
)
335+
body = create_or_update_test_without_config(
336+
test_id=test_id,
337+
body=existing_test,
338+
baseline_test_run_id=test_run_id,
339+
)
340+
response = test_client.create_or_update_test(test_id=test_id, body=body)
341+
logger.debug("Set test run %s as baseline for test: %s", test_run_id, test_id)
342+
return response.as_dict()
343+
344+
345+
def compare_to_baseline(
346+
cmd,
347+
load_test_resource,
348+
test_id,
349+
resource_group_name=None,
350+
response_time_aggregate=AllowedTrendsResponseTimeAggregations.MEAN.value,
351+
):
352+
logger.info("Showing test trends for test with test ID: %s", test_id)
353+
test_client = get_admin_data_plane_client(cmd, load_test_resource, resource_group_name)
354+
test_run_client = get_testrun_data_plane_client(cmd, load_test_resource, resource_group_name)
355+
test = test_client.get_test(test_id)
356+
if test.get("baselineTestRunId") is None:
357+
raise InvalidArgumentValueError(
358+
f"Test with ID: {test_id} does not have a baseline test run associated with it."
359+
)
360+
baseline_test_run_id = test.get("baselineTestRunId")
361+
baseline_test_run = test_run_client.get_test_run(baseline_test_run_id)
362+
all_test_runs = test_run_client.list_test_runs(
363+
orderby="executedDateTime desc",
364+
test_id=test_id,
365+
status="CANCELLED,DONE",
366+
maxpagesize=20,
367+
)
368+
all_test_runs = [run.as_dict() for run in all_test_runs]
369+
logger.debug("Total number of test runs: %s", len(all_test_runs))
370+
count = 0
371+
recent_test_runs = []
372+
for run in all_test_runs:
373+
if (
374+
run.get("testRunId") != baseline_test_run_id
375+
and count < 10 # Show only 10 most recent test runs
376+
):
377+
recent_test_runs.append(run)
378+
count += 1
379+
380+
logger.debug("Number of recent test runs: %s", len(recent_test_runs))
381+
rows = [
382+
generate_trends_row(baseline_test_run, response_time_aggregate=response_time_aggregate)
383+
]
384+
for run in recent_test_runs:
385+
rows.append(
386+
generate_trends_row(run, response_time_aggregate=response_time_aggregate)
387+
)
388+
logger.debug("Retrieved test trends: %s", rows)
389+
return rows
390+
391+
265392
def add_test_app_component(
266393
cmd,
267394
load_test_resource,
@@ -403,6 +530,10 @@ def upload_test_file(
403530
response = upload_file_to_test(
404531
client, test_id, path, file_type=file_type, wait=not no_wait
405532
)
533+
if not no_wait and response is not None and response.get("validationStatus") == "VALIDATION_FAILURE":
534+
raise FileOperationError(
535+
f"File upload failed due to validation failure: {response.get('validationFailureDetails')}"
536+
)
406537
logger.debug("Upload test file response: %s", response)
407538
logger.info("Upload test file completed")
408539
return response.as_dict()

src/load/azext_load/data_plane/load_test/help.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@
3737
az load test create --test-id sample-test-id --load-test-resource sample-alt-resource --resource-group sample-rg --display-name "Sample Name" --autostop-error-rate 80.5 --autostop-time-window 120
3838
az load test create --test-id sample-test-id --load-test-resource sample-alt-resource --resource-group sample-rg --display-name "Sample Name" --autostop disable
3939
az load test create --test-id sample-test-id --load-test-resource sample-alt-resource --resource-group sample-rg --display-name "Sample Name" --autostop enable
40+
- name: Create a test with a multi-region load configuration using region names in the format accepted by Azure Resource Manager (ARM). Ensure the specified regions are supported by Azure Load Testing. Multi-region load tests are restricted to public endpoints only.
41+
text: |
42+
az load test create --test-id sample-test-id --load-test-resource sample-alt-resource --resource-group sample-rg --engine-instances 3 --regionwise-engines eastus=1 westus2=1 germanywestcentral=1 --test-plan sample-jmx.jmx
43+
- name: Create an advanced URL test with multiple HTTP requests using a JSON file.
44+
text: |
45+
az load test create --test-id sample-test-id --load-test-resource sample-alt-resource --resource-group sample-rg --test-plan ~/resources/sample-url-requests.json --test-type URL
4046
"""
4147

4248
helps[
@@ -82,6 +88,12 @@
8288
- name: Update the Key Vault reference identity to system assigned Managed Identity.
8389
text: |
8490
az load test update --load-test-resource sample-alt-resource --resource-group sample-rg --test-id sample-existing-test-id --keyvault-reference-id null
91+
- name: Update autostop criteria.
92+
text: |
93+
az load test update --load-test-resource sample-alt-resource --resource-group sample-rg --test-id sample-existing-test-id --autostop-error-rate 90 --autostop-time-window 180
94+
- name: Update multi-region load configuration.
95+
text: |
96+
az load test update --load-test-resource sample-alt-resource --resource-group sample-rg --test-id sample-existing-test-id --engine-instances 5 --regionwise-engines eastus=2 westus2=1 eastasia=2
8597
"""
8698

8799
helps[
@@ -95,6 +107,42 @@
95107
az load test delete --load-test-resource sample-alt-resource --resource-group sample-rg --test-id sample-existing-test-id
96108
"""
97109

110+
helps[
111+
"load test convert-to-jmx"
112+
] = """
113+
type: command
114+
short-summary: Convert a URL type test to JMX test.
115+
examples:
116+
- name: Convert to JMX test.
117+
text: |
118+
az load test convert-to-jmx --load-test-resource sample-alt-resource --resource-group sample-rg --test-id sample-existing-test-id
119+
"""
120+
121+
helps[
122+
"load test set-baseline"
123+
] = """
124+
type: command
125+
short-summary: Set a test run as the baseline for comparison with other runs in the test.
126+
examples:
127+
- name: Set baseline test run.
128+
text: |
129+
az load test set-baseline --load-test-resource sample-alt-resource --resource-group sample-rg --test-id sample-existing-test-id --test-run-id sample-associated-test-run-id
130+
"""
131+
132+
helps[
133+
"load test compare-to-baseline"
134+
] = """
135+
type: command
136+
short-summary: Compare the sampler statistics from recent test runs with those of the baseline test run.
137+
examples:
138+
- name: Compare recent test runs to baseline.
139+
text: |
140+
az load test compare-to-baseline --load-test-resource sample-alt-resource --resource-group sample-rg --test-id sample-existing-test-id -o table
141+
- name: Compare recent test runs to baseline with specific aggregation.
142+
text: |
143+
az load test compare-to-baseline --load-test-resource sample-alt-resource --resource-group sample-rg --test-id sample-existing-test-id --aggregation P95 -o table
144+
"""
145+
98146
helps[
99147
"load test download-files"
100148
] = """
@@ -234,4 +282,7 @@
234282
- name: Upload zipped artifacts to a test.
235283
text: |
236284
az load test file upload --test-id sample-test-id --load-test-resource sample-alt-resource --resource-group sample-rg --path ~/Resources/sample-zip.zip --file-type ZIPPED_ARTIFACTS
285+
- name: Upload URL requests JSON configuration file to a test which is of type URL.
286+
text: |
287+
az load test file upload --test-id sample-test-id --load-test-resource sample-alt-resource --resource-group sample-rg --path ~/Resources/sample-url-requests.json --file-type URL_TEST_CONFIG
237288
"""

0 commit comments

Comments
 (0)