Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 21 additions & 16 deletions chef/lib/chef/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ def run_action(resource, action)
# the case where the resource was defined lazily (ie. in a ruby_block)
resource.resolve_notification_references

resource.run_action(action)
retry_if_allowed(resource) do
resource.run_action(action)
end

# Execute any immediate and queue up any delayed notifications
# associated with the resource, but only if it was updated *this time*
Expand Down Expand Up @@ -78,21 +80,9 @@ def converge

# Execute each resource.
run_context.resource_collection.execute_each_resource do |resource|
begin
Chef::Log.debug("Processing #{resource} on #{run_context.node.name}")

# Execute each of this resource's actions.
Array(resource.action).each {|action| run_action(resource, action)}
rescue => e
Chef::Log.error("#{resource} (#{resource.source_line}) had an error:\n#{e}\n#{e.backtrace.join("\n")}")
if resource.retries > 0
resource.retries -= 1
Chef::Log.info("Retrying execution of #{resource}, #{resource.retries} attempt(s) left")
sleep resource.retry_delay
retry
end
raise e unless resource.ignore_failure
end
Chef::Log.debug("Processing #{resource} on #{run_context.node.name}")

Array(resource.action).each { |action| run_action(resource, action)}
end

# Run all our :delayed actions
Expand All @@ -105,5 +95,20 @@ def converge

true
end

def retry_if_allowed(resource)
begin
yield
rescue => e
Chef::Log.error("#{resource} (#{resource.source_line}) had an error:\n#{e}\n#{e.backtrace.join("\n")}")
if resource.retries > 0
resource.retries -= 1
Chef::Log.info("Retrying execution of #{resource}, #{resource.retries} attempt(s) left")
sleep resource.retry_delay
retry
end
raise e unless resource.ignore_failure
end
end
end
end
41 changes: 40 additions & 1 deletion chef/spec/unit/runner_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,45 @@ def action_third_action
lambda { @runner.converge }.should raise_error(ArgumentError)
end

it "should execute immediate actions on changed resources and retry if specified" do
@first_resource.retries 3
notifying_resource = Chef::Resource::Cat.new("peanut", @run_context)
notifying_resource.action = :purr
@run_context.resource_collection << notifying_resource
@first_resource.action = :nothing
@runner.should_receive(:sleep).with(2).exactly(3).times
notifying_resource.notifies(:sell, @first_resource, :immediately)
@first_resource.should_receive(:run_action).with(:nothing).once.and_return(true)
@first_resource.should_receive(:run_action).with(:sell).exactly(4).and_raise(ArgumentError)
lambda { @runner.converge }.should raise_error(ArgumentError)
end

it "should execute delayed actions on changed resources and retry if specified" do
@first_resource.retries 3
notifying_resource = Chef::Resource::Cat.new("peanut", @run_context)
notifying_resource.action = :purr
@run_context.resource_collection << notifying_resource
@first_resource.action = :nothing
@runner.should_receive(:sleep).with(2).exactly(3).times
notifying_resource.notifies(:sell, @first_resource, :delayed)
@first_resource.should_receive(:run_action).with(:nothing).once.and_return(true)
@first_resource.should_receive(:run_action).with(:sell).exactly(4).and_raise(ArgumentError)
lambda { @runner.converge }.should raise_error(ArgumentError)
end

it "should not retry failed resources it notifies" do
notifying_resource = Chef::Resource::Cat.new("peanut", @run_context)
notifying_resource.action = :purr
notifying_resource.retries 3
@run_context.resource_collection << notifying_resource
@first_resource.action = :nothing
@runner.should_not_receive(:sleep)
notifying_resource.notifies(:sell, @first_resource, :delayed)
@first_resource.should_receive(:run_action).with(:nothing).once.and_return(true)
@first_resource.should_receive(:run_action).with(:sell).once.and_raise(ArgumentError)
lambda { @runner.converge }.should raise_error(ArgumentError)
end

it "should execute immediate actions on changed resources" do
notifying_resource = Chef::Resource::Cat.new("peanut", @run_context)
notifying_resource.action = :purr # only action that will set updated on the resource
Expand All @@ -132,7 +171,7 @@ def action_third_action

@first_resource.should be_updated
end

it "should follow a chain of actions" do
@first_resource.action = :nothing

Expand Down