Skip to content
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

Inject build_flavor in track templates #1750

Merged
merged 4 commits into from
Jul 26, 2023
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
4 changes: 4 additions & 0 deletions docs/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Rally uses `Jinja2 <http://jinja.pocoo.org/docs/dev/>`_ as a template language s

Elasticsearch utilizes Mustache formatting in a few places, notably in `search templates <https://www.elastic.co/guide/en/elasticsearch/reference/7.4/search-template.html>`_ and `Watcher templates <https://www.elastic.co/guide/en/elasticsearch/reference/7.4/actions-email.html>`_. If you are using Mustache in your Rally tracks you must `escape them properly <https://jinja.palletsprojects.com/en/2.10.x/templates/#escaping>`_. See :ref:`put_pipeline` for an example.

.. _advanced_extensions:

Extensions
""""""""""

Expand Down Expand Up @@ -85,6 +87,8 @@ Assuming we pass ``--track-params="es_snapshot_restore_recovery_max_bytes_per_se

The parameter ``default_value`` controls the value to use for the setting if it is undefined. If the setting is undefined and there is no default value, nothing will be added.

* ``build_flavor``: a global variable that holds build flavor reported by Elasticsearch cluster targetted by Rally.

.. _adding_tracks_custom_param_sources:

Controlling Operation Parameters Using Custom Parameter Sources
Expand Down
14 changes: 14 additions & 0 deletions docs/migrate.rst
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
Migration Guide
===============

Migrating to Rally 2.9.0
------------------------

``build_flavor`` becomes a global variable in track templates
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Rally now treats ``build_flavor`` as a :ref:`global variable<advanced_extensions>` in Jinja2 templates. The value of the variable depends on the build flavor reported by Elasticsearch cluster targetted by Rally.
The ``build_flavor`` becomes a reserved name so user-supplied track parameters must be named differently. Rename your track parameters before upgrade if needed.
ebadyano marked this conversation as resolved.
Show resolved Hide resolved

Example error when running with ``--track-params="build_flavor:test"``::

Some of your track parameter(s) "build_flavor" are defined by Rally and cannot be modified.

Migrating to Rally 2.7.1
------------------------

Elasticsearch client logs are now captured by the `elastic_transport <https://github.com/elastic/elastic-transport-python/>`_ logger
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Rally migrated to the 8.x version of the ``elasticsearch-py`` library which uses a new logger named ``elastic_transport``. Rally will automatically configure this logger to only emit logs of level ``WARNING`` and above, even if a past Rally version configured logging using the ``~./rally/logging.json`` file without that logger.

Snapshot repository plugins are no longer built from source
Expand Down
38 changes: 31 additions & 7 deletions esrally/track/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -732,13 +732,13 @@ def read_glob_files(self, pattern):
return ",\n".join(source)


def default_internal_template_vars(glob_helper=lambda f: [], clock=time.Clock):
def default_internal_template_vars(glob_helper=lambda f: [], clock=time.Clock, build_flavor=None):
"""
Dict of internal global variables used by our jinja2 renderers
"""

return {
"globals": {
"build_flavor": build_flavor,
"now": clock.now(),
"glob": glob_helper,
},
Expand Down Expand Up @@ -806,7 +806,7 @@ def register_all_params_in_track(assembled_source, complete_track_params=None):
complete_track_params.populate_track_defined_params(j2_variables)


def render_template_from_file(template_file_name, template_vars, complete_track_params=None):
def render_template_from_file(template_file_name, template_vars, complete_track_params=None, build_flavor=None):
def relative_glob(start, f):
result = glob.glob(os.path.join(start, f))
if result:
Expand All @@ -823,7 +823,7 @@ def relative_glob(start, f):
loader=jinja2.FileSystemLoader(base_path),
template_source=template_source.assembled_source,
template_vars=template_vars,
template_internal_vars=default_internal_template_vars(glob_helper=lambda f: relative_glob(base_path, f)),
template_internal_vars=default_internal_template_vars(glob_helper=lambda f: relative_glob(base_path, f), build_flavor=build_flavor),
)


Expand Down Expand Up @@ -971,6 +971,11 @@ def __init__(self, user_specified_track_params=None):
self.track_defined_params = set()
self.user_specified_track_params = user_specified_track_params if user_specified_track_params else {}

def internal_user_defined_track_params(self):
set_user_params = set(list(self.user_specified_track_params.keys()))
set_internal_params = set(default_internal_template_vars()["globals"].keys())
return list(set_user_params & set_internal_params)

def populate_track_defined_params(self, list_of_track_params=None):
self.track_defined_params.update(set(list_of_track_params))

Expand All @@ -996,12 +1001,14 @@ def __init__(self, cfg):
track_schema_file = os.path.join(cfg.opts("node", "rally.root"), "resources", "track-schema.json")
with open(track_schema_file, encoding="utf-8") as f:
self.track_schema = json.loads(f.read())
self.build_flavor = cfg.opts("mechanic", "distribution.flavor", default_value="default", mandatory=False)
self.track_params = cfg.opts("track", "params", mandatory=False)
self.complete_track_params = CompleteTrackParams(user_specified_track_params=self.track_params)
self.read_track = TrackSpecificationReader(
track_params=self.track_params,
complete_track_params=self.complete_track_params,
selected_challenge=cfg.opts("track", "challenge.name", mandatory=False),
build_flavor=self.build_flavor,
)
self.logger = logging.getLogger(__name__)

Expand All @@ -1020,7 +1027,9 @@ def read(self, track_name, track_spec_file, mapping_dir):
# involving lines numbers and it also does not bloat Rally's log file so much.
with tempfile.NamedTemporaryFile(delete=False, suffix=".json") as tmp:
try:
rendered = render_template_from_file(track_spec_file, self.track_params, complete_track_params=self.complete_track_params)
rendered = render_template_from_file(
track_spec_file, self.track_params, complete_track_params=self.complete_track_params, build_flavor=self.build_flavor
)
with open(tmp.name, "w", encoding="utf-8") as f:
f.write(rendered)
self.logger.info("Final rendered track for '%s' has been written to '%s'.", track_spec_file, tmp.name)
Expand Down Expand Up @@ -1076,6 +1085,16 @@ def read(self, track_name, track_spec_file, mapping_dir):

current_track = self.read_track(track_name, track_spec, mapping_dir, track_spec_file)

internal_user_defined_track_params = self.complete_track_params.internal_user_defined_track_params()
if len(internal_user_defined_track_params) > 0:
params_list = ",".join(opts.double_quoted_list_of(sorted(internal_user_defined_track_params)))
err_msg = f"Some of your track parameter(s) {params_list} are defined by Rally and cannot be modified.\n"

self.logger.critical(err_msg)
gbanasiak marked this conversation as resolved.
Show resolved Hide resolved
# also dump the message on the console
console.println(err_msg)
raise exceptions.TrackConfigError(f"Reserved track parameters {sorted(internal_user_defined_track_params)}.")

unused_user_defined_track_params = self.complete_track_params.unused_user_defined_track_params()
if len(unused_user_defined_track_params) > 0:
err_msg = (
Expand Down Expand Up @@ -1161,9 +1180,10 @@ class TrackSpecificationReader:
Creates a track instances based on its parsed JSON description.
"""

