Writing a Chef Cookbook



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.



  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



  • 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]:












 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]
template "/etc/ntp.conf" do
    source "ntp.conf.erb"
    variables( :ntp_server => "time.nist.gov" )
    notifies :restart, "service[ntpd]"
service "ntpd" do
    action [:enable,:start]


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 -6 ::1
server <%= @ntp_server %>
server     # 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


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"
  default[:ntp][:service] = "ntpd"


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]                           

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

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


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

server     # 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