The Magic of Pillar Targeting In SaltStack
Overview
In the course of using salt for all sorts of use cases I happened upon a particular pattern of using the states top file that, in my opinionated view, is the most elegant way.
Of the many ways targeting can be done in the states top file (see the SaltStack Documentation), the one I’m writing about here is the pillar target type.
Basic States and Top File Setup
Let’s say we have three minions called minion1
, minion2
, and minion3
, and we have the breakfast.sls
and dinner.sls
states to deploy on them. Where our states are:
Breakfast:
{{ sls }} - write out the breakfast menu:
file.managed:
- name: /tmp/breakfast.txt
- contents: |
eggs: scrambled
meats:
- sausage
- bacon
sides:
spam: gross
beans: baked
Dinner:
{{ sls }} - write out the dinner menu:
file.managed:
- name: /tmp/dinner.txt
- contents: |
eggs: poached
seafood: lobster
other:
- truffle pate
- mornay sauce
sides:
spam: delicious
eggs: over easy
The most common form of targeting used in the states top file is globbing. So to deploy our states above on the minions, the states top file will look like:
'minion1':
- breakfast 'minion2':
- dinner 'minion3':
- breakfast
- dinner
Now in our states top file we have specified that minion1
is to run the breakfast.sls
state, minion2
is to run the dinner.sls
state, and minion3
is to run both breakfast
and dinner
.
Moving Data to Pillar
The above setup works just fine. But, it’s very rigid. Using the model above, If we were to add another minion that required a different value for dinner eggs, we would have to write a new state and add an entry to the top file. There’s no code reuse here.
Instead, lets move the data out of the states and into pillar by creating a couple of pillar files called breakfast-items.sls
and dinner-items.sls
:
breakfast_items:
menu:
egg: scrambled
meats:
- sausage
- bacon
sides:
spam: gross
beans: bakeddinner_items:
menu:
egg: poached
seafood: lobster
other:
- truffle pate
- mornay sauce
sides:
spam: delicious
egg: over easy
Now pillar files are also assigned to minions by way of a pillar top file. So we’ll need to make some similar entries as to what is in the states top file.
'minion1':
- breakfast-menu-items 'minion2':
- dinner-menu-items 'minion3':
- breakfast-menu-items
- dinner-menu-items
And of course, we need to update our breakfast.sls
and dinner.sls
states:
Breakfast:
{{ sls }} - write out the breakfast menu:
file.managed:
- name: /tmp/breakfast.txt
- contents_pillar: breakfast:menu
Dinner:
{{ sls }} - write out the dinner menu:
file.managed:
- name: /tmp/dinner.txt
- contents_pillar: dinner:menu
Magic
As can be seen above, the states top file and the pillar top file are very similar. Lets simplify the states top file by using pillar targeting.
The first step is to add a key to the pillar files that we can target against. Adding a key specific isn’t required, but IMO, it’s the best way. For this purpose, I like to add a key/value of install: true
. It's easy to remember and easy to override in other pillars if necessary.
So breakfast-items.sls
and dinner-items.sls
become:
breakfast:
install: true
menu:
egg: scrambled
meats:
- sausage
- bacon
sides:
spam: gross
beans: bakeddinner:
install: true
menu:
egg: poached
seafood: lobster
other:
- truffle pate
- mornay sauce
sides:
spam: delicious
egg: over easy
And then our states top file becomes:
'breakfast:install:true'
- match: pillar
- breakfast 'dinner:install:true'
- match: pillar
- dinner
… let the above sink in. You need one entry in the states top.sls
for every state created. That's it.
Conclusion
While states and pillar files will proliferate in any environment. In my opinionated view, this is the easiest way of releaving overhead thoughts about what minions need to get which state in the top file.
Assignment happens automatically in most cases as almost all states (at least ones that I write) use data from pillars.
Originally published at https://smartaleksolutions.com on October 13, 2020.