summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/chef/resource_builder.rb2
-rw-r--r--lib/chef/resource_collection.rb50
-rw-r--r--lib/chef/run_context.rb26
-rw-r--r--lib/chef/runner.rb40
4 files changed, 93 insertions, 25 deletions
diff --git a/lib/chef/resource_builder.rb b/lib/chef/resource_builder.rb
index f3ca2e95ad..138e401d5c 100644
--- a/lib/chef/resource_builder.rb
+++ b/lib/chef/resource_builder.rb
@@ -137,7 +137,7 @@ class Chef
@prior_resource ||=
begin
key = "#{type}[#{name}]"
- run_context.resource_collection.lookup(key)
+ run_context.resource_collection.lookup_local(key)
rescue Chef::Exceptions::ResourceNotFound
nil
end
diff --git a/lib/chef/resource_collection.rb b/lib/chef/resource_collection.rb
index 6c5b4289d8..1429c25d7f 100644
--- a/lib/chef/resource_collection.rb
+++ b/lib/chef/resource_collection.rb
@@ -33,9 +33,12 @@ class Chef
extend Forwardable
attr_reader :resource_set, :resource_list
- private :resource_set, :resource_list
+ attr_accessor :run_context
- def initialize
+ protected :resource_set, :resource_list
+
+ def initialize(run_context = nil)
+ @run_context = run_context
@resource_set = ResourceSet.new
@resource_list = ResourceList.new
end
@@ -79,11 +82,50 @@ class Chef
resource_list_methods = Enumerable.instance_methods +
[:iterator, :all_resources, :[], :each, :execute_each_resource, :each_index, :empty?] -
- [:find] # find needs to run on the set
- resource_set_methods = [:lookup, :find, :resources, :keys, :validate_lookup_spec!]
+ [:find] # find overridden below
+ resource_set_methods = [:resources, :keys, :validate_lookup_spec!]
def_delegators :resource_list, *resource_list_methods
def_delegators :resource_set, *resource_set_methods
+ def lookup_local(key)
+ resource_set.lookup(key)
+ end
+
+ def find_local(*args)
+ resource_set.find(*args)
+ end
+
+ def lookup(key)
+ if run_context.nil?
+ lookup_local(key)
+ else
+ lookup_recursive(run_context, key)
+ end
+ end
+
+ def find(*args)
+ if run_context.nil?
+ find_local(*args)
+ else
+ find_recursive(run_context, *args)
+ end
+ end
+
+ private
+
+ def lookup_recursive(rc, key)
+ rc.resource_collection.resource_set.lookup(key)
+ rescue Chef::Exceptions::ResourceNotFound
+ raise if rc.parent_run_context.nil?
+ lookup_recursive(rc.parent_run_context, key)
+ end
+
+ def find_recursive(rc, *args)
+ rc.resource_collection.resource_set.find(*args)
+ rescue Chef::Exceptions::ResourceNotFound
+ raise if rc.parent_run_context.nil?
+ find_recursive(rc.parent_run_context, *args)
+ end
end
end
diff --git a/lib/chef/run_context.rb b/lib/chef/run_context.rb
index cb3338d3de..29c936a932 100644
--- a/lib/chef/run_context.rb
+++ b/lib/chef/run_context.rb
@@ -130,6 +130,14 @@ class Chef
#
attr_reader :delayed_notification_collection
+ #
+ # An Array containing the delayed (end of run) notifications triggered by
+ # resources during the converge phase of the chef run.
+ #
+ # @return [Array[Chef::Resource::Notification]] An array of notification objects
+ #
+ attr_reader :delayed_actions
+
# Creates a new Chef::RunContext object and populates its fields. This object gets
# used by the Chef Server to generate a fully compiled recipe list for a node.
#
@@ -152,6 +160,7 @@ class Chef
@loaded_attributes_hash = {}
@reboot_info = {}
@cookbook_compiler = nil
+ @delayed_actions = []
initialize_child_state
end
@@ -172,10 +181,11 @@ class Chef
#
def initialize_child_state
@audits = {}
- @resource_collection = Chef::ResourceCollection.new
+ @resource_collection = Chef::ResourceCollection.new(self)
@before_notification_collection = Hash.new { |h, k| h[k] = [] }
@immediate_notification_collection = Hash.new { |h, k| h[k] = [] }
@delayed_notification_collection = Hash.new { |h, k| h[k] = [] }
+ @delayed_actions = []
end
#
@@ -221,6 +231,18 @@ class Chef
end
#
+ # Adds a delayed action to the +delayed_actions+.
+ #
+ def add_delayed_action(notification)
+ if delayed_actions.any? { |existing_notification| existing_notification.duplicates?(notification) }
+ Chef::Log.info( "#{notification.notifying_resource} not queuing delayed action #{notification.action} on #{notification.resource}"\
+ " (delayed), as it's already been queued")
+ else
+ delayed_actions << notification
+ end
+ end
+
+ #
# Get the list of before notifications sent by the given resource.
#
# TODO seriously, this is actually wrong. resource.name is not unique,
@@ -640,6 +662,8 @@ ERROR_MESSAGE
audits
audits=
create_child
+ add_delayed_action
+ delayed_actions
delayed_notification_collection
delayed_notification_collection=
delayed_notifications
diff --git a/lib/chef/runner.rb b/lib/chef/runner.rb
index ce128203f2..cd5615bcec 100644
--- a/lib/chef/runner.rb
+++ b/lib/chef/runner.rb
@@ -30,13 +30,14 @@ class Chef
attr_reader :run_context
- attr_reader :delayed_actions
-
include Chef::Mixin::ParamsValidate
def initialize(run_context)
- @run_context = run_context
- @delayed_actions = []
+ @run_context = run_context
+ end
+
+ def delayed_actions
+ @run_context.delayed_actions
end
def events
@@ -48,16 +49,11 @@ class Chef
def run_action(resource, action, notification_type = nil, notifying_resource = nil)
# If there are any before notifications, why-run the resource
# and notify anyone who needs notifying
- # TODO cheffish has a bug where it passes itself instead of the run_context to us, so doesn't have before_notifications. Fix there, update dependency requirement, and remove this if statement.
- before_notifications = run_context.before_notifications(resource) if run_context.respond_to?(:before_notifications)
- if before_notifications && !before_notifications.empty?
- whyrun_before = Chef::Config[:why_run]
- begin
- Chef::Config[:why_run] = true
+ before_notifications = run_context.before_notifications(resource) || []
+ unless before_notifications.empty?
+ forced_why_run do
Chef::Log.info("#{resource} running why-run #{action} action to support before action")
resource.run_action(action, notification_type, notifying_resource)
- ensure
- Chef::Config[:why_run] = whyrun_before
end
if resource.updated_by_last_action?
@@ -65,8 +61,8 @@ class Chef
Chef::Log.info("#{resource} sending #{notification.action} action to #{notification.resource} (before)")
run_action(notification.resource, notification.action, :before, resource)
end
+ resource.updated_by_last_action(false)
end
-
end
# Actually run the action for realsies
@@ -82,12 +78,8 @@ class Chef
end
run_context.delayed_notifications(resource).each do |notification|
- if delayed_actions.any? { |existing_notification| existing_notification.duplicates?(notification) }
- Chef::Log.info( "#{resource} not queuing delayed action #{notification.action} on #{notification.resource}"\
- " (delayed), as it's already been queued")
- else
- delayed_actions << notification
- end
+ # send the notification to the run_context of the receiving resource
+ notification.resource.run_context.add_delayed_action(notification)
end
end
end
@@ -137,5 +129,15 @@ class Chef
rescue Exception => e
e
end
+
+ # helper to run a block of code with why_run forced to true and then restore it correctly
+ def forced_why_run
+ saved = Chef::Config[:why_run]
+ Chef::Config[:why_run] = true
+ yield
+ ensure
+ Chef::Config[:why_run] = saved
+ end
+
end
end