In learning chef, I see conflicting patterns for wrapper cookbooks. For example.
Some cookbooks use default.rb and others use customize.rb for overrides.
attributes/default.rb
attributes/customize.rb
Which is best practice?
Also, some wrapper cookbooks have parameters like this, in the recipes/default.rb file :
normal['foo']['bar'] = 42
Whereas others have
default['foo']['bar'] = 42
And some have
node.set['foo']['bar'] = 42
Additionally some cookbooks use symbols, and other strings
['foo']['bar']
[:foo][:bar]
Which style should I use?
Update
Looks like chef has released an official style guide, which addresses at least part of the question (e.g. symbol vs strings). https://docs.chef.io/ruby.html#
I haven't seen any formal best practice on how to override attributes in wrapper cookbooks, I think one has to pick a style and as long as all your cookbooks are consistent within your organization, you should be good. What follows is my person opinions.
We initially followed the style of separating attribute files similar to that attributes/customize.rb
format you described. However, we found it's much simpler to just keep all attributes in attributes/default.rb
, especially since our attributes stay under 50 lines of code.
We always use the lowest attribute precedence when overriding attributes in our wrapper cookbook. We stick to default
whenever possible, because the order of precedence dictates your cookbook's default
wins out over the wrapped cookbooks default
.
Most cookbooks you see out there use the string access format.
default['foo']['bar'] = 42
In fact there's a foodcritic rule specifically discouraging the use of symbol access. Foodcritic also encourages using a consistent style for accessing attributes. If you're deciding on a style, keep in mind that the community has pretty much decided on the string access format.
However, there is another way. Attributes in Chef are not a Hash
, they're actually a Mash. Mash
happens to support accessing attributes as methods, so:
default.foo.bar = 42
We prefer that syntax because it's more compact.
However, be cautious, as Tejay Cardon points out, Chef implemented this with method_missing
, so you could run into issues if the attribute name is the same as a current (or future) node
object method. We preface all our cookbooks with our org name, so collision is unlikely, but when we override community cookbooks we might run into issues. Thankfully testing should catch any such lapses.
Overriding attributes in a wrapper cookbook will not work in surprising ways, particularly when string interpolation is involved. This is explained very well in this blog post. The main gist is if a cookbook defines attributes as:
default['version'] = '1.0'
default['url'] = "http://example.com/#{node['version']}.zip"
And in your wrapper cookbook you override version
:
default['version'] = '2.0'
The default['url']
will still resolve to http://example.com/1.0.zip
, not http://example.com/2.0.zip
as you might expect. This happens because the url attribute is interpolated before your override happens. The blog post goes into more depth and offers a potential solution.
At the end of the day, there are multiple ways to do things in Chef and the community is still maturing. There's some best practices around writing reusable cookbooks, etc, but the practices are still evolving.
The best thing you can do is try the various styles and figure out which one works best for you and your team. Once you've decided on a style, I'd suggest putting together a style guide (I've used this Angular style guide as inspiration). You can also enforce your style guide with custom foodcritic rules. We've had success with this approach.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With