123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- .. _tutorial-states-part-3:
- =======================================================
- States tutorial, part 3 - Templating, Includes, Extends
- =======================================================
- .. note::
- This tutorial builds on topics covered in :ref:`part 1 <states-tutorial>` and
- :ref:`part 2 <tutorial-states-part-2>`. It is recommended that you begin there.
- This part of the tutorial will cover more advanced templating and
- configuration techniques for ``sls`` files.
- Templating SLS modules
- ======================
- SLS modules may require programming logic or inline execution. This is
- accomplished with module templating. The default module templating system used
- is `Jinja2`_ and may be configured by changing the :conf_master:`renderer`
- value in the master config.
- .. _`Jinja2`: https://jinja.palletsprojects.com/en/2.11.x/
- All states are passed through a templating system when they are initially read.
- To make use of the templating system, simply add some templating markup.
- An example of an sls module with templating markup may look like this:
- .. code-block:: jinja
- {% for usr in ['moe','larry','curly'] %}
- {{ usr }}:
- user.present
- {% endfor %}
- This templated sls file once generated will look like this:
- .. code-block:: yaml
- moe:
- user.present
- larry:
- user.present
- curly:
- user.present
- Here's a more complex example:
- .. code-blocK:: jinja
- # Comments in yaml start with a hash symbol.
- # Since jinja rendering occurs before yaml parsing, if you want to include jinja
- # in the comments you may need to escape them using 'jinja' comments to prevent
- # jinja from trying to render something which is not well-defined jinja.
- # e.g.
- # {# iterate over the Three Stooges using a {% for %}..{% endfor %} loop
- # with the iterator variable {{ usr }} becoming the state ID. #}
- {% for usr in 'moe','larry','curly' %}
- {{ usr }}:
- group:
- - present
- user:
- - present
- - gid_from_name: True
- - require:
- - group: {{ usr }}
- {% endfor %}
- Using Grains in SLS modules
- ===========================
- Often times a state will need to behave differently on different systems.
- :ref:`Salt grains <targeting-grains>` objects are made available in the template
- context. The `grains` can be used from within sls modules:
- .. code-block:: jinja
- apache:
- pkg.installed:
- {% if grains['os'] == 'RedHat' %}
- - name: httpd
- {% elif grains['os'] == 'Ubuntu' %}
- - name: apache2
- {% endif %}
- Using Environment Variables in SLS modules
- ==========================================
- You can use ``salt['environ.get']('VARNAME')`` to use an environment
- variable in a Salt state.
- .. code-block:: bash
- MYENVVAR="world" salt-call state.template test.sls
- .. code-block:: jinja
- Create a file with contents from an environment variable:
- file.managed:
- - name: /tmp/hello
- - contents: {{ salt['environ.get']('MYENVVAR') }}
- Error checking:
- .. code-block:: jinja
- {% set myenvvar = salt['environ.get']('MYENVVAR') %}
- {% if myenvvar %}
- Create a file with contents from an environment variable:
- file.managed:
- - name: /tmp/hello
- - contents: {{ salt['environ.get']('MYENVVAR') }}
- {% else %}
- Fail - no environment passed in:
- test.fail_without_changes
- {% endif %}
- Calling Salt modules from templates
- ===================================
- All of the Salt modules loaded by the minion are available within the
- templating system. This allows data to be gathered in real time on the target
- system. It also allows for shell commands to be run easily from within the sls
- modules.
- The Salt module functions are also made available in the template context as
- ``salt:``
- The following example illustrates calling the ``group_to_gid`` function in the
- ``file`` execution module with a single positional argument called
- ``some_group_that_exists``.
- .. code-block:: jinja
- moe:
- user.present:
- - gid: {{ salt['file.group_to_gid']('some_group_that_exists') }}
- One way to think about this might be that the ``gid`` key is being assigned
- a value equivalent to the following python pseudo-code:
- .. code-block:: python
- import salt.modules.file
- file.group_to_gid("some_group_that_exists")
- Note that for the above example to work, ``some_group_that_exists`` must exist
- before the state file is processed by the templating engine.
- Below is an example that uses the ``network.hw_addr`` function to retrieve the
- MAC address for eth0:
- .. code-block:: python
- salt["network.hw_addr"]("eth0")
- To examine the possible arguments to each execution module function,
- one can examine the `module reference documentation </ref/modules/all>`_:
- Advanced SLS module syntax
- ==========================
- Lastly, we will cover some incredibly useful techniques for more complex State
- trees.
- Include declaration
- -------------------
- A previous example showed how to spread a Salt tree across several files.
- Similarly, :ref:`requisites` span multiple files by
- using an :ref:`include-declaration`. For example:
- ``python/python-libs.sls:``
- .. code-block:: yaml
- python-dateutil:
- pkg.installed
- ``python/django.sls:``
- .. code-block:: yaml
- include:
- - python.python-libs
- django:
- pkg.installed:
- - require:
- - pkg: python-dateutil
- Extend declaration
- ------------------
- You can modify previous declarations by using an :ref:`extend-declaration`. For
- example the following modifies the Apache tree to also restart Apache when the
- vhosts file is changed:
- ``apache/apache.sls:``
- .. code-block:: yaml
- apache:
- pkg.installed
- ``apache/mywebsite.sls:``
- .. code-block:: yaml
- include:
- - apache.apache
- extend:
- apache:
- service:
- - running
- - watch:
- - file: /etc/httpd/extra/httpd-vhosts.conf
- /etc/httpd/extra/httpd-vhosts.conf:
- file.managed:
- - source: salt://apache/httpd-vhosts.conf
- .. include:: /_incl/extend_with_require_watch.rst
- Name declaration
- ----------------
- You can override the :ref:`id-declaration` by using a :ref:`name-declaration`.
- For example, the previous example is a bit more maintainable if rewritten as
- follows:
- ``apache/mywebsite.sls:``
- .. code-block:: yaml
- :emphasize-lines: 8,10,12
- include:
- - apache.apache
- extend:
- apache:
- service:
- - running
- - watch:
- - file: mywebsite
- mywebsite:
- file.managed:
- - name: /etc/httpd/extra/httpd-vhosts.conf
- - source: salt://apache/httpd-vhosts.conf
- Names declaration
- -----------------
- Even more powerful is using a :ref:`names-declaration` to override the
- :ref:`id-declaration` for multiple states at once. This often can remove the
- need for looping in a template. For example, the first example in this tutorial
- can be rewritten without the loop:
- .. code-block:: yaml
- stooges:
- user.present:
- - names:
- - moe
- - larry
- - curly
- Next steps
- ==========
- In :ref:`part 4 <tutorial-states-part-4>` we will discuss how to use salt's
- :conf_master:`file_roots` to set up a workflow in which states can be
- "promoted" from dev, to QA, to production.
|