The way attributes get merged during the chef client run may appear troublesome in a situation when you need to overwrite an array of attributes.
Imagine you want to store an array of, say, memcache pool members in attributes. You’ve chosen to do it with array, so it looks like this (attributes file notation):
default['fooapp']['memcache'] = [ "memcache-a1:11211", "memcache-a2:11211" ] |
Now you want to configure different memcache pools, for different datacenters. You want to overwrite default attributes from attributes file, you also want to be able to have different number of pool members (array elements) in each datacenter. Let’s try with a roles specific to each datacenter, like this:
"fooapp": { "memcache": [ "memcache-b1:11211", "memcache-b2:11211", "memcache-b3:11211" ] } |
What you’ll getting on a node in datacenter “b” is:
$knife node show somenode-b1 -a fooapp fooapp: memcache: memcache-a1:11211 memcache-a2:11211 memcache-b1:11211 memcache-b2:11211 memcache-b3:11211 |
Oh, of course it’s merged! You didn’t want app from colo “b” to use memcache in colo “a”!
What you could do is remove default elements using special :knockout_prefix attribute allowing for subtractive merge. Elements prefixed with this custom prefix would get removed during merge. This is however not available since Chef 11 so I’ll skip discussing it.
The idea I got is to workaround it by keeping the elements in string instead of an array, like this:
default['fooapp']['memcache'] = "memcache-a1:11211,memcache-a2:11211" |
and in role:
"fooapp": { "memcache": "memcache-b1:11211,memcache-b2:11211,memcache-b3:11211" }, |
And then split them in a template:
<% node[:fooapp][:memcache].split(",").each do |m| %> |
It’s less elegant than using array but it works.
Different approach, to solve the problem, that I recommend you to get familiar with, by Noah Kantrowitz, can be found on his blog in a post called arrays and chef.