Notes about puppet-litmus testing

March 7, 2021 

Puppet Litmus is a Puppet acceptance test harness that leverages on many existing, proven technologies. In the long run Litmus aims to replace Beaker. However, the latter is currently still the only way to run multi-machine (e.g. cluster) acceptance tests in a standardized way. For example we use Beaker for our Keycloak domain mode cluster tests in puppet-module-keycloak. On a high-level the test process is the same as with Beaker:

  • Provision the SUT ("System Under Test")
  • Run tests
  • Tear down the SUT

Litmus supports various backends for creating the SUT, including, but not limited to, Docker and Vagrant. Provisioning of the SUT is done with Puppet Bolt. Acceptance tests are written using ServerSpec.

To get started with Litmus testing check out the following articles first:

In a perfect world where everything is set up correctly the Litmus test process in Vagrant is as follows:

# Create the SUT. "vagrant" is an arbitrary name for the
# node list in provision.yaml
pdk bundle exec rake 'litmus:provision_list[vagrant]'

# Install Puppet 6 on the SUT
pdk bundle exec rake 'litmus:install_agent[puppet6]'

# This step is only required with Vagrant
pdk bundle exec bolt task run provision::fix_secure_path --modulepath spec/fixtures/modules -i inventory.yaml -t ssh_nodes

# Install our module and its dependencies on the SUT
pdk bundle exec rake "litmus:install_module"

# Run the acceptance tests
pdk bundle exec rake litmus:acceptance:parallel

# Tear down the SUT
pdk bundle exec rake litmus:tear_down

As the world is rarely perfect, this will probably break on many levels. Here are the issues I encounted - your mileage may vary:

  • You can't have "StrictHostKeyChecking no" in ~/.ssh/config or Litmus will choke with a somewhat misleading error message.
  • Litmus uses puppet module install to install the dependency modules. on top of that it gets the dependency module list from metadata.json and not from .fixtures.yml or Puppetfile. This introduces some challenges:
    • metadata.json does not support locking Puppet module dependencies to certain versions (e.g. puppetlabs/stdlib = 7.0.0). Therefore you will need to give an upper and lower bound for the module versions. This means that your unit tests, which use .fixtures.yml might pass, but you Litmus acceptance tests might fail, because a dependency module version is not identical to the one used in the unit tests. Or the other way around.
    • puppet module install downloads module dependencies by default. This canpotentially result in a dependency hell. It seems possible to override dependency management, though.
    • For Puppet Litmus you'd prefer to have strict dependencies (in metadata.json), but for generic use you'd like to have loose dependencies to avoid creating a depency hell for users of your module who may be using puppet module install.

Some useful tricks I learned while debugging Litmus:

# Run arbitrary command on the SUT
pdk bundle exec bolt command run "ls /etc/puppetlabs/code/environments/production/modules" -i inventory.yaml -t ssh_nodes

# List all Litmus (rake) tasks
pdk bundle exec rake --tasks

# Debug dependency module installation
pdk bundle exec rake "litmus:install_module" --trace

More blog posts about Litmus will follow as I figure out reasonable solutions to the challenges listed above.

More on Puppet testing:

Samuli Seppänen
Samuli Seppänen
Author archive