Book Image

Mastering SaltStack

By : Joseph Hall
Book Image

Mastering SaltStack

By: Joseph Hall

Overview of this book

Table of Contents (19 chapters)
Mastering SaltStack
Credits
Foreword
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Using States for configuration management


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.

Using include blocks

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 base.sls (or base/init.sls) and emacs.sls (or 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

Ordering with requisites

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

The 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 States will ensure that Apache is properly configured before it is even installed.

watch

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...

The 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.reload, service.full_restart, or service.restart, as appropriate.

As of version 2015.5, the following States modules support using the watch requisite: service, cmd, event, module, mount, supervisord, docker, tomcat, and test.

onchanges

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.

onfail

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

use

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 user, 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

prereq

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.

The 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.

Inverting requisites

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 use_in requisite.

Extending SLS files

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.

Whereas a use or 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.