Not a massively complicated deal, but the absence of best practice examples makes some seemingly simple SaltStack activities something less than blindingly obvious.
Their documentation is generally superb in terms of covering everything the system can do, while sometimes leaving you a bit bewildered with the lack of descriptions on how to tie things together properly – or how to use some features at the command-line versus a state file (or vice versa), where they've described one method and not the other.
The broad challenge here is to come up with a way of defining a state where:
- a package is installed (easy)
- a file is installed (also easy)
- something is executed once and once only, after the package and file are present
I've only been using SaltStack for a short while, and hit this particular problem first with etckeeper.
A quick search for someone having a nice sls fragment that would demonstrate a suitable pattern ... failed.
With some advice from the always helpful guys on IRC, I came up with this (now in my base-packages.sls)
etckeeper: pkg: - installed # This way we ensure git's present first - otherwise Ubunutu uses bzr. - require: - pkg: git file.managed: - name: /etc/etckeeper/etckeeper.conf - source: salt://fs/etc/etckeeper/etckeeper.conf cmd: - run - cwd: /etc - name: /usr/bin/etckeeper init ; /usr/bin/etckeeper commit 'Initial commit' # We ensure that the file is in place before running init/initial commit. - require: - pkg: etckeeper - file: /etc/etckeeper/etckeeper.conf # And run the init/initial commit IF AND ONLY IF there's no extant repo. - unless: ls -d /etc/.git
- I have a git install prerequisite here (it is satisfied earlier in my sls file)
- also, I have an etckeeper.conf file on my master - the only change right now is VCS=git
- the require on lines 5-6 ensures git is installed before etckeeper – otherwise, as noted, Ubuntu decides that you want bzr and you never want bzr
- the require on lines 15-17 with the unless on line 19 ensures the package and configuration file are present, and 'etckeeper init' has never been run before, before actually running the init and first commit
- it's not clear in the Salt Documentation, at least not to me, that you can put your command-line script right after the name: parameter, and more interestingly you can string them together with semicolons like this. It does look like it encapsulates everything in double-quotes (") so I think it's safer to stick with using single-quotes around strings, as I've done here
- older versions of Ubuntu (LTS 12.04 and earlier perhaps?) bewilderingly chose to rename their git package to git-core – it's not clear why they did this, and I see that on Precise Penguin that the git-core package is now obsolete and a sensible git package is back, but it's worth noting that this bites you in a heterogeneous network, and that I haven't touched on handling that at all here. Exercise for the reader, and all that.
- finally, yes, the cwd: is almost certainly superfluous here, I realised later
Alternative approaches I could have adopted include:
- editing the packaged-delivered etckeeper.conf on each minion using built-in or sed magic – but for now I expect all my etckeeper configurations to be identical
- use a mod_init approach, which would involve a /srv/salt/etckeeper/ directory populated with some more sophisticated magic, specifically a small python fragment as well as a state file wrapper
The latter is something I'll likely revisit for an upcoming Riak cluster project that I want to deploy using Salt, but on reflection seemed excessively convoluted in this particular instance.