Backbeat Software

Building a SaltStack development machine

Using Vagrant and Salt to work on Salt's codebase.

Glynn Forrest
Wednesday, July 31, 2019

Like many great infrastructure projects, SaltStack is open source and regularly accepts code contributions from the community.

I think it’s important to give back to the open source tools, so when I discovered various bugs in Salt’s x509 state module I decided to open a pull request to improve its reliability and ease of use.

Unfortunately, Salt has a large and complicated test suite that can be difficult to run locally. The tests require a lot of Python dependencies, and the tests themselves can make changes to the host system.

This is a perfect use case for a disposable virtual machine. In this post we’ll use Hashicorp Vagrant and SaltStack itself create a machine that can run Salt’s test suite easily and safely.

The full source code for this post is available here.

Init

Make sure Vagrant is installed on your machine, see the installation guide.

Let’s start by spinning up a blank virtual machine. We’ll use Ubuntu 18.04, one of the operating systems used in Salt’s Jenkins infrastructure. In a new folder, create Vagrantfile then power up the machine:

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/bionic64"
  config.vm.hostname = 'salt-test-box'

  # Give the machine 2048MB of RAM.
  # This assumes you're using the virtualbox provider, see
  # https://www.vagrantup.com/docs/providers/ for information on the
  # other providers.
  config.vm.provider "virtualbox" do |vb|
    vb.memory = "2048"
  end
end
vagrant up

Let’s write some provisioning code while the ubuntu/bionic64 image is downloading and the machine is booting.

Configuring the Salt provisioner

While this machine will be used to work on the Salt codebase, there’s nothing stopping us from provisioning the machine itself with Salt!

Add to Vagrantfile:

  config.vm.synced_folder "salt", "/srv/salt", :nfs => true
  # required for nfs shared folder
  config.vm.network "private_network", type: "dhcp"

  config.vm.provision :salt do |salt|
    salt.install_type = "stable"
    salt.masterless = true
    salt.minion_config = "salt/minion"
    salt.run_highstate = true
  end

Create salt/minion:

file_client: local

Create salt/top.sls:

base:
  '*':
    - provision

And finally, create salt/provision.sls:

test:
  file.managed:
    - name: /home/vagrant/test.txt
    - contents: 'Provisioned with Salt'

This is the bare minimum setup required for provisioning a Vagrant VM with Salt.

Hopefully the machine has downloaded and booted by now. Tell Vagrant to provision the machine:

vagrant reload --provision && vagrant ssh

Check the file we defined in provision.sls has been created:

cat ~/test.txt
# Provisioned with Salt

Provisioning the machine

Now that we’ve tested a simple state, let’s update salt/provision.sls to install everything we need.

This code is a rough application of the steps written in Salt’s testing guide.

Start with installing required packages:

required_packages:
  pkg.installed:
    - names:
      - git
      - python3-pip

Then checkout the codebase:

codebase:
  git.latest:
    - name: https://github.com/saltstack/salt.git
    - target: /home/vagrant/salt
    - user: vagrant

And use pip3 to install the required dependencies:

python_requirements:
  pip.installed:
    - requirements: /home/vagrant/salt/requirements/tests.txt
    - bin_env: /usr/bin/pip3

Note the use of bin_env to use pip3.

Let’s run the state! As well as using vagrant provision or vagrant reload --provision, we can simply run salt-call without reloading the machine. We’ll include some logging to see what’s going on, especially because some of these operations will take a while.

sudo salt-call state.apply --log-level info

Running tests

Use python3 to execute the runtests.py script. Make sure to run python3 tests/runtests.py --help before anything else to see the full list of options. Running the script without arguments will start the entire testsuite, which can take a while!

Start by running a single unit test:

python3 tests/runtests.py --name unit.modules.test_ps

Then move onto the full collection of unit tests.

python3 tests/runtests.py --unit

Here’s the test I’m interested in, the x509 integration test:

python3 tests/runtests.py --name=integration.states.test_x509

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Starting integration.states.test_x509 Tests
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# sss
# ----------------------------------------------
# Ran 3 tests in 0.023s

# OK (skipped=3)

# -> integration.states.test_x509.x509Test.test_cert_signing  ->  Skip when no M2Crypto found
# -> integration.states.test_x509.x509Test.test_issue_49008   ->  Skip when no M2Crypto found
# -> integration.states.test_x509.x509Test.test_issue_49027   ->  Skip when no M2Crypto found

Hmmm, the tests were skipped due to a missing dependency. You’ll often find this since Salt has functionality for such a wide range of tools. We need to install the m2crypto Python package.

Unfortunately M2Crypto isn’t available for Python 3 yet, so we’ll have to run the tests using Python 2.

Let’s update the list of installed packages and add another pip.installed state call to install the requirements for Python 2.

  required_packages:
    pkg.installed:
      - names:
        - git
        - python3-pip
+       - python-pip
+       - python-m2crypto
python2_requirements:
  pip.installed:
    - requirements: /home/vagrant/salt/requirements/tests.txt

Running again with Python 2:

python tests/runtests.py --name=integration.states.test_x509

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Starting integration.states.test_x509 Tests
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ----------------------------------------------
# Ran 3 tests in 47.598s

# OK

Success!

If you’re feeling brave, you might want to run the entire suite:

python tests/runtests.py

Conclusion

Working with Salt’s testsuite may seem intimidating, but by leveraging Salt to spin up a temporary test environment we can get going quickly. SaltStack use Salt to provision their test setup in the cloud, see the salt-jenkins project.

To be truly productive with a setup like this, you could use another shared folder for /home/vagrant/salt, making it possible to edit the Salt source code on the host machine.

More from the blog

Rotating pet servers with SaltStack cover image

Rotating pet servers with SaltStack

How to rotate a machine with minimal downtime.


Glynn Forrest
Saturday, August 31, 2019

Secure servers with SaltStack and Vault (part 5) cover image

Secure servers with SaltStack and Vault (part 5)

Using the Consul storage backend and Consul Template for dynamic configuration files.


Glynn Forrest
Sunday, June 30, 2019

Secure servers with SaltStack and Vault (part 4) cover image

Secure servers with SaltStack and Vault (part 4)

Securing Vault with https and comparing Salt’s x509 states with Vault’s PKI secrets engine.


Glynn Forrest
Friday, May 31, 2019