Holistic Engineering

A random assortment of shit with sprinkles.

Abusing the Chef API for Fun and Profit

| Comments

This post is about Chef, but perhaps not in the way you’ve experienced it before. Believe it or not, Chef has a beautiful API underneath all those tools, and very few of us in our day-to-day work exploit the opportunities available to us.

A Quick Start: Chef Search

Chef Search is one of the things that differentiates it from other tools of the same mind. There are a lot of opportunities in our daily environment to (ab)use chef search to automate tasks.

Here’s a small script that looks for all nodes that match a query, and prints their names:

chef_search_1.rb
1
2
3
4
5
6
7
8
9
10
11
#!ruby

require 'rubygems'
require 'chef/rest'
require 'chef/search/query'

Chef::Config.from_file(File.expand_path("~/.chef/knife.rb"))
query = Chef::Search::Query.new
nodes = query.search('node', ARGV.shift).first rescue []

puts nodes.map(&:name).join("\n")

You can try this out like so:

ruby chef_search_1.rb 'roles:my_role'

And it will print all your nodes that have “role[my_role]” in their run_list.

This isn’t very useful by itself. How about a script that does something with those nodes… maybe like printing out their ec2.instance_id’s?

chef_search_2.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!ruby

require 'rubygems'
require 'chef/rest'
require 'chef/node'
require 'chef/search/query'

Chef::Config.from_file(File.expand_path("~/.chef/knife.rb"))
query = Chef::Search::Query.new
nodes = query.search('node', ARGV.shift).first rescue []

nodes.each do |node|
  puts node.name
  puts "\tinstance_id: " + node.ec2.instance_id
end

Still not very useful. Are you using EC2 tagging for your cloud machines? If not, you should. Our tags from ec2-describe-instances look something like this (and a lot more stuff I can’t show):

TAG     instance        i-f3220280      Name    backend.test.example.com
TAG     instance        i-f3220280      environment     test

Now, we can extract this information with a variety of tools, like Fog and RightAws. The EC2 API is arguably the simplest, but shelling out to all that java is a time sink. RightAws is my favorite not because it’s pretty code or that it’s especially fancy or elaborate, but because it’s documented.

ri RightAws::Ec2
ri RightAws::Ec2.describe_tags

Gets us what we want.

Anyhow, the below script takes a search, finds out its instance id, and then proceeds to extract and print the tags with right_aws’s describe_tags method. It uses the same environment variables you’d use for the EC2 API Tools, so if you use EC2, you’re probably good to go.

chef_search_3.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!ruby

require 'rubygems'
require 'right_aws'
require 'chef/rest'
require 'chef/node'
require 'chef/search/query'

Chef::Config.from_file(File.expand_path("~/.chef/knife.rb"))
query = Chef::Search::Query.new
nodes = query.search('node', ARGV.shift).first rescue []
right_aws = RightAws::Ec2.new(ENV["AWS_ACCESS_KEY_ID"], ENV["AWS_SECRET_ACCESS_KEY"] )

nodes.each do |node|
  puts node.name

  unless (node.ec2.instance_id rescue nil)
    puts "\tNot an EC2 machine"
    next
  end

  puts "\tinstance_id: " + node.ec2.instance_id

  right_aws.describe_tags(:filters => { 'resource-id' => node.ec2.instance_id }).each do |tag|
    puts "\t\t%s:\t%s" % [tag[:key], tag[:value]]
  end
end

So now we have something marginally useful. I’m sure you can come up with all sorts of applications for this! Additionally, check out the code for these classes:

  • Chef::Node
  • Chef::Role
  • Chef::DataBagItem

For a lot more things to explore!

Comments