Chef

Table Of Contents

About Definitions

A definition is used to declare resources so they can be added to the resource collection.

  • A definition is not a resource or a lightweight resource
  • A definition groups two (or more) resource declarations; there is no limit to the number of resources that can be part of this declaration
  • A definition is often described as a “recipe macro”
  • A definition is never declared into a cookbook; all definitions must be located within the definitions/ directory
  • Unlike resources, a definition does not have an associated provider

A definition is best-used when:

  • Data needs to be passed from one (or more) recipes into a single definition
  • A repeating usage pattern exists for one (or more) resources

Syntax

A definition has three components:

  • A resource name
  • One (or more) arguments that are used to define a parameter and set its default value; if a default value is not specified, it is assumed to be nil
  • A hash that is used within a definition to provide access to parameters and their values

The parameter values that are provided are used as the default values, in case a value is not provided when the resource is invoked.

The basic syntax of a definition:

define :resource_name, :parameter => :argument, :parameter => :argument do
  params_hash
end

For example, a definition named apache_site with an parameter called action with an argument for enable would look something like:

define :apache_site, :action => :enable do
  if params[:action] == :enable
     ...
  else
     ...
  end
end

Or the following definition, which looks like a resource when used in a recipe, but also contains resources—directory and file—that are repeated, but with slightly different parameters:

define :host_porter, :port => 4000, :hostname => nil do
  params[:hostname] ||= params[:name]

  directory "/etc/#{params[:hostname]}" do
    recursive true
  end

  file "/etc/#{params[:hostname]}/#{params[:port]}" do
    content "some content"
  end
end

which is then used in a recipe like this:

host_porter node['hostname'] do
 port 4000
end

host_porter "www1" do
  port 4001
end

Examples

The following examples show how to use cookbook definitions.

Create a Resource

A definition file can be used to create an object that the chef-client can then use like a resource. For example:

apache_site Definition
define :apache_site, :enable => true do
  include_recipe "apache2"

  if params[:enable]
    execute "a2ensite #{params[:name]}" do
      command "/usr/sbin/a2ensite #{params[:name]}"
      notifies :restart, resources(:service => "apache2")
      not_if do
        ::File.symlink?("#{node[:apache][:dir]}/sites-enabled/#{params[:name]}") or
        ::File.symlink?("#{node[:apache][:dir]}/sites-enabled/000-#{params[:name]}")
      end
      only_if do ::File.exists?("#{node[:apache][:dir]}/sites-available/#{params[:name]}") end
    end
  else
    execute "a2dissite #{params[:name]}" do
      command "/usr/sbin/a2dissite #{params[:name]}"
      notifies :restart, resources(:service => "apache2")
      only_if do ::File.symlink?("#{node[:apache][:dir]}/sites-enabled/#{params[:name]}") end
    end
  end
end

Once created, the definition can be used by placing it in a recipe:

apache_site resource
# Enable my_site.conf
apache_site "my_site.conf" do
  enable true
end

# Disable my_site.conf
apache_site "my_site.conf" do
  enable false
end

The attributes of the new apache_site object are made accessible with the params hash. Within the context of a chef-client run, the definition will be replaced by all the resources that are specified within the definition. For example, in the enabled case, the definition will be expanded to:

execute "a2ensite my_site.conf" do
  command "/usr/sbin/a2ensite my_site.conf"
  notifies :restart, resources(:service => "apache2")
  not_if do
    ::File.symlink?("/etc/apache2/sites-enabled/my_site.conf") or
      ::File.symlink?("/etc/apache2/sites-enabled/000-my_site.conf")
  end
end

Many Recipes, One Definition

Data can be passed to a definition from more than one recipe. For example, when both /etc/aliases and /etc/sudoers require updates from multiple recipes during a single chef-client run. A definition file that reopens resources would look something like:

# example provided by community member "Mithrandir". Thank you!

define :email_alias, :recipients => [] do
  execute "newaliases" do
    action :nothing
  end

  t = nil
  begin
    t = resources(:template => "/etc/aliases")
  rescue Chef::Exceptions::ResourceNotFound
    t = template "/etc/aliases" do
      source "aliases.erb"
      cookbook "aliases"
      variables({:aliases => {} })
      notifies :run, "execute[newaliases]"
    end
  end

  if not t.variables[:aliases].has_key?(params[:name])
    t.variables[:aliases][params[:name]] = []
  end
  t.variables[:aliases][params[:name]] << [ params[:recipients] ]
end

Virtual Hosts

Two applications need to be deployed and run on a single node under the same domain and sub-domain. A Ruby on Rails application will reside as the main application at example.com and a WordPress application will reside at blog.example.com. The domain is running Apache2 as the web server. The domain is expected to grow, but for now only two run_list resources are created, with the appropriate roles added to them. At some point in the future, when a new sub-domain is required, a new run_list resource would also be created.

The virtual host on the Apache2 server is only one per node, which can create challenges when a node requires updates. Using a definition helps get around this issue. For example, the web_app definition exists in the apache2 cookbook, and can be used like this:

web_app "blog_site" do
  server_name "blog"
  server_aliases [ "blog.#{node['domain']}", node['fqdn'] ]
  docroot "/srv/www/blog_site"
end

When the chef-client processes a recipe that contains this definition, it will find the web_app resource and will attempt to recognize it as a resource. Assuming that the apache2 cookbook is available, the resources contained within that cookbook will be found and loaded, replacing the definition.