Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Template module

The module supports the following template engines:

  • MiniJinja (which is a subset of Jinja2)
  • Mustache
  • Jinja2 (available only on Linux)

Minijinja is the default and preferred engine, Jinja2 and Mustache are provided for backward compatibility. The Minijinja and Mustache engines are native and fast, Jinja2 is very slow, requires a Python interpreter, but allows dynamic extensions.

The module takes the following arguments:

NameDescriptionPossible values
pathOutput file pathA path on the filesystem
template_pathSource template path (mutually exclusive with template_src)A path on the filesystem that points to a template to be rendered
template_srcInlined source template (mutually exclusive with template_path)A template in the form of a string (for example: {{%-top-}})
engineTemplate enginemustache, minijinja, jinja2 (default: minijinja)
dataJSON data used for templatingA valid JSON
show_contentControls output of diffs in the reporttrue/false (default: true)

CLI

The module provides a CLI to help debug the rendering and audit of templates.

$ /opt/rudder/bin/rudder-module-template --help
Usage: rudder-module-template [OPTIONS] --template <TEMPLATE> --data <DATA> --out <OUT>

Options:
  -e, --engine <ENGINE>      Template engine [default: minijinja] [possible values: mustache, minijinja, jinja2]
  -t, --template <TEMPLATE>  Template file
  -d, --data <DATA>          JSON data file
  -o, --out <OUT>            Output file
  -a, --audit                Audit mode
  -h, --help                 Print help
  -V, --version              Print version

Minijinja

MiniJinja is a subset of the Jinja2 templating language, implemented in pure Rust. For a complete reference on MiniJinja syntax, see the Jinja2 documentation and the MiniJinja compatibility.

Syntax

This section presents some simple cases that cover what can be done with MiniJinja templating.

Note: You can add comments in the template, that will not be rendered in the output file with:

