Something I’ve started to discuss more often is the idea of using a cookbook-generator sooner then later. Unfortunately, if you start too soon, it can cause some significant confusion to a newbie Chef user. My post here is going to do the best I can to explain why you should and give you a clear understanding why you need to put it in your workflow as soon as possible.

Why?

This spurs from my conversations with VMware based companies. If you have used chef-provisioning-vsphere to do any test-kitchen work, you’ll quickly realize that getting a .kitchen.yml that works is hard. (If you’re curious here’s an example kitchen.yml for one of my development back-ends.) As you can see, figuring out all of those options can be overwhelming and frustrating. I actually found myself at one company posting it to their internal wiki, and quickly realizing that that was the wrong way to share that gem of an incantation.

Here is another possible situation. You have grown your team that writes Chef cookbooks, but you start noticing that your documentation starts to become lackluster like most projects. ;) Using a standard cookbook generator with specific TODOs in it, will encourage good practices from the outset. Here is an example of this for the Chef Partner Cookbook Program that I wrote up for the same type of situation from a generic community standardization stand point. Also, you can take it to the next step too, set up a simple CI job that looks for TODO: and fails when it greps out that line. As I like to say let the bots do the work for you and encourage people into documenting their code correctly.

How?

There are a couple ways to get a generator to be built. You can use the chefdk to build it for you with the following command:

$ chef generate generator <name-generator>

This gives you a generic generator, that you can start from scratch. Personally, I think it’s a tad bit too stripped down for most, so lets start with something that is a bit more complete.

~ $ git clone https://github.com/chef-partners/cookbook-guide-generator.git
~ $ mv cookbook-guide-generator <name-generator>
~ $ cd <name-generator>
~/<name-generator> $ rm -rf .git/
~/<name-generator> $ git init

This will pull down the Chef Partner Cookbook-guide generator, remove the git history, and initialize a new git repository, giving you a directory with many more options.

Lets start with what the generator gives you, run the following command to see what you get:

~ $ chef generate cookbook -g ~/<name-generator>/cookbooks tempcookbook
~ $ cd tempcookbook
~/tempcookbook $ ls
Berksfile       Gemfile         LICENSE         Rakefile        chefignore      recipes         test
CONTRIBUTING.md Guardfile       README.md       TESTING.md      metadata.rb     spec
~/tempcookbook $ ls

As you can see, it’s a pretty large framework for a typical community cookbook. Everything ranging from a TESTING.md to a test/ directory, to even two .kitchen.ymls! Now, lets start playing with it. Go ahead and open up the following:

~ $ vi ~/<name-generator>/cookbooks/code_generator/recipes/cookbook.rb
~ $ # Change the line for .kitchen-docker.yml to .kitchen.docker.yml
~ $ chef generate cookbook -g ~/<name-generator>/cookbooks tempcookbook2
~ $ ls -a tempcookbook2/.kitchen*
.kitchen.docker.yml .kitchen.yml

Pretty slick eh? Now lets edit the contents of a file. Go ahead and do the following:

~ $ vi ~/<name-generator>/cookbooks/code_generator/cookbooks/code_generator/templates/default/kitchen.docker.yml.erb
~ $ # Edit the line under privileged to: socket: <%= ENV['DOCKER_HOST'] || "localhost" %>
~ $ chef generate cookbook -g ~/<name-generator>/cookbooks tempcookbook3
~ $ head -5 tempcookbook3/.kitchen.docker.yml
driver:
  name: dokken
  chef_version: latest
  privileged: true # because Docker and SystemD/Upstart
  socket: localhost
~ $ DOCKER_HOST=tcp://192.168.1.1:2375 chef generate cookbook -g ~/<name-generator>/cookbooks tempcookbook4
~ $ head -5 tempcookbook4/.kitchen.docker.yml
driver:
  name: dokken
  chef_version: latest
  privileged: true # because Docker and SystemD/Upstart
  socket: tcp://192.168.1.1:2375

As you can see if you have a remote DOCKER_HOST or for that matter any ENV variables you can have a sane default and an override if needed. This can take effect if you have a remote DOCKER_HOST already enabled so you know your cookbooks point to the correct location.

As a final example I’d like to point you to the README.md.erb, this file is probably one of the most neglected yet powerful of the generator. The first thing anyone does when looking at a cookbook is read the README.md. This allows you to fill out and automatically have sections in the README.md to force required documentation. One of the sections that has slowly become a best practice for cookbook development is the SCOPE section. As you can see from the following it’s right there at the top to make sure it’s “scoped” properly.

~ $ > head -13 tempcookbook/README.md
# tempcookbook

TODO: Enter one line description of the cookbook here.

## SCOPE

TODO: Enter a description of the scope of this cookbook, if you
need an example the [mysql](https://github.com/chef-cookbooks/mysql) cookbook
is a good place to start.

## Requirements

TODO: Enter any requirements for the coobook.

Awesome, now that you’ve played around with your cookbook understood some of the advantages, the next step is to push this generator to a shared repository. However you share your git repositories create a repo there and push it.

After that, as time progresses you can add change things to make it more delightful, but be sure that before people use the generate command, they do a git pull to get the most up to date.