In configuration management, execution order is important. You can't run apache if apache isn't installed. You can't write to a file if the directory doesn't exist. Successful configuration management requires careful management of the execution order of different commands.
By default, SaltStack compiles the requisite (dependency) graph by reading state options such as
watch. The advantage of using these state options is that SaltStack is able to safely execute multiple unconnected states in parallel rather than executing everything in serial. We'll discuss how to setup the dependency graph to be able to rely on your states being applied in the proper and correct order.
In our quickstart, we setup the example of installing and starting apache2.
apache2: pkg: - installed service: - running - require: - pkg: apache2
While we didn't discuss the
require option in-depth, we used it to ensure that the
pkg: apache2 is installed before the apache2 service could be set to running. Visually, this looks like:
In order to run the the service: apache2, the apache2 package must be installed.
Note that the way we are setting up the requisite is by using the key of
pkg and the value of apache2. This is called requisite matching. requisite matching basically matches on the
ID and the
This sets the graph to relate one state to another state. This is important to note because it requires two things to work:
You must have a state defined to require another state
In our example, we have two states, a pkg and a service. Broken out, our state from above looks like:
apache2_package: pkg.installed: - name: apache2 apache2_service: service.running: - require: - pkg: apache2_package
In order for the
apache2_service to set a requisite, it must be able to refer to another state entirely, as the states above clearly show.
One state type exists per state declaration
The dependency graph requires that one type of state refers to another state declaration. If there are multiple types of the same type, then SaltStack will not know which state to set in the dependency graph. For example, the following is invalid:
# THIS IS INVALID SYNTAX apache2_package: pkg.installed: - name: apache2 pkg.installed: - name: apache2-tools
SaltStack will respect this order if it's explicitly set as an order.
This is called direct requisites where we explicitly set the requirement inside the state. Salt enables a second way for states to be set in the dependency graph through setting up requisites in.
To set a requisite in, use the option:
require_in. For instance, the above example could be rewritten with the same dependency graph as:
apache2_package: pkg.installed: - name: apache2 - require_in: - service: apache2_service apache2_service: service.running: - name: apache2
require_in sets up a similar graph, in the reverse order:
The order of appearance does not set a dependency graph.
Unlike other tools for configuration management, the order in which the state appears in the file does not set an order in which the states run.
To restart a service, or act upon action inside of SaltStack, SaltStack provides the
watch option. The
watch option sets establishes a dependency graph, just the same as
require does, but it also runs the state if and only if the state that it points to has made any changes to the system.
Watches are used to restart services or cause actions only if another state actions succeeds. For instance, if we look back at the second example in our quickstart, we have the apache2 config being set by SaltStack:
apache2: pkg: - installed service: - running - require: - pkg: apache2 file.managed: - name: /etc/apache2/apache2.conf - source: salt://apache2.conf - template: jinja - user: apache2 - group: apache2 - mode: 644 - require: - user: apache2
What happens in the case that we want to restart the apache2 service if we make a chance to the apache2 config? Currently, the service
apache2 will not be restarted (or reloaded). To set the service to restart, we simply need to apply the
watch option. The service then can be changed to look like:
service.running: - require: - pkg: apache2 - watch: - file: apache2
Using the requisite matching syntax, we can simply add the
watch option and whenever
file: apache2 is seen to change the system, the service.running will be triggered.
An example of when you'd want to use a
watch would be if you were going to download a file and need to untar it. You obviously cannot untar a file that doesn't exist on the system, so you have to wait for the file to exist on the system, but you don't want to run untar everytime you run a highstate.
watch will fix this for us:
get-nginx: pkg.installed: - names: - libpcre3-dev - build-essential - libssl-dev file.managed: - name: /usr/local/src/nginx.tar.gz - source: http://nginx.org/download/nginx-1.5.2.tar.gz - source_hash: sha1=3546be28a72251f8823ab6be6a1180d300d06f76 cmd.wait: - cwd: /usr/local/src - name: tar -zxf /usr/local/src/nginx.tar.gz -C /usr/local/nginx - require: - pkg: get-nginx - watch: - file: get-nginx
watch has a
watch_in option. The
watch_in option works just like the
require_in in that we can tell another state that it requires the containing state.
prereq requisite allows for actions to be taken based off the results of a state that has not yet been executed. This is a complex way of saying that a state will only run if the specified state expects to change the underlying system and that it will be run before the state requiring the
An example of this is the case in which we want to deploy new site code. We don't want to stop the apache service if we aren't going to deploy new code and we want to ensure that apache has been gracefully shutdown before we deploy the new code:
graceful-down: cmd.run: - name: service apache graceful - prereq: - file: site-code site-code: file.recurse: - name: /opt/site_code - source: salt://site/code
Just like the
prereq_in allows us to specify in advance that we will need to require a prereq in another state.
Although by default SaltStack encourages the use of requisite declarations to determine execution order, it is also possible to specify execution order using the
order keyword. When this keyword is used, commands are executed in order according to the number specified in the
order command. Multiple commands with the same
order number will be executed with the same priority level. The
order: last option will forcibly set a state to execute after all other states set with or without the
Notes: If a requisite declaration is also used, it will override an
order option. Any states without the
order option will be executed after states specifying the
order keyword can be useful for state files where you want to execute lots of commands in a row:
nginx-install: cmd.run: - name: apt-get install nginx - order: 1 nginx-delete-config: cmd.run: - name: rm -rf /etc/nginx/conf.d/ - order: 2 nginx-create-log-dir: cmd.run: - name: mkdir /var/log/nginx - order: 3 nginx-set-conf-dir-permissions: cmd.run: - name: chown -R nginx:nginx /etc/nginx - order: 4 nginx-set-log-dir-permissions: cmd.run: - name: chown -R nginx:nginx /var/log/nginx - order: 4 nginx-start: cmd.run: - name: service nginx start - order: last