Each cookbook in RightScale's repository contains a 'default.rb' recipe, which installs and configures any prerequisite utilities, configuration variables, etc. that are required in order to successfully execute any of the cookbooks resources (e.g. recipes). Before a recipe from a cookbook can be successfully executed, the cookbook's 'default.rb' recipe should be run first to resolve any cookbook dependencies.
Important! The Default Cookbook Pattern is used in the v12 and v13 ServerTemplate releases. See ServerTemplates.
You should use the Default Cookbook Pattern when:
The best way to explain the benefit of the default pattern is with a practical example.
For example, the OpsCode gems cookbook sets up a local gem server repository. The 'gems::mirror' recipe uses the built-in Chef 'cron' resource to set up a cron job that performs an rsync of gem directories every couple of hours. All Chef resources assume some basic pre-configured server state. (i.e. Any prerequisite requirements were resolved such as the successful installation and configuration of any required utilities.) For example, the 'cron' Chef resource assumes that the cron package is installed. Although the 'cron' package is typically installed on most distributions, there may be some instances where it's not included. So, how can we make sure that the 'cron' package is always installed to satisfy the 'cron' resource dependency?
One way to satisfy the requirement is to install the 'cron' package in the 'gems::mirror' recipe itself using the following code.
package "cron"
But the problem with that method is that you would have to repeat the code above in every Chef recipe that uses the 'cron' resource. So if you have to update the cron package to satisfy a configuration change that affects platform X, you would have to grep all of the cookbooks for the above package and add the following code.
package "cron"
bash "cron tweak for platform X" do
only_if do node[:platform] == "platform_x" end
code <<-EOH
sed -e "s,something/somthingelse/,,g" /etc/crontab
EOH
end
Although the method above may seem like a minor fix, it becomes difficult to scale and manage over the lifetime of your codebase, especially if your software development team has several developers. To prevent maintenance nightmares and bugs associated with duplicated code, it's recommended that you follow a more modular and reusable design pattern that's easier to maintain and modify over time.
A better way to ensure that all Chef resource dependencies are properly installed is to use a reusable default pattern, where dependencies are resolved by a cookbook's default recipe (default.rb). Each cookbook should have a default.rb recipe that installs and configures all base packages, prerequisites and resources required to run other recipes in the cookbook.
For example, instead of installing the 'cron' package in every recipe that uses the 'cron' resource, you can create a reusable default pattern following the steps below.
package "cron" # Add any other code to configure cron below
maintainer "Company X"
maintainer_email "user@example.com"
license "Apache 2.0"
description "Sets up a local gem server repository"
long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc'))
version "0.8"
depends "apache2"
depends "cron"
recipe "gems::server", "Sets up a local gem server repository"
recipe "gems::mirror", "Crons an rsync of rubyforge"
#
# Cookbook Name:: gems
# Recipe:: mirror
#
...
cron "mirror_rubyforge" do
command "rsync -av rsync://master.mirror.rubyforge.org/gems/ #{node[:gem_server][:rf_directory]}/gems && gem generate_index -d #{node[:gem_server][:rf_directory]}"
hour "2"
minute "0"
end
Some of the cookbooks published by RightScale contain a 'default.rb' recipe. In this recipe we install packages, setup configurations and initialize Chef resources that other cookbooks may depend on. We also setup any prerequisites and attributes that might be needed for the other recipes in the cookbook. Be sure to add the default recipe to your ServerTemplate boot scripts or your Role's runlist before running any other of the recipes in the cookbook. You should also add the default recipe of the cookbooks that your cookbook depends on.
See examples below for further details.
Chef-based ServerTemplates published by RightScale use the Default Pattern described above. In the screenshot below, a cookbook's 'default.rb' recipe is listed before any of its other recipes. Since a cookbook's non-default recipes depend on its default recipe to set any prerequisite configuration settings, its 'default.rb' recipe is listed before its other recipes.
Important!
It's also recommended that you resolve any cookbook dependencies for any operational scripts or decomission scripts in the boot script list to ensure than any operational or decommission script can be successfully executed.
Similarly, it's important to make sure that any cookbook dependencies are resolved before running any related recipes. For example, in the screenshot below, notice that the 'db' cookbook has several cookbook dependencies defined in its metadata. Before any of the 'db' cookbook's recipes are executed, all of its cookbook dependencies are resolved by first running the default recipes of the other cookbooks. Since the example below is for MySQL 5.1, the cookbook dependency on 'db_postgres' does not apply. Also, since both MySQL 5.1 and 5.5 are supported, the appropriate default recipe that's specific to MySQL 5.1 is listed. In fact, one of the only differences between the MySQL 5.1 and MySQL 5.5 ServerTemplates is that a different default recipe (db_mysql::default_5_5) is used for the MySQL 5.5 ServerTemplate.
© 2006-2014 RightScale, Inc. All rights reserved.
RightScale is a registered trademark of RightScale, Inc. All other products and services may be trademarks or servicemarks of their respective owners.