How to check a chef recipe with serverspec

I like test-driven development. I’m used to thinking that test-driving chef cookbooks is hard – until I failed an interview because I lacked knowledge of chefspec. So that same date I looked into chefspec and related tools (rubocop, food critic, and serverspec). Chefspec itself is easy. The good news is that it allows some sort of testing. The bad news is that it’s stubbed unit tests, not integration tests. Serverspec to the rescue!

Serverspec allows functional integration tests of the cookbooks. But how do you set it up? I have some experience with vagrant, but honestly I don’t use it extensively in personal development. I do however use virtualbox a lot (along with chef server).

In my /etc/hosts I have written out a basic local network like so:

127.0.0.1       localhost          piousbox-samsung
127.0.0.1       sentact.local      zend.local
127.0.0.1       pi.local           webdevzine.local     sedux.local
127.0.0.1       cities.local       api.local
127.0.0.1       sleeper.local      nagios.local
127.0.0.1       wasya_co.local     wasya_co2.local
127.0.0.1       bjjc.local         bjjc-angular.local   anything.local

192.168.56.2    ubuntu15-virgin
192.168.56.3    centos-virgin
192.168.56.4    ubuntu14-virgin    ubuntu-virgin

192.168.56.10   lb_10.ubuntu       lb_10_spec

192.168.56.22   bjjc_22.ubuntu15   bjjc_22.ubuntu
192.168.56.23   bjjc_23.ubuntu14   bjjc_23.ubuntu
192.168.56.24   spec_24.ubuntu14   spec_24.ubuntu

192.168.56.31   jenkins1.local     jenkins.local
192.168.56.32   jenkins2.local
192.168.56.33   jenkins3.local
192.168.56.35   petclinic.local
192.168.56.37   nexus.local

192.168.56.40   centos-virgin.local
192.168.56.41   jenkins.centos

The above local DNS is not necessary, but I have found it convenient to have.

Next, there is a repo that contains my chef server workstation and a number of other things. Although chef-spec of each recipe goes into that recipe, the serverspecs all go into spec/ folder of my chef workstation. So let’s say I am testing that a recipe installs ruby. Generate the server spec with `bundle exec serverspec-init`. In spec/spec_24.ubuntu/sample_spec.rb I have the following:

require 'spec_helper'

describe command("/usr/local/rbenv/shims/ruby --version") do
its(:stdout) { should match /2.0.0/ }
end

and in the README.md for the repo I have the instructions to run it:

#
# vm_spec_24
#
# verifies ish::install_ruby
#
knife client delete vm_spec_24 -y ; \
knife node delete vm_spec_24 -y ; \
# sshpass -p "the_password" ssh oink@spec_24.ubuntu "echo the_password | sudo -S rm -rfv /etc/chef" ; \
VBoxManage controlvm "ubuntu14 spec_24" poweroff ; \
VBoxManage snapshot "ubuntu14 spec_24" restore "network ok" && \
VBoxManage startvm "ubuntu14 spec_24" --type headless && \
while ! ping -c1 spec_24.ubuntu &>/dev/null; do :; done ; \
knife bootstrap spec_24.ubuntu -N vm_spec_24 --ssh-user oink --ssh-password the_password -r "recipe[ish::install_ruby]" --sudo --use-sudo-password -y --environment vm_samsung && \
SUDO_PASSWORD=the_password TARGET_HOST=spec_24.ubuntu be rspec spec/spec_24.ubuntu

What the script does is:

  1. Deletes chef node and client
  2. (does not) delete the target machine’s chef identity – that’s the quick and dirty way, but the following step takes care of the same task cleaner.
  3. reverts the VM to a good clean configuration. Only the static network is configured.
  4. waits for the machine to boot
  5. bootstraps the machine with chef
  6. runs the serverspec

Voila! This answers my need for local semi-automatic testing using serverspec. Hope this helps! The disadvantage of this is that it doesn’t quite fit into CI/CD pipelines, but I will address that in a later post.

Leave a Reply

Your email address will not be published. Required fields are marked *