Debugging rspec-puppet

February 7, 2020 – Samuli Seppänen

Usually writing rspec-puppet tests is straightforward. Then you run into some problem that does not make any sense. In my case I ran into a couple of duplicate resource declaration issues when adding rspec tests to our puppetmaster-installer. The error message was not that useful:

$ pdk test unit --tests=spec/classes/puppetboard_spec.rb
--- snip ---
 1) puppetmaster::puppetboard minimal settings on ubuntu-18.04-x86_64 is expected
    to compile into a catalogue without dependency cycles
    Failure/Error: it { compile }
      error during compilation: Evaluation Error: Error while evaluating a
      Resource Statement, Duplicate declaration: Class[Apache] is already
      declared; cannot redeclare (file: /home/samuli/opt/puppeteers/puppet-
      line: 156) (file: /home/samuli/opt/puppeteers/puppet-puppetmaster
      /spec/fixtures/modules/puppetmaster/manifests/puppetboard.pp, line: 156,
      column: 3) on node
--- snip ---

That particular block of code in puppetboard.pp did include a parameterized call to the apache class. But nothing else (afaics) included that class. Also, in Vagrant the same code worked just fine, which made things seem even more bizarre. To add insult to the injury the error message did not tell where else the apache class was included. That would probably have been way too helpful.

The key here was to understand that rspec-puppet's catalogue generation is more strict than the catalogue generation done by Puppet itself. The above issue was caused by a dependency module doing an "include ::apache" when it was called with the default settings. Another closely related issue cropped up in my rspec-puppet tests. The apache class included certain apache modules by default, and rspec-puppet did not like the fact that they were included - without any parameters - a second time in our code.

The lesson is that rspec-puppet does not tolerate duplicate includes - no matter whether they're in the format of "include ::someclass", "class { 'someclass': param => 'value' }", or a combination of the two. You just need to refactor the Puppet code so that you can keep rspec-puppet happy.

While debugging I scoured the Internet for rspec-puppet debugging tips and here are a few. To get rspec-puppet to print debug output add this to the context (from here):

Puppet::Util::Log.level = :debug

In the above case this did not help at all, but in some cases it might. A second trick (from here) was printing the catalogue that rspec-puppet generates by adding a new "test" to the context block:

it { pp catalogue.resources }

This spits out the catalog which contains all the classes, resources and their parameters. If you're having duplicate class declaration issues you won't get a catalog, of course. But when you comment out the offending code temporarily, you should see the duplicate class declaration in the catalog and can then figure out where it came from.

Of course old-school methods like grepping the (dependency) module directory for "include ::classname" and "class { '::classname'" can give some hints as well.

More on Puppet testing:

Want to talk to an expert?

If you want to reach us, just send us a message or book a free call!