I often find myself writing salt configurations and therefore need an easy way to test, debug and write particular salt states. Here is how I manage that.

Salt structure

I use a very simple layout for the saltstack repository with two main folders: states and pillars at the root level. Secrets are gpg-encrypted and stay in the same repository in the pillars/_secret subfolder.
There is also a _secret_mock folder which mirrors the _secret but contains unencrypted dummy values.

├── pillars
│   ├── ...
│   └── _secret
├── _secret_mock
├── states
├── minion.config
└── Vagrantfile

Test environment

To test salt states you need salt minions. One can of course use docker, but I prefer to avoid the unnecessary complications and don't want to dig into kernel and system-level differences, so I use Vagrant with proper virtualization instead.

Since I use Fedora Linux workstation, I also prefer to use native Linux tools (nicely packaged and working out of the box) over the cross-platform toolchains provided by some third-parties. Thus I use Vagrant with libvirt vm provider.

Now, to test something we need a way to deliver the test data into the test environment. While Vagrant by default uses NFS to mount external volumes, I don't like it, as it required NFS server setup on the host machine, asks for root password,.. and generally I don't like NFS.

Fortunately, there is a vagrant-sshfs plugin. It is easy to setup, it doesn't require specific configuration on the host and/or client, it (as opposed to rsync) allows you to edit files on the fly from both host and guest command line without need to reload the vagrant box.

Code snippets

To install requirements:

$ sudo dnf install vagrant vagrant-libvirt vagrant-sshfs

Here is the ./Vagrantfile:

Vagrant.configure(2) do |config|

  config.vm.box = "debian/jessie64"
  config.vm.network "private_network", ip: ""

  config.vm.synced_folder "./states", "/srv/salt/states", type: "sshfs"
  config.vm.synced_folder "./pillars", "/srv/salt/pillars", type: "sshfs"
  config.vm.synced_folder "./_secret_mock", "/srv/salt/pillars/_secret",
                          type: "sshfs",
                          # mock secrets are mounted on top of existing _secret folder
                          sshfs_opts_append: "-o nonempty" 

  # ca-certificates required for salt botstrap script to work
  config.vm.provision "shell",
                      inline: "apt-get update; apt-get -qq install ca-certificates curl"

  config.vm.provision :salt do |salt|
      salt.minion_config = "minion.config"
      salt.masterless = true


and ./minion.config:

file_client: local
    - /srv/salt/states
    - /srv/salt/pillars