I have put a lot of work into my Ark cookbook lately and it has grown to such complexity that it my existing set of workflow and tools were not enough. I needed some way to sanity check my code. I needed tests.
You can't refactor with confidence without tests. I have fully rewritten ark several times already. After making serious changes, I then had to spend roughly 20 minutes manually testing the cookbook to ensure that it still worked the way I intended it to. This manual testing was getting tedious, really tedious.
Looking at my various options, I first took a look at Cucumber. There are some nice tools out there like simple_cuke, cuken, and ironfan-ci. I made some attempts at using simple_cuke but they failed fairly miserably. That's because I thought I could use Cucumber without having to actually learn it. Cucumber is a powerful and somewhat complex tool. To benefit from using Cucumber, I was really going to have to read the The Cucumber Book, something I don't have time for right now. I needed the simplest thing that could possibly work. Further, the real benefit of Cucumber appears to be the communication it facilitates between customer and engineer. In this case I am the client and the extra layer of communication is unnecessary.
Enter MiniTest
Luckily for me, BTM and Andrew Crump have been doing great work adapting David Calavera's chef-minitest-handler https://github.com/calavera/minitest-chef-handler. MiniTest is the simplest thing that could possibly work. It works by gathering all the files that match the path files/default/tests/minitest/*_test.rb for the cookbooks in your run_list into /var/chef/minitest/. Once your regular chef run completes, the minitest-handler cookbook executes each of those tests and displays the results.
Note: As of 30 April, 2012 there is a known bug in minitest-handler that requires you run chef-client twice in a row for the tests to actually execute.
Update: Set the network type to bridged and you won't have this problem config.vm.network :bridged
My ark cookbook is really just an LWRP. The default recipe does nothing more than install the unzip package on those *nix distributions that don't have it by default. I created an ark::test recipe that exercises all of the features of the ark resource. Here is a snippet of it.
ark/recipes/test.rb
Next, I have my actual MiniTest tests classes, scoped according to my recipe "test"
ark/files/default/tests/minitest/ark_test.rb
To run these tests I have to first add chef_handler and minitest-handler to the beginning of my run_list. Note that the minitest-handler-cookbook doesn't yet support chef-solo, but I patched it over the weekend so that it does.
Here is what the output of the tests looks like
I originally wrote my tests using MiniTest::Unit test cases. However, just a few days ago I discovered the awesomeness that Andrew Crump has built into MiniTest::Spec and a whole set of helper modules that really simplify the code you have to write.
Here is a snippet to test whether a file exists and has the correct owner
I especially love all of the resource matchers that let you connect to the actual resource objects created during your Chef run and to check whether their configuration matches the actual system state.
For some detailed examples, check out this example.
The Not so Simple Part
Ok, that was the easy part. The harder part is to run your tests on a clean environment. You need a relatively fresh VM to ensure that the tests are actually testing changes made by your cookbook and not some change made eons ago for another purpose. The simplest tool for this is Vagrant. Here is the Vagrantfile I use to test the ark cookbook.
Here are the commands you run to provision your new server using vagrant
Note when using Vagrant that bridging your VM's network to your wireless network connection is not a straightforward process, at least not on my linux laptop. If you run linux as your primary OS, I recommend you spare yourself the pain of trying to bridge your VM to wlan0 and just stick an Ethernet cable into your laptop.
While Vagrant is an awesome tool it is not the best tool for all situations. One issue I have is that my corporate workstation is a very locked down Windows Vista installation. I do virtually all of my work within an Ubuntu virtual machine running on top of Virtualbox. Vagrant uses virtualbox to create new virtual machines and there is no mechanism to run virtualbox within virtualbox. Earlier this year I attempted to get ruby and vagrant running on Windows Vista but after 4 hours I didn't even have ruby running on Vista.
Another issue with Virtualbox is that it's flexibility comes at a cost. Virtualbox runs entirely in userspace which means it is less performant than other virtualization tools such as KVM and linux containers (LXC). This performance lag could become very noticeable once you try to spin up a 4-node postgresql cluster. Of the three virtualization technologies mentioned here, LXC adds the least overhead as each container shares the same copy of the running kernel. There may be other optimizations that LXC provides which I am not aware of.
Enter LXC and Toft
As mentioned earlier, LXC has the least overhead of the other virtualization technologies available. Toft is a ruby library for testing chef and puppet cookbooks using LXC containers. Toft has a beautiful interface for working with containers as you can see in the following code.
I can easily see spinning up a multi-node cluster with toft, running my tests, then destroying the whole setup.
While toft has an excellent interface it is a young project with some rough edges, some of which have more to do with LXC than the toft library itself. To get started with toft, there are some nice guides on the project wiki
I very unpleasantly discovered that the version of the cgroup-lite package for Ubuntu 11.10 installed with LXC will make your system unbootable. The solution is to install cgroup-lite from the oneiric-proposed repository.
Another pitfall is that the bind9 and dhcpd servers installed along with LXC will report erroneous information after initial installation. The fix is easy. Run the `lxc-prepare-host` command and you will be in business.
Finally, at this time Toft only works with Ruby 1.8.7 and does not support Ruby 1.9.*
As noted in the Vagrant section, I highly recommend connecting your laptop to Ethernet if your primary OS is Linux.
Summary
Testing is an exciting and increasing critical part of developing infrastructure. While the existing tools need some polish, you can get a lot done with them. The linkbait title of this post is a bit disingenous. Testing w/ minitest is a breeze, but using vagrant and toft are not straightforward affairs. Toft, in particular, is extremely promising and worthy of your attention. It is extremely well-suited to many of the usecases that Vagrant is not.
You can't refactor with confidence without tests. I have fully rewritten ark several times already. After making serious changes, I then had to spend roughly 20 minutes manually testing the cookbook to ensure that it still worked the way I intended it to. This manual testing was getting tedious, really tedious.
Looking at my various options, I first took a look at Cucumber. There are some nice tools out there like simple_cuke, cuken, and ironfan-ci. I made some attempts at using simple_cuke but they failed fairly miserably. That's because I thought I could use Cucumber without having to actually learn it. Cucumber is a powerful and somewhat complex tool. To benefit from using Cucumber, I was really going to have to read the The Cucumber Book, something I don't have time for right now. I needed the simplest thing that could possibly work. Further, the real benefit of Cucumber appears to be the communication it facilitates between customer and engineer. In this case I am the client and the extra layer of communication is unnecessary.
Enter MiniTest
Luckily for me, BTM and Andrew Crump have been doing great work adapting David Calavera's chef-minitest-handler https://github.com/calavera/minitest-chef-handler. MiniTest is the simplest thing that could possibly work. It works by gathering all the files that match the path files/default/tests/minitest/*_test.rb for the cookbooks in your run_list into /var/chef/minitest/. Once your regular chef run completes, the minitest-handler cookbook executes each of those tests and displays the results.
Note: As of 30 April, 2012 there is a known bug in minitest-handler that requires you run chef-client twice in a row for the tests to actually execute.
Update: Set the network type to bridged and you won't have this problem config.vm.network :bridged
However, running in bridged mode may require sudo.
My ark cookbook is really just an LWRP. The default recipe does nothing more than install the unzip package on those *nix distributions that don't have it by default. I created an ark::test recipe that exercises all of the features of the ark resource. Here is a snippet of it.
ark/recipes/test.rb
Next, I have my actual MiniTest tests classes, scoped according to my recipe "test"
ark/files/default/tests/minitest/ark_test.rb
To run these tests I have to first add chef_handler and minitest-handler to the beginning of my run_list. Note that the minitest-handler-cookbook doesn't yet support chef-solo, but I patched it over the weekend so that it does.
Here is what the output of the tests looks like
I originally wrote my tests using MiniTest::Unit test cases. However, just a few days ago I discovered the awesomeness that Andrew Crump has built into MiniTest::Spec and a whole set of helper modules that really simplify the code you have to write.
Here is a snippet to test whether a file exists and has the correct owner
I especially love all of the resource matchers that let you connect to the actual resource objects created during your Chef run and to check whether their configuration matches the actual system state.
For some detailed examples, check out this example.
The Not so Simple Part
Ok, that was the easy part. The harder part is to run your tests on a clean environment. You need a relatively fresh VM to ensure that the tests are actually testing changes made by your cookbook and not some change made eons ago for another purpose. The simplest tool for this is Vagrant. Here is the Vagrantfile I use to test the ark cookbook.
Here are the commands you run to provision your new server using vagrant
Note when using Vagrant that bridging your VM's network to your wireless network connection is not a straightforward process, at least not on my linux laptop. If you run linux as your primary OS, I recommend you spare yourself the pain of trying to bridge your VM to wlan0 and just stick an Ethernet cable into your laptop.
While Vagrant is an awesome tool it is not the best tool for all situations. One issue I have is that my corporate workstation is a very locked down Windows Vista installation. I do virtually all of my work within an Ubuntu virtual machine running on top of Virtualbox. Vagrant uses virtualbox to create new virtual machines and there is no mechanism to run virtualbox within virtualbox. Earlier this year I attempted to get ruby and vagrant running on Windows Vista but after 4 hours I didn't even have ruby running on Vista.
Another issue with Virtualbox is that it's flexibility comes at a cost. Virtualbox runs entirely in userspace which means it is less performant than other virtualization tools such as KVM and linux containers (LXC). This performance lag could become very noticeable once you try to spin up a 4-node postgresql cluster. Of the three virtualization technologies mentioned here, LXC adds the least overhead as each container shares the same copy of the running kernel. There may be other optimizations that LXC provides which I am not aware of.
Enter LXC and Toft
As mentioned earlier, LXC has the least overhead of the other virtualization technologies available. Toft is a ruby library for testing chef and puppet cookbooks using LXC containers. Toft has a beautiful interface for working with containers as you can see in the following code.
I can easily see spinning up a multi-node cluster with toft, running my tests, then destroying the whole setup.
While toft has an excellent interface it is a young project with some rough edges, some of which have more to do with LXC than the toft library itself. To get started with toft, there are some nice guides on the project wiki
I very unpleasantly discovered that the version of the cgroup-lite package for Ubuntu 11.10 installed with LXC will make your system unbootable. The solution is to install cgroup-lite from the oneiric-proposed repository.
Another pitfall is that the bind9 and dhcpd servers installed along with LXC will report erroneous information after initial installation. The fix is easy. Run the `lxc-prepare-host` command and you will be in business.
Finally, at this time Toft only works with Ruby 1.8.7 and does not support Ruby 1.9.*
As noted in the Vagrant section, I highly recommend connecting your laptop to Ethernet if your primary OS is Linux.
Summary
Testing is an exciting and increasing critical part of developing infrastructure. While the existing tools need some polish, you can get a lot done with them. The linkbait title of this post is a bit disingenous. Testing w/ minitest is a breeze, but using vagrant and toft are not straightforward affairs. Toft, in particular, is extremely promising and worthy of your attention. It is extremely well-suited to many of the usecases that Vagrant is not.










