CNK's Blog

Chef Concepts

The best - and sometimes the worst - thing about Chef is its thriving ecosystem. There appear to be cookbooks for just about everything, including one just for installing emacs. And there are lots of tools, lots of instructions, and blog posts about using the tools. So many in fact that it is sometimes hard to sort out what is what - especially as many of them are focused on showing you an example project rather than talking about broad concepts. I have picked up a number of useful tidbits but it’s been a little tough to integrate everything.

So, let me start with what I think I want. I want to use chef to set up my Linode VPS. Before I make changes to the server, I would like to run the same recipes locally on a Vagrant VM and run some integration tests to check that I am going to get what I intend. Eventually I may want to add unit tests for my custom cookbooks but for now, I want to just focus on ‘outside in’ testing which, in this situation, seems to me to be integration tests. This blog post, Getting Started Writing Chef Cookbooks the Berkshelf Way, Part 3, appears to do what I want to do, so let’s give that a try.

Initialize

I was originally going to manage everything using chef solo because I thought that would be easier. But it turns out that chef and all the tooling around it are optimized for using chef server to manage lots of nodes, so chef server is actually the easier option - especially since Opscode has a free tier for managing up to five nodes.

When I created my Linode VM with knife-linode I did it all on the command line - no files. So that means that while I have a node named ‘linode’ on my hosted chef and my Linode VPS is set up as a client of that node, I don’t have any files and no git repository keeping track of anything. So let’s see if I can construct something to connect to that node on https://manage.opscode.com/. So I stared with the chef gemset I had previously set up and upgraded (chef 11.10.0, kitchen-vagrant 0.14.0, test-kitchen 1.1.1).

    $ mkdir ~/chef-linode
    $ cd chef-linode
    $ kitchen init
          create  .kitchen.yml
          create  test/integration/default
             run  gem install kitchen-vagrant from "."
    Successfully installed kitchen-vagrant-0.14.0
    Parsing documentation for kitchen-vagrant-0.14.0
    Installing ri documentation for kitchen-vagrant-0.14.0
    1 gem installed

kitchen init created the .kitchen.yml file which is used to configure my test kithcet setup. I added the port forwarding I will need once I have a web server running on my VM. It is configured to spin up a Vagrant VM with the Opscode Ubuntu 12.04 provisionless VM and then use chef_omnibus to install the latest version of chef and provision with chef_solo. I am not sure whether I should be using chef_solo (so I can test out my cookbooks before they get uploaded to Opscode) or if I should change that configuration to point to the linode node on my chef server.

First server spec

So based on the example I worked through from the TDD book, and some other blog posts, I created a spec file in chef-linode/test/integration/default/serverspec/emacs_spec.rb

    require 'spec_helper'

    describe "emacs cookbook" do
      it "should have installed emacs" do
        expect(package 'emacs23-nox').to be_installed
      end
    end

I think this should indicate to test kitchen that I want it to use serverspec to check that I have installed emacs on the VM. Right now, I don’t have a spec_helper file; according to the Getting Started section, I get one by doing serverspec-init. But I am a little unclear about where to put it. The serverspec docs indicate I should have the directory a directory structure like:

    |-- spec
    |   |-- spec_helper.rb
    |   |-- <servername> # e.g localhost or cynthiakiser.com
    |       |-- emacs_spec.rb

But the blog post I am following, shows a directory structure of:

    |-- serverspec
    |   |-- spec_helper.rb
    |   |-- <servername> # e.g localhost or cynthiakiser.com
    |       |-- emacs_spec.rb

And I need a spec_helper.rb. serverspec-init will generate one:

    (brazen:~/chef-linode/test/integration/default) $ serverspec-init
    Select OS type:

      1) UN*X
      2) Windows

    Select number: 1

    Select a backend type:

      1) SSH
      2) Exec (local)

    Select number: 2

     + spec/
     + spec/localhost/
     + spec/localhost/httpd_spec.rb
     + spec/spec_helper.rb
     + Rakefile


    (brazen:~/chef-linode/test/integration/default) $ cat serverspec/spec_helper.rb
    require 'serverspec'

    include SpecInfra::Helper::Exec
    include SpecInfra::Helper::DetectOS

    RSpec.configure do |c|
      if ENV['ASK_SUDO_PASSWORD']
        require 'highline/import'
        c.sudo_password = ask("Enter sudo password: ") { |q| q.echo = false }
      else
        c.sudo_password = ENV['SUDO_PASSWORD']
      end
    end

But the spec_helper file in the blog post is:

    # myface/test/integration/default/serverspec/spec_helper.rb
    require 'serverspec'
    require 'pathname'

    include Serverspec::Helper::Exec
    include Serverspec::Helper::DetectOS

    RSpec.configure do |c|
      c.before :all do
        c.os = backend(Serverspec::Commands::Base).check_os
      end
    end

I am going to try a combination. I moved my emacs_spec.rb into spec/localhost/ and then renamed the spec directory to serverspec. I left the includes of the SpecInfra::Helper modules but removed the sudo stuff and replaced it with the check_os stuff from the blog post. So now I have:

    # chef-linode/test/integration/default/serverspec/spec_helper.rb
    require 'serverspec'

    include SpecInfra::Helper::Exec
    include SpecInfra::Helper::DetectOS

    RSpec.configure do |c|
      c.before :all do
        c.os = backend(Serverspec::Commands::Base).check_os
      end
    end

Yeah!!! now my kitchen converge boots, tries to converge (but I have nothing in my runlist) and then runs the test.