Replace vagrant-berkshelf with berkshelf and vagrant-triggers

If you are doing Chef cookbook developement then you are probably using Vagrant as you local dev/test environment. And most of the time you will also use Berkshelf for cookbook dependency management.

The usual way to integrate Vagrant and Berkshelf is the vagrant-berkshelf plug-in for vagrant. Basically, this is the Berkshelf functionality bundled in the form of an vagrant plug-in. If you have any experience with plugin development for vagrant you'll probably know, that development of more complex Vagrant plug-ins sucks big time: Major changes on the plugin API with every release or Vagrant not being distributed as Ruby gem anymore, making testing with multiple Vagrant versions a real pain. I really like Vagrant for local testing setups and development - it is a great tool. But to be honest, its plug-in concept has major flaws, if you plan to write complex plug-ins, which bring a few Ruby dependencies along themselves.

Basically the root of the problem: Vagrant brings its own Ruby gem dependencies. All plug-ins run in the same Ruby process - the Vagrant Ruby process. So in case your plug-in needs one Ruby gem in another version than Vagrant, stuff starts to break. Just have a look at the vagrant-berkshelf issue tracker. The developers of Berkshelf finally got tired of maintaining vagrant-berkshelf and will retire it in the future.

Since I rely heavily on Vagrant / Berkshelf integration for a customers Chef cookbook development life cycle, used an alternative approach to integrate Berkshelf into Vagrant.

The Approach

The approach I am using is simple: Use both tools, Vagrant and Berkshelf, avoid the complex plug-in trap and "glue" it all with another simple tool. You can find all of my example files on github. My example uses Berkshelf, Vagrant with VirtualBox and the simple but great Vagrant plug-in vagrant-triggers to glue it all together. I am also assuming that you are at least somewhat familiar with Vagrant and Berkshelf.

The Tools

VirtualBox and Vagrant

My examples use Vagrant with VirtualBox as virtualization environment. Please check the Vagrant and VirtualBox documentation for setup instructions.

Ruby

So, since you are doing chef cookbook developement, I assume you already have a working Ruby installation. If not, I strongly suggest you use rbenv to set it up.

Berkshelf

The next thing you need is a working Berkshelf, which easily can be installed with:

gem install berkshelf --no-ri --no-rdoc

In case you are using rbenv, make sure your shims are updated:

rbenv reshash

Now check your Berkshelf version (3.1.3 on time of writing):

berks --version

Plug-in: vagrant-triggers

Our little helper is the vagrant-triggers Vagrant plug-in. It will enable us to trigger specific code on execution of Vagrant commands. This plugin has no special gem dependencies, so it should always run without any dependency issues in the Vagrant Ruby process. Its code is also quite easy to adapt, should Vagrant change its plug-in API (...again, sigh ...).

You can easily install it with:

vagrant plugin install vagrant-triggers

Running the Example

Have a look at the Vagrantfile in the example. The following part "glues" Vagrant and Berkshelf together:

[:up, :provision].each do |cmd|
  config.trigger.before cmd, :stdout => true do
    info 'Cleaning cookbook directory'
    run "rm -rf #{cookbooks_path}"
    info 'Installing cookbook dependencies with berkshelf'
    run "berks vendor #{cookbooks_path}"
  end
end

The code simply executes the locally installed Berkshelf before vagrant up and provision events. Berkshelf is called with the vendor command, followed by a directory, e.g.:

berks vendor cookbooks

This will resolve all cookbook dependencies the Berkshelf way and install the needed cookbooks in a directory named cookbooks. If you take another look at the code snippet above, you'll see that first we actually delete the cookbooks directory. Berkshelf refuses to install cookbooks into an already existing cookbook directory, so we need to delete it before vendoring to it. The cookbooks directory is mounted into the Vagrant VM and used by chef-solo as cookbook location by default.

So basically we replaced vagrant-berkshelf with a locally installed Ruby/Berkshelf (when doing cookbook development, you normally have these tools already) and vagrant-triggers (which should be much easier to maintain than a complex plug-in). This makes it easy to use the most recent Berkshelf and Vagrant versions without having to worry about vagrant-berkshelf breakage.