So far we played with Salt State files to apply configurations to Minions and we learned that these are generic by design and describe only what configuration should be achieved. And we used the concept of Targeting to specify where one, or more, State should be applied.
In this post I’ll try to cover a different file, called the Top file, that brings together States and Targeting, that is what and where configurations should be applied. States and the Top file work together to create the core of SaltStack’s configuration management capability.
Top Files
A Top file is used to apply multiple State files to your Salt Minions, this action is also called a highstate. The States that are applied to each system are determined by the targets that are specified in the Top file.
A Top file has three elements:
- Environment: that is the
saltenv
, a state tree directory containing a set of State files to configure Minions; - Target: a grouping of Minions which will have a set of states applied to them;
- State files: a list of State files to be applied to a target. Each State file describes one or more configuration to be enforced on the targeted Minions.
The relationship between these three components is nested as follows:
- Environments contain Targets
- Targets contain States
Here below there is a very basic Top file to introduce the structure just described.
base: # Apply SLS files for the 'base' environment
'web*': # All minions with a minion_id that begins with 'web'
- apache # Apply the state file apache
When Top file is applied, each Salt Minion is matched against the targets listed in the top.sls
file. When a Salt Minion matches a target, it receives all of the State files defined in the list underneath that target.
Top File Example
In my opinion, Top file is one of those things that are best learned by example. Instead of further elaborating and digging into details, here after there is a simple Top file and further below we will go through each section.
base:
'*':
- /config/users
- /config/scripts
'*web*':
- /applications/nginx
'application:mongodb':
- match: grain
- /applications/mongodb
All Minions get applied users
and scripts
States
'*':
- /config/users
- /config/scripts
Every Minion having the string web
in the ID gets applied with the ngninx
State file
'*web*':
- /applications/nginx
Every Minion having grain application
: mongodb
get applied mongodb
State file. In a Top file you can use all the advanced targeting capabilities made available by SaltStack as documented here.
'application:mongodb':
- match: grain
- /applications/mongodb
Wait! I forgot the very first thing in our example! The base
at the begin of the Top file identifies the applicable saltenv
environment. You can have multiple environment sections in your Top file, of course all the states that you use within an environment section have to exists in that environment.
Get Started with Top Files
Before you get started with Top file you need to make sure file_roots
property is defined for all your environments in the master configuration file, the following is what I have in my SaltStack deployment. Make sure to adjust root path as per your environment specifics as this is the place where you will store your Top file.
file_roots:
base:
- /srv/salt
Place your Top file in the specified path: /srv/salt/top.sls
. Please, note that top.sls
is a kind of special name. Top files are named top.sls
by default and they are so-named because they always exist in the “top” of a directory hierarchy that contains state files. That directory hierarchy is called a state tree.
To apply a Top file you can simply use one of the the following commands:
salt '*' state.apply
In the example above we are using the state.apply
command that we already know, calling state.apply
with no arguments starts a highstate (the highstate is the action to apply Top file). Alternatively we can explicitly use the state.highstate
command as reported below.
salt '*' state.highstate
If you are in a multiple environments Saltstack deployment you can use the saltenv
argument to set the saltenv
for the highstate.
salt '*' state.apply saltenv=dev
salt '*' state.highstate saltenv=test
Lab Time!
In my lab I have some minions, but for this activity I will restrict the scope to 3 minions having the string secops
in their Minion IDs. One of them has a grain application:nginx
.
My Top file is reported below. Please, note that specifying match: compound
is redundant as Compound is the default matcher for Top files.
base:
'*':
- /config/users
'G@os:CentOS and G@application:apache':
- match: compound
- /applications/apache
'G@os:CentOS and G@application:nginx':
- match: compound
- /applications/nginx
'G@os:CentOS and G@application:mysql':
- match: compound
- /applications/mysql
'G@os:CentOS and G@application:mongodb':
- match: compound
- /applications/mongodb
Applying my Top file to the 3 Minions in scope of this activity enforces users
to be present on any Minions and than install nginx
on the centos-secops.iberia.local
. User’s State file creates 3 local users by collecting data from a map file and using Jinja2 templating (I’ll cover templating and State files parameterization in a future post).
{% from 'config/users/map.jinja' import users with context %}
{% for user in users %}
add_user_{{ user.username }}:
user.present:
- name: {{ user.username }}
- uid: {{ user.uid }}
- password: {{ user.password }}
{% endfor %}
Here is the map.jinja file:
{% set users = [
{'username': 'luca', 'uid': 2001, 'password': 'be3bed2f000aae03f8cf880d77507e028280fb692a1e3b16cd3afb053e5ff30c'},
{'username': 'marco', 'uid': 2002, 'password': 'be3bed2f000aae03f8cf880d77507e028280fb692a1e3b16cd3afb053e5ff30c'},
{'username': 'pietro', 'uid': 2003, 'password': 'be3bed2f000aae03f8cf880d77507e028280fb692a1e3b16cd3afb053e5ff30c'},
]
%}
The Nginx’s State file includes 3 state IDs: install epel as it is a pre-requisites for Nginx, install Nginx and start Nginx.
# Install NGINX on CentOS/RHEL
install_epel_release:
pkg.installed:
- name: epel-release
install_nginx:
pkg.installed:
- name: nginx
- requres:
- install_epel_release
start_nginx:
service.running:
- name: nginx
- enable: True
- requires:
- install_nginx
Here is the CLI command and some section of the return:
The ubuntu-secops.iberia.local
has 3 succeeded states as it get applied to User’s State file only (a state for each user).
Also the windows-secops.iberia.local
has 3 succeeded states as it get applied to User’s state file only (a state for each user).
The centos-secops.iberia.local
has 6 succeeded states as it get applied to User’s State file (a state for each user) and the Nginx’s State file (that is made up of 3 state IDs).
This is post just provides the very basic to get started with Top files, you can use the snippets included in this post to replicate this in your lab and surely you can improve it! I hope this can help you in your Salt exploration journey.