Skip to main content

Cleaner Chef environment files with Ruby

·350 words·2 mins

The problem #

The syntax used in all the examples of chef environment files have something similar to the following:

	"default_attributes": {
    "apache": {
      "prefork": {
        "startservers": "30",
        "minspareservers": "5",
        "maxspareservers": "30",
        "serverlimit": "65",
        "maxclients": "60",
        "maxrequestsperchild": "1000"
      }
    }
	},
	"override_attributes": {
	}

This gets really really unwieldy, really quickly when you have over a thousand environment variables. It gets even worse when you have an attribute like this:

default_attributes": {
  "application": {
    "partner": {
      "service": {
        "api": {
          "platform": {
            "stat": "foo"
          }
        }
      }
    }
  }
}

That’s equivalent to:

node['application']['partner']['service']['api']['platform']['stat'] = "foo"

This format should look more familiar to you. Doesn’t this look like one of your cookbook attribute files?

	# Prefork Attributes
	default['apache']['prefork']['startservers'] = 16
	default['apache']['prefork']['minspareservers'] = 16
	default['apache']['prefork']['maxspareservers'] = 32
	default['apache']['prefork']['serverlimit'] = 400
	default['apache']['prefork']['maxclients'] = 400
	default['apache']['prefork']['maxrequestsperchild'] = 10000

So why do it the ugly default way? #

Don’t. Clean it up with a little Ruby. Put this in your environment file:

$environment = Hash.new{|h,k| h[k]=Hash.new(&h.default_proc) }
$override = Hash.new{|h,k| h[k]=Hash.new(&h.default_proc) }

$environment['apache']['prefork']['startservers'] = 16
$environment['apache']['prefork']['minspareservers'] = 16
$environment['apache']['prefork']['maxspareservers'] = 32
$environment['apache']['prefork']['serverlimit'] = 400
$environment['apache']['prefork']['maxclients'] = 400
$environment['apache']['prefork']['maxrequestsperchild'] = 10000

$override['apache']['prefork']['startservers'] = 16
$override['apache']['prefork']['minspareservers'] = 24
$override['apache']['prefork']['maxspareservers'] = 24
$override['apache']['prefork']['serverlimit'] = 400
$override['apache']['prefork']['maxclients'] = 400
$override['apache']['prefork']['maxrequestsperchild'] = 10000

default_attributes(Chef::Mixin::DeepMerge.merge($_default_environment, $environment))
override_attributes($override)

It’s better, but still pretty dirty. Make it even better.

Take all those default environment settings and put them in $HOMEBASE/environments/common/apache.rb, then use Ruby to include them.

$environment = Hash.new{|h,k| h[k]=Hash.new(&h.default_proc) }
$override = Hash.new{|h,k| h[k]=Hash.new(&h.default_proc) }

require File.expand_path("./common/apache.rb", File.dirname(__FILE__))

$override['apache']['prefork']['startservers'] = 16
$override['apache']['prefork']['minspareservers'] = 24
$override['apache']['prefork']['maxspareservers'] = 24
$override['apache']['prefork']['serverlimit'] = 400
$override['apache']['prefork']['maxclients'] = 400
$override['apache']['prefork']['maxrequestsperchild'] = 10000

default_attributes(Chef::Mixin::DeepMerge.merge($_default_environment, $environment))
override_attributes($override)

Wow, that’s a lot cleaner, anything else? #

This arrangement allows you to keep things that are the same across all environments managed in common files, separate from your environment files. We use this kind of arrangement to test things in certain environments, especially for new features or load testing new perfomance tuning variables. Once we know what we want, it makes it much easier to copy the $override attributes to the common files as defaults, instead of using the json format.