Sean Kilgore

logikal is : black shirt ninja

Chef Environment Files That Are Easy on the Eye

The problem

The syntax used in all the examples of chef environemnt files have something similar to the following: Ever get tired of looking at chef environment files with default attributes that look like this?

dirty environment attributes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
"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:

json format looooong attribute example
1
2
3
4
5
6
7
8
9
10
11
12
13
default_attributes": {
  "application": {
    "partner": {
      "service": {
        "api": {
          "platform": {
            "stat": "foo"
          }
        }
      }
    }
  }
}

That’s equivalent to:

1
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?

default attribute files
1
2
3
4
5
6
7
# 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:

how to use ruby to make your chef environment files cleaner (this is a bad example)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$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.

use includes in chef environments!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$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.