{# ... #}.

Conditions

To display content based on conditions definition:

{% if classes.my_condition is defined  %}
   display this if defined
{% endif %}
{% if not classes.my_condition is defined %}
   display this if not defined
{% endif %}

Note: You cannot use condition expressions here.

You can also use other tests, for example other built-in ones:

{% if vars.variable_prefix.my_number is odd  %}
   display if my_number is odd
{% endif %}

Scalar variables

Here is how to display a scalar variable value (integer, string, ...), if you have defined variable_string("variable_prefix", "my_variable", "my_value"):

{{ vars.variable_prefix.my_variable }}

You can also modify what is displayed by using filters.

{{ vars.variable_prefix.my_variable | uppercase }}

Will display the variable in uppercase.

Iteration

To iterate over a list, for example defined with:

variable_iterator("variable_prefix", "iterator_name", "a,b,c", ",")

Use the following file:

{% for item in vars.variable_prefix.iterator_name %}
{{ item }} is the current iterator_name value
{% endfor %}

Which will be expanded as:

a is the current iterator_name value
b is the current iterator_name value
c is the current iterator_name value

To iterate over a container defined by the following json file, loaded with variable_dict_from_file("variable_prefix", "dict_name", "path"):

{
   "hosts": [
       "host1",
       "host2"
   ],
   "files": [
       {"name": "file1", "path": "/path1", "users": [ "user1", "user11" ] },
       {"name": "file2", "path": "/path2", "users": [ "user2" ] }
   ],
   "properties": {
       "prop1": "value1",
       "prop2": "value2"
   }
}

Use the following template:

{% for item in vars.variable_prefix.dict_name.hosts %}
{{ item }} is the current hosts value
{% endfor %}

# will display the name and path of the current file
{% for file in vars.variable_prefix.dict_name.files %}
{{ file.name }}: {{ file.path }}
{% endfor %}

# will display the users list of each file
{% for file in vars.variable_prefix.dict_name.files %}
{{ file.name }}: {{ file.users|join(' ') }}
{% endfor %}


# will display the current properties key/value pair
{% for key, value in vars.variable_prefix.dict_name.properties.items() %}
{{ key }} -> {{ value }}
{% endfor %}

Which will be expanded as:

host1 is the current hosts value
host2 is the current hosts value

# will display the name and path of the current file
file1: /path1
file2: /path2

# will display the users list of each file
file1: user1 user11
file2: user2

# will display the current properties key/value pair
prop1 -> value1
prop2 -> value2

Filters

NameDescriptionRequired ArgsOptional ArgsExample
absReturns the absolute value of a number.NoneNone|a - b| = {{ (a - b)|abs }} -> |2 - 4| = 2
attrLooks up an attribute.keyNone{{ value['key'] == value|attr('key') }} -> true
batchBatch items.countfill_with{% for row in items|batch(3, '&nbsp;') %}
boolConverts the value into a boolean value.NoneNone{{ 42|bool }} -> true
capitalizeConvert the string with all its characters lowercased apart from the first char which is uppercased.NoneNone{{ chapter.title|capitalize }}
defaultIf the value is undefined it will return the passed default value, otherwise the value of the variable:Noneother{{ my_variable|default("my_variable was not defined") }}
dictsortDict sorting functionality.Noneby, case_sensitive, reverse{{ dict|sort('reverse') }}
escapeEscapes a string. By default to HTML.NoneNone{{ value|escape }}
firstReturns the first item from an iterable.NoneNone{{ user.email_addresses|first|default('no user') }}
floatConverts a value into a float.NoneNone{{ "42.5"|float == 42.5 }} -> true
groupbyGroup a sequence of objects by an attribute.Noneattribute, default, case_sensitive{% for city, items in users|groupby("city") %}
indentIndents Value with spaceswidthindent_first_line, indent_blank_lines{{ global_config|indent(2) }}
intConverts a value into an integer.NoneNone{{ "42"|int == 42 }} -> true
itemsReturns an iterable of pairs (items) from a mapping.NoneNone{% for key, value in my_dict|items %}
joinJoins a sequence by a characterNonejoiner{{ "Foo Bar Baz" | join(", ") }} -> foo, bar, baz
lastReturns the last item from an iterable.NoneNone{% with update = updates|last %}
lengthReturns the “length” of the valueNoneNone {{ results|length }}
linesSplits a string into lines.NoneNone{{ "foo\nbar\nbaz"|lines }} -> ["foo", "bar", "baz"]
listConverts the input value into a list.NoneNone{{ value|list }}
lowerConverts a value to lowercase.NoneNone{{ chapter.title|lower }}
mapApplies a filter to a sequence of objects or looks up an attribute.attributeNone{{ users|map(attribute='username')|join(', ') }}
maxReturns the largest item from an iterable.NoneNone{{ [1, 2, 3, 4]|max }} -> 4
minReturns the smallest item from an iterable.NoneNone{{ [1, 2, 3, 4]|min }} -> 1
pprintPretty print a variable.NoneNone{{ value|pprint }}
rejectCreates a new sequence of values that don’t pass a test.test_nameNone[1, 2, 3, 4]|reject("odd")
rejectattrCreates a new sequence of values of which an attribute does not pass a test.attrtest_name{{ users|rejectattr("id", "even") }}
replaceDoes a string replace.from, toNone{{ "Hello World"|replace("Hello", "Goodbye") }} -> Goodbye World
reverseReverses an iterable or stringNoneNone{% for user in users|reverse %}
roundRound the number to a given precision.Noneprecision{{ 42.55|round }} -> 43.0
safeMarks a value as safe. This converts it into a string.NoneNone{{ value|safe }}
selectCreates a new sequence of values that pass a test.Nonetest_name{{ [1, 2, 3, 4]|select("odd") }} -> [1, 3]
selectattrCreates a new sequence of values of which an attribute passes a test.attrtest_name{{ users|selectattr("id", "even") }} -> returns all users with an even id
sliceSlice an iterable and return a list of lists containing those items.countfill_with{% for column in items|slice(3) %}
sortReturns the sorted version of the given list.Nonetest_name{{ [1, 3, 2, 4]|sort }} -> [4, 3, 2, 1]
splitSplit a string into its substrings, using split as the separator string.Nonesplit, maxsplit{{ "hello world"|split|list }} -> ["hello", "world"]
stringConverts a value into a string if it’s not one already.NoneNone{{ value|string }}
sumSums up all the values in a sequence.NoneNone{{ range(10)|sum }} -> 45
titleConverts a value to title case.NoneNone{{ chapter.title|title }}
tojsonDumps a value to JSON.Noneident, ident{{ global_config|tojson }}
trimTrims a valueNonechars{{ value|trim(' ') }}
uniqueReturns a list of unique items from the given iterable.Noneattribute, case_sensitive{{ ['foo', 'bar', 'foobar', 'foobar']|unique|list }} -> ['foo', 'bar', 'foobar']
upperConverts a value to uppercase.NoneNone{{ chapter.title|upper }}
urlencodeURL encodes a value.NoneNone<a href="/search?{{ {"q": "my search", "lang": "fr"}|urlencode }}">Search</a>
dateformatFormats a timestamp as date.Noneformat, tz{{ value|dateformat(format="short", tz="Europe/Vienna") }}
datetimeformatFormats a timestamp as date and time.Noneformat, tz{{ value|datetimeformat(format="short", tz="Europe/Vienna") }}
filesizeformatFormats the value like a “human-readable” file size.Nonebinary{{ value|filesizeformat }}
pluralizeReturns a plural suffix if the value is not 1, ‘1’, or an object of length 1.Nonesingular, plural{{ entities|pluralize("y", "ies") }}
random :warning: Usage in a Rudder template will cause reparation at each run of the agent.Chooses a random element from a sequence or string.NoneNone{{ [1, 2, 3, 4]|random }}
timeformatFormats a timestamp as time.Noneformat, tz{{ value|timeformat(format="short") }}
truncateReturns a truncated copy of the string.Nonelength, killwords, end, leeway{{ "Hello World"|truncate(length=5) }}
wordcountCounts the words in a string.NoneNone{{ "Hello world!"|wordcount }}
wordwrapWrap a string to the given width.Nonewidth, break_long_words, break_on_hyphens, wrapstring{{ value|wordwrap(width=12) }}
b64encodeEncode a string as Base64.NoneNone{{ plain|b64encode }}
b64decodeDecode a Base64 string.NoneNone{{ encoded|b64decode }}
basenameGet a path’s base name.NoneNone{{ path|basename }}
dirnameGet a path’s directory name.NoneNone{{ path|dirname }}
urldecodeDecode percent-encoded sequences.NoneNone{{ encoded_url|urldecode }}
hashHash of input data (SHA-1, SHA-256, SHA-512, Uses SHA-1 by default).Nonealgorithm (sha-1, sha-256, sha-512){{ value|hash('sha-256') }}
quoteShell quoting.NoneNone{{ value|quote }}
regex_escapeEscape regex chars.NoneNone{{ re|regex_escape }}
regex_replaceReplace a string via regex.Nonecount{{ re|regex_replace }}

The following methods on basic types are provided:

  • dict.get
  • dict.items
  • dict.keys
  • dict.values
  • list.count
  • str.capitalize
  • str.count
  • str.endswith
  • str.find
  • str.isalnum
  • str.isalpha
  • str.isascii
  • str.isdigit
  • str.islower
  • str.isnumeric
  • str.isupper
  • str.join
  • str.lower
  • str.lstrip
  • str.replace
  • str.rfind
  • str.rstrip
  • str.split
  • str.splitlines
  • str.startswith
  • str.strip
  • str.title

Jinja2

The Jinja2 engine is provided for backward compatibility and is only available on Linux systems.

Jinja2 behaves for the most part the same way as MiniJinja. For a complete reference of features, see the official Jinja2 documentation.

You can extend the Jinja2 templating engine by adding custom FILTERS and TESTS in the script /var/rudder/configuration-repository/ncf/10_ncf_internals/modules/extensions/jinja2_custom.py

For instance, to add a filter to uppercase a string and a test if a number is odd, you can create the file /var/rudder/configuration-repository/ncf/10_ncf_internals/modules/extensions/jinja2_custom.py on your Rudder server with the following content:

def uppercase(input):
    return input.upper()

def odd(value):
    return True if (value % 2) else False

FILTERS = {'uppercase': uppercase}
TESTS = {'odd': odd}

These filters and tests will be usable in your jinja2 templates automatically.

Mustache

Mustache is a logic-less templating language. For a complete reference of features, see the official mustache specification.

Syntax

This section presents some simple cases that cover what can be done with Mustache templating.

The main specificity compared to standard mustache syntax of prefixes in all expanded values:

  • classes to access conditions
  • vars to access all variables

Classes

Here is how to display content depending on conditions definition:

{{#classes.my_condition}}
   content when my_condition is defined
{{/classes.my_condition}}

{{^classes.my_condition}}
   content when my_condition is *not* defined
{{/classes.my_condition}}

Note: You cannot use condition expressions here.

Scalar variable

Here is how to display a scalar variable value (integer, string, ...), if you have defined variable_string("variable_prefix", "my_variable", "my_value"):

{{{vars.variable_prefix.my_variable}}}

We use the triple {{{ }}} to avoid escaping html entities.

Use {{#vars.container}} content {{/vars.container}} to iterate Use {{{.}}} for the current element value in iteration Use {{{key}}} for the key value in current element Use {{{@}}} for the current element key in iteration

To iterate over a list, for example defined with:

variable_iterator("variable_prefix", "iterator_name", "a,b,c", ",")

Use the following file:

{{#vars.variable_prefix.iterator_name}}
{{{.}}} is the current iterator_name value
{{/vars.variable_prefix.iterator_name}}

Which will be expanded as:

a is the current iterator_name value
b is the current iterator_name value
c is the current iterator_name value

To iterate over a container defined by the following json file, loaded with variable_dict_from_file("variable_prefix", "dict_name", "path"):

{
   "hosts": [
       "host1",
       "host2"
   ],
   "files": [
       {"name": "file1", "path": "/path1", "users": [ "user1", "user11" ] },
       {"name": "file2", "path": "/path2", "users": [ "user2" ] }
   ],
   "properties": {
       "prop1": "value1",
       "prop2": "value2"
   }
}

Use the following template:

{{#vars.variable_prefix.dict_name.hosts}}
{{{.}}} is the current hosts value
{{/vars.variable_prefix.dict_name.hosts}}

# will display the name and path of the current file
{{#vars.variable_prefix.dict_name.files}}
{{{name}}}: {{{path}}}
{{/vars.variable_prefix.dict_name.files}}
# Lines below will only be properly rendered in unix Nodes
# will display the users list of each file
{{#vars.variable_prefix.dict_name.files}}
{{{name}}}:{{#users}} {{{.}}}{{/users}}
{{/vars.variable_prefix.dict_name.files}}


# will display the current properties key/value pair
{{#vars.variable_prefix.dict_name.properties}}
{{{@}}} -> {{{.}}}
{{/vars.variable_prefix.dict_name.properties}}

Which will be expanded as:

host1 is the current hosts value
host2 is the current hosts value

# will display the name and path of the current file
file1: /path1
file2: /path2

# Lines below will only be properly rendered in unix Nodes
# will display the users list of each file
file1: user1 user11
file2: user2

# will display the current properties key/value pair
prop1 -> value1
prop2 -> value2

Note: You can use {{#-top-}} ... {{/-top-}} to iterate over the top level container.

System variables

Some sys dict variables (like sys.ipv4) are also accessible as string, for example:

${sys.ipv4} gives 54.32.12.4
$[sys.ipv4[ethO]} gives 54.32.12.4
$[sys.ipv4[eth1]} gives 10.45.3.2

These variables are not accessible as dict in the templating data, but are represented as string:

ipv4 is a string variable in the sys dict with value 54.32.12.4
ipv4[ethO] is a string variable in the sys dict with value 54.32.12.4
ipv4 is not accessible as a dict in the template

To access these value, use the following syntax in your mustache templates:

{{{vars.sys.ipv4[eth0]}}}

Top level container

The top level container can be iterated over:

{{#-top-}} ... {{/-top-}}

It can also be rendered as a compact JSON representation

{{$-top-}}

Or rendered as a multi-line JSON representation

{{%-top-}}