This site uses cookies and by using the site you are consenting to this. We utilize cookies to optimize our brand’s web presence and website experience. To learn more about cookies, click here to read our privacy statement.

Using Built-in Chef Classes to Learn about Your Chef Environment

Author: Joe Gange Posted In: DevOps

One thing I’ve come to learn about Chef is there are many ways to accomplish the same task. Since I’m usually under pressure to produce a working solution quickly, I often start by Googling specific questions. I want to discover if someone has already created a recipe or a library that accomplishes the same task with minimal tweaking.

Of course, using other people’s code brings distinct challenges and risks. Do I really understand what the code I’m borrowing does? How much time do I want to spend trying to figure that out?

Rolling your own code also presents its own obstacles. If I had to write the code myself, I’d have to work through the usual development process of learn, code, test, correct, repeat.

Fortunately, I realized there is a middle ground between tweaking someone else’s code and writing my own.

Digging into the Source Code

The Chef developers chose to write Chef in Ruby, an interpreted object-oriented language. As someone who likes to peek under the covers, I took some time to gain a working knowledge of Ruby after I started using Chef.

In this case, I needed to provide usage data about a client’s Chef environment. Fortunately, I was able to quickly scan the code for some promising leads because the source code for Chef is available on Github.

Specifically, I needed to report on cookbooks by version, existing databags and configured environments. If I was doing this interactively, I could write some queries using knife and get the information I needed. But the client wanted this data as part of a larger set of validations in the form of a recipe.

After poking around Git, I quickly realized the information I was looking for was in the lib folder (https://github.com/chef/chef/tree/master/lib/chef). Better yet, I discovered individual Ruby files included methods for the objects I needed data about.

The next challenge was to figure out how to get the information from within a recipe. Chef allows you to import external libraries (in the form of Ruby gems) during recipe execution, but what I needed was internal to Chef.

Accessing the proper method

One way to access methods from a class is to use the namespace resolution operator in Ruby. Ruby uses the following syntax to describe nested methods: <top level object>::<second level object>::<third level> and so on. I had to figure out the hierarchy to access the methods I needed.

I found the following information on how to access the methods in the comments of the cookbook_version.rb file.

# == Chef::CookbookVersion

# CookbookVersion is a model object encapsulating the data about a Chef

# cookbook. Chef supports maintaining multiple versions of a cookbook on a

# single server; each version is represented by a distinct instance of this

# class.

Specifically, I needed to list all the versions of the cookbooks loaded on the server, which led me to the list_all_versions method. I experimented with the list method but it only returned the latest version of a cookbook.

# The API returns only a single version of each cookbook in the result from the cookbooks method
  def self.list

# Alias latest_cookbooks as list
  class << self
    alias :latest_cookbooks :list

  def self.list_all_versions

Applying the internal Chef class method

The list_all_versions method returns a hash containing the all the cookbooks loaded on the Chef server by version. I extracted those fields from the hash in my library method.

The method to extract that data looked like this:

def build_cookbook_list()
  cookbooks = Chef::CookbookVersion.list_all_versions
  cookbook_report = Hash.new {|h, k| h[k] = []}
  cookbooks.each do |k,v|
    0.upto(cookbooks[k]["versions"].size-1) do |ver|
    cookbook_report[k] << cookbooks[k]["versions"][ver]["version"]

The Chef developers also provided a list method to access information about databags and environments. You can call the method with the syntax Chef::Databags.list and Chef::Environment.list respectively.

Once I had created my methods in a helper library, the recipe code was easy to write. I used the statement cookbooks = build_cookbook_list to return the data about the cookbooks in the system.

As a bonus, I avoided having to manage external class references inside my methods. Because I was using built in Chef methods, the methods I was calling were already within the scope of the recipe.

I hope this helps you find a way to easily obtain information about your Chef environment inside a recipe.