Mustache templating

Table of Contents

CFEngine specific extensions

CFEngine has several extensions to the mustache standard.

  • -top- special key representing the complete data given.
  • % variable prefix causing data to be rendered as multi-line json representation.
  • $ variable prefix causing data to be rendered as compact json representation.
  • @ expands the current key being iterated.

See also: template_method mustache extensions

How can I pass a data variable to template_data?

Just use template_data => @(mycontainer).

If you need to extract a portion of the container or merge it with another, use template_data => mergedata("mycontainer[piece]", "othercontainer").

Can I render a Mustache template into a string?

Yes, see string_mustache().

How do I render a section only if a given class is defined?

In this Mustache example the word 'Enterprise' will only be rendered if the class 'enterprise' is defined.

This template should not be passed a data container; it uses the datastate() of the CFEngine system. That's where classes.enterprise and vars.sys.cf_version came from.

Version: CFEngine {{#classes.enterprise}}Enterprise{{/classes.enterprise}} {{vars.sys.cf_version}}

How do I render a section only if a given class is not defined?

In the mustache documentation this is referred to as an inverted section.

In this mustache example the word Enterprise will only be rendered if the class cfengine_enterprise is defined and the word Community will only be rendered if the class cfengine_enterprise is not defined.

This template should not be passed a data container; it uses the datastate() of the CFEngine system. That's where classes.cfengine_enterprise and vars.sys.cf_version came from.

Version: CFEngine {{#classes.cfengine_enterprise}}Enterprise{{/classes.cfengine_enterprise}}{{^classes.cfengine_enterprise}}Community{{/classes.cfengine_enterprise}} {{vars.sys.cf_version}}

How do I use class expressions?

Mustache does not understand CFEngine's class expression logic and it is not possible to use full class expressions in mustache templates. Instead, use class expressions inside cfengine policy to define a singular class which can be used to conditionally render a block.

bundle agent main
{
  classes:

      "known_day_of_week"
        expression => "(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)";

  vars:

      "rendered"
        string => string_mustache(
"{{#classes.known_day_of_week}}I recognize the day of the week.{{/classes.known_day_of_week}}
{{^classes.class_you_are_looking_for}}
The class you are looking for is not defined.
{{/classes.class_you_are_looking_for}}",
                                  datastate());
  reports:
      "$(rendered)";

}

Here we define the class known_day_of_week as long as there is a class representing a known day. Then we render the value of the string variable "rendered" using string_mustache() with a template that includes a section that is conditional when classes.known_day_of_week is true and another section when classes.class_you_are_looking_for is not defined based on the data provided from datastate() which is the default set of data to use for mustache templates when explicit data is not provided. Finally we report the variable to see the rendered template.


R: I recognize the day of the week.
The class you are looking for is not defined.

We can see in the output that the conditional text was rendered as expected. Try adjusting the template or the class expression.

This policy can be found in /var/cfengine/share/doc/examples/mustache_classes.cf and downloaded directly from github.

How do I iterate over a list?

This template should not be passed a data container; it uses the datastate() of the CFEngine system. That's where vars.mon.listening_tcp4_ports came from.

{{#vars.mon.listening_tcp4_ports}}
  * {{.}}
{{/vars.mon.listening_tcp4_ports}}

How can I access keys when iterating over a dict?

In CFEngine, the @ symbol expands to the current key when iterating over a dict.

bundle agent main
{
  reports:
      "$(with)"
        with => string_mustache("datastate() provides {{#-top-}} {{{@}}}{{/-top-}}", datastate() );
}
R: datastate() provides  classes vars

This policy can be found in /var/cfengine/share/doc/examples/mustache_extension_expand_key.cf and downloaded directly from github.

Can you use nested classes?

You can. This is handy when options slightly differ for different operating systems. In this example for ssh daemon the authorized key configuration will only be added if class SSH_LDAP_PUBKEY_BUNDLE is true and for the class debian/centos diffenrent keywords are added.

{{#classes.SSH_LDAP_PUBKEY_BUNDLE}}
    {{#classes.debian}}
AuthorizedKeysCommand {{vars.sara_data.ssh.authorized_keys_command}}
AuthorizedKeysCommandUser {{vars.sara_data.ssh.authorized_keys_commanduser}}
    {{/classes.debian}}
    {{#classes.centos}}
AuthorizedKeysCommand {{vars.sara_data.ssh.authorized_keys_command}}
AuthorizedKeysCommandRunAs {{vars.sara_data.ssh.authorized_keys_commanduser}}
    {{/classes.centos}}
{{/classes.SSH_LDAP_PUBKEY_BUNDLE}}