Roles and Role-Cookbooks hierarchy, inheritance, and ordering

Hi all.

Here is an example of a common practice with cookbooks and roles.
1/ I use a community cookbook, say postgres. In its attributes/default.rb, it has node.default[‘postgres’][‘memory’] = ‘1G’

2/ I use a role that overrides these attributes, called mypostgres.json for example it sets
“default_attributes”: { “postgres”: { “memory”:“2G” },
“run_list”:[ “recipe[postgres]” ]

And other parameters. Let’s keep to this one parameter for the sake of the example

3/ I use a specific role that overrides again. This time it’s for a specific project db that has yet more memory
“default_attributes”: { “postgres”: { “memory”:“4G” },
“run_list”:[ “role[mypostgres]” ]

Here, the behavior is that if I put role[projectdb] in my node run_list, when recipe[postgres::default], memory will be at 4G.

So this is fine, except roles don’t have versioning, and I’d like to be able to test things before using them in all environments for example. I can also do many levels of inclusions which can be nice.

So I try to emulate a similar behavior with two ‘role-cookbooks’

cookbook projectdb
projectdb/metadata.rb : depends 'mypostgres’
projectdb/recipes/default.rb : node.default[‘postgres’][‘memory’] = ‘4G’ ; include_recipe ‘mypostgres’

cookbook mypostgres
mypostgres/metadata.rb : depends 'postgres’
mypostgres/recipes/default.rb : node.default[‘postgres’][‘memory’] = ‘2G’ ; include_recipe ‘postgres’

Now because of execution order, the value of node[‘postgres’][‘memory’] will be at 2G, node 4G. I understand how this works and this behavior is not a bug. However, it does make replacing roles by role-cookbooks quite difficult if we have several wrappers.

For now the only way I have found is to use force_default and override. Which is not quite as nice as roles in run_list, because there you need to be aware of all the precedences used, and it limits the levels of inheritance. The upside is that it doesn’t allow more than 3 includes (default < force_default < override < force_override) and thus forces less deep inheritance and is more readable.

What are your thoughts on this? Any other methods/ideas/other for transition from roles to role-cookbooks?


I’m unsure you really understand how it works, as the problem would be the same with roles.
The order in the runlist has importance (same things in the depends), just use the same order, with role or cookbook, and you’ll end up with the same behavior.

If not give more details on your runlist and how you get to this situation…

Edit: Just got why you have a problem:

projectdb/recipes/default.rb : node.default['postgres']['memory'] = '4G' ; include_recipe 'mypostgres' cookbooks have an attributes directory, exactly aimed at defining attributes before recipes are compiled. instead of node.default in recipe use it like this:

projectdb/attributes/default.rb: default['postgres']['memory'] = '4G'

this way the attributes files will be loaded in correct order of dependency and you’ll end up with I meant above.

I have run into similar issues. I should start by disclosing I am a firm believer in role cookbooks rather than “raw” roles.

When I started using role cookbooks, and wrapper cookbooks in general, I started by putting the attribute settings in the recipes just like you have shown below. I had many rationalizations for why I was doing this, but in the end it proved to be a mistake because those attribute settings/overrides (in a recipe) execute too late.

As soon as I started putting attribute settings in the attributes/.rb files, where they really belong, things got infinitely better since it is my understanding that that ALL the attributes/.rb files are executed before any of the recipes/*.rb files.

So, I suggest you do the same: move all your “node.default[….” into attributes/*.rb files in your role and wrapper cookbooks.