Shef: The Chef REPL
Shef is a way to run chef in an Interactive Ruby (IRb) session. Shef currently supports recipe and attribute file syntax, as well as interactive debugging features.
Shef has three run modes:
"Hello World" in Shef: A Simple Example
For this example, we'll use the standalone mode. (For solo or client modes, you would need to run shef using the
If chef is installed from gems or your platform's package manager, shef should be in your path already. (If you are running chef from a git clone, shef is in
At any point, you may use the
Now your typing will be evaluated the same context as recipes. We can create a file resource:
At this point, shef has created the resource and put it in the run list, but not yet created the file. To initiate the chef run, use the
Shef also lets you switch to the same context as attribute files. You can set an attribute with the familiar syntax:
Now you can switch back to recipe context and use the attribute you set:
Now, run chef again:
Note that the first resource we created, (
Finally, we can see that the files were created using shef's ls method:
Client and Solo Modes
Shef provides client and solo modes which allow it to emulate running chef-client or chef-solo with additional debugging features. When running in client mode, shef will parse any provided JSON attributes file, then connect to the chef server and synchronize cookbooks. The node will have the same attributes and run list as it would during a chef client run. Solo mode is similar, except that attributes are loaded from a JSON file instead of a Chef server. Solo mode is activated with the
Shef's configuration file support is new in Chef 0.9.8. (With older versions of chef, use the
The shef configuration file
In your shef.rb you can configure shef just as you would configure the chef-client. To configure shef to properly authenticate with your Chef server, you might copy the
You can also disable ohai plugins to speed up shef's boot process or include any arbitrary ruby code in your shef configuration file.
Running Shef as a Client Using Your Knife Credentials
By default, shef loads in standalone mode that doesn't connect to a server. On your laptop/desktop, you might want to run shef in client mode in order to test a feature that's only available when using Chef in the client-server configuration (i.e., Search or Data Bags).
If you've already configured Knife on your box, you can borrow knife's credentials to connect to the server. In order for this to work, you need to create a node with the same name that your knife client uses to authenticate. This is the
Once you've done this, you can run shef in client mode, where it will connect to the server via the config value with 'chef_server_url'. By passing it your knife.rb, it will connect to the server via the config value.
Managing Chef Server with Shef
When properly configured to access your Chef Server, shef can list, show, search for, and edit cookbooks, clients, nodes, roles, and databags.
These commands all have a syntax of the form items.command so to list nodes, you use
The list command can take a code block, which is applied (but not saved) to each object returned from the server. You can list all of your nodes and their run lists:
To find a specific node, use
You can also use search from within shef. To search for nodes, use
Again, you can give a code block to "format" the results:
Debugging Recipes with Shef
Shef gives you the ability to manipulate your current position in the run list during a chef run. To use this feature, you'll need at least one breakpoint in one recipe in the client's run list.
The Breakpoint Resource
Breakpoints are implemented as a chef resource. The breakpoint resource has no custom attributes. Its default action is break. When the break action is called on the breakpoint provider, the provider tests if it is running under shef, or a normal chef-client (or solo) run. If it's running under chef-client or chef-solo, no action is taken; if it's running under shef, the chef run will be paused.
Stepping Through the Run List
To explore how breakpointing and manually controlling chef execution works, create a simple recipe in shef:
Now, run chef:
We can see that chef ran the first resource before the breakpoint,
From here, we can tell shef to finish the chef run using
If we list our
We can also rewind the chef run and step through it:
From the output, we see that we rewound the run list, but when we executed the resources again, they repeated their checks for the existence of the files and found that they now exist, so chef skipped creating them. If we delete the files:
Then we can rewind and resume the chef run and get the expected results:
Debugging existing recipes
You may also find it helpful to use Shef to debug existing recipes. First you will need to add the recipe to the run_list for the node so it is cached when starting shef. Once this is done, you can use the recipes for debugging. You can see which recipes it cached when starting, as it will output these when loading shef:
If you just want to just load one recipe from the run_list, you can do this by going into recipe mode and using the "include_recipe" command:
If you wanted to load all of the recipes from the run_list, you could do this in recipe mode by using this code:
Once the recipes you want to debug have been loaded, you can use the
Turn Debugging up to 11
In shef, it's possible get get extremely verbose debugging using irb's tracing feature. Shef provides a shortcut to turn tracing on and off, using
For example, here's the output from turning tracing on and off:
You can see the help in Shef by using the command "help". There is different help information posted if you are in recipe or attributes mode.
Use Case Scenario
To get a list of nodes with recipe postfix I'm using search(:node,"recipe:postfix"), but in this case I actually only want nodes with the postfix sub-recipe "delivery" (postfix::delivery). How do I express that correctly in the search syntax? (just putting in "postfix::delivery" returns an error)
One approach would be to use shef:
Single vs. double quotes is significant here since you want to include a backslash in the string instead of having ruby interpret it as an escape.