DevOps Zone is brought to you in partnership with:

Ranjib is a system administrator at Google. Prior to Google, Ranjib was a senior consultant with ThoughtWorks. He works on private cloud implementation strategies, cloud adoption, system automation etc. He has worked on both application development as well as system administration, for past 6 years. Prior to ThoughtWorks, Ranjib was working with Persistent Systems . Ranjib has done his gradation in lifescience and masters in Bioinformatics. Ranjib is a staunch FOSS supporter. Ranjib is a DZone MVB and is not an employee of DZone and has posted 13 posts at DZone. You can read more from them at their website. View Full User Profile

Chef Patterns: Part 1 - Modeling Environment-Specific Differences

09.07.2012
| 10204 views |
  • submit to reddit

At work we have the notion of multiple environments. Dev (used by the developers), Staging (used for showcasing stories), QA/Testing (used by testers) and Production (caters to end user). At times, a pre-production environment is also present to simulate production like scenarios.

Though we fight hard to minimize the differences between these environments, they do exist and it is necessary to measure them, which in turn can provide us with some automated mechanisms to easily spot environment-specific differences. In this write up I'll be writing some of the techniques I'm using to model them. There are two generic goals for this techniques:

  • Testability of the recipe.
  • Ease with which one can spot the environment-specific differences

The techniques:

  1. Conditionals:  Use raw if else conditions to apply different resources in different environments. If you are sure that the difference will only be limited to certain minute details I will start employing raw conditional  statements like this:
    if node.chef_environment == 'development' do
      #
      # do not configure basic auth
      #
    else
      #
      #  configure basic auth
      #
    end
    What is important is that we should also write equivalent unit tests for such scenarios.
  2. Attributes: Use attributes to model the differences in environments.  In Chef, attributes can be overridden on per environment basis. This provides an elegant way to implement environment-specific changes. In the following example, the version of Ruby needs to be different in two environments.
    # the default attribute looks like
    default[:ruby][:version] = '1.8.7' 
    
    #
    # the resource inside ruby recipe might use it like this
    #
    ruby_version= 'ruby-'+ node[:ruby][:version]
    
    execute "download_ruby" do 
      cwd "/opt"
      command "wget -c http://someurl/#{ruby_version}.tgz"
    end
    
    #
    #
    Then this can be overridden in all servers within the staging environment with this snippet
    name "staging"
    override_attributes("ruby"=>{"version"=>'1.9.3'})
  3. Environment-specific Chef recipes/components:  Use environment specific recipes and other components on the fly. If the amount of differences is large enough, involving multiple resources and significant configuration differences within a single file, you can use the environment name itself to dynamically load an environment-specific template or  environment-specific sub recipe. For example the deploy recipe invokes database backup as well as load balancer rotation (the instance should be out of the load balancer during the deployment) tasks that are only in production and not in any other environments. For this we can have a dedicated recipe name deploy::production. On the other hand the main deploy recipe uses something like this
    pre_deployment_sub_recipe = "deploy::#{node.chef_environment}_pre_deployment"
    include_recipe sub_recipe
    #
    # Perform deployment
    #
    post_deployment_sub_recipe = "deploy::#{node.chef_environment}_post_deployment"
    You can use similar strategies to include environment-specific configurations as well.  Here's an example:
    template "/etc/nginx/nginx.conf" do
      source "nginx." + node.chef_environment + "conf.erb"
    end
    This one will pick up nginx.staging.conf.erb as a template in the staging environment's nginx configuration while nginx.production.conf.erb is in production environments. This does impose a problem because now every environment should have a corresponding sub-recipe or template. But again you can further scope this using one of the earlier two techniques. In this case, it is possible to do a file glob and pick up the environment-specific differences.
Published at DZone with permission of Ranjib Dey, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)