If you are using code to set up your infrastructure, then that code
demands as good or better software development practices as your
application code. One of the reasons I wanted to try Chef is because I
knew that people were doing automated testing of chef
configurations. (The puppet community may be doing similar kinds of
testing; I don’t know.) There is even a book about TDD and Chef:
Test-Driven Infrastructure With Chef
(and it’s even in its second edition). So I am going to try working
through it. The first several chapters are background or things I have
sort of done already. But they start to use Opscode’s Hosted Chef and
Vagrant in chapter 4.
Setting Up a New Playground
When I did the Quick Start exercises, I downloaded the starter kit
which included a .chef directory with a knife configuration file and
two .pem files: ckiser.pem and ckiser-validator.pem. I explored
downloading the starter kit again - but it wanted to reset my keys
which would have made my example stuff not work any more. It took me a
little while to figure out the instrutions on
http://docs.opscode.com/config_rb_knife.html
mean that one or the other file is read - not that one is read and
then the second file is read, overwriting configuration variables. I
was hoping to put shared information in ~/.chef/knife.rb and then
project specific information in a knife.rb in the project
directory. From my ‘puts’ statement, only one of the files is read - either
the one in the current directory or the one in ~/.chef/.
I can still get part of what I want - reuse of my keys - but moving
them to ~/.chef/ and then editing my knife.rb file to look for them
there. And then I created a new directory for working the TDI Chef
exercises, ~/chef-tdd/. I copied the knife.rb from the starter kit
into ~/.chef/knife.rb. I also created a cookbooks directory and copied
the chefignore file from the starter kit in there. And added the
extensive .gitignore from the starter kit. There are some other things
I think I will probably need but am going to wait until something I am
doing uses them before mving them here.
Vagrant
The last part of chapter 4 is about installing Vagrant and using it to
create virtual machines. Mostly this is just like what I have been
doing for a while now - but it linked me to a very promising source
for base boxes, Opscode’s Bento Boxes
One thing that makes them especially attractive is that the don’t come
with Chef preinstalled. Instead they recommend installing the most
current Chef using the vagrant-omnibus plugin. From the TDI book:
The plug-in we installed into Vagrant (vagrant plugin install
vagrant-omnibus) works with Vagrant boxes that do not have Chef
installed, and adds a hook to vagrant up to install Chef using the
omnibus package, just as we did in “Exercise 1: Install Chef ” on
page 47. This helps keep the Vagrant box slim and as close to
upstream as possible, and does not require a fleet of Vagrant boxes
to be created with every Chef patch release.
I want to test with a box that matches my Linode VPS as closely as
possible so despite the fact I already have an Opscode Ubuntu 12.04 in
32 bit, I downloaded and added the 64 bit version:
Then in ‘~/chef-tdd/ I did vagrant init opscode-ubuntu-12.04` and
then edited the Vagrant file to have the following:
Then I did vagrant up. But I didn’t see the lines I expected
regarding installing chef. I tried destroying the VM and then tring
vagrant up again - same result. I do have warnings about the Guest
Additions not matching the version of VirtualBox that the Opscode base
boxes were built against:
So I downloaded the latest version of VirtualBox, 4.3.6, and tried
again. That wasn’t so great. I got the following error message:
I currently have Vagrant 1.2.2 installed. Looking at vagrantup.com, it
looks like the latest is 1.4.3. Let’s see if upgrading Vagrant fixes
this. I downloaded the latest .dmg file and ran the installer. Then
my next attempt at vagrant up gave me:
I unstalled both plugins. I don’t remember installing
vagrant-berkshelf so, for now, I am not going to reinstall it. I only
reinstalled vagrant-omnibus (1.2.1). But still no joy: no chef-client
or knife when I log into the box. ARRRGGGGGG because I was editing the
wrong Vagrantfile! I had put the line about vagrant-omnibus into the
file in chef-repo that I had opened for reference. OK let’s destroy
and recreate the VM. And now, we get chef:
Ch 7.2: Berkshelf
Much of configuration management (and really any software development)
is dependency management. Throughout the TDI book we were managing
that manually - downloading cookbooks, checking dependencies,
downloading more cookbooks. Finally in chapter 7 the author introduces
Berkshelf to do the managing for us. So now I am going to need the
vagrant-berkshelf plugin that I delayed reinstalling in the section
above. vagrant plugin install vagrant-berkshelf. And then the book
told me to run berks configure. This creates a default configuration
file in ~/.berkshelf/config.json. Not really sure what the implications
of some of the items in this are. I’ll have to see as they arise.
Since I didn’t make the stand alone IRC cookbook from chapter 3, I
can’t do the berks init stuff. And the discussion of Berkshelf kind
of trailed off promising more information about preferred workflows
later in the chapter.
Ch 7.3: Application Cookbooks and Test Kitchen
What are application cookbooks and why should we use them?
The application cookbook pattern is characterized by having decided
the top-level service that we provide and creating a cookbook for
that service. That cookbook wraps all the dependent services that
are needed to deliver the top-level service. … This looks a lot
like the kind of thing that might be accomplished using a Chef role,
but has some significant advantages. First of all, cookbooks can be
explicitly versioned and tracked in a way that roles can’t.
If there is a need to alter the behavior of an upstream cookbook,
attributes can be set in a recipe, and if functionality needs to be
added, tested, or tweaked, this can be achieved by wrapping upstream
cookbooks in a manner that looks much like object inheritance. This
has the twin advantages again of being testable, but also of
avoiding constant fork‐ ing of upstream cookbooks.
The test harness tool of choice - at least of this author - is
Test Kitchen. I installed
it with gem install test-kitchen. The tests themselves are created
using a combination of cucumber, rspec, and leibniz (a library written
by the author).
Things were going OK until about page 200 where there were some
missing steps - such as where are we putting our specs and how did we
get our spec_helper - and for that matter, where did we get rspec
since it wasn’t on the list of gems we were told to add to our
Gemfile. The errata page has a complaint from someone else who noticed
this - but not resolution. I spent some time wandering around, read,
asked some questions on the #chef IRC channel. And then with some
perspective, went back to the book to see if I could reverse engineer
stuff using the code listings.
First, if we are going to run rspec -fd, we need rspec installed. So
I added it to the Gemfile and did bundle install. Now I can run
rspec --init and that created a spec directory and put a
spec_helper.rb file in there. I am kind of unclear about where to put
the integration test at the bottom of p 202 so I skipped them and
started with the unit test at the top of p 204. I typed in the ‘let’
and the first ‘it’ and then ran the spec. It complained uninitialized
constant ChefSpec so I added ‘chefspec’ to my Gemfile and a “require
‘chefspec’” to the top of my default_spec.rb. Then it complained it
couldn’t find chef. I know I have chef - but it wasn’t in my
Gemfile. So I added it to my Gemfile and reran ‘bundle’. Now I am
getting an odd error about about Chef - and a really old version of
chef. How did that get in here? And how did I end up with chefspec
0.0.1?
I created a completely new rvm gemset, removed my Gemfile.lock and did
a completely new bundle install. This time I only have chef 11.8.2
along with chef-spec 3.2.0 But even after fiddling with how I required
chefspec, I am still getting complaints abotu uninitialized constant
ChefSpec::ChefRunner. So I am going to give up on the example in
Chapter 7 “Acceptance Testing: Cucumber and Liebniz”.
Ch 7.5: Integration Testing: Test Kitchen with Serverspec
The next section on integration testing does a better job of showing
us the steps. But introduces one more layer - using Test Kitchen to
manage your Vagrant boxes. It is sort of nice because it gives you one
file that declares which OSs you are going to test your cookbook on -
and takes care of setting up what one needs to do that. But it is one
additional layer AND it means I am swimming in Vagrantfiles - most of
which are not used. I had thought I would test my full configuration
on a local Vagrant instance, so I created a Vagrantfile at the top of
my chef-tdd directory. When I did berks cookbook cnk-blog, that
automatically created a Vagrant file. And now when I edited the
.kitchen.ym file (which berks also created for me) and ran kitchen
create all, that created a Vagrantfile in
.kitchen/kitchen-vagrant/default-ubuntu-1204/ I think it is this last
Vagrantfile that I am actually seeing when I do kitchen list - since
I can’t ssh in when I am in ~/chef-tdd/cookbooks/cnk-blog/ but I can
if I am in
~/chef-tdd/cookbooks/cnk-blog/.kitchen/kitchen-vagrant/default-ubuntu-1204
My cookbook doesn’t yet have a run list, but if I run kitchen
converge anyway, that installs ruby and chef from the Opscode Omnibus
installer. So now, where do I put the tests. I don’t think I want to
use BATS unless I have to - I am better at Ruby than bash. So I read
that part but then started working through the Serverspec exercises
starting at p 241. I created a test file in the magic place (test
kitchen figures out it needs busser and then what kinds of tests to
run based on the directory names).
Oops. I didn’t read carefully and missed creating the helper file in
cnk-blog/test/integration/default/serverspec/spec_helper.rb
Now I get properly failing tests - that is failing because I have not
written the cookbook code to make them work.