[chef] Attribute value precedence order in nested roles


Chronological Thread 
  • From: Ruby Newbie < >
  • To:
  • Subject: [chef] Attribute value precedence order in nested roles
  • Date: Sun, 06 Jun 2010 15:10:11 -0400

Ohai, Master Chefs!

I am trying to understand an aspect of Chef's behavior that is not fully 
explained in "Anatomy of a Chef Run"[1]: the precedence order of attribute 
values in nested roles. I beg your indulgence for an explanation of my use 
case and a simple example, which I believe will apply to many of those 
reading this list.

Like many others who have shared their tips here, I use Chef's roles to model 
information about my deployments at several different levels: there are 
"simple" roles, which contain just a few recipes and some default attributes; 
and more complex roles, which include one or more of these simpler roles in 
their run_list, perhaps with different values for the same attributes. By 
necessity, the simple roles are applied more widely than the complex roles 
(since the latter includes the former), so under this use case, the attribute 
values in the more complex, "compound" role should take precedence over the 
values in the simpler, more generic role.

For example, a simple role for a worker node might define an attribute to 
hold the address of its "queue server", for picking up new jobs:
================================
  name "simple_role"
  default_attributes "myrecipe" => {
    "queue_server" => 'workqueue.mydomain.com'
  }
================================

A more complex role might define a "standalone" server, which also runs 
worker threads, but also acts as its own queue server. So it would include 
all the functionality of the worker role, but specify a different 
queue_server:
================================
  name "compound_role"
  run_list "role[simple_role]", "recipe[...]", ...
  default_attributes "myrecipe" => {
    "queue_server" => 'localhost'
  }
================================

For the above use case, the desired value of node[:myrecipe][:queue_server] 
for all systems running the "compound_role" is "localhost". Unfortunately, 
Chef 0.8.16 does not behave this way...

When nested roles are applied during a Chef run, the values in the *included* 
role are preferred over those in the *including* role. So applying the above 
"compound_role" to a new node results in a value of 'workqueue.mydomain.com' 
for the 'queue_server' attribute, which is *not* the desired behavior for the 
described use case.

Note that the distinction between default_attributes and override_attributes 
is not directly relevant here -- override_attributes behave the same way, 
with respect to the precedence order of values set from from nested roles. I 
have not yet tested the result of setting the same attribute value from more 
than one included role, or from a role nested more than "once-deep" -- does 
the behavior depend on the include order?

Now, it is true that for this particular example, one could just use 
override_attributes in the compound role to get the desired result -- but 
just because of the oversimplified example. Roles can be nested indefinitely 
(as far as I know), so what if you later want to build an even more complex 
role, one that includes "compound_role", while still preserving the ability 
to run nodes with *only* "compound_role"? You can't, because once you set 
override_attributes from within a role, those values will be preferred over 
any of the same values you might attempt to set from an including role.

As a separate but related issue, any override_attributes defined in your 
roles will *also* be preferred over any attribute values you might try to 
assign via knife, the via Chef web GUI, or by passing run-time JSON file to 
chef-client. (This took me awhile to get used to in my first few weeks with 
Chef -- I kept setting attribute values for running nodes, only to see them 
overridden on the next client run.) Using override_attributes removes your 
ability to tweak the attribute values of a specific node without disabling 
chef-client altogether. For this reason, I now *only* use override_attributes 
to make a deployment-wide, run-time change (that is, all servers running a 
given role) -- and afterwards, I remove the override_attributes immediately, 
making sure the defaults are correct.

With all that said, I have the following questions for any and all readers:

1) Is there a workaround I am unaware of? Here is the challenge. Given the 
above example, can you produce a Chef configuration with the following 
requirements:
  a) by default, nodes with "simple_role" have queue_server = 
"workqueue.mydomain.com"
  b) by default, nodes with "compound_role" have queue_server = "localhost"
  c) Individual nodes (of either type) can be created with JSON to override 
the queue_server
  
2) Does the current inheritance behavior of attribute values work well for 
you as a user? Do you rely on this behavior, or code around it, as I must?

3) What are the objections to reversing this behavior? What if attribute 
values for the *including* role were preferred over (is that the same as 
"interpreted after"?) the values in the *included* role? Again, this is based 
on the assumption that the including role is more specific, and the included 
role applied more generally. If this change were made, the above example 
configuration would work as expected.

Thanks for your input,
- Ruby Newbie

[1]: http://wiki.opscode.com/display/chef/Anatomy+of+a+Chef+Run



  • [chef] Attribute value precedence order in nested roles, Ruby Newbie, 06/06/2010

Archive powered by MHonArc 2.6.16.

§