def __init__(self, track_params=None, complete_track_params=None, selected_challenge=None, source=io.FileSource):
def __init__(self, track_params=None, complete_track_params=None, selected_challenge=None, source=io.FileSource, build_flavor=None):
self.name = None
self.base_path = None
self.build_flavor = build_flavor
self.track_params = track_params if track_params else {}
self.complete_track_params = complete_track_params
self.selected_challenge = selected_challenge
Expand Down Expand Up @@ -1291,7 +1311,11 @@ def _load_template(self, contents, description):
self.logger.info("Loading template [%s].", description)
register_all_params_in_track(contents, self.complete_track_params)
try:
rendered = render_template(template_source=contents, template_vars=self.track_params)
rendered = render_template(
template_source=contents,
template_vars=self.track_params,
template_internal_vars={"globals": {"build_flavor": self.build_flavor}},
)
return json.loads(rendered)
except Exception as e:
self.logger.exception("Could not load file template for %s.", description)
Expand Down
44 changes: 41 additions & 3 deletions tests/track/loader_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -955,13 +955,14 @@ def assert_equal_ignore_whitespace(expected, actual):


class TestTemplateRender:
unittest_template_internal_vars = loader.default_internal_template_vars(clock=StaticClock)
unittest_template_internal_vars = loader.default_internal_template_vars(clock=StaticClock, build_flavor="test")

def test_render_simple_template(self):
template = """
{
"key": {{'01-01-2000' | days_ago(now)}},
"key2": "static value"
"key2": "static value",
"key3": "{{build_flavor}}"
}
"""

Expand All @@ -970,7 +971,8 @@ def test_render_simple_template(self):
expected = """
{
"key": 5864,
"key2": "static value"
"key2": "static value",
"key3": "test"
}
"""
assert rendered == expected
Expand Down Expand Up @@ -1063,6 +1065,29 @@ def test_render_template_with_variables(self):
"""
assert_equal_ignore_whitespace(expected, rendered)

def test_render_template_with_conditions(self):
template = """
{
{%- if build_flavor != "test" %}
"key1": "build_flavor"
{%- elif lifecycle == "ilm" %}
"key1": "ilm"
{%- else %}
"key1": "else"
{%- endif %}
}
"""
rendered = loader.render_template(
template, template_vars={"lifecycle": "ilm"}, template_internal_vars=self.unittest_template_internal_vars
)

expected = """
{
"key1": "ilm"
}
"""
assert_equal_ignore_whitespace(expected, rendered)


class TestCompleteTrackParams:
assembled_source = textwrap.dedent(
Expand All @@ -1071,6 +1096,7 @@ class TestCompleteTrackParams:
"key2": {{ value2 | default(3) }},
"key3": {{ value3 | default("default_value3") }}
"key4": {{ value2 | default(3) }}
"key5": {{ build_flavor }}
"""
)

Expand All @@ -1086,6 +1112,13 @@ def test_check_complete_track_params_does_not_fail_with_no_track_params(self):

assert complete_track_params.sorted_track_defined_params == []

def test_internal_user_defined_track_params(self):
# track params that deliberatly collide with internal variables
track_params = {"now": "test", "build_flavor": "test"}
complete_track_params = loader.CompleteTrackParams(user_specified_track_params=track_params)

assert sorted(complete_track_params.internal_user_defined_track_params()) == ["build_flavor", "now"]

def test_unused_user_defined_track_params(self):
track_params = {"number_of_repliacs": 1, "enable_source": True, "number_of_shards": 5} # deliberate typo # unknown parameter

Expand Down Expand Up @@ -2960,7 +2993,11 @@ def test_parse_valid_track_specification_with_composable_template(self):
{
"template": {
"settings": {
{%- if build_flavor == "test" %}
"index.number_of_replicas": {{ number_of_replicas }}
{%- else %}
"index.number_of_replicas": 99
{%- endif %}
},
"mappings": {
"properties": {
Expand All @@ -2975,6 +3012,7 @@ def test_parse_valid_track_specification_with_composable_template(self):
],
}
),
build_flavor="test",
)
resulting_track = reader("unittest", track_specification, "/mappings")
assert ["index_pattern", "number_of_replicas", "number_of_shards"] == complete_track_params.sorted_track_defined_params
Expand Down