summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLamont Granquist <lamont@scriptkiddie.org>2021-01-14 20:33:44 -0800
committerLamont Granquist <lamont@scriptkiddie.org>2021-01-15 09:37:44 -0800
commit1f0e72db99e8d409ba90c7cd875ff4a51e5a1157 (patch)
tree7ae5867e6d0ab80f2cc39949d6ebd61a99c19e93
parented1d1ad509ac3dd5a38b983ea04b212187cf3f6f (diff)
downloadchef-lcg/lazy-attributes.tar.gz
add test, fix node attributes deep merge buglcg/lazy-attributes
Signed-off-by: Lamont Granquist <lamont@scriptkiddie.org>
-rw-r--r--chef-utils/lib/chef-utils/mash.rb15
-rw-r--r--lib/chef/node/attribute.rb4
-rw-r--r--spec/unit/node_spec.rb78
3 files changed, 95 insertions, 2 deletions
diff --git a/chef-utils/lib/chef-utils/mash.rb b/chef-utils/lib/chef-utils/mash.rb
index 484e172b33..bb48064aa3 100644
--- a/chef-utils/lib/chef-utils/mash.rb
+++ b/chef-utils/lib/chef-utils/mash.rb
@@ -94,6 +94,10 @@ module ChefUtils
end
end
+ unless method_defined?(:regular_reader)
+ alias_method :regular_reader, :[]
+ end
+
unless method_defined?(:regular_writer)
alias_method :regular_writer, :[]=
end
@@ -102,6 +106,11 @@ module ChefUtils
alias_method :regular_update, :update
end
+ # @param key<Object> The key to get.
+ def [](key)
+ regular_reader(key)
+ end
+
# @param key<Object> The key to set.
# @param value<Object>
# The value to set the key to.
@@ -114,6 +123,12 @@ module ChefUtils
# internal API for use by Chef's deep merge cache
# @api private
+ def internal_get(key)
+ regular_reader(key)
+ end
+
+ # internal API for use by Chef's deep merge cache
+ # @api private
def internal_set(key, value)
regular_writer(key, convert_value(value))
end
diff --git a/lib/chef/node/attribute.rb b/lib/chef/node/attribute.rb
index 29b60a98d5..3383b3c7e5 100644
--- a/lib/chef/node/attribute.rb
+++ b/lib/chef/node/attribute.rb
@@ -596,7 +596,7 @@ class Chef
merge_with.each do |key, merge_with_value|
value =
if merge_onto.key?(key)
- deep_merge!(safe_dup(merge_onto[key]), merge_with_value)
+ deep_merge!(safe_dup(merge_onto.internal_get(key)), merge_with_value)
else
merge_with_value
end
@@ -632,7 +632,7 @@ class Chef
merge_with.each do |key, merge_with_value|
value =
if merge_onto.key?(key)
- hash_only_merge!(safe_dup(merge_onto[key]), merge_with_value)
+ hash_only_merge!(safe_dup(merge_onto.internal_get(key)), merge_with_value)
else
merge_with_value
end
diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb
index ec7beb9a50..284a993f5c 100644
--- a/spec/unit/node_spec.rb
+++ b/spec/unit/node_spec.rb
@@ -1951,4 +1951,82 @@ describe Chef::Node do
expect(node["foo"]["bar"]["test"]).to eql("right")
end
end
+
+ describe "lazy values" do
+ it "supports lazy values in attributes" do
+ node.instance_eval do
+ default["foo"]["bar"] = lazy { node["fizz"]["buzz"] }
+ default["fizz"]["buzz"] = "works"
+ end
+ expect(node["foo"]["bar"]).to eql("works")
+ end
+
+ it "lazy values maintain laziness" do
+ node.instance_eval do
+ default["foo"]["bar"] = lazy { node["fizz"]["buzz"] }
+ default["fizz"]["buzz"] = "works"
+ end
+ expect(node["foo"]["bar"]).to eql("works")
+ node.default["fizz"]["buzz"] = "still works"
+ expect(node["foo"]["bar"]).to eql("still works")
+ end
+
+ it "supports recursive lazy values in attributes" do
+ node.instance_eval do
+ default["cool"]["beans"] = lazy { node["foo"]["bar"] }
+ default["foo"]["bar"] = lazy { node["fizz"]["buzz"] }
+ default["fizz"]["buzz"] = "works"
+ end
+ expect(node["cool"]["beans"]).to eql("works")
+ node.default["fizz"]["buzz"] = "still works"
+ expect(node["cool"]["beans"]).to eql("still works")
+ end
+
+ it "supports top level lazy values in attributes" do
+ # due to the top level deep merge cache these are special cases
+ node.instance_eval do
+ default["cool"] = lazy { node["foo"] }
+ default["foo"] = lazy { node["fizz"] }
+ default["fizz"] = "works"
+ end
+ expect(node["cool"]).to eql("works")
+ node.default["fizz"] = "still works"
+ expect(node["cool"]).to eql("still works")
+ end
+
+ it "supports deep merged values in attributes" do
+ node.instance_eval do
+ override["bar"]["cool"] = lazy { node["bar"]["foo"] }
+ override["bar"]["foo"] = lazy { node["bar"]["fizz"] }
+ override["bar"]["fizz"] = "works"
+ end
+ expect(node["bar"]["cool"]).to eql("works")
+ node.override["bar"]["fizz"] = "still works"
+ expect(node["bar"]["cool"]).to eql("still works")
+ end
+
+ it "supports overridden deep merged values in attributes (deep_merge)" do
+ node.instance_eval do
+ role_override["bar"] = { "cool" => "bad", "foo" => "bad", "fizz" => "bad" }
+ force_override["bar"]["cool"] = lazy { node["bar"]["foo"] }
+ force_override["bar"]["foo"] = lazy { node["bar"]["fizz"] }
+ force_override["bar"]["fizz"] = "works"
+ end
+ expect(node["bar"]["cool"]).to eql("works")
+ node.force_override["bar"]["fizz"] = "still works"
+ expect(node["bar"]["cool"]).to eql("still works")
+ end
+
+ it "supports overridden deep merged values in attributes (hash_only_merge)" do
+ node.instance_eval do
+ default["bar"] = { "cool" => "bad", "foo" => "bad", "fizz" => "bad" }
+ override["bar"]["cool"] = lazy { node["bar"]["foo"] }
+ override["bar"]["foo"] = lazy { node["bar"]["fizz"] }
+ override["bar"]["fizz"] = "works"
+ end
+ expect(node["bar"]["cool"]).to eql("works")
+ node.override["bar"]["fizz"] = "still works"
+ expect(node["bar"]["cool"]).to eql("still works")
+ end
+ end
end