Changes to Attributes in Chef 11

Chefs,

I’d like to make you all aware of some changes to attributes that are set to be released with Chef 11.

TLDR

You can no longer use code like:

node[:foo] = "bar"
node.foo(“bar”)

Instead, use:

node.set[:foo] = "bar"
node.set.foo = “bar”

Background

In bygone versions of Chef, there was only a single level of attribute precedence, which corresponds to today’s “normal” attributes. In order to support compatibility for cookbooks, when multiple layers of attributes were added, the node attribute code supported compatibility with the previous implementation by making code like this:

node[:foo] = "bar"

…set an attribute at the “normal” level. The right way to do that with the new implementation is:

node.set[:foo] = "bar"

This had a number of consequences. Since there was ambiguity between whether the ultimate intention of a line of code was to read or write a value (up to the last method call, where this ambiguity is resolved), the code for attributes was very complicated and difficult to understand. You would also see that if you looked at an intermediate level of the attributes tree, you would get a Chef::Node::Attribute object, which would be somewhat opaque unless you call #to_hash on it. And finally, the implementation had a few bugs and surprising behaviors, such as:

  • iteration or calling a value repeatedly would not work:

    chef > i = node[:network][:interfaces] ; nil
    => nil
    chef > i.en0.to_hash
    => {“addresses”=>{“20:c9:d0:45:45:63”=> # snipped for brevity
    chef > i.en0.to_hash
    ArgumentError: Attribute en0 is not defined!

  • You could accidentally set attributes by calling an undefined method:

    chef > node.some_attribute?(:foo)
    => :foo
    chef > node[:some_attribute?]
    => :foo

Chef 11 Changes

In Chef 11, we’ve rewritten the attributes implementation to be much simpler, and behave as you’d expect in the vast majority of cases. In order to do so, we needed to remove the ability to write normal level attributes without first specifying which level you wish to write to. Code such as this:

node[:foo] = "bar"

Will now raise a Chef::Exceptions::ImmutableAttributeModification error. Put another way, read and write are now separate operations, and reads return an immutable, merged view of your attributes.

This change allowed us to fix the bugs/surprises I detailed above. Here’s the same code run under Chef 11:

chef > i = node[:network][:interfaces] ; nil
 => nil  
chef > i.en0
 => {"addresses"=>{"20:c9:d0:45:45:63"=> # snip for brevity
chef > i.en0
 => {"addresses"=>{"20:c9:d0:45:45:63"=> # snip for brevity


chef > node.some_attribute?(:foo)
NoMethodError: Undefined node attribute or method `some_attribute?' on `node'

Also notice that when reading attribute values, you don’t need to call #to_hash to get something readable back; instead you get a plain Hash (well, actually a Chef::Node::ImmutableMash, which inherits from Mash which inherits from Hash).

Finally, when setting an attribute using method call syntax, you need to use attribute="value" form instead of attribute("value") form. That is, do this:

# Works:
node.set.my_attribute = "value"

…instead of:

# Does not work:
node.set.my_attribute "value"

Impact and What to Look For

All modern cookbook code should work with the new attribute implementation out of the box. You are most likely to see problems if you have cookbooks written before Chef 0.8 or in a pre-0.8 style. For example, an old version of the nginx attributes file looked like this:

nginx Mash.new unless attribute?("nginx")
nginx[:version] = "1.0.5"
nginx[:dir]     = "/etc/nginx"

This code is using the implicit normal attribute setting that is retired in Chef 11, and running this cookbook under Chef 11 will cause an error. If you have code like this in your attributes files, the first thing to do is to consider whether these attributes should, in fact, be normal level attributes. Leaving them at the normal level means that you need to use override attributes to set different values via roles or environments, and that these values will be persisted across multiple chef-client runs. In the above example, these settings are really cookbook level defaults, so you’d want to change them like so:

default[:nginx][:version] = "1.0.5"
default[:nginx}[:dir]     = "/etc/nginx"

After making this change, existing nodes that have run the older version of the recipe will need to have the nginx attributes removed from their normal attributes. For a small number of nodes, you can make this change with knife node edit. For a larger number of nodes, you can automate this process with shef (incidentally renamed chef-shell in Chef 11) or knife exec.

What’s Not Changed

Hopefully, the above examples make this clear, but to be explicit: you can still set and access attributes using element reference with strings (e.g., node.default["foo"] = "bar"), element reference with symbols (e.g., node.default[:foo] = "bar"), and method calls (e.g., node.default.foo = "bar").

Documentation

It is our goal for the Chef 11 release is to exhaustively document all breaking changes (where we cannot avoid them in the first place). Changes that have been accepted and merged are documented here:

http://wiki.opscode.com/display/chef/Breaking+Changes+in+Chef+11

Further Changes

We are considering an additional change to attributes to address CHEF-2936. Right now we’re evaluating the impact of the proposed solution. I’ll send a similar post to this one if the proposed patch is merged.

Let us know if you have further questions.

Whip up some awesome,


Daniel DeLeo

On 10/31/2012 09:20 AM, Daniel DeLeo wrote:

Chefs,

I'd like to make you all aware of some changes to attributes that are set to
be released with Chef 11.

This is epically awesome.

I can't wait to s/.to_hash//g my entire chef codebase. :slight_smile:

--
Phil Dibowitz phil@ipom.com
Open Source software and tech docs Insanity Palace of Metallica
http://www.phildev.net/ http://www.ipom.com/

"Be who you are and say what you feel, because those who mind don't matter
and those who matter don't mind."

  • Dr. Seuss

Any chance we can get foodcritic to report some of the breaking chef 11
changes?

On Thu, Nov 1, 2012 at 12:54 AM, Phil Dibowitz phil@ipom.com wrote:

On 10/31/2012 09:20 AM, Daniel DeLeo wrote:

Chefs,

I'd like to make you all aware of some changes to attributes that are
set to
be released with Chef 11.

This is epically awesome.

I can't wait to s/.to_hash//g my entire chef codebase. :slight_smile:

--
Phil Dibowitz phil@ipom.com
Open Source software and tech docs Insanity Palace of Metallica
http://www.phildev.net/ http://www.ipom.com/

"Be who you are and say what you feel, because those who mind don't matter
and those who matter don't mind."

  • Dr. Seuss

On Thursday, November 1, 2012 at 11:44 AM, Zuhaib Siddique wrote:

Any chance we can get foodcritic to report some of the breaking chef 11 changes?

It's possible, but a bit more work than usual because you need to teach foodcritic to know about the differences in context between attribute files and recipes, and be a bit smarter about attribute access. I got a start on this but didn't have time to get any foodcritic code written:

--
Daniel DeLeo