Let's say I have a default attribute in a cookbook:
default.nginx_upstreams = {
'service1' => ['service1.server.com'],
'service2' => ['service2.server.com'],
}
Then it gets modified and overridden in roles and environments until it finally gets to my recipe. There, I compute some additional services that I would like to add to the attribute. If I do something like this:
node.nginx_upstreams.merge! {'service3' => ['service3.server.com']}
then when I try to use the attribute in my template, I get a undefined method 'each' for nil:NilClass
in my template when I try to do
<% node.nginx_upstreams.each do |name, servers| %>
Plus, I also get a WARN: Setting attributes without specifying a precedence is deprecated and will be removed in Chef 11.0
. The helpful warning tells me how to set attributes at normal precedence (apparently, using node.set["key"] = "value"
, but doesn't tell me how to specify default or override attributes.
I can get around this problem by doing something like this:
upstreams = node.nginx_upstreams.to_hash
upstreams.merge! {'service3' => ['service3.server.com']}
template "nginx_config" do
variables({:upstreams=>upstreams})
end
but that feels like a hack. I cannot find any documentation on node.set()
beyond this page, which also indicates that you can set both normal and override attributes in the recipe but doesn't say how.
So... how do you properly set attributes (that get deep-merged along with everything else) from inside the recipe? What does the node.set()
call actually do, and can I tell it the precedence I want to merge at?
node.default["key"] = "value"
,node.set["key"] = "value"
andnode.override["key"] = "value"
for default, normal, and override precedence respectively – Impressment