diff --git a/tools/internal_ci/linux/grpc_e2e_performance_gke.sh b/tools/internal_ci/linux/grpc_e2e_performance_gke.sh
index 693fa4077b7..30bb95ecaa2 100755
--- a/tools/internal_ci/linux/grpc_e2e_performance_gke.sh
+++ b/tools/internal_ci/linux/grpc_e2e_performance_gke.sh
@@ -15,7 +15,7 @@
set -ex
# Enter the gRPC repo root.
-cd $(dirname $0)/../../..
+cd "$(dirname "$0")/../../.."
source tools/internal_ci/helper_scripts/prepare_build_linux_rc
@@ -72,8 +72,8 @@ popd
# Build test configurations.
buildConfigs() {
- local pool="$1"
- local table="$2"
+ local -r pool="$1"
+ local -r table="$2"
shift 2
tools/run_tests/performance/loadtest_config.py "$@" \
-t ./tools/run_tests/performance/templates/loadtest_template_prebuilt_all_languages.yaml \
diff --git a/tools/internal_ci/linux/grpc_e2e_performance_v2.sh b/tools/internal_ci/linux/grpc_e2e_performance_v2.sh
index a520a1d4a64..f891f78aa7e 100755
--- a/tools/internal_ci/linux/grpc_e2e_performance_v2.sh
+++ b/tools/internal_ci/linux/grpc_e2e_performance_v2.sh
@@ -15,7 +15,7 @@
set -ex
# Enter the gRPC repo root.
-cd $(dirname $0)/../../..
+cd "$(dirname "$0")/../../.."
source tools/internal_ci/helper_scripts/prepare_build_linux_rc
@@ -73,8 +73,8 @@ popd
# Build test configurations.
buildConfigs() {
- local pool="$1"
- local table="$2"
+ local -r pool="$1"
+ local -r table="$2"
shift 2
tools/run_tests/performance/loadtest_config.py "$@" \
-t ./tools/run_tests/performance/templates/loadtest_template_prebuilt_all_languages.yaml \
diff --git a/tools/run_tests/performance/README.md b/tools/run_tests/performance/README.md
index e1e3c8bd988..b87b9f53724 100644
--- a/tools/run_tests/performance/README.md
+++ b/tools/run_tests/performance/README.md
@@ -163,7 +163,7 @@ repository, [grpc/test-infra](https://github.com/grpc/test-infra).
### Generating scenarios
-The benchmarks framework uses the same test scenarios as the legacy one. These
+The benchmarks framework uses the same test scenarios as the legacy one. The
script [scenario_config_exporter.py](./scenario_config_exporter.py) can be used
to export these scenarios to files, and also to count and analyze existing
scenarios.
@@ -231,6 +231,9 @@ run by applying the test to a cluster running the LoadTest controller with
$ kubectl apply -f loadtest_config.yaml
```
+> Note: The most common way of running tests generated by this script is to use
+> a _test runner_. For details, see [running tests](#running-tests).
+
A basic template for generating tests in various languages can be found here:
[loadtest_template_basic_all_languages.yaml](./templates/loadtest_template_basic_all_languages.yaml).
The following example generates configurations for C# and Java tests using this
@@ -254,7 +257,7 @@ The script `loadtest_config.py` takes the following options:
- `-t`, `--template`
Template file. A template is a configuration file that
may contain multiple client and server configuration, and may also include
substitution keys.
-- `p`, `--prefix`
Test names consist of a prefix_joined with a uuid with a
+- `-p`, `--prefix`
Test names consist of a prefix_joined with a uuid with a
dash. Test names are stored in `metadata.name`. The prefix is also added as
the `prefix` label in `metadata.labels`. The prefix defaults to the user name
if not set.
@@ -281,7 +284,7 @@ The script `loadtest_config.py` takes the following options:
- `--allow_server_language`
Allows cross-language scenarios where the server
is of a specified language, different from the scenario language. This is
typically `node` or `c++`. This flag may be repeated.
-- `--instances_per_client`
This option generates multiple instances of the
+- `--instances_per_client`
This option generates multiple instances of the
clients for each test. The instances are named with the name of the client
combined with an index (or only an index, if no name is specified). If the
template specifies more than one client for a given language, it must also
@@ -333,6 +336,19 @@ script can be invoked as follows:
$ loadtest_concat_yaml.py -i infile1.yaml infile2.yaml -o outfile.yaml
```
+### Generating load test examples
+
+The script [loadtest_examples.sh](./loadtest_examples.sh) is provided to
+generate example load test configurations in all supported languages. This
+script takes only one argument, which is the output directory where the
+configurations will be created. The script produces a set of basic
+configurations, as well as a set of template configurations intended to be used
+with prebuilt images.
+
+The [examples](https://github.com/grpc/test-infra/tree/master/config/samples)
+in the repository [grpc/test-infra](https://github.com/grpc/test-infra) are
+generated by this script.
+
### Generating configuration templates
The script [loadtest_template.py](./loadtest_template.py) generates a load test
@@ -349,7 +365,7 @@ was generated from the example configurations in
```
$ ./tools/run_tests/performance/loadtest_template.py \
-i ../test-infra/config/samples/*_example_loadtest.yaml \
- --inject_client_pool --inject_driver_pool --inject_server_pool \
+ --inject_client_pool --inject_server_pool \
--inject_big_query_table --inject_timeout_seconds \
-o ./tools/run_tests/performance/templates/loadtest_template_basic_all_languages.yaml \
--name basic_all_languages
@@ -361,7 +377,7 @@ was generated by the following command:
```
$ ./tools/run_tests/performance/loadtest_template.py \
- -i ../test-infra/config/samples/*_example_loadtest_with_pre_built_workers.yaml \
+ -i ../test-infra/config/samples/templates/*_example_loadtest_with_prebuilt_workers.yaml \
--inject_client_pool --inject_driver_image --inject_driver_pool \
--inject_server_pool --inject_big_query_table --inject_timeout_seconds \
-o ./tools/run_tests/performance/templates/loadtest_template_prebuilt_all_languages.yaml \
@@ -405,3 +421,20 @@ Annotations, on the other hand, are passed on to the test configurations, and
may be set to values or to substitution keys in themselves, allowing future
automation scripts to process the tests generated from these configurations in
different ways.
+
+### Running tests
+
+Collections of tests generated by `loadtest_config.py` are intended to be run
+with a test runner. The code for the test runner is stored in a separate
+repository, [grpc/test-infra](https://github.com/grpc/test-infra).
+
+The test runner applies the tests to the cluster, and monitors the tests for
+completion while they are running. The test runner can also be set up to run
+collections of tests in parallel on separate node pools, and to limit the number
+of tests running in parallel on each pool.
+
+The test runner is used in the continuous integration setup defined in
+[grpc_e2e_performance_gke.sh] and [grpc_e2e_performance_v2.sh].
+
+[grpc_e2e_performance_gke.sh]: ../../internal_ci/linux/grpc_e2e_performance_gke.sh
+[grpc_e2e_performance_v2.sh]: ../../internal_ci/linux/grpc_e2e_performance_v2.sh
diff --git a/tools/run_tests/performance/loadtest_config.py b/tools/run_tests/performance/loadtest_config.py
index 13ae6f5980b..330649b3d12 100755
--- a/tools/run_tests/performance/loadtest_config.py
+++ b/tools/run_tests/performance/loadtest_config.py
@@ -46,11 +46,9 @@ CONFIGURATION_FILE_HEADER_COMMENT = """
"""
-def label_language(language: str) -> str:
- """Convert scenario language to place in a resource label."""
- return {
- 'c++': 'cxx',
- }.get(language, language)
+def safe_name(language: str) -> str:
+ """Returns a name that is safe to use in labels and file names."""
+ return scenario_config.LANGUAGES[language].safename
def default_prefix() -> str:
@@ -136,10 +134,8 @@ def gen_loadtest_configs(
"""
validate_annotations(annotations)
prefix = loadtest_name_prefix or default_prefix()
- cl = label_language(language_config.client_language or
- language_config.language)
- sl = label_language(language_config.server_language or
- language_config.language)
+ cl = safe_name(language_config.client_language or language_config.language)
+ sl = safe_name(language_config.server_language or language_config.language)
scenario_filter = scenario_config_exporter.scenario_filter(
scenario_name_regex=scenario_name_regex,
category=language_config.category,
@@ -162,8 +158,7 @@ def gen_loadtest_configs(
metadata['name'] = name
if 'labels' not in metadata:
metadata['labels'] = dict()
- metadata['labels']['language'] = label_language(
- language_config.language)
+ metadata['labels']['language'] = safe_name(language_config.language)
metadata['labels']['prefix'] = prefix
if 'annotations' not in metadata:
metadata['annotations'] = dict()
@@ -194,7 +189,7 @@ def gen_loadtest_configs(
'unique names, name counts for language %s: %s') %
(cl, c.most_common()))
- # Name client instances with an index starting from 0.
+ # Name client instances with an index starting from zero.
client_instances = []
for i in range(instances_per_client):
client_instances.extend(copy.deepcopy(clients))
@@ -222,12 +217,14 @@ def gen_loadtest_configs(
# Set servers to named instances.
spec['servers'] = servers
- # Name driver with an index for consistency with workers.
- if 'driver' not in spec:
- spec['driver'] = dict()
- driver = spec['driver']
- driver['language'] = 'cxx'
- driver['name'] = component_name((driver.get('name', ''), str(i)))
+ # Name the driver with an index for consistency with workers.
+ # There is only one driver, so the index is zero.
+ if 'driver' in spec and 'run' in spec['driver']:
+ driver = spec['driver']
+ if 'language' not in driver:
+ driver['language'] = safe_name('c++')
+ if 'name' not in driver or not driver['name']:
+ driver['name'] = '0'
spec['scenariosJSON'] = scenario_str
@@ -284,15 +281,6 @@ def config_dumper(header_comment: str) -> Type[yaml.SafeDumper]:
self.write_indent()
self.write_indicator(header_comment, need_whitespace=False)
- def expect_block_sequence(self):
- super().expect_block_sequence()
- self.increase_indent()
-
- def expect_block_sequence_item(self, first=False):
- if isinstance(self.event, yaml.SequenceEndEvent):
- self.indent = self.indents.pop()
- super().expect_block_sequence_item(first)
-
def str_presenter(dumper, data):
if '\n' in data:
return dumper.represent_scalar('tag:yaml.org,2002:str',
diff --git a/tools/run_tests/performance/loadtest_examples.sh b/tools/run_tests/performance/loadtest_examples.sh
new file mode 100755
index 00000000000..55efcadda4b
--- /dev/null
+++ b/tools/run_tests/performance/loadtest_examples.sh
@@ -0,0 +1,120 @@
+#!/bin/bash
+# Copyright 2021 The gRPC Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This script generates a set of load test examples from templates.
+
+LOADTEST_CONFIG=tools/run_tests/performance/loadtest_config.py
+
+if (( $# < 1 )); then
+ echo "Usage: ${0}