Jeff Roberts
RHCE #804006066322833
Vim-Fu is now iPhone and Android friendly

Check out the Vim-Fu Store!

Vim-Fu

Patching Chef to support dry-run mode by Darren Dao

Disclaimer: I am not an expert on Chef, nor on Ruby. This post simply shows what works for me. The code haven’t been tested thoroughly. It’s only there to serve as an example.

TL;DR: I ended up monkey-patching each of the Chef providers that we use (e.g. directory, file, template, etc). The method you want to patch is the “action_create” method. You can overwrite this method to output the result to stdout, to a staging area, etc.

Background: At our company, we decided to use Chef for app config management. We set up our Chef cookbooks and recipes in such a way that we can deploy them to different hosts, and depending on what environment the hosts are in (prod vs. qa vs. dev), different configuration files will be generated.

Problem: One of the requirements we were given is the ability to see what config files would be generated on a given Chef run. Another requirement is to be able to provide the diff of the generated config files between two environments. To satisfy those two requirements, we need to run Chef in a staging area, or in dryrun/noop mode. Chef supports neither one. A chef user has opened a ticket since Jan 2009, asking for this feature to be added to Chef (http://tickets.opscode.com/browse/CHEF-13) . However, it doesn’t look like there has been any progress on it. Searching on google for “Chef dryrun” or “Chef noop” only resulted in Puppet users bashing Chef for not having a dryrun/noop mode. Since it looks like Chef developers are not going to address this issue anytime soon, we decide to patch Chef ourselves.

Solution: The key to having Chef dryrun/noop mode is to patch each of the providers that you use in your cookbook. For more information on what a provider is, visit http://wiki.opscode.com/display/chef/Providers. In patching the providers, we overwrite the “action_create” method, so that instead of having the providers carrying out the actual actions, we tell them to either display what the actions are, or perform the actions in a staging area. For example, this is how we patch Chef to have to output the resulting resources to a staging directory (defined as a variable in node.json)

# Overwrite providers to output files to staging area
class Chef
  class Provider
    class Template < Chef::Provider::File
      def action_create
        render_with_context(template_location) do |rendered_template|
          dest = ::File.join(Chef::Config["stage_dir"], @current_resource.path)
          dir = ::File.dirname(dest)
          FileUtils.mkdir_p(dir) unless ::File.exists? dir
          FileUtils.cp(rendered_template.path, dest)
        end
      end
    end
    class Directory < Chef::Provider::File
      def action_create
        ::FileUtils.mkdir_p(::File.join(Chef::Config["stage_dir"], @new_resource.path))
      end
    end
    class File < Chef::Provider
      def action_create
        dest = ::File.join(Chef::Config["stage_dir"], @new_resource.path)
        ::File.open(dest, 'w'){|f| f.write(@new_resource.content)}
      end
    end
    class CookbookFile < Chef::Provider::File
      def action_create
        dest = ::File.join(Chef::Config["stage_dir"], @new_resource.path)
        FileUtils.cp(file_cache_location, dest)
      end
    end
  end
end

You can put that code inside your ruby script, and then have your ruby script load either chef-solo or chef-client, depending on what you use. For us, we use chef-solo, so I ended up created a ruby script called chef-solo.dryrun, inside it, I have the code above, and then I call chef-solo like this:

require 'rubygems'
require 'chef'
require 'fileutils'

# patch code from above goes in here

# Call chef-solo
version = ">= 0"
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
  version = $1
  ARGV.shift
end
gem 'chef', version
load Gem.bin_path('chef', 'chef-solo', version)

The downside to this solution is that it is fragile and will break if Chef developers decide to change the methods inside of the providers (e.g. the “action_create” method gets renamed). It is definitely not the best way to add dryrun/noop to Chef, but given the lack of response/progress from Chef developers on this issue, we are happy with this solution.

1 comment to Patching Chef to support dry-run mode by Darren Dao

  • jroberts

    This is an awesome feature addition to Chef. I hope the good people responsible for Chef read this!!!

    Thanks Darren for contributing to Vim-Fu.

    -Jeff

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>