Having recently becmoe frustrated with the lack of hierarchy and no ability to merge in salt pillars, I decided to look into alternative options.
I briely toyed with pillarstack
(gitstack
) and varstack
, but found that these were really specialist layers over the top of pillars - what I needed was something that completely replaced pillars.
I found this initially in reclass which offers all of the above, and then subsequently in saltclass which is now bundled with saltstack.
saltclass
is built on top of reclass, but offers better integration with pillar and grain information, so I opted for this as the tool of choice.
Where it is lacking is that there is no built-in support for reading saltclass configuration from a git repo, as you can do with the git
ext_pillar. As a result I set about working out how to implement this using a post-commit hook from a (non-salt managed) git repository. (If your git server is a minion of your master, then a nice alternaitve is given at this blog post: Salt git integration without gitfs).
This blog post is written for saltclass, but the technique is applicable to reclass, or any other git-based config that is required for running jobs on the master. I assume youalready understand the basic concepts of reclass/saltclass, and have suitable configuration ready (nodes/classes etc).
Overview
We run the salt-api, listening for web events at the /hook
url path. Certain hooks will trigger a reactor update, which will call git.latest
to update a local copy of the saltclass state.`
API
You need the rest_cherrypy
API up and running to make this work. Since authentication using external auth can be tricky for git hooks, we will disable this, and instead implement our own auth inside the reactor state.
The salt-api package must be installed and the service restarted after updating the config.
Self-signed certs can be generated by running:
salt-call tls.create_ca_signed_cert master CN=salt-master.example.com
Master config:
rest_cherrypy:
port: 5417
ssl_key: /etc/pki/tls/certs/master.example.com.key
ssl_crt: /etc/pki/tls/certs/master.example.com.crt
# disable external auth in saltstack
webhook_disable_auth: True
Note that the webhook has auth disabled - so auth must be handled elsewhere - in our case in the Reactor.
Reactor
Now that the API is listening, we need to react to certain events. In this case, we want to react to a hook called saltclass
:
reactor.conf
reactor:
- 'salt/netapi/hook/saltclass':
- salt://reactor/saltclass.sls
Now we must deliver the reactor state to the path provided above. Note that this can be a salt://
path, or a filesystem path (e.g. /srv/salt/reactor/
). In this case we provide state config from another git repository which is provided by gitfs_remotes
in the master config.
salt://reactor/saltclass.sls
{% set postdata = data.get('post', {}) %}
{% if postdata.secretkey == "secretgoeshere" %}
reactor_git_saltclass:
local.state.single:
- tgt: 'salt-master.example.com'
- arg:
- git.latest
- https://user@git.example.com/saltclass.git
- kwarg:
target: /srv/saltclass
{% endif %}
Trying it Out
Now we can watch for events on the master:
salt-run state.event pretty=True
Then try ‘kicking’ the api hook path, passing in the secretkey token (note -k
due to self-signed certs):
curl -k -d secretkey="secretgoeshere" https://salt-master.example.com:5417/hook/saltclass
You should see the hook event, and then the subsequent git.latest
job:
salt/netapi/hook/saltclass { <...snip...> }
salt/job/20180830100053150164/new {
"_stamp": "2018-08-30T09:00:53.152986",
"arg": [
"git.latest",
"https://user@git.example.com/saltclass.git"
]
<...snip...>
Assuming the above all worked, you should now have your saltclass repo available at /srv/saltclass
.
To finish off, simply add the above curl call (or suitable web post)
Using saltclass config
Now you can tell the master to use your saltclass config for pillars and states:
master config
master_tops:
saltclass:
path: /srv/saltclass
ext_pillar:
- saltclass:
- path: /srv/saltclass
Note: If you are trying to configure the above using the salt-formula module then please see Issue 383 for a workaround to limitations in the config format.
Finally you can look for saltclass config in your pillars:
salt 'host.example.com' pillar.items
host.example.com:
----------
__saltclass__:
----------
classes:
- a
- b
environment:
base
nodename:
hostname.example.com
states:
- c
- d