Last Updated: February 25, 2016
·
26.8K
· vaskas

Organise your site-cookbooks with Berkshelf and this trick

Berkshelf is a bundler-like dependency manager for Chef cookbooks. The approach it encourages is to treat your cookbooks as libraries or applications.

  • Library cookbooks should be focused on a particular component, reusable and configurable.
  • Application cookbooks should be stitching the library ones together to achieve specific business goals.

As much as Berkshelf advocates you to move Chef cookbooks to their own git repositories, a Chef Solo user is still very likely to end up with a few application cookbooks (or site-cookbooks if we follow Librarian terminology).

chef-repo
  |  Berksfile
  |_ site-cookbooks
    |_ my-wordpress
    |_ my-rails
    |_ my-sinatra

Unfortunately the way Berkshelf is designed is that it will ignore Berksfiles for application cookbooks, i.e. this layout won't work:

chef-repo
  |_ site-cookbooks
    |_ my-wordpress
    |    metadata.rb
    |    Berksfile
    |_ my-rails
    |    metadata.rb
    |    Berksfile
    |_ my-sinatra
         metadata.rb
         Berksfile

That is, even if you create a top-level Berksfile like this:

site :opscode
metadata

cookbook 'my-wordpress', :path => './site-cookbooks/my-wordpress'
cookbook 'my-rails', :path => './site-cookbooks/my-rails'
cookbook 'my-sinatra', :path => './site-cookbooks/my-sinatra'

Only metadata.rb dependencies of site-cookbook are going to be satisfied with Berkshelf.

Why? Because of a design decision. Which you can get around if you're using Chef Server for cookbooks storage, but that's a different story.

Fortunately, I found a compromise for my use case. This is the Berksfile I've got in the root of my Chef Solo repo:

# vi:ft=ruby:
site :opscode

def dependencies(path)
  berks = "#{path}/Berksfile.in"
  instance_eval(File.read(berks)) if File.exists?(berks)
end

Dir.glob('./site-cookbooks/*').each do |path|
  dependencies path
  cookbook File.basename(path), :path => path
end

And this is the layout I have got:

chef-repo
  |  Berksfile
  |_ site-cookbooks
    |_ my-wordpress
    |    metadata.rb
    |    Berksfile.in
    |_ my-rails
    |    metadata.rb
    |    Berksfile.in
    |_ my-sinatra
         metadata.rb
         Berksfile.in

This way,

  • ./site-cookbooks get noticed by Berkshelf which means that their metadata.rb dependencies are satisfied
  • Their specific Berksfile.in files will be evaluated so that all cookbook dependencies make it nicely into the main Berksfile (thanks god, it's Ruby).

Berksfile.in files are like this:

cookbook 'nodejs',  :github => 'locomote-cookbooks/nodejs-cookbook'
cookbook 'npm',     :github => 'locomote-cookbooks/chef-npm'
cookbook 'forever', :github => 'trabian/chef-forever'

They look like normal Berksfiles sans the site and metadata sections - seeing them will make Berkshelf complain. Fortunately, you don't need them anyway because of the Berksfile in the root.

Hopefully this saves you from some Berkshelf rage that I've been through. Also, we all have ambitions and different opinions, let's listen to each other and not be fanatical. Peace.

4 Responses
Add your response

You are fucking awesome.

over 1 year ago ·

This is badass, thank you so much!

over 1 year ago ·

smart)

over 1 year ago ·

badass rock star tech!

over 1 year ago ·