Help with understanding attribute mashing

We’re taking advantage of the Elasticsearch community cookbook and decided to
create a wrapper cookbook for it so we can customize the attributes without
touching the community cookbook. To do this, we’re just using override
attributes in the wrapper cookbook, but we found some odd behavior.

For example, to override the following attribute in the elasticsearch cookbook:

default.elasticsearch[:version] = “0.90.2”

We’re doing the following in the attributes of the wrapper cookbook:

node.override.elasticsearch[:version] = “0.90.3”

The override works as expected. However, in the following case where another
default attribute assignment references the version attribute i.e.

default.elasticsearch[:filename] =
“elasticsearch-#{node.elasticsearch[:version]}.tar.gz”

The filename attribute gets the default of 0.90.2 instead the override of
0.90.3. There are two workarounds we’ve found to fix this:

  1. Put an include_attribute “elasticsearch_wrapper” in the attributes file of
    the community elasticsearch cookbook
  2. Place the version override attribute in a role

What I can’t figure out is why it works in the role. As far as I understand
it, the override is a higher precedent, be it in an another attribute file or a
role attribute, so the version assignment should pickup the override attribute
in either method. I’m guessing it has to do with the order of the mashing?

Any help would be appreciated.

Thanks!

-Chris

On Tuesday, November 19, 2013 at 2:37 PM, cwornell@spanning.com wrote:

We're taking advantage of the Elasticsearch community cookbook and decided to
create a wrapper cookbook for it so we can customize the attributes without
touching the community cookbook. To do this, we're just using override
attributes in the wrapper cookbook, but we found some odd behavior.

For example, to override the following attribute in the elasticsearch cookbook:

default.elasticsearch[:version] = "0.90.2"

We're doing the following in the attributes of the wrapper cookbook:

node.override.elasticsearch[:version] = "0.90.3"

The override works as expected. However, in the following case where another
default attribute assignment references the version attribute i.e.

default.elasticsearch[:filename] =
"elasticsearch-#{node.elasticsearch[:version]}.tar.gz"

The filename attribute gets the default of 0.90.2 instead the override of
0.90.3. There are two workarounds we've found to fix this:

  1. Put an include_attribute "elasticsearch_wrapper" in the attributes file of
    the community elasticsearch cookbook
  2. Place the version override attribute in a role

What I can't figure out is why it works in the role. As far as I understand
it, the override is a higher precedent, be it in an another attribute file or a
role attribute, so the version assignment should pickup the override attribute
in either method. I'm guessing it has to do with the order of the mashing?

Any help would be appreciated.

Thanks!

-Chris
In the past (Chef 10.x and before), attributes from roles were not distinct from attributes from other sources, but since they are specific to your own infrastructure (whereas cookbooks may be completely general if they are community cookbooks), we wanted attributes from roles to have priority over cookbook attributes from attributes files. The problem with this was that the only way we could accomplish that was to stash the role attributes and apply them at a later time in the chef run, after attribute files had been evaluated. This meant that they were not visible when you evaluated attributes files, so you would run into problems with computed attributes in attributes files.

For Chef 11, we implemented the priority of role attributes in code and did away with the ordering dance.

There’s a little more information about that here: Chef 11 In-Depth: Attributes Changes - Chef Blog | Chef

Finally, I’ll say that while I certainly understand the use case of role cookbooks, they way they work is a little backwards in this respect, so if you choose to use them, you need to be prepared to work around this problem.

--
Daniel DeLeo

Thanks for the info and the link Daniel. We still want to try using custom attributes in the wrapper cookbook instead of a role. One of the big pluses for us putting custom attributes in the wrapper cookbook is we can write code to set the attributes.

Searching around, I found there may be a couple of ways we can get away with reloading the attributes of the community cookbook once we load the attributes in our wrapper cookbook:

  1. The submitter for https://tickets.opscode.com/browse/CHEF-4234 placed 'node.from_file(run_context.resolve_attribute(*parse_attribute_file_spec("elasticsearch")))' at the end of their wrapper attributes file and that apparently worked. I have not been able to get it working though.

  2. Use the following ruby block in a recipe that is part of the run_list to reload attributes:
    ruby_block 'reload_attribs' do
    block do
    node.from_file(run_context.resolve_attribute("elasticsearch", "default"))
    end
    action :create
    end

No luck with option 2 either. Should the above allow the community attributes to get assigned the interpolated value from an override attribute in our wrapper?

Thanks!

-Chris
On Nov 20, 2013, at 11:02 AM, Daniel DeLeo dan@kallistec.com wrote:

On Tuesday, November 19, 2013 at 2:37 PM, cwornell@spanning.com wrote:

We're taking advantage of the Elasticsearch community cookbook and decided to
create a wrapper cookbook for it so we can customize the attributes without
touching the community cookbook. To do this, we're just using override
attributes in the wrapper cookbook, but we found some odd behavior.

For example, to override the following attribute in the elasticsearch cookbook:

default.elasticsearch[:version] = "0.90.2"

We're doing the following in the attributes of the wrapper cookbook:

node.override.elasticsearch[:version] = "0.90.3"

The override works as expected. However, in the following case where another
default attribute assignment references the version attribute i.e.

default.elasticsearch[:filename] =
"elasticsearch-#{node.elasticsearch[:version]}.tar.gz"

The filename attribute gets the default of 0.90.2 instead the override of
0.90.3. There are two workarounds we've found to fix this:

  1. Put an include_attribute "elasticsearch_wrapper" in the attributes file of
    the community elasticsearch cookbook
  2. Place the version override attribute in a role

What I can't figure out is why it works in the role. As far as I understand
it, the override is a higher precedent, be it in an another attribute file or a
role attribute, so the version assignment should pickup the override attribute
in either method. I'm guessing it has to do with the order of the mashing?

Any help would be appreciated.

Thanks!

-Chris
In the past (Chef 10.x and before), attributes from roles were not distinct from attributes from other sources, but since they are specific to your own infrastructure (whereas cookbooks may be completely general if they are community cookbooks), we wanted attributes from roles to have priority over cookbook attributes from attributes files. The problem with this was that the only way we could accomplish that was to stash the role attributes and apply them at a later time in the chef run, after attribute files had been evaluated. This meant that they were not visible when you evaluated attributes files, so you would run into problems with computed attributes in attributes files.

For Chef 11, we implemented the priority of role attributes in code and did away with the ordering dance.

There’s a little more information about that here: Chef 11 In-Depth: Attributes Changes - Chef Blog | Chef

Finally, I’ll say that while I certainly understand the use case of role cookbooks, they way they work is a little backwards in this respect, so if you choose to use them, you need to be prepared to work around this problem.

--
Daniel DeLeo