Chef Snippets

1 minute read

I thought I’d upload some interesting Chef-related snippets I accumulated.

Chef Shell

chef-shell is an easy way to gain the context of a Chef client.
I mainly use it to debug recipes by executing little bits of them in the shell.

As client

If you’re running it on a standard Chef node (connected to a Chef server), you can simply use:

sudo chef-shell -z

As human client

If you want to run chef-shell from your Knife-wielding workstation, use a similar trick:

chef-shell -z -c ~/.chef/knife.rb

Of course, I’m assuming your Knife config is in the default location. Modify if necessary.

As solo in Vagrant

When using Chef solo in Vagrant, you can run Chef shell to simulate it by running something like

sudo chef-shell -s --config /tmp/vagrant-chef/solo.rb --json-attributes /tmp/vagrant-chef/dna.json

I got the arguments from running vagrant provision and during the provisioning running something like ps aux | grep chef.
To load your cookbooks (which is not automatic) you can run:

node.run_context.load(node.expand!) # Normal runlist

OR

# Custom set of recipes
recipes=['nginx']
node.run_context.load(OpenStruct.new(recipes: recipes))

Chef in Ruby

Sometimes I want to use Chef logic in my Ruby scripts or I just want direct access to Chef’s libraries.
The easiest way to accomplish it is to use Chef’s own Ruby instance, available under /opt/chefdk/embedded/bin/ruby (for ChefDK) or /opt/chef/embedded/bin/ruby (for Chef client). I sometimes use the irb binary instead.
You can also use chef exec in newer editions of Chef.
I have this boilerplate snippet to load the Chef config file:

require 'chef'
if human_user then # Whether we're running as a Chef user or as a Chef client
  config_path = File.expand_path("~/.chef/knife.rb")
else
  config_path = '/etc/chef/client.rb'
  Chef::Config[:node_name]||=`hostname`.strip
end
Chef::Config.from_file(config_path)

Then you can run some Chef-dependant code, like:

Getting all nodes

nodes = Chef::Node.list.keys.map{|n|Chef::Node.load n}

Translate node FQDN to AWS instance ID

include Chef::DSL::DataQuery

search(:node,'ec2_instance_id:*').each do |n|
  puts "#{n['fqdn']},#{n['ec2']['instance_id']}"
end

Update a databag

include Chef::DSL::DataQuery

mu=data_bag_item('song','of')
mu['ice']='fire'
mu.save

Query for group membership

Taken from the knife-acl project

def is_usag?(gname)
  gname.length == 32 && gname =~ /^[0-9a-f]+$/
end

client_name='best.machine.com'
rest = Chef::Search::Query.new.rest
groups=rest.get_rest('/groups').keys.reject{|k|is_usag?(k)}
current_groups=groups.select do |g|
  rest.get_rest("/groups/#{g}")['actors'].include?(client_name)
end

Mass operations in Bash

Some of these commands use parallel. It can be switches with xargs -I {} but it won’t run as fast.

Get all nodes who’s last run failed

knife node list | parallel 'knife runs list -r1 {} | egrep "status:\s+failure">/dev/null && echo {}'