4

Let's say I have two cookbooks, Foo and Bar. In cookbook Foo, attributes/default.rb contains the following:

default[:test] = [{:baz => 'A', :qux => 'B'}]

In cookbook B, I'd like to expand that array with another object (i.e. merge the two arrays): {:baz => 'C', :qux => 'D'}, ideally from within a recipe.

I tried to put the following Bar's recipe, or the attributes file:

default[:test] = [{:baz => 'C', :qux => 'D'}]

Expecting the two to get merged and result in:

node[:test] == [{:baz => 'A', :qux => 'B'}, {:baz => 'C', :qux => 'D'}]

But that is not what happens. Instead, only one of the two objects is contained in the array during a Chef-Solo run. I also tried with default.override and default.set with the same results. How can I merge the two arrays?

Thanks.

Joseph S.
  • 171
  • 1
  • 2
  • 4

2 Answers2

6

If you want to add individual hash elements to an array you can do it using the insertion operator << instead of the assignment operator =

In cookbook A

# Create the default attribute as an array
default[:test]=[{:baz => 'A', :qux => 'B'}]

In cookbook B

# Using array insertion on an existing array
default[:test] << {:baz => 'C', :qux => 'D'}



If the runlist order is not guaranteed to be A,B then you need to guard against trying to insert into an array which doesn't yet exist.

In cookbook A

default[:test] ||= []
default[:test] << {:baz => 'A', :qux => 'B'}

In cookbook B

default[:test] ||= []
default[:test] << {:baz => 'C', :qux => 'D'}



If you want to merge 2 arrays just use +=

# In cookbook A
default[:test]=[{:baz => 'A', :qux => 'B'}]

# In cookbook B
default[:test] += [ {:baz => 'C', :qux => 'D'}, {:baz => 'E', :qux => 'F'} ]
ctc
  • 103
  • 4
Tricky
  • 376
  • 2
  • 5
0

You can try using Chef's DeepMerge method from inside the second recipe like:

a = Chef::Mixin::DeepMerge.merge(node[:test], [ (new array here) ])

a will contain the deep-merged hashes!

priestjim
  • 669
  • 3
  • 8
  • That doesn't seem to work when I want to set node[:test] to the deeply merged array. Here's the exact code I'm running: Cookbook1/attributes/default.rb: `default[:test] = [{:baz => 'A', :qux => 'B'}]` Cookbook2/recipes/default.rb: `a = Chef::Mixin::DeepMerge.merge(node[:test], [{:baz => 'C', :qux => 'D'}])` followed by `node.override[:test] = a` This however results in node[:test] being empty. – Joseph S. Mar 05 '13 at 08:34
  • I proposed an edit to change from `merge` to `deep_merge`. If there are conflicts the value in the first "source" hash will win. – KCD Apr 09 '15 at 02:32