I’ve collected a few notes together into a working Ruby script that shows just a little of what the Chef API can do for future reference.
The Chef API provides a route into collecting Chef data; perhaps for an environments and nodes status report. The Chef API can be used to integrate Chef into your CI/CD process. But before spending too much time on the API it is worth understanding what the out of box tools like Knife can do.
Knife is a Chef command line tool that wraps the API into a very complete and well documented set of commands. The advantage of Knife is that it will be in sync with the API. Writing your own code to use the API runs the risk that with breaking changes in the API your code will no longer work.
It is important to version control changes to the CI/CD process. Changes to Chef artefacts such as attributes and cookbooks should be version controlled; Json files containing attribute values stored in SCM and uploaded from SCM into Chef following change is a good way to go.
So with those warnings and advice out of the way here’s the Chef API in a Ruby script…
I’ll assume you have Chef all nicely installed and Ruby is in your executable path. The following Ruby script should be placed in the chef-repo folder so that it can make use of an existing setup and configuration.
The first step is connecting to the Chef server. These three lines of Ruby script will do that…
require 'chef'
Chef::Config.from_file("./.chef/knife.rb")
rest = Chef::REST.new(Chef::Config[:chef_server_url])

The configuration file will need to define the correct pem file for accessing the Chef server. To keep it simple I’ve used the knife.rb file located in the .chef folder of chef-repo.

The Ruby script file is put into the chef-repo folder so that the knife.rb file can be referenced relative to that folder. This makes it clear which chef server and what credentials are being used.

So lets see the script run.
ruby chef_api_notes_demo.rb
It simply lists the environments and updates some attribute data in an environment[1] It also lists the nodes.
[1] I’ve commented out the section in the script that does the updates. However just a quick warning. Although it is unlikely that you have an environment with version attributes for component1 and component2 please be aware that if you do… the script will update those attributes… if you enable that section of script.

Here’s the script which I put into a file called ruby_chef_api_notes_demo.rb in the chef-repo folder…
require 'chef'
#add a bit of colour by extending the string class
class String
def g;c(self,"\e[0m\e[32");end
def r;c(self,"\e[0m\e[31");end
def c(t,c_c,m='m') "#{c_c}#{m}#{t}\e[0m" end
end
#get an ***appropriate*** chef configuration file
#this defines what we connect to and the key to use...
#***key needs to relate to the url***
Chef::Config.from_file("./.chef/knife.rb")
#with the config loaded
#establish a restful connection according to the loaded config.
rest = Chef::REST.new(Chef::Config[:chef_server_url])
puts "=== Connected to [#{Chef::Config[:chef_server_url]}] ==="
#get a list of the environments
#this is a hash - a hash is a collection of key value pairs
environments = rest.get_rest("/environments")
puts "\n-----ENVIRONMENTS-----"
environments.each do |key, value|
puts "----------------------".g
puts "Environment name : #{key}".g
env_url = value
puts " URL : #{env_url}"
#get the environment instance
environment = rest.get_rest(env_url)
#puts environment.inspect # .inspect is useful to view whole object instance
d_attributes = environment.default_attributes
unless d_attributes.nil?
d_attributes.each do |key, value|
puts "----------------------".g
puts "Attribute key : [#{key}]"
unless key == "tags"
puts "Current source value : [#{value["version"]}]"
end
end
end
#The attributes can be accessed directly and set to a new value
#begin
#d_attributes["component1"]["version"] = "3.7.1"
#d_attributes["component2"]["version"] = "5.12.1"
#rescue
#end
#begin
#rest.put_rest(env_url, environment)
#rescue
#puts "Did not update environment [#{environment.name}]".r
#else
#puts "Updated environment [#{environment.name}] OK"
#end
puts "----------------------".g
end
puts "\n-----NODES------------"
#get a list of the nodes
#this is a hash - a hash is a collection of key value pairs
nodes = rest.get_rest("/nodes")
#iterate through the nodes hash
nodes.each do |key, value|
#key is the node name...
#value is the URL in this case...
puts "----------------------".g
puts "Node name : [#{key}]".g
node_url = value
puts " URL : [#{node_url}]"
puts "----------------------".g
#now get the node object using the URL
#can then read the node data
#and if required can update some data
node = rest.get_rest(node_url)
#show the environment that this node is under
puts "Environment...".g
puts "#{node.environment}"
puts "----------------------".g
end
.