Thursday, April 7, 2016

Chef-Cookbooks Walls of chef-house..



As we work with cookbook in our previous blog, we are now aware about how chef really works and manage machines using cookbooks. Its crucial to understand the potential of thoughts and theories behind any concept. According to Albert Einstein,

A Theory Can Be Proved By Experiment;
But No Paths Leads from Experiments To The Birth Of Theory


To follow this article you need a prior information about Git and Vagrant. This blog uses centos7   as platform. It needs basic understanding of chef and it's cookbooks. To know about chef cookbooks follow our previous blogs of this series Chef Start here with ease.. .

Get started

Clone our github repository and spin up a bare centos7 vagrant machine.

Go to Chef/centos/chefCookbooksBackings directory.

$ cd  Chef/centos/chefCookbooksBackings

  • This directory have a Vagrantfile. Which can initiate a centos7 vagrant box.
$ cat Vagrantfile

This file update and install some basic tools in your vagrant machine using vagrant shell provisioning.
  • Download Chefdk using below available command

$ cd Chef/centos/chefResources

$ wget

  • This directory also includes a knife.rb file which sets the cookbook folder path and default editor for the virtual machine.

  • Launch new vagrant machine and login into it via ssh.
$ vagrant up

$ vagrant ssh


As per chef’s official document “A cookbook is the fundamental unit of configuration and policy distribution. A cookbook defines a scenario and contains everything that is required to support that scenario.”  
Chef cookbooks are the first configurational unit of chef. These are the like a box which contains all the basic tools for the comfortable management of any machine. It consist of
  • Recipes
  • Attribute
  • File
  • Templates
  • libraries, definitions, and custom resources

Directory Structure

The directory structure of any cookbook  is very straight and simple. We took an example cookbook which we are going to create in our next section, so have a look on what it looks like on completion.

$ tree cookbooks/nginxVhostExtended/

  • Attributes
This directory contains all files which holds the values of the variables used in the cookbook.
  • Definitions
This directory contains definition of new created resources.
  • Files
This folder holds static file used in cookbook.
  • Libraries
Allows users to use extended ruby libraries for new class declarations.
  • Providers
This will contains actions which will be taken on using a custom resource declared in resources directory.
  • Recipes
This directory holds all the the recipes of any cookbook. These are the main execution part of the cookbook.
  • Resources
Here custom resources are defined.
  • Templates
This directory provide templates for the dynamic solutions of a cookbook.
  • Metadata.rb
This file ensures that every cookbook is deployed correctly. Also holds the general information for any cookbook as like author copyright and version.

Try with a complex one

This time we introduce some extended concepts of chef cookbooks in our example cookbook. As in our previous blogs problem statement remain same to install nginx and setup nginx vhost with our cookbook. But this time we tried to be more efficient so that we can create multiple vhost in a single chef-client run.

First create a dedicated directory for our cookbooks. As in knife.rb file it will be created by following command.

$ mkdir /vagrant/cookbooks

Chef manage its cookbooks using a version control system so next we initialize and also make our first commit for /vagrant/cookbooks directory. Provide your name and email for git configuration.
$ mkdir /vagrant/cookbooks
$ cd /vagrant/cookbooks
$ git init
$ git add .
$ git config --global ""
$ git config --global "Your Name"
$ git commit -m "Initial Commit"

Now you are ready to start with the creation of your next cookbook.

$ knife cookbook create nginxVhostExtended -C "Saurabh Vajpayee" -m "" -I nginxv1 -r md

Create default recipe

Create a default recipe with following content to install  nginx.

$ vim /vagrant/cookbooks/nginxVhostExtended/recipes/default.rb

include_recipe 'yum-epel'

package node['nginx']['packages'] do
action :install

service 'iptables' do
action :stop

service 'nginx' do
action [:start, :enable]

This time we also  included another recipe “yum-epel” in our default recipe.

Generate another recipe to configure vhost with following content.

$ chef generate recipe /vagrant/cookbooks/nginxVhostExtended/ vhost

$ vim /vagrant/cookbooks/nginxVhostExtended/recipes/vhost.rb

include_recipe 'nginxVhostExtended'

node['nginx']['vhost'].each do |name, attrs|
 nginxVhostExtended_vhost 'name' do
   port attrs['port']
   webroot attrs['webroot']
   servername attrs['servername']
   conffile attrs['conffile']

service 'nginx' do
action :restart

This recipe contains a custom resource  “nginxVhostExtended_vhost” which has its definition and attribute declaration under provider and resource directory.

