Tales of a Chef Workflow: Attribute Layering

One of the first things you'll learn about in Chef, and possibly one of the more confusing concepts, is attributes. In this post I am making the assumption that you've given Chef a try and have come across cookbook attributes. If you have wondered which of the 15 possible attribute levels (see figure below) to use in your setup, then this article is for you. Consider attributes to be the tunable knobs and dials of your Chef environment. They allow you to change the behavior of your cookbooks without having to alter the cookbooks themselves. Attributes also serve as an indexed, searchable guide to your infrastructure because they get rolled up with the Ohai data collected about the system. What version of nginx are we installing versus the one we are running now? That question can be answered with a simple knife search instead of a local code search. knife search node 'nginx:version' will return the nginx version attribute from any nodes that save that data.

Many layers deep

When I first started with Chef years ago one of the most striking things I came across in the documentation was the concept of attributes. It was a table with 15 numbers ordered by precedence and to this day I still couldn't quite recite it from memory if you asked. What am I talking about? Let me show you:

In the above picture, the various levels of attributes are ordered from 1 to 15 where 1 is the most permissive and 15 is more or less accepted as the most true data. Automatic attributes come from Chef's Ohai system which inspects your server and reports back facts about it such as the filesystem, operating system version, CPU, Network, etc. Any attribute named after something in Ohai data for example will always be erased by the incoming data from Ohai.

The attributes themselves break up into three larger categories which are default, normal, and override. There are additional categories for each which add force_ in front of each if you prefer a lot more granularity with your attributes. Chef even includes additional exclamation versions of each such as override! which can get really tricky for precedence.

Beware of normal attributes

It is worth noting that the normal level of attributes are unique in that they are persisted back to the node data after a completed Chef run, which factor into the final computed attributes. The default and override level attributes don't have this property. An example of this is the tags feature of Chef which are normal level attributes keyed under "tags". If you add a tag to a node in Chef, use knife node edit to take a look at your newly added normal attribute. Keep in mind you cannot easily see this happening from within your cookbook, so it's another place data can emerge from and potentially caused unexpected issues.

Normals themselves used to be part of a soon to be removed feature in Chef called node.set which would save data permanently with the node (even if you altered the data with an override). The idea being that a given node or set of nodes could have their own unique settings and data. To this day you can now do node.normal to set a normal level attribute, but keep in mind that data will hang around the node until it is cleared manually or by a node.rm which is a newer feature in Chef 12 to remove attributes from the saved node data.

What does dnsimple do?

At dnsimple, we have started aggressively phasing out normal level attributes in our stack because of the unexpected side affects it can create. Many of the older community cookbooks used to use node.set everywhere, though that is now beginning to change with more modern cookbook design and the upcoming Chef deprecation. It is our philosophy that cookbooks, especially shared library ones, should be as permissive as possible. Defaults are the sensible default which can be easily changed via override in any number of places. Need something special for production over the defaults in testing? Override in a role, environment, policy, or wrapper cookbook will always do the trick.

Attributes are hard to get right, but we also don't have complex environmental or managerial needs to require 15 layers of attributes. When you think about making Chef accessible to other teams within your organization, go simple. Over time we realized that our setup really only needed defaults in our cookbooks and overrides in what we call run list cookbooks that just run recipes for us. Roles and even environment level overrides will soon be a thing of the past, simplifying how we deploy and where we look for data about our infrastructure. The run list attributes will eventually migrate over to Policyfiles, but that post is for another day in this series.