Our Blogs

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…

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 classclass Stringdef 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" endend#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 pairsenvironments = rest.get_rest("/environments")puts "\n-----ENVIRONMENTS-----"environments.each do |key, value|puts "----------------------".gputs "Environment name : #{key}".genv_url = valueputs " URL : #{env_url}"#get the environment instanceenvironment = rest.get_rest(env_url)#puts environment.inspect # .inspect is useful to view whole object instanced_attributes = environment.default_attributesunless d_attributes.nil?d_attributes.each do |key, value|puts "----------------------".gputs "Attribute key : [#{key}]"unless key == "tags"puts "Current source value : [#{value["version"]}]"endendend#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"#endputs "----------------------".gendputs "\n-----NODES------------"#get a list of the nodes#this is a hash - a hash is a collection of key value pairsnodes = rest.get_rest("/nodes")#iterate through the nodes hashnodes.each do |key, value|#key is the node name...#value is the URL in this case...puts "----------------------".gputs "Node name : [#{key}]".gnode_url = valueputs " 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 datanode = rest.get_rest(node_url)#show the environment that this node is underputs "Environment...".gputs "#{node.environment}"puts "----------------------".gend