Just getting started with using chef
recently. I gather that attributes are stored in one large monolithic hash named node
that's available for use in your recipes and templates.
There seem to be multiple ways of defining attributes
- Directly in the recipe itself
- Under an attributes file - e.g.
attributes/default.rb
- In a JSON object that's passed to the
chef-solo
call. e.g.chef-solo -j web.json
Given the above 3, I'm curious
- Are those all the ways attributes can be defined?
- What's the order of precedence here? I'm assuming one of these methods supercedes the others
- Is #3 (the
JSON
method) only valid forchef-solo
? - I see both
node
anddefault
hashes defined. What's the difference? My best guess is that thedefault
hash defined inattributes/default.rb
gets merged into thenode
hash?
Thanks!
default['foo']['bar']['baz'] = 'qux'
Is exactly the same as this in recipes/whatever.rb:
node.default['foo']['bar']['baz'] = 'qux'
In retrospect having different syntaxes for recipes and attributes is confusing, but this design choice dates back to extremely old versions of Chef.
node.normal['foo'] = 'bar'
ornode.set['foo'] = 'bar'
in recipe (or attribute) files. The difference is that if you delete thenode.normal
line from the recipe the old setting on a node will persist, while if you delete anode.default
setting out of a recipe then when your run chef-client on the node that setting will get deleted.What happens in a chef-client run to make this happen is that at the start of the run the client issues a GET to get its old node document from the server. It then wipes the default, override and automatic(ohai) attributes while keeping the 'normal' attributes. The behavior of the default, override and automatic attributes makes the most sense -- you start over at the start of the run and then construct all the state, if its not in the recipe then you don't see a value there. However, normally the run_list is set on the node and nodes do not (often) manage their own run_list. In order to make the run_list persist it is a normal attribute.
The choice of the word 'normal' is unfortunate, as is the choice of 'node.set' setting 'normal' attributes. While those look like obvious choices to use to set attributes users should avoid using those. Again the problem is that they came first and were and are necessary and required for the run_list. Generally stick with default and override attributes only. And typically you can get most of your work done with default attributes, those should be preferred.
https://docs.chef.io/attributes.html#attribute-precedence
That's the ultimate source of truth for attribute precedence.
The problem with Chef Attributes is that they've grown organically and sprouted many options to try to help out users who painted themselves into a corner. In general you should never need to touch automatic, normal, force_default or force_override levels of attributes. You should also avoid setting attributes in recipe code. You should move setting attributes in recipes to attribute files. What this leaves is these places to set attributes:
You also can set attributes in recipes, but when you do that you invariably wind up getting your next lesson in the two-phase compile-converge parser that runs through the Chef Recipes. If you have recipes that need to communicate with each other its better to use the node.run_state which is just a hash that doesn't get written as node attributes. You can drop node.run_state[:foo] = 'bar' in one recipe and read it in another. You probably will see recipes that set attributes though so you should be aware of that.
Hope That Helps.
When writing a cookbook, I visualize three levels of attributes:
attributes/default.rb