A library allows arbitrary Ruby code to be included in a cookbook, either as a way to extend the Chef language or to implement a new class directly. A library is defined in /libraries/library_name.rb for each cookbook. A library that is included in a cookbook is automatically required and will be available to all recipes, attributes, file definitions, providers, and definitions. A library is defined in the library_name.rb, which is found in the libraries folder for each cookbook. The contents of a library will determine the potential uses of that library in a cookbook.
A library can be used to:
The basic syntax of a library:
your_cookbook/libraries/your_example_library.rb
# define a module to mix into Chef::Recipe::namespace
module YourExampleLibrary
def your_function()
# ... do something useful
end
end
your_cookbook/recipes/default.rb
# open the Chef::Recipe class and mix in the library module
class Chef::Recipe::namespace
include YourExampleLibrary
end
your_function()
Note
In the preceding example, the ::namespace part of the Chef::Recipe::namespace syntax should only be used when a custom namespace has been added that extends the default Chef libraries.
The following examples show how to use cookbook libraries.
A database can contain a list of virtual hosts that are used by customers. A custom namespace could be created that looks something like:
require 'sequel'
class Chef::Recipe::ISP
# We can call this with ISP.vhosts
def self.vhosts
v = []
@db = Sequel.mysql(
'web',
:user => 'example',
:password => 'example_pw',
:host => 'dbserver.example.com'
)
@db[
"SELECT virtualhost.domainname,
usertable.userid,
usertable.uid,
usertable.gid,
usertable.homedir
FROM usertable, virtualhost
WHERE usertable.userid = virtualhost.user_name"
].all do |query|
vhost_data = {
:servername => query[:domainname],
:documentroot => query[:homedir],
:uid => query[:uid],
:gid => query[:gid],
}
v.push(vhost_data)
end
Chef::Log.debug("About to provision #{v.length} vhosts")
v
end
end
Note
The preceding was provided by Opscode community member “Arjuna (fujin)”. Thank you!
After the custom namespace is created, it could then be used in a recipe, like this:
# Using ISP.vhosts in a Recipe
ISP.vhosts.each do |vhost|
directory vhost[:documentroot] do
owner vhost[:uid]
group vhost[:gid]
mode 0755
action :create
end
directory "#{vhost[:documentroot]}/#{vhost[:domainname]}" do
owner vhost[:uid]
group vhost[:gid]
mode 0755
action :create
end
end
A customer record is stored in an attribute file that looks like this:
mycompany_customers({
:bob => {
:homedir => "/home/bob",
:webdir => "/home/bob/web"
}
}
)
A simple recipe may contain something like this:
directory node[:mycompany_customers][:bob][:webdir] do
owner "bob"
group "bob"
action :create
end
Or a less verbose version of the same simple recipe:
directory customer(:bob)[:webdir] do
owner "bob"
group "bob"
action :create
end
A simple library could be created that extends Chef::Recipe::, like this:
class Chef
class Recipe
# A shortcut to a customer
def customer(name)
node[:mycompany_customers][name]
end
end
end
A customer record is stored in an attribute file that looks like this:
mycompany_customers({
:bob => {
:homedir => "/home/bob",
:webdir => "/home/bob/web"
}
}
)
If there are many customer records in an environment, a simple recipe can be used to loop over every customer, like this:
all_customers do |name, info|
directory info[:webdir] do
owner name
group name
action :create
end
end
A simple library could be created that extends Chef::Recipe::, like this:
class Chef
class Recipe
def all_customers(&block)
node[:mycompany_customers].each do |name, info|
block.call(name, info)
end
end
end
end