Writing a Chef Cookbook

Hi,

 

In continuation to a Chef Introduction session we had last week on meetup, I thought a blog post was called for in order to emphasize the process of writing a recipie. And/Or working with chef in general as a buy product of that.

I will be using the basic "ntp" example Opscode uses on their wiki, but in order to understand the components of a recipe I will streach it a bit further in order to show the true power of Attributes and Templates.

 

At the end of this post [or now if you really insist] you can clone https://github.com/tikalk/chef-intro-repo and see the ntp recipe alogside other stuff which was presented in the meetup.

 

Prequisets:

  1. A Chef server [see: http://www.tikalk.com/alm/blog/installing-chef-server if you want and set one up ...]
  2. Configured Knife workstation

 

Step 1: Install the service you want to recipe ... 

yum install ntp
rpm -ql ntp

 

Identify:

  • package name you wish to install
  • Files which are template candidates [which chef will need to populate with your data]

 

I found the following:

  • service name "ntpd" [ init file name: /etc/rc.d/init.d/ntpd ]
  • configuration file our tempalte candidate "/etc/ntp.conf" [ also found via rpm -ql ]

 

Step 2: Setup a git repository [clone opscode's "template" repository]

git clone git://github.com/opscode/chef-repo.git

  

Step 3: Create a cookbook  [named ntp]

cd ~/chef-repo
knife cookbook create ntp

 

The knife command above will create the foloowing structure [under cookbooks directory]:

 

attributes/

definitions/

files/

libraries/

metadata.rb

providers/

README.rdoc

recipes/

resources/

templates/

 We won't be using all of these in this tutorial ... highlighted are the ones we are going to use (at this stage)

 

Step 4: Create the recipe

vim  cookbooks/ntp/recipes/default.rb

 

Add the following ruby code [link]:

package "ntp" do
    action [:install]
end
 
template "/etc/ntp.conf" do
    source "ntp.conf.erb"
    variables( :ntp_server => "time.nist.gov" )
    notifies :restart, "service[ntpd]"
end
 
service "ntpd" do
    action [:enable,:start]
end

 

The first block of code will use chefs built-in packadge installer providor to use the os's package manager (in our case yum/rpm) and use the service name "ntp" the one we located whilst installing the package in step1 above.

 

Step 5: Create a template

 

vim cookbooks/ntp/templates/default/ntp.conf.erb

 

Unlike files which are placed by chef "as is" files under templates folder ending with erb are interpolated and created on the node during a chef-client run.

The content of the file [link]:

restrict default kod nomodify notrap nopeer noquery
restrict -6 default kod nomodify notrap nopeer noquery
restrict 127.0.0.1
restrict -6 ::1
server <%= @ntp_server %>
server  127.127.1.0     # local clock
driftfile /var/lib/ntp/drift
keys /etc/ntp/keys

 

In this simple use case line #7 from  cookbooks/ntp/recipes/default.rb will be the one setting the ntp_server parameter for the tempalte file in line #5 of the template above.

 

At this stage you could create a role add this recipe to a run_list and it will just work ... until you try to apply this recipe on ubuntu for example, there you will find an issue with the service name ... and whilst were at it , let's add support for more than one ntp server [in case the single one we added is down :(].

apt-get install ntp

Will revieal the issue I just mentioned

and 

dpkg -L ntp 

will give us the list of files [ /etc/ntp.conf ] and service name [ ntp ] => notice in this case there is no "d" at the end.

 

Step 6: Improovment #1 - adding service name resolution to our recipe

Add an attributes file:

vim  cookbooks/ntp/attributes/default.rb

 

With the following content:

case platform
when "redhat","centos","fedora","scientific"
  default[:ntp][:service] = "ntpd"
when "ubuntu","debian"
  default[:ntp][:service] = "ntp"
else
  default[:ntp][:service] = "ntpd"
end

 

This case statment will help our recipe in the service name resolution for redhat / centos & other rpm based distros "ntpd" for ubutnu / debian use "ntp".

 

Let's tell our recipe to respect this attribute ...:

package "ntp" do                                
    action [:install]                           
end

service node[:ntp][:service] do
    service_name node[:ntp][:service]
    action [:enable,:start,:restart]
end

template "/etc/ntp.conf" do                     
    source "ntp.conf.erb"                       
    owner "root"                                
    group "root"                                
    mode 0644                                   
    notifies :restart, resources(:service => node[:ntp][:service])
end

 

The diff is in line #7 & line #12 which now uses the node:[ntp][:service] attribute we defined in the attributes.rb file above.

 

Step 7: Add support for more than 1 ntp server

In cookbooks/ntp/attributes/default.rb file add the following array:

 

default[:ntp][:servers] = ["0.pool.ntp.org", "1.pool.ntp.org", "2.pool.ntp.org", "3.pool.ntp.org"]

 

And in our template file let's add support for more than one line of ntp server:

 

# Generated by Chef for <%= node[:fqdn] %> 
# node[:fqdn] = ohai data collected on node !
# Local modifications will be overwritten.

restrict -6 ::1
#server <%= @ntp_server %>

<% node[:ntp][:servers].each do |ntpsrv| -%>
  server <%= ntpsrv %> iburst
  restrict <%= ntpsrv %> nomodify notrap noquery
<% end -%>

restrict default kod nomodify notrap nopeer noquery
restrict -6 default kod nomodify notrap nopeer noquery
restrict 127.0.0.1

server  127.127.1.0     # local clock
driftfile /var/lib/ntp/drift
keys /etc/ntp/keys

 

As you can see I marked out linet #5 which is our old ntp decleration

and added lines# 7-10:

<% node[:ntp][:servers].each do |ntpsrv| -%>
  server <%= ntpsrv %> iburst
  restrict <%= ntpsrv %> nomodify notrap noquery
<% end -%>

chef will itterate over the array and inject the vlaues in our case whilst using defaults we will recieve the following:

 

  server 0.pool.ntp.org iburst
 restrict 0.pool.ntp.org nomodify notrap noquery
  server 1.pool.ntp.org iburst
 restrict 1.pool.ntp.org nomodify notrap noquery
  server 2.pool.ntp.org iburst
 restrict 2.pool.ntp.org nomodify notrap noquery
  server 3.pool.ntp.org iburst
 restrict 3.pool.ntp.org nomodify notrap noquery

 

 

That's it all is left is to upload this cookbook to your chef server and add it to one of your nodes and you are good to go.

knife cookbook upload ntp

(you might want to bump the version up just so it becomes a habbit - edit the metadata.rb file under the recipies directory).

Coming up [hopefully in the next few days] a test environment for chef cookbooks - I will be taking this example and present how to setup an environment for testing comining Chef-Solo (in case you don't have a chef server), Vergrant & VirtualBox

 

Fell free to comment / remark :)

Thank you for your interest!

We will contact you as soon as possible.

Send us a message

Oops, something went wrong
Please try again or contact us by email at info@tikalk.com