Intelligent looping in saltstack state files – Testing for pillar data on the fly

So bit ago I made a post about looping through a set of keys in pillar data to populate an ssh-keys file. I’d like to expand on that a bit.

I’ve got a salt state to provide a link to my internal yum repository. Now my internal yum repository has multiple branches, setup in the style of cPanel. i.e., a RELEASE branch (tied to production) and an EDGE branch (tied to staging). RELEASE obviously has the stable/tested packages while EDGE has currently built/cutting edge versions of packages we use.

Now, I wanted saltstack to provide the appropriate repository configuration for each environment, which seems fairly simple… Since we’re a rails/ruby shop I decided to use my rails:rails_environment key to dictate which yum repository gets installed. This worked fine until I tried to highstate a database server that had no Apache (and consequently, no rails) version.


{% if pillar['rails']['rails_environment'] == 'production' %}
/etc/yum.repos.d/SNIP-RELEASE.repo:
file.managed:
- source: salt://yum/SNIP-RELEASE.repo
- mode: 655
- user: root
- group: root
- order: 1
- require:
- cmd: run-gpg-import

{% elif pillar['rails']['rails_environment'] == 'staging' %}
/etc/yum.repos.d/SNIP-EDGE.repo:
file.managed:
- source: salt://yum/SNIP-EDGE.repo
- mode: 655
- user: root
- group: root
- order: 1
- require:
- cmd: run-gpg-import

{% elif pillar['rails']['rails_environment'] == 'integration' %}
/etc/yum.repos.d/SNIP-EDGE.repo:
file.managed:
- source: salt://yum/SNIP-EDGE.repo
- mode: 655
- user: root
- group: root
- order: 1
- require:
- cmd: run-gpg-import
{% else %}
/etc/yum.repos.d/SNIP-RELEASE.repo:
file.managed:
- source: salt://yum/SNIP-RELEASE.repo
- mode: 655
- user: root
- group: root
- order: 1
- require:
- cmd: run-gpg-import
{% endif %}

Okay, that works initially, but if there’s no rails:rails_environment flag, we explode in a stack trace. Uncool.

The reasoning for that is that [% if pillar[‘key’][‘key’] %] uses a python builtin for the acquisition of the pillar data. Now, most languages I’ve used will return false unless the match succeeds which is what I expected. That’s not so true with python. If the key just blatantly doesn’t exist it returns nothing. Not a NULL response, just… nothing. The conditional test sees *nothing*, can’t evaluate it and boom, we get explodey.

Now, thanks to UtahDave in the #saltstack freenode channel, he pointed out that I can use the salt builtin pillar.get function which will at least return NULL if the key doesn’t exist.

So, a few quick changes, and now our state looks like this…


{% if salt['pillar.get']('rails:rails_environment') %}
{% if pillar['rails']['rails_environment'] == 'production' %}
/etc/yum.repos.d/SNIP-RELEASE.repo:
file.managed:
- source: salt://yum/SNIP-RELEASE.repo
- mode: 655
- user: root
- group: root
- order: 1
- require:
- cmd: run-gpg-import

{% elif pillar['rails']['rails_environment'] == 'staging' %}
/etc/yum.repos.d/SNIP-EDGE.repo:
file.managed:
- source: salt://yum/SNIP-EDGE.repo
- mode: 655
- user: root
- group: root
- order: 1
- require:
- cmd: run-gpg-import

{% elif pillar['rails']['rails_environment'] == 'integration' %}
/etc/yum.repos.d/SNIP-EDGE.repo:
file.managed:
- source: salt://yum/SNIP-EDGE.repo
- mode: 655
- user: root
- group: root
- order: 1
- require:
- cmd: run-gpg-import
{% endif %}
{% else %}
/etc/yum.repos.d/SNIP-RELEASE.repo:
file.managed:
- source: salt://yum/SNIP-RELEASE.repo
- mode: 655
- user: root
- group: root
- order: 1
- require:
- cmd: run-gpg-import
{% endif %}

Note the use of {% if salt['pillar.get']('rails:rails_environment') %}
at the top. This calls the salt builtin pillar.get function, if it doesn’t exist, it returns NULL and we skip down to our Else as expected.

This is a pretty darned helpful nugget to know for your conditionals and extending your state formulas and jinja templates.

One comment
  1. Hello,

    I usually use :

    {% if ‘rails_environment’ in pillar[‘rails’] %}

    instead of :

    {% if salt[‘pillar.get’](‘rails:rails_environment’) %}

    It’s more pythonic and easily allows to use variables in pillar keys.

    Olivier

Add Comment

Required fields are marked *. Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.