Generate attribute file

Generate default attribute file with following defined variables.

$ chef generate attribute /vagrant/cookbooks/nginxVhostExtended/ default

$ vim /vagrant/cookbooks/nginxVhostExtended/attributes/default.rb

default['nginx']['packages'] = "nginx"
default['nginx']['port'] = 80
default['nginx']['webroot'] = "/usr/share/nginx/blog"
default['nginx']['servername'] = ""
default['nginx']['conffile'] = ""
default['nginx']['vhost'] = {}

This attribute file holds default values for variable used in templates

Generate template files

Generate two templates for conf and index.html files. Use following content to paste.

$ chef generate template /vagrant/cookbooks/nginxVhostExtended/ chefmanagedconf.conf

$ chef generate template /vagrant/cookbooks/nginxVhostExtended/ index.html

$ vim /vagrant/cookbooks/nginxVhostExtended/templates/default/chefmanagedconf.conf.erb

server {
   listen       <%= @port  %>;
   server_name  <%= @servername  %>;

   location / {
       root   <%= @webroot  %>;
       index  index.html index.htm;

   error_page  404              /404.html;
   location = /404.html {
       root   <%= @webroot  %>;

   # redirect server error pages to the static page /50x.html
   error_page   500 502 503 504  /50x.html;
   location = /50x.html {
       root   <%= @webroot  %>;

$ vim /vagrant/cookbooks/nginxVhostExtended/templates/default/index.html.erb

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "">

<html xmlns="" xml:lang="en">
       <title>Test Page for the Opstree Server </title>

       <h1>Welcome to <strong> <%= @servername %> </strong></h1>

Generate a lwrp

Generate a lwrp (light weight resource provider ) using knife command and use following content.

$ chef generate lwrp /vagrant/cookbooks/nginxVhostExtended/ vhost

$ vim  /vagrant/cookbooks/nginxVhostExtended/resources/vhost.rb

actions :configure
default_action :configure

attribute :name,
 kind_of: String,
 required: true,
 name_attribute: true
attribute :port,
 kind_of: Fixnum
attribute :webroot,
 kind_of: String
attribute :servername,
 kind_of: String
attribute :conffile,
 kind_of: String

$ vim /vagrant/cookbooks/nginxVhostExtended/providers/vhost.rb

action :configure do

[:webroot, :conffile, :servername, :port].each do |attr|
      unless new_resource.instance_variable_get("@#{attr}")
        new_resource.instance_variable_set("@#{attr}", node['nginx'][attr])

[:webroot].each do |attr|
      directory new_resource.instance_variable_get("@#{attr}") do
        mode '0755'
        recursive true
      template "#{new_resource.webroot}/index.html" do
        source 'index.html.erb'
         servername: new_resource.servername

template "/etc/nginx/conf.d/#{new_resource.conffile}" do
  source 'chefmanagedconf.conf.erb'
         port: new_resource.port,
         servername: new_resource.servername,
         webroot: new_resource.webroot

line = " #{new_resource.servername}"
file ='/etc/hosts')
file.insert_line_if_no_match(/#{line}/, line)


Now our cookbook is ready but as we define in yum-epel dependency in our default  cookbook so in next steps we install this cookbook from chef repository.

$ git status
$ git add .
$ git commit -m "nginxVhostExtended cookbook added"

$ knife cookbook site install yum-epel

Go for it

Lets run our cookbook to just install nginx using default recipe. Create   installrunlist.json with following content.   

$ vim installrunlist.json

"run_list": [

$ chef-client  --local-mode -j /vagrant/runlist.json
This will install your nginx server only.

Now we create a runlist file for configuring vhost.

$ vim vhostrunlist.json

  "nginx": {
     "vhost": {
       "vhost1": {
         "webroot": "/usr/share/nginx/chef",
             "servername": "",
             "conffile": ""
        "vhost2": {
          "webroot": "/usr/share/nginx/blog",
             "servername": "",
             "conffile": ""
  "run_list": [

$ chef-client  --local-mode -j /vagrant/vhostrunlist.json

This will run and configure two vhost and on your machine. Now with our new cookbook we are able to create and configure multiple vhost in single run.

We took some extended concepts of chef cookbooks. Chef and it's cookbooks are beyond away from all limits. This is just a drop from the ocean but every drop in the ocean counts…

You need an entire life just to know about tomatoes. Ferran Adria

Don't be panic by messing up with a lot of things and start playing with your own written cookbooks.