The files inside the
/srv/salt/ directory define the Salt states. This is a configuration management format that enforces the state that a minion will be in:
package X needs to be installed,
file Y needs to look a certain way,
service Z needs to be enabled and running, and so on. For example:
apache2: pkg: - installed service: - running file: - name: /etc/apache2/apache2.conf
States may be saved in a single SLS file, but it is far better to separate them into multiple files, in a way that makes sense to you and your organization. SLS files can use include blocks that pull in other SLS files.
In a large SLS tree, it often becomes reasonable to have SLS files include other SLS files. This is done using an
include block, which usually appears at the top of an SLS file:
include: - base - emacs
In this example, the SLS file in question will replace the include block with the contents of
emacs/init.sls). This imposes some important restrictions on the user. Most importantly, the SLS files that are included may not contain IDs that already exist in the SLS file that includes them.
It is also important to remember that
include itself, being a top-level declaration, cannot exist twice in the same file. The following is invalid:
include: - base include: - emacs
State SLS files are unique among configuration management formats in that they are both declarative and imperative. They are imperative, as each state will be evaluated in the order in which it appears in the SLS file. They are also declarative because states may include requisites that change the order in which they are actually executed. For instance:
web_service: service.running: - name: apache2 - require: - pkg: web_package web_package: pkg.installed: - name: apache2
If a service is declared, which requires a package that appears after it in the SLS file, the
pkg states will be executed first. However, if no requirements are declared, Salt will attempt to start the service before installing the package, because its codeblock appears before the
pkg codeblock. The following will require two executions to complete properly:
web_service: service.running: - name: apache2 web_package: pkg.installed: - name: apache2
Requisites point to a list of items elsewhere in the SLS file that affect the behavior of the state. Each item in the list contains two components: the name of the module and the ID of the state being referenced.
The following requisites are available inside Salt states and other areas of Salt that use the state compiler.
require requisite is the most basic; it dictates that the state that it is declared in is not executed until every item in the list that has been defined for it has executed successfully. Consider the following example:
apache2: pkg: - installed - require - file: apache2 service: - running - require: - pkg: apache2 file: - managed - name: /etc/apache2/apache2.conf - source: salt://apache2/apache2.conf
In this example, a file will be copied to the minion first, then a package installed, then the service started. Obviously, the service cannot be started until the package that provides it is installed. But Debian-based operating systems such as Ubuntu automatically start services the moment they're installed, which can be problematic if the default configuration files aren't correct. This state will ensure that Apache is properly configured before it is even installed.
In the preceding example, a new minion will be properly configured the first time. However, if the configuration file changes, the
apache2 service will need to be restarted. Adding a
watch requisite to the service will force that state to perform a specific action when the state that it is watching reports changes.
apache2: ...SNIP... service: - running - require: - pkg: apache2 - watch: - file: apache2 ...SNIP...
watch requisite is not available for every type of state module. This is because it performs a specific action, depending on the type of module. For instance, when a service is triggered with a
watch, Salt will attempt to start a service that is stopped. If it is already running, it will attempt either a
service.restart, as appropriate.
As of version 2016.3, the following states modules support using the
onchanges requisite is similar to
watch, except that it does not require any special support from the state module that is using it. If changes happen, which should only occur when a state completes successfully, then the list of items referred to with
onchanges will be evaluated.
In a simple state tree, the
onfail requisite is less commonly used. However, a more advanced state tree, which is written to attempt alerting the user, or to perform auto-correcting measures, can make use of
onfail. When a state is evaluated and fails to execute correctly, every item listed under
onfail will be evaluated. Assuming that the
PagerDuty service is properly configured via Salt and an
apache_failure state has been written to use it, the following state can notify the operations team if Apache fails to start:
apache2: service: - running - onfail - pagerduty: apache_failure
It is possible to declare default values in one state and then inherit them into another state. This typically occurs when one state file has an
include statement that refers to another file.
If an item in the state that is being used has been redeclared, it will be overwritten with the new value. Otherwise, the item that is being used will appear unchanged. Requisites will not be inherited with
use; only non-requisite options will be inherited. Therefore, in the following SLS, the
mysql_conf state will safely inherit the
group, and mode from the
apache2_conf state, without also triggering Apache restarts:
apache2_conf: file: - managed - name: /etc/apache2/apache2.conf - user: root - group: root - mode: 755 - watch_in: - service: apache2 mysql_conf: file: - managed - name: /etc/mysql/my.cnf - use: - file: apache2_conf - watch_in: - service: mysql
There are some situations in which a state does not need to run, unless another state is expected to make changes. For example, consider a web application that makes use of Apache. When the codebase on a production server changes, Apache should be turned off, so as to avoid errors with the code that has not yet finished being installed.
prereq requisite was designed exactly for this kind of use. When a state makes use of
prereq, Salt will first perform a test run of the state to see if the items referred to in the
prereq are expected to make changes. If so, then Salt will flag the state with the
prereq as needing to execute.
apache2: service: - running - watch: - file: codebase codebase: file: - recurse ...SNIP... shutdown_apache: service: - dead - name: apache2 - prereq: - file: codebase
In the preceding example, the
shutdown_apache state will only make changes if the
codebase state reports that changes need to be made. If they do, then Apache will shutdown, and then the
codebase state will execute. Once it is finished, it will trigger the
apache2 service state, which will start up Apache again.
Each of the aforementioned requisites can be used inversely, by adding
_in at the end. For instance, rather than state X requiring state Y, an SLS can be written so that state X declares that it is required by state Y, as follows:
apache2: pkg: - installed - require_in: - service: apache2 service: - running
It may seem silly to add inverses of each of the states but there is in fact a very good use case for doing so: include blocks.
SLS files cannot use requisites that point to a code that does not exist inside them. However, using an include block will cause the contents of other SLS files to appear inside the SLS file. Therefore, generic (but valid) configuration can be defined in one SLS file, included in another, and modified to be more specific with a
In addition to an include block, state SLS files can also contain an
extend block that modifies SLS files that appear in the include block. Using an
extend block is similar to a use requisite, but there are some important differences.
use_in requisite will copy defaults to or from another state, the extend block will only modify the state that has been extended.
# cat /srv/generic_apache/init.sls apache2_conf: file: - managed - name: /etc/apache2/apache2.conf - source: salt://apache2/apache2.conf (In django_server/init.sls) include: - generic_apache extend: apache2_conf: file: - source: salt://django/apache2.conf (In image_server/init.sls) include: - generic_apache extend: apache2_conf: file: - source: salt://django/apache2.conf
The preceding example makes use of a generic Apache configuration file, which will be overridden as appropriate for either a Django server or a web server that is only serving images.