Note: Please go to docs.rightscale.com to access the current RightScale documentation set. Also, feel free to Chat with us!
Home > Guides > Chef Cookbooks Developer Guide > Developer > Cookbook Development Resources > Chef Cookbook Design Patterns > Default Cookbook Pattern

Default Cookbook Pattern


Overview

What is the Default Cookbook Pattern?

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.

When should the Default Pattern be used?

You should use the Default Cookbook Pattern when:

  • your cookbook depends on packages, configurations, or LWRP to be present that are core to your cookbook and/or that other cookbooks might also depend on.
  • your cookbook provides packages, configurations, or Chef resources that other cookbooks may depend on.

Examples

Without the Default Cookbook Pattern

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.

With the Default Cookbook Pattern

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.

  1. Create a cookbook that describes the Chef resource. (e.g. cron) The cookbook will manage any 'cron' dependencies. 
  2. In the cookbook's default recipe (e.g. 'cron::default.rb') add the code that's required to install and configure the 'cron' package. (cookbooks/cron/recipes/default.rb)
    • package "cron"
      
      # Add any other code to configure cron below 
      
  3. For every other cookbook that contains a Chef resource (e.g. Chef recipe) that uses the 'cron' resource, add the 'depends' line of code (e.g. depends "cron") to the cookbook's metadata. For example, if 'cron' is used in a recipe of the 'gems' cookbook, you would add the following line of code to the 'gem' cookbook's 'metadata.rb' file (cookbooks/gems/metadata.rb) because one of its resources depends on cron being installed beforehand. Since the 'cron' cookbook implements the default pattern, when you explicitly define a cookbook dependency in a cookbook's metadata with a cookbook that implements the default pattern, the creators of a runlist/role must include 'cron::default' before any recipe from the gems cookbook to ensure that it can be successfully executed. In the example code below, the default recipes for the 'apache2' and 'cron' cookbooks should be executed before any of the 'gem' cookbook's recipes.
    • 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"
  4. Use the cron resource in a recipe. (cookbooks/gems/recipes/mirror.rb)
    • #
      # 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
  5. In the ServerTemplate's boot scripts list or Chef run list, make sure the 'cron::default' recipe is listed before the 'gems::mirror' recipe to ensure that the 'cron' package will be properly installed and configured before the 'gems::mirror' recipe (which uses the 'cron' resource) is executed.

    screen-CronScriptExample-v1.png

How does RightScale use the Default Cookbook Pattern?

The Default Recipe

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.

Example

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.

screen_ScriptsCookbookDefaults_v2.png
 

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.

screen_CookbookDependenciesMatch_v1.png

You must to post a comment.
Last modified
14:57, 23 Sep 2013

Tags

This page has no custom tags.

Classifications

This page has no classifications.

Announcements

None


© 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.