summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiacomo Bagnoli <gbagnoli@gmail.com>2016-09-20 15:15:08 +0100
committerGiacomo Bagnoli <gbagnoli@gmail.com>2016-09-21 15:58:07 +0100
commit39774946fbbd8b1f7d3267df3fcb7369ad5cac6d (patch)
treeb675e2919c982bc8a9fe0e50ef15d4a9a55c5e16
parentce694677fdc808143a28457dc44d56e97930b9ec (diff)
downloadchef-39774946fbbd8b1f7d3267df3fcb7369ad5cac6d.tar.gz
Convert attributes defined as literal arrays
VividMash, unlike Mash, does not recursively convert values inside arrays, just the array itself. As such, hashes inside arrays are not converted to *Mash, introducing a behaviour that differs from Chef 11, e.g. having this attr defined node['foo'] = [ { 'attr' => 'value' } ] in Chef 11 `node[:foo][0][:attr]` returns `'value'` while in Chef 12 it returns `nil` This commit fixes VividMash so arrays are recursively converted and the behaviour restored. Plus, immutablize merge_all result, so to be sure that hashes contained in Arrays are properly immutablized as well, so not existing attributes are not automatically vivified (thus returning an empty VividMash instead of nil) This problem was first reported in chef/chef#2871. Signed-off-by: Giacomo Bagnoli <gbagnoli@gmail.com>
-rw-r--r--lib/chef/node/attribute.rb4
-rw-r--r--lib/chef/node/attribute_collections.rb2
-rw-r--r--spec/unit/node_spec.rb44
3 files changed, 46 insertions, 4 deletions
diff --git a/lib/chef/node/attribute.rb b/lib/chef/node/attribute.rb
index 95b3b09f7e..a4a07275c0 100644
--- a/lib/chef/node/attribute.rb
+++ b/lib/chef/node/attribute.rb
@@ -452,9 +452,7 @@ class Chef
#
def merged_attributes(*path)
- # immutablize(
- merge_all(path)
- # )
+ immutablize(merge_all(path))
end
def combined_override(*path)
diff --git a/lib/chef/node/attribute_collections.rb b/lib/chef/node/attribute_collections.rb
index b739ea8490..b36d4a1540 100644
--- a/lib/chef/node/attribute_collections.rb
+++ b/lib/chef/node/attribute_collections.rb
@@ -187,7 +187,7 @@ class Chef
when Hash
VividMash.new(root, value)
when Array
- AttrArray.new(root, value)
+ AttrArray.new(root, value.map { |e| convert_value(e) })
else
value
end
diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb
index 2c8fc4408b..d1ad30c47a 100644
--- a/spec/unit/node_spec.rb
+++ b/spec/unit/node_spec.rb
@@ -784,6 +784,50 @@ describe Chef::Node do
expect(node["passenger"]["root_path_2"]).to eql("passenger-4.0.57")
expect(node[:passenger]["root_path_2"]).to eql("passenger-4.0.57")
end
+
+ it "should deep merge array attributes defined as literals" do
+ node.default["attr_literal_hash"] = { "key" => { "inner" => "value" } }
+ expect(node[:attr_literal_hash][:key]).not_to be_nil
+ expect(node[:attr_literal_hash][:key]).to be_a_kind_of(Mash)
+ expect(node[:attr_literal_hash]["key"]).not_to be_nil
+ expect(node["attr_literal_hash"]["key"]).not_to be_nil
+ expect(node["attr_literal_hash"][:key]).not_to be_nil
+ expect(node[:attr_literal_hash][:key][:inner]).to eql("value")
+ expect(node[:attr_literal_hash]["key"][:inner]).to eql("value")
+ expect(node[:attr_literal_hash]["key"]["inner"]).to eql("value")
+ expect(node[:attr_literal_hash][:key]["inner"]).to eql("value")
+ expect(node["attr_literal_hash"][:key][:inner]).to eql("value")
+ expect(node["attr_literal_hash"]["key"][:inner]).to eql("value")
+ expect(node["attr_literal_hash"]["key"]["inner"]).to eql("value")
+ expect(node["attr_literal_hash"][:key]["inner"]).to eql("value")
+ expect(node["attr_literal_hash"][:key]["not_existing"]).to be_nil
+
+ node.default["attr_literal_array"] = [{ "key" => { "inner" => "value" } }]
+ expect(node[:attr_literal_array]).to be_a_kind_of(Array)
+ expect(node[:attr_literal_array].first[:key]).not_to be_nil
+ expect(node[:attr_literal_array].first["key"]).not_to be_nil
+ expect(node["attr_literal_array"].first["key"]).not_to be_nil
+ expect(node["attr_literal_array"].first[:key]).not_to be_nil
+ expect(node[:attr_literal_array].first[:key][:inner]).to eql("value")
+ expect(node[:attr_literal_array].first["key"][:inner]).to eql("value")
+ expect(node[:attr_literal_array].first["key"]["inner"]).to eql("value")
+ expect(node[:attr_literal_array].first[:key]["inner"]).to eql("value")
+ expect(node["attr_literal_array"].first[:key][:inner]).to eql("value")
+ expect(node["attr_literal_array"].first["key"][:inner]).to eql("value")
+ expect(node["attr_literal_array"].first["key"]["inner"]).to eql("value")
+ expect(node["attr_literal_array"].first[:key]["inner"]).to eql("value")
+ expect(node["attr_literal_array"].first[:key]["not_existing"]).to be_nil
+
+ node.default["nested_array_literal"] = [[ { "key" => "value" } ], [ { "key" => "value" } ]]
+ expect(node[:nested_array_literal]).to be_a_kind_of(Array)
+ expect(node[:nested_array_literal].first).to be_a_kind_of(Array)
+ expect(node[:nested_array_literal].last).to be_a_kind_of(Array)
+ expect(node[:nested_array_literal].first.first[:key]).not_to be_nil
+ expect(node[:nested_array_literal].first.first["key"]).to eql("value")
+ expect(node[:nested_array_literal].first.last[:key]).not_to be_nil
+ expect(node[:nested_array_literal].first.last["key"]).to eql("value")
+ expect(node[:nested_array_literal].first.last[:key]["not_existing"]).to be_nil
+ end
end
it "should raise an ArgumentError if you ask for an attribute that doesn't exist via method_missing" do