summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLamont Granquist <lamont@scriptkiddie.org>2016-11-01 07:34:27 -0700
committerGitHub <noreply@github.com>2016-11-01 07:34:27 -0700
commit46d5d1e731a754d0639c4d7c20c88beb02d9a025 (patch)
treea80fdd46784d56b808d1fec482da4d98a303a489
parent492f61885abe228a14f29c6f9806c0043f03028f (diff)
parentedede76c46eb388b74292a20b4701a1b9b88c535 (diff)
downloadchef-46d5d1e731a754d0639c4d7c20c88beb02d9a025.tar.gz
Merge pull request #5495 from chef/lcg/attribute-setting-event
Core: add attribute_changed hook to event handlers
-rw-r--r--lib/chef/event_dispatch/base.rb3
-rw-r--r--lib/chef/node.rb2
-rw-r--r--lib/chef/node/attribute.rb48
-rw-r--r--lib/chef/node/attribute_collections.rb18
-rw-r--r--lib/chef/node/immutable_collections.rb4
-rw-r--r--lib/chef/node/mixin/state_tracking.rb54
-rw-r--r--spec/unit/node/attribute_spec.rb47
7 files changed, 126 insertions, 50 deletions
diff --git a/lib/chef/event_dispatch/base.rb b/lib/chef/event_dispatch/base.rb
index 04c960c7af..926bbe24b5 100644
--- a/lib/chef/event_dispatch/base.rb
+++ b/lib/chef/event_dispatch/base.rb
@@ -421,6 +421,9 @@ class Chef
def msg(message)
end
+ # Called when an attribute is changed by simple assignment
+ def attribute_changed(precedence, keys, value)
+ end
end
end
end
diff --git a/lib/chef/node.rb b/lib/chef/node.rb
index 34a92d325b..7351a7bfa5 100644
--- a/lib/chef/node.rb
+++ b/lib/chef/node.rb
@@ -78,7 +78,7 @@ class Chef
@policy_name = nil
@policy_group = nil
- @attributes = Chef::Node::Attribute.new({}, {}, {}, {})
+ @attributes = Chef::Node::Attribute.new({}, {}, {}, {}, self)
@run_state = {}
end
diff --git a/lib/chef/node/attribute.rb b/lib/chef/node/attribute.rb
index 2d6aff0b21..d5b0ee5d72 100644
--- a/lib/chef/node/attribute.rb
+++ b/lib/chef/node/attribute.rb
@@ -187,21 +187,22 @@ class Chef
# return the automatic level attribute component
attr_reader :automatic
- def initialize(normal, default, override, automatic)
- @default = VividMash.new(default, self)
- @env_default = VividMash.new({}, self)
- @role_default = VividMash.new({}, self)
- @force_default = VividMash.new({}, self)
+ def initialize(normal, default, override, automatic, node = nil)
+ @default = VividMash.new(default, self, node, :default)
+ @env_default = VividMash.new({}, self, node, :env_default)
+ @role_default = VividMash.new({}, self, node, :role_default)
+ @force_default = VividMash.new({}, self, node, :force_default)
- @normal = VividMash.new(normal, self)
+ @normal = VividMash.new(normal, self, node, :normal)
- @override = VividMash.new(override, self)
- @role_override = VividMash.new({}, self)
- @env_override = VividMash.new({}, self)
- @force_override = VividMash.new({}, self)
+ @override = VividMash.new(override, self, node, :override)
+ @role_override = VividMash.new({}, self, node, :role_override)
+ @env_override = VividMash.new({}, self, node, :env_override)
+ @force_override = VividMash.new({}, self, node, :force_override)
- @automatic = VividMash.new(automatic, self)
- super()
+ @automatic = VividMash.new(automatic, self, node, :automatic)
+
+ super(nil, self, node, :merged)
end
# Debug what's going on with an attribute. +args+ is a path spec to the
@@ -232,59 +233,59 @@ class Chef
# Set the cookbook level default attribute component to +new_data+.
def default=(new_data)
reset
- @default = VividMash.new(new_data, self)
+ @default = VividMash.new(new_data, self, __node__, :default)
end
# Set the role level default attribute component to +new_data+
def role_default=(new_data)
reset
- @role_default = VividMash.new(new_data, self)
+ @role_default = VividMash.new(new_data, self, __node__, :role_default)
end
# Set the environment level default attribute component to +new_data+
def env_default=(new_data)
reset
- @env_default = VividMash.new(new_data, self)
+ @env_default = VividMash.new(new_data, self, __node__, :env_default)
end
# Set the force_default (+default!+) level attributes to +new_data+
def force_default=(new_data)
reset
- @force_default = VividMash.new(new_data, self)
+ @force_default = VividMash.new(new_data, self, __node__, :force_default)
end
# Set the normal level attribute component to +new_data+
def normal=(new_data)
reset
- @normal = VividMash.new(new_data, self)
+ @normal = VividMash.new(new_data, self, __node__, :normal)
end
# Set the cookbook level override attribute component to +new_data+
def override=(new_data)
reset
- @override = VividMash.new(new_data, self)
+ @override = VividMash.new(new_data, self, __node__, :override)
end
# Set the role level override attribute component to +new_data+
def role_override=(new_data)
reset
- @role_override = VividMash.new(new_data, self)
+ @role_override = VividMash.new(new_data, self, __node__, :role_override)
end
# Set the environment level override attribute component to +new_data+
def env_override=(new_data)
reset
- @env_override = VividMash.new(new_data, self)
+ @env_override = VividMash.new(new_data, self, __node__, :env_override)
end
def force_override=(new_data)
reset
- @force_override = VividMash.new(new_data, self)
+ @force_override = VividMash.new(new_data, self, __node__, :force_override)
end
def automatic=(new_data)
reset
- @automatic = VividMash.new(new_data, self)
+ @automatic = VividMash.new(new_data, self, __node__, :automatic)
end
#
@@ -480,6 +481,7 @@ class Chef
if symbol == :to_ary
merged_attributes.send(symbol, *args)
elsif args.empty?
+ puts symbol
Chef.log_deprecation %q{method access to node attributes (node.foo.bar) is deprecated and will be removed in Chef 13, please use bracket syntax (node["foo"]["bar"])}
if key?(symbol)
self[symbol]
@@ -565,7 +567,7 @@ class Chef
return nil if components.compact.empty?
- components.inject(ImmutableMash.new({}, self)) do |merged, component|
+ components.inject(ImmutableMash.new({}, self, __node__, :merged)) do |merged, component|
Chef::Mixin::DeepMerge.hash_only_merge!(merged, component)
end
end
diff --git a/lib/chef/node/attribute_collections.rb b/lib/chef/node/attribute_collections.rb
index b01b447978..694b5fbc3a 100644
--- a/lib/chef/node/attribute_collections.rb
+++ b/lib/chef/node/attribute_collections.rb
@@ -34,7 +34,6 @@ class Chef
:compact!,
:default=,
:default_proc=,
- :delete,
:delete_at,
:delete_if,
:fill,
@@ -69,6 +68,11 @@ class Chef
end
end
+ def delete(key, &block)
+ send_reset_cache(__path__, key)
+ super
+ end
+
def initialize(data = [])
super(data)
map! { |e| convert_value(e) }
@@ -94,9 +98,9 @@ class Chef
when AttrArray
value
when Hash
- VividMash.new(value, __root__)
+ VividMash.new(value, __root__, __node__, __precedence__)
when Array
- AttrArray.new(value, __root__)
+ AttrArray.new(value, __root__, __node__, __precedence__)
else
value
end
@@ -143,7 +147,7 @@ class Chef
# object.
def delete(key, &block)
- send_reset_cache(__path__ + [ key ])
+ send_reset_cache(__path__, key)
super
end
@@ -170,7 +174,7 @@ class Chef
def []=(key, value)
ret = super
- send_reset_cache(__path__ + [ key ])
+ send_reset_cache(__path__, key)
ret
end
@@ -209,9 +213,9 @@ class Chef
when AttrArray
value
when Hash
- VividMash.new(value, __root__)
+ VividMash.new(value, __root__, __node__, __precedence__)
when Array
- AttrArray.new(value, __root__)
+ AttrArray.new(value, __root__, __node__, __precedence__)
else
value
end
diff --git a/lib/chef/node/immutable_collections.rb b/lib/chef/node/immutable_collections.rb
index 938135cbee..dad712e078 100644
--- a/lib/chef/node/immutable_collections.rb
+++ b/lib/chef/node/immutable_collections.rb
@@ -27,9 +27,9 @@ class Chef
def immutablize(value)
case value
when Hash
- ImmutableMash.new(value, __root__)
+ ImmutableMash.new(value, __root__, __node__, __precedence__)
when Array
- ImmutableArray.new(value, __root__)
+ ImmutableArray.new(value, __root__, __node__, __precedence__)
else
value
end
diff --git a/lib/chef/node/mixin/state_tracking.rb b/lib/chef/node/mixin/state_tracking.rb
index 9be102eeeb..469d4d300f 100644
--- a/lib/chef/node/mixin/state_tracking.rb
+++ b/lib/chef/node/mixin/state_tracking.rb
@@ -21,33 +21,30 @@ class Chef
module StateTracking
attr_reader :__path__
attr_reader :__root__
+ attr_reader :__node__
+ attr_reader :__precedence__
- NULL = Object.new
-
- def initialize(data = NULL, root = self)
+ def initialize(data = nil, root = self, node = nil, precedence = nil)
# __path__ and __root__ must be nil when we call super so it knows
# to avoid resetting the cache on construction
- data == NULL ? super() : super(data)
+ data.nil? ? super() : super(data)
@__path__ = []
@__root__ = root
+ @__node__ = node
+ @__precedence__ = precedence
end
def [](key)
ret = super
- if ret.is_a?(StateTracking)
- ret.__path__ = __path__ + [ convert_key(key) ]
- ret.__root__ = __root__
- end
- ret
+ next_path = [ __path__, convert_key(key) ].flatten.compact
+ copy_state_to(ret, next_path)
end
def []=(key, value)
ret = super
- if ret.is_a?(StateTracking)
- ret.__path__ = __path__ + [ convert_key(key) ]
- ret.__root__ = __root__
- end
- ret
+ next_path = [ __path__, convert_key(key) ].flatten.compact
+ send_attribute_changed_event(next_path, value)
+ copy_state_to(ret, next_path)
end
protected
@@ -60,10 +57,35 @@ class Chef
@__root__ = root
end
+ def __precedence__=(precedence)
+ @__precedence__ = precedence
+ end
+
+ def __node__=(node)
+ @__node__ = node
+ end
+
private
- def send_reset_cache(path = __path__)
- __root__.reset_cache(path.first) if !__root__.nil? && __root__.respond_to?(:reset_cache) && !path.nil?
+ def send_attribute_changed_event(next_path, value)
+ if __node__ && __node__.run_context && __node__.run_context.events
+ __node__.run_context.events.attribute_changed(__precedence__, next_path, value)
+ end
+ end
+
+ def send_reset_cache(path = nil, key = nil)
+ next_path = [ path, key ].flatten.compact
+ __root__.reset_cache(next_path.first) if !__root__.nil? && __root__.respond_to?(:reset_cache) && !next_path.nil?
+ end
+
+ def copy_state_to(ret, next_path)
+ if ret.is_a?(StateTracking)
+ ret.__path__ = next_path
+ ret.__root__ = __root__
+ ret.__node__ = __node__
+ ret.__precedence__ = __precedence__
+ end
+ ret
end
end
end
diff --git a/spec/unit/node/attribute_spec.rb b/spec/unit/node/attribute_spec.rb
index 00081a9fd9..a3e62ff939 100644
--- a/spec/unit/node/attribute_spec.rb
+++ b/spec/unit/node/attribute_spec.rb
@@ -21,7 +21,11 @@ require "spec_helper"
require "chef/node/attribute"
describe Chef::Node::Attribute do
+ let(:events) { instance_double(Chef::EventDispatch::Dispatcher) }
+ let(:run_context) { instance_double(Chef::RunContext, :events => events) }
+ let(:node) { instance_double(Chef::Node, :run_context => run_context) }
before(:each) do
+ allow(events).to receive(:attribute_changed)
@attribute_hash =
{ "dmi" => {},
"command" => { "ps" => "ps -ef" },
@@ -166,7 +170,7 @@ describe Chef::Node::Attribute do
},
}
@automatic_hash = { "week" => "friday" }
- @attributes = Chef::Node::Attribute.new(@attribute_hash, @default_hash, @override_hash, @automatic_hash)
+ @attributes = Chef::Node::Attribute.new(@attribute_hash, @default_hash, @override_hash, @automatic_hash, node)
end
describe "initialize" do
@@ -1196,4 +1200,45 @@ describe Chef::Node::Attribute do
expect(@attributes["foo"]["baz"]["bar"]).to be true
end
end
+
+ describe "node state" do
+ it "sets __root__ correctly" do
+ @attributes.default["foo"]["bar"]["baz"] = "quux"
+ expect(@attributes["foo"].__root__).to eql(@attributes)
+ expect(@attributes["foo"]["bar"].__root__).to eql(@attributes)
+ expect(@attributes.default["foo"].__root__).to eql(@attributes)
+ expect(@attributes.default["foo"]["bar"].__root__).to eql(@attributes)
+ end
+
+ it "sets __node__ correctly" do
+ @attributes.default["foo"]["bar"]["baz"] = "quux"
+ expect(@attributes["foo"].__node__).to eql(node)
+ expect(@attributes["foo"]["bar"].__node__).to eql(node)
+ expect(@attributes.default["foo"].__node__).to eql(node)
+ expect(@attributes.default["foo"]["bar"].__node__).to eql(node)
+ end
+
+ it "sets __path__ correctly" do
+ @attributes.default["foo"]["bar"]["baz"] = "quux"
+ expect(@attributes["foo"].__path__).to eql(["foo"])
+ expect(@attributes["foo"]["bar"].__path__).to eql(%w{foo bar})
+ expect(@attributes.default["foo"].__path__).to eql(["foo"])
+ expect(@attributes.default["foo"]["bar"].__path__).to eql(%w{foo bar})
+ end
+
+ it "sets __precedence__ correctly" do
+ @attributes.default["foo"]["bar"]["baz"] = "quux"
+ expect(@attributes["foo"].__precedence__).to eql(:merged)
+ expect(@attributes["foo"]["bar"].__precedence__).to eql(:merged)
+ expect(@attributes.default["foo"].__precedence__).to eql(:default)
+ expect(@attributes.default["foo"]["bar"].__precedence__).to eql(:default)
+ end
+
+ it "notifies on attribute changes" do
+ expect(events).to receive(:attribute_changed).with(:default, ["foo"], {})
+ expect(events).to receive(:attribute_changed).with(:default, %w{foo bar}, {})
+ expect(events).to receive(:attribute_changed).with(:default, %w{foo bar baz}, "quux")
+ @attributes.default["foo"]["bar"]["baz"] = "quux"
+ end
+ end
end