Attribute precedence in chef cookbook
Asked Answered
F

3

7

I am trying to implement a wrapper cookbook by taking inspiration from How to Write Reusable Chef Cookbooks, Gangnam Style. I wish to install tomcat 7 on my node without manager app. I have created a wrapper cookbook with the following attributes/default.rb file:

default["tomcat"]["base_version"] = 7
default["tomcat"]["deploy_manager_apps"] = false

The default attributes provided in tomcat/attributes/default.rb are:

default["tomcat"]["base_version"] = 6
#other attributes
default["tomcat"]["deploy_manager_apps"] = true
default["tomcat"]["user"] = "tomcat#{node["tomcat"]["base_version"]}

I wish to override these values across all attributes. However attributes such as ["tomcat"]["user"] are not getting overriden. The above still has the value of tomcat6 in node["tomcat"]["user"].

Do I have to override all the attributes which refer to ["tomcat"]["base_version"]}"? If my attributes/default.rb were loaded before tomcat cookbook's default.rb this would have worked fine.

I am using Berkshelf, Vagrant and Chef solo for development. In metadata.rb of my cookbook, I have mentioned depends "tomcat".

My custom cookbook is located at https://github.com/vaibhavguptaIITD/hcentive-ops/tree/master/hc-tomcat and tomcat community cookbook is located at https://github.com/opscode-cookbooks/tomcat.

Falgout answered 8/1, 2014 at 15:41 Comment(0)
T
9

This is due to how/when ruby code is evaluated during a Chef run. In a typical Chef run, the attribute files are evaluated first, in the dependency order dictated by the run_list as mentioned here: Chef 11 In-Depth: Attributes Changes.

Chef detects the dependency on the tomcat cookbook and loads/evaluates it's attributes first. So default["tomcat"]["user"] = "tomcat#{node["tomcat"]["base_version"]} is set to tomcat6 because at the time, the value of node["tomcat"]["base_version"] is 6.

Later, Chef evaluates your wrapper cookbook and properly sets the node["tomcat"]["base_version"] attribute to 7, however node["tomcat"]["user"] is never reevaluated.

So you will need to set the value for node["tomcat"]["user"] in your wrapper cookbook if you would like to change it's value.

Titfer answered 8/1, 2014 at 16:44 Comment(3)
Thanks for your response. Just being curious, isn't this procedure a bit cumbersome while writing a wrapper cookbook. Shouldn't it be logical to load wrapper cookbook's attributes first, before the wrapped cookbook.Falgout
There is a discussion about this behavior in tickets.opscode.com/browse/CHEF-4837Titfer
This blog post defines it well getchef.com/blog/2013/12/03/doing-wrapper-cookbooks-right hopefully they fix it in the next client. This issue turns a 2 line attribute file into a 50 line+ attribute fileSheen
M
3

This is not a bug in chef-client parse order. If we reversed it then from the wrapper cookbook you could never read the default values set in the base class since those values would not have been parsed yet.

It also allows your attributes set in the default precedence level to have priority over the cookbook you are wrapping. If we reversed the topological sort that would force wrapper cookbooks to use the override level. If you have wrapper cookbooks on top of wrapper cookbooks then now you've run out of standard attribute precedence levels. Eventually you run out of precedence levels and you've made a mess.

The order that attributes are parsed with dependents-first instead of parents-first gets these precedence issues correct so that everyone can just use the default level in their wrapper cookbooks.

The blog post here goes into the derived attributes problem in more detail and proposes a solution:

https://coderanger.net/derived-attributes/

I've cut an issue against chef-client to add support for lazy attributes officially here (the poise-derived repo referenced off of that blog is abandoned and should not be used):

https://github.com/chef/chef/issues/10345

Milone answered 25/8, 2020 at 3:41 Comment(0)
D
1

I ran into this same problem. It makes sense to me also to set a base variable and set other variables from it like:

default["apache"]["apache_docroot"] = '/var/www'

#other attributes:
default['apache']['webapp1_docroot'] = "#{node['apache']['apache_docroot']/webapp1}"

to get: /var/www/webapp1

As was pointed out, what happens is Chef find your depends cookbooks and loads their attributes first. Seems wrong in some sense. Why not load my parent

override["apache"]["apache_docroot"] = '/net1/websites'

first, then the depends will work fine. They are lower and will not get overridden.

I found a way to get around this problem. It's not great but it works. You end up doing:

  • Load depend tomcat *.rb
  • Load parent wrapper *.rb
  • Reload depend tomcat specific.rb

You can use this command node.from_file to reload attributes from another file:

puts "*** RUNNING bundle-apache-java-tomcat-example default.rb"

# Reload bundle-apache-java-jboss::default attributes to reset var's depending on apache_docroot value
node.from_file(run_context.resolve_attribute( "bundle-apache-java-tomcat", "default" ) )

this is loading cookbook: bundle-apache-java-tomcat, attribs file: default.rb

side note: I ended up leaving this in my cookbook but I wanted to set website attributes a 'simpler' way using a hash. I cannot set one attrib from another while I am initializing a hash at the same time, but I left that code in there in case I might still need it.

I created 2 new bundle cookbooks that I hope makes it easy to set up multiple web sites. You can set or not set a proxy link from apache to tomcat also.

https://github.com/stant/bundle-apache-java-tomcat-example (how to use main one) https://github.com/stant/bundle-apache-java-tomcat (main one)

Dirtcheap answered 13/2, 2015 at 21:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.