.. _map.jinja: ``map.jinja``: gather formula configuration values ================================================== The `documentation`_ explains the use of a ``map.jinja`` to gather parameters values for a formula. As `pillars`_ are rendered on the Salt master for every minion, this increases the load on the master as the pillar values and the number of minions grows. As a good practice, you should: - store non-secret data in YAML files distributed by the `fileserver`_ - store secret data in: - `pillars`_ (and look for the use of something like `pillar.vault`_) - `SDB`_ (and look for the use of something like `sdb.vault`_) Current best practice is to let ``map.jinja`` handle parameters from all sources, to minimise the use of pillars, grains or configuration from ``sls`` files and templates directly. .. contents:: **Table of Contents** For formula users ----------------- Quick start: configure per role and per DNS domain name values ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ We will see a quick setup to configure the ``unbound`` formula for different DNS domain names and several roles. For this example, I'll define 2 kinds of `fileserver`_ sources: 1. formulas git repositories with hard-coded version reference to avoid breaking my setup randomly at upstream update. they are the last sources where files are looked up 2. parameters of the formulas in the file backend `roots`_ Configure the fileserver backends ````````````````````````````````` I configure the `fileserver`_ backends to serve: 1. files from `roots`_ first 2. `gitfs`_ repositories last Create the file ``/etc/salt/master.d/fileserver.conf`` and restart the ``master``: .. code-block:: yaml --- ## ## file server ## fileserver_backend: # parameters values and override - roots # formulas - gitfs # The files in this directory will take precedence over git repositories file_roots: base: - /srv/salt # List of formulas I'm using gitfs_remotes: - https://github.com/saltstack-formulas/template-formula.git: - base: v4.1.1 - https://github.com/saltstack-formulas/openssh-formula.git: - base: v2.0.1 ... Create per DNS configuration for ``unbound`` formula ````````````````````````````````````````````````````` Now, we can provides the per DNS domain name configuration files for the ``unbound`` formulas under ``/srv/salt/unbound/parameters/``. We create the directory for ``dns:domain`` grain and we add a symlink for the ``domain`` grain which is extracted from the minion ``id``: .. code-block:: console mkdir -p /srv/salt/unbound/parameters/dns:domain/ ln -s dns:domain /srv/salt/unbound/parameters/domain We create a configuration for the DNS domain ``example.net`` in ``/srv/salt/unbound/parameters/dns:domain/example.net.yaml``: .. code-block:: yaml --- values: config: /etc/template-formula-example-net.conf ... We create another configuration for the DNS domain ``example.com`` in the Jinja YAML template ``/srv/salt/unbound/parameters/dns:domain/example.com.yaml.jinja``: .. code-block:: yaml --- values: config: /etc/template-formula-{{ grains['os_family'] }}.conf ... Create per role configuration for ``unbound`` formula `````````````````````````````````````````````````````` Now, we can provides the per role configuration files for the ``unbound`` formulas under ``/srv/salt/unbound/parameters/``. We create the directory for roles: .. code-block:: console mkdir -p /srv/salt/unbound/parameters/roles We will define 2 roles: - ``unbound/server`` - ``unbound/client`` We create a configuration for the role ``unbound/server`` in ``/srv/salt/unbound/parameters/roles/unbound/server.yaml``: .. code-block:: yaml --- values: config: /etc/template-formula-server.conf ... We create another configuration for the role ``unbound/client`` in ``/srv/salt/unbound/parameters/roles/unbound/client.yaml``: .. code-block:: yaml --- values: config: /etc/template-formula-client.conf ... Enable roles and the ``dns:domain`` and ``domain`` grains for ``map.jinja`` ``````````````````````````````````````````````````````````````````````````` We need to redefine the sources for ``map.jinja`` to load values from our new configuration files, we provide a global configuration for all our minions. We create the global parameters file ``/srv/salt/parameters/map_jinja.yaml``: .. code-block:: yaml --- values: sources: # default values - "Y:G@osarch" - "Y:G@os_family" - "Y:G@os" - "Y:G@osfinger" - "C@{{ tplroot ~ ':lookup' }}" - "C@{{ tplroot }}" # Roles activate/deactivate things # then thing are configured depending on environment # So roles comes before `dns:domain`, `domain` and `id` - "Y:C@roles" # DNS domain configured (DHCP or resolv.conf) - "Y:G@dns:domain" # Based on minion ID - "Y:G@domain" # default values - "Y:G@id" ... The syntax is explained later at `Sources of configuration values`_. Bind roles to minions ````````````````````` We associate roles `grains`_ to minion using `grains.append`_. For the servers: .. code-block:: console salt 'server-*' grains.append roles unbound/server For the clients: .. code-block:: console salt 'client-*' grains.append roles unbound/client .. note:: Since we used ``Y:C@roles``, ``map.jinja`` will do a ``salt['config.get']('roles')`` to retrieve the roles so you could use any other method to bind roles to minions (`pillars`_ or `SDB`_) but `grains`_ seems to be the preferred method. Note for Microsoft Windows systems `````````````````````````````````` If you have a minion running under windows, you can't use colon ``:`` as a delimiter for grain path query (see `bug 58726`_) in which case you should use an alternate delimiter: Modify ``/srv/salt/parameters/map_jinja.yaml`` to change the query for ``dns:domain`` to define the `alternate delimiter`_: .. code-block:: yaml --- values: sources: # default values - "Y:G@osarch" - "Y:G@os_family" - "Y:G@os" - "Y:G@osfinger" - "C@{{ tplroot ~ ':lookup' }}" - "C@{{ tplroot }}" # Roles activate/deactivate things # then thing are configured depending on environment # So roles comes before `dns:domain`, `domain` and `id` - "Y:C@roles" # DNS domain configured (DHCP or resolv.conf) - "Y:G:!@dns!domain" # Based on minion ID - "Y:G@domain" # default values - "Y:G@id" ... And then, rename the directory: .. code-block:: console mv /srv/salt/unbound/parameters/dns:domain/ '/srv/salt/unbound/parameters/dns!domain/' Format of configuration YAML files ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When you write a new YAML file, note that it must conform to the following layout: - a mandatory ``values`` key to store the configuration values - two optional keys to configure the use of `salt.slsutil.merge`_ - an optional ``strategy`` key to configure the merging strategy, for example ``strategy: 'recurse'``, the default is ``smart`` - an optional ``merge_lists`` key to configure if lists should be merged or overridden for the ``recurse`` and ``overwrite`` strategy, for example ``merge_lists: 'true'`` Here is a valid example: .. code-block:: yaml --- strategy: 'recurse' merge_lists: 'false' values: pkg: name: 'some-package' config: '/path/to/a/configuration/file' ... Using Jinja2 YAML template `````````````````````````` You can provide a Jinja2 YAML template file with a name suffixed with ``.yaml.jinja``, it must produce a YAML file conform to the `Format of configuration YAML files`_, for example: .. code-block:: jinja2 --- strategy: 'overwrite' merge_lists: 'true' values: {%- if grains["os"] == "Debian" %} output_dir: /tmp/{{ grains["id"] }} {%- endif %} ... Sources of configuration values ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The ``map.jinja`` file aggregates configuration values from several sources: - YAML files stored in the `fileserver`_ - `pillars`_ - `grains`_ - configuration gathered with `salt['config.get']`_ For the values loaded from YAML files, ``map.jinja`` will automatically try to load a Jinja2 template with the same name as the YAML file with the addition of the ``.jinja`` extension, for example ``foo/bar/quux.yaml.jinja``. After loading values from all sources, it will try to include the ``salt://parameters/post-map.jinja`` Jinja file if it exists which can post-process the ``mapdata`` variable. Configuring ``map.jinja`` sources ````````````````````````````````` The ``map.jinja`` file uses several sources where to lookup parameter values. The list of sources can be configured in two places: 1. globally 1. with a plain YAML file ``salt://parameters/map_jinja.yaml`` 2. with a Jinja2 YAML template file ``salt://parameters/map_jinja.yaml.jinja`` 2. per formula 1. with a plain YAML file ``salt://{{ tplroot }}/parameters/map_jinja.yaml`` 2. with a Jinja2 YAML template file ``salt://{{ tplroot }}/parameters/map_jinja.yaml.jinja`` .. note:: The ``map.jinja`` configuration files must conform to the `format of configuration YAML files`_. Each source definition has the form ``[[: