Chef Development with Shef

I thought I’d do a post how to use Shef, the interactive chef console, for iterative cookbook development for those times when you just want to experiment without uploading anything to the server as it’s a workflow I use heavily and have found really useful.

Getting Started

To get yourself set up for developing with shef, you’ll need to perform the following steps:

  • Make sure you have a copy of your chef repository on a machine similar to that on which your cookbooks normally run
  • Make sure you’re on the latest version of the chef gem (a bunch of shef bugs were fixed post-0.10.4)
  • Create a file called solo.rb somewhere, and paste the following into it (changing cookbook_path to wherever you have your chef repo):
file_cache_path "/var/chef-solo"
cookbook_path "/home/myuser/chef/cookbooks"
  • Create a file called anythingyoulike.json, containing the following (changing mycookbook as appropriate):
{ "run_list": [ "recipe[mycookbook::default]" ] }
  • Please note, this run_list should contain all the cookbooks you want to be available to you while using Shef. It doesn’t have to be the entire cookbooks directory, but make sure that if your cookbook has any dependancies, you include them here.
  • Run the following command (changing file paths as applicable):
sudo shef --solo --config ~/solo.rb -j ~/anythingyoulike.json
  • You’re now in the shef console, and should see output like the following:
loading configuration: /etc/chef/solo.rb

Session type: solo

Loading..[Tue, 07 Feb 2012 10:49:36 +0000] INFO: Run List is []

[Tue, 07 Feb 2012 10:49:36 +0000] INFO: Run List expands to []

done.

[Tue, 07 Feb 2012 10:49:36 +0000] INFO: Setting the run_list to ["recipe[mycookbook]"] from JSON

This is shef, the Chef shell.

Chef Version: 0.10.8

http://www.opscode.com/chef

http://wiki.opscode.com/display/chef/Home

run `help' for help, `exit' or ^D to quit.

Ohai2u me@mydomain.com!

chef >

You’re now using the shef console!

You can see that the run_list we specified in the JSON file has been picked up by shef. In the context of shef, this run_list is the list of cookbooks available to it, none of then will actually be run until we tell it to.

Performing a Chef Run

Now, to actually run our cookbook, we want to enter the recipe context. This is done as follows:

chef >
chef > recipe
chef:recipe >

Next, we need to load our cookbook into the current context. Please note, you can only do this with cookbooks that are shown in the run_list. Otherwise shef won’t know where to find them.

We do that with the include_recipe command:

chef:recipe>
chef:recipe > include_recipe "mycookbook::default"
=> [#<Chef::Recipe:0x00000004b5bdb0 @cookbook_name=:mycookbook, @recipe_name="default", @run_context=#<Chef::RunContext:0x00000004c1a300

<snip>

chef:recipe>

The above command will give you a huge amount of output, as it’s basically loading all of the resources from the recipe we gave it.

Next, to perform a chef run, use the following command:

chef:recipe>
chef:recipe > run_chef

At this point, you’ll see the mycookbook::default recipe run, producing the same output as you’d expect to see during a normal chef run, with the same sort of errors too.

Advanced Debugging

Trace Logging

If the standard output of a chef run doesn’t give you enough information, you can also turn on more verbose logging by using irb’s trace facility. This is enabled by running the following command:

chef > tracing on

Breakpoints

One of the most awesome features of Shef is the ability to add breakpoints to your recipes. This allows you to pause chef runs, and step forwards and backwards through the run between breakpoints.

The chef wiki goes into a lot of detail on breakpoints here, so I won’t repeat all of what it says, just give an outline.

To add a breakpoint to your recipe, simply add the following:

breakpoint "foo"

Breakpoints will be ignored during the course of a normal chef run (ie using chef-client), so don’t worry if you forget to remove one from your code. If you’re running using Shef, however, when the run hits the breakpoint, it will be paused.

You can now check the state of the system, make sure the recipe has done what you expected it to so far, and then assuming you’re happy to continue, run the following command:

chef:recipe > chef_run.resume

The opscode wiki page I linked above goes into more detail on actions like rewinding the chef run, and stepping through the run pausing at the next breakpoint, so if you’re still reading this post after all that, I’d recommend you have a look.