diff options
Diffstat (limited to 'spec/unit/runner_spec.rb')
-rw-r--r-- | spec/unit/runner_spec.rb | 483 |
1 files changed, 245 insertions, 238 deletions
diff --git a/spec/unit/runner_spec.rb b/spec/unit/runner_spec.rb index 68d8b0e011..9bd4199418 100644 --- a/spec/unit/runner_spec.rb +++ b/spec/unit/runner_spec.rb @@ -81,322 +81,329 @@ end describe Chef::Runner do - before(:each) do - @node = Chef::Node.new - @node.name "latte" - @node.automatic[:platform] = "mac_os_x" - @node.automatic[:platform_version] = "10.5.1" - @events = Chef::EventDispatch::Dispatcher.new - @run_context = Chef::RunContext.new(@node, Chef::CookbookCollection.new({}), @events) - @first_resource = Chef::Resource::Cat.new("loulou1", @run_context) - @run_context.resource_collection << @first_resource - Chef::Platform.set( - :resource => :cat, - :provider => Chef::Provider::SnakeOil - ) - @runner = Chef::Runner.new(@run_context) + let(:node) do + node = Chef::Node.new + node.name "latte" + node.automatic[:platform] = "mac_os_x" + node.automatic[:platform_version] = "10.5.1" + node end - it "should pass each resource in the collection to a provider" do - @run_context.resource_collection.should_receive(:execute_each_resource).once - @runner.converge - end + let(:events) { Chef::EventDispatch::Dispatcher.new } + let(:run_context) { Chef::RunContext.new(node, Chef::CookbookCollection.new({}), events) } + let(:first_resource) { Chef::Resource::Cat.new("loulou1", run_context) } + let(:runner) { Chef::Runner.new(run_context) } - it "should use the provider specified by the resource (if it has one)" do - provider = Chef::Provider::Easy.new(@run_context.resource_collection[0], @run_context) - # Expect provider to be called twice, because will fall back to old provider lookup - @run_context.resource_collection[0].should_receive(:provider).twice.and_return(Chef::Provider::Easy) - Chef::Provider::Easy.should_receive(:new).once.and_return(provider) - @runner.converge + before do + run_context.resource_collection << first_resource end - it "should use the platform provider if it has one" do - Chef::Platform.should_receive(:find_provider_for_node).once.and_return(Chef::Provider::SnakeOil) - @runner.converge - end + context "when we fall through to old Chef::Platform resolution" do + before do + # set up old Chef::Platform resolution instead of provider_resolver + Chef::Platform.set( + :resource => :cat, + :provider => Chef::Provider::SnakeOil + ) + allow(run_context.provider_resolver).to receive(:maybe_dynamic_provider_resolution).with(first_resource, anything()).and_return(nil) + end - it "should run the action for each resource" do - Chef::Platform.should_receive(:find_provider_for_node).once.and_return(Chef::Provider::SnakeOil) - provider = Chef::Provider::SnakeOil.new(@run_context.resource_collection[0], @run_context) - provider.should_receive(:action_sell).once.and_return(true) - Chef::Provider::SnakeOil.should_receive(:new).once.and_return(provider) - @runner.converge + it "should use the platform provider if it has one" do + expect(Chef::Platform).to receive(:find_provider_for_node).with(node, first_resource).and_call_original + runner.converge + end end - it "should raise exceptions as thrown by a provider" do - provider = Chef::Provider::SnakeOil.new(@run_context.resource_collection[0], @run_context) - Chef::Provider::SnakeOil.stub(:new).once.and_return(provider) - provider.stub(:action_sell).once.and_raise(ArgumentError) - lambda { @runner.converge }.should raise_error(ArgumentError) - end + context "when we are doing dynamic provider resolution" do - it "should not raise exceptions thrown by providers if the resource has ignore_failure set to true" do - @run_context.resource_collection[0].stub(:ignore_failure).and_return(true) - provider = Chef::Provider::SnakeOil.new(@run_context.resource_collection[0], @run_context) - Chef::Provider::SnakeOil.stub(:new).once.and_return(provider) - provider.stub(:action_sell).once.and_raise(ArgumentError) - lambda { @runner.converge }.should_not raise_error - end + it "should pass each resource in the collection to a provider" do + expect(run_context.resource_collection).to receive(:execute_each_resource).once + runner.converge + end - it "should retry with the specified delay if retries are specified" do - @first_resource.retries 3 - provider = Chef::Provider::SnakeOil.new(@run_context.resource_collection[0], @run_context) - Chef::Provider::SnakeOil.stub(:new).once.and_return(provider) - provider.stub(:action_sell).and_raise(ArgumentError) - @first_resource.should_receive(:sleep).with(2).exactly(3).times - lambda { @runner.converge }.should raise_error(ArgumentError) - end + it "should use the provider specified by the resource (if it has one)" do + provider = Chef::Provider::Easy.new(run_context.resource_collection[0], run_context) + # Expect provider to be called twice, because will fall back to old provider lookup + expect(run_context.resource_collection[0]).to receive(:provider).twice.and_return(Chef::Provider::Easy) + expect(Chef::Provider::Easy).to receive(:new).once.and_return(provider) + runner.converge + 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 + it "should run the action for each resource" do + provider = Chef::Provider::SnakeOil.new(run_context.resource_collection[0], run_context) + expect(provider).to receive(:action_sell).once.and_return(true) + expect(Chef::Provider::SnakeOil).to receive(:new).once.and_return(provider) + runner.converge + end - @run_context.resource_collection << notifying_resource - @first_resource.action = :nothing # won't be updated unless notified by other resource + it "should raise exceptions as thrown by a provider" do + provider = Chef::Provider::SnakeOil.new(run_context.resource_collection[0], run_context) + allow(Chef::Provider::SnakeOil).to receive(:new).once.and_return(provider) + allow(provider).to receive(:action_sell).once.and_raise(ArgumentError) + expect { runner.converge }.to raise_error(ArgumentError) + end - notifying_resource.notifies(:purr, @first_resource, :immediately) + it "should not raise exceptions thrown by providers if the resource has ignore_failure set to true" do + allow(run_context.resource_collection[0]).to receive(:ignore_failure).and_return(true) + provider = Chef::Provider::SnakeOil.new(run_context.resource_collection[0], run_context) + allow(Chef::Provider::SnakeOil).to receive(:new).once.and_return(provider) + allow(provider).to receive(:action_sell).once.and_raise(ArgumentError) + expect { runner.converge }.not_to raise_error + end - @runner.converge + it "should retry with the specified delay if retries are specified" do + first_resource.retries 3 + provider = Chef::Provider::SnakeOil.new(run_context.resource_collection[0], run_context) + allow(Chef::Provider::SnakeOil).to receive(:new).once.and_return(provider) + allow(provider).to receive(:action_sell).and_raise(ArgumentError) + expect(first_resource).to receive(:sleep).with(2).exactly(3).times + expect { runner.converge }.to raise_error(ArgumentError) + end - @first_resource.should be_updated - 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 - it "should follow a chain of actions" do - @first_resource.action = :nothing + run_context.resource_collection << notifying_resource + first_resource.action = :nothing # won't be updated unless notified by other resource - middle_resource = Chef::Resource::Cat.new("peanut", @run_context) - middle_resource.action = :nothing - @run_context.resource_collection << middle_resource - middle_resource.notifies(:purr, @first_resource, :immediately) + notifying_resource.notifies(:purr, first_resource, :immediately) - last_resource = Chef::Resource::Cat.new("snuffles", @run_context) - last_resource.action = :purr - @run_context.resource_collection << last_resource - last_resource.notifies(:purr, middle_resource, :immediately) + runner.converge - @runner.converge + expect(first_resource).to be_updated + end - last_resource.should be_updated # by action(:purr) - middle_resource.should be_updated # by notification from last_resource - @first_resource.should be_updated # by notification from middle_resource - end + it "should follow a chain of actions" do + first_resource.action = :nothing - it "should execute delayed actions on changed resources" do - @first_resource.action = :nothing - second_resource = Chef::Resource::Cat.new("peanut", @run_context) - second_resource.action = :purr + middle_resource = Chef::Resource::Cat.new("peanut", run_context) + middle_resource.action = :nothing + run_context.resource_collection << middle_resource + middle_resource.notifies(:purr, first_resource, :immediately) - @run_context.resource_collection << second_resource - second_resource.notifies(:purr, @first_resource, :delayed) + last_resource = Chef::Resource::Cat.new("snuffles", run_context) + last_resource.action = :purr + run_context.resource_collection << last_resource + last_resource.notifies(:purr, middle_resource, :immediately) - @runner.converge + runner.converge - @first_resource.should be_updated - end + expect(last_resource).to be_updated # by action(:purr) + expect(middle_resource).to be_updated # by notification from last_resource + expect(first_resource).to be_updated # by notification from middle_resource + end - it "should execute delayed notifications when a failure occurs in the chef client run" do - @first_resource.action = :nothing - second_resource = Chef::Resource::Cat.new("peanut", @run_context) - second_resource.action = :purr + it "should execute delayed actions on changed resources" do + first_resource.action = :nothing + second_resource = Chef::Resource::Cat.new("peanut", run_context) + second_resource.action = :purr - @run_context.resource_collection << second_resource - second_resource.notifies(:purr, @first_resource, :delayed) + run_context.resource_collection << second_resource + second_resource.notifies(:purr, first_resource, :delayed) - third_resource = FailureResource.new("explode", @run_context) - @run_context.resource_collection << third_resource + runner.converge - lambda {@runner.converge}.should raise_error(FailureProvider::ChefClientFail) + expect(first_resource).to be_updated + end - @first_resource.should be_updated - end + it "should execute delayed notifications when a failure occurs in the chef client run" do + first_resource.action = :nothing + second_resource = Chef::Resource::Cat.new("peanut", run_context) + second_resource.action = :purr - it "should execute delayed notifications when a failure occurs in a notification" do - @first_resource.action = :nothing - second_resource = Chef::Resource::Cat.new("peanut", @run_context) - second_resource.action = :purr + run_context.resource_collection << second_resource + second_resource.notifies(:purr, first_resource, :delayed) - @run_context.resource_collection << second_resource + third_resource = FailureResource.new("explode", run_context) + run_context.resource_collection << third_resource - third_resource = FailureResource.new("explode", @run_context) - third_resource.action = :nothing - @run_context.resource_collection << third_resource + expect { runner.converge }.to raise_error(FailureProvider::ChefClientFail) - second_resource.notifies(:fail, third_resource, :delayed) - second_resource.notifies(:purr, @first_resource, :delayed) + expect(first_resource).to be_updated + end - lambda {@runner.converge}.should raise_error(FailureProvider::ChefClientFail) + it "should execute delayed notifications when a failure occurs in a notification" do + first_resource.action = :nothing + second_resource = Chef::Resource::Cat.new("peanut", run_context) + second_resource.action = :purr - @first_resource.should be_updated - end + run_context.resource_collection << second_resource - it "should execute delayed notifications when a failure occurs in multiple notifications" do - @first_resource.action = :nothing - second_resource = Chef::Resource::Cat.new("peanut", @run_context) - second_resource.action = :purr + third_resource = FailureResource.new("explode", run_context) + third_resource.action = :nothing + run_context.resource_collection << third_resource - @run_context.resource_collection << second_resource + second_resource.notifies(:fail, third_resource, :delayed) + second_resource.notifies(:purr, first_resource, :delayed) - third_resource = FailureResource.new("explode", @run_context) - third_resource.action = :nothing - @run_context.resource_collection << third_resource + expect {runner.converge}.to raise_error(FailureProvider::ChefClientFail) - fourth_resource = FailureResource.new("explode again", @run_context) - fourth_resource.action = :nothing - @run_context.resource_collection << fourth_resource + expect(first_resource).to be_updated + end - second_resource.notifies(:fail, third_resource, :delayed) - second_resource.notifies(:fail, fourth_resource, :delayed) - second_resource.notifies(:purr, @first_resource, :delayed) + it "should execute delayed notifications when a failure occurs in multiple notifications" do + first_resource.action = :nothing + second_resource = Chef::Resource::Cat.new("peanut", run_context) + second_resource.action = :purr - exception = nil - begin - @runner.converge - rescue => e - exception = e - end - exception.should be_a(Chef::Exceptions::MultipleFailures) + run_context.resource_collection << second_resource + + third_resource = FailureResource.new("explode", run_context) + third_resource.action = :nothing + run_context.resource_collection << third_resource + + fourth_resource = FailureResource.new("explode again", run_context) + fourth_resource.action = :nothing + run_context.resource_collection << fourth_resource + + second_resource.notifies(:fail, third_resource, :delayed) + second_resource.notifies(:fail, fourth_resource, :delayed) + second_resource.notifies(:purr, first_resource, :delayed) + + exception = nil + begin + runner.converge + rescue => e + exception = e + end + expect(exception).to be_a(Chef::Exceptions::MultipleFailures) - expected_message =<<-E + expected_message =<<-E Multiple failures occurred: * FailureProvider::ChefClientFail occurred in delayed notification: [explode] (dynamically defined) had an error: FailureProvider::ChefClientFail: chef had an error of some sort * FailureProvider::ChefClientFail occurred in delayed notification: [explode again] (dynamically defined) had an error: FailureProvider::ChefClientFail: chef had an error of some sort -E - exception.message.should == expected_message + E + expect(exception.message).to eq(expected_message) - @first_resource.should be_updated - end - - it "does not duplicate delayed notifications" do - SnitchyProvider.clear_action_record - - Chef::Platform.set( - :resource => :cat, - :provider => SnitchyProvider - ) + expect(first_resource).to be_updated + end - @first_resource.action = :nothing + it "does not duplicate delayed notifications" do + SnitchyProvider.clear_action_record - second_resource = Chef::Resource::Cat.new("peanut", @run_context) - second_resource.action = :first_action - @run_context.resource_collection << second_resource + first_resource.action = :nothing + first_resource.provider = SnitchyProvider - third_resource = Chef::Resource::Cat.new("snickers", @run_context) - third_resource.action = :first_action - @run_context.resource_collection << third_resource + second_resource = Chef::Resource::Cat.new("peanut", run_context) + second_resource.action = :first_action + second_resource.provider = SnitchyProvider + run_context.resource_collection << second_resource - second_resource.notifies(:second_action, @first_resource, :delayed) - second_resource.notifies(:third_action, @first_resource, :delayed) + third_resource = Chef::Resource::Cat.new("snickers", run_context) + third_resource.action = :first_action + third_resource.provider = SnitchyProvider + run_context.resource_collection << third_resource - third_resource.notifies(:second_action, @first_resource, :delayed) - third_resource.notifies(:third_action, @first_resource, :delayed) + second_resource.notifies(:second_action, first_resource, :delayed) + second_resource.notifies(:third_action, first_resource, :delayed) - @runner.converge - # resources 2 and 3 call :first_action in the course of normal resource - # execution, and schedule delayed actions :second and :third on the first - # resource. The duplicate actions should "collapse" to a single notification - # and order should be preserved. - SnitchyProvider.all_actions_called.should == [:first, :first, :second, :third] - end + third_resource.notifies(:second_action, first_resource, :delayed) + third_resource.notifies(:third_action, first_resource, :delayed) - it "executes delayed notifications in the order they were declared" do - SnitchyProvider.clear_action_record + runner.converge + # resources 2 and 3 call :first_action in the course of normal resource + # execution, and schedule delayed actions :second and :third on the first + # resource. The duplicate actions should "collapse" to a single notification + # and order should be preserved. + expect(SnitchyProvider.all_actions_called).to eq([:first, :first, :second, :third]) + end - Chef::Platform.set( - :resource => :cat, - :provider => SnitchyProvider - ) + it "executes delayed notifications in the order they were declared" do + SnitchyProvider.clear_action_record - @first_resource.action = :nothing + first_resource.action = :nothing + first_resource.provider = SnitchyProvider - second_resource = Chef::Resource::Cat.new("peanut", @run_context) - second_resource.action = :first_action - @run_context.resource_collection << second_resource + second_resource = Chef::Resource::Cat.new("peanut", run_context) + second_resource.action = :first_action + second_resource.provider = SnitchyProvider + run_context.resource_collection << second_resource - third_resource = Chef::Resource::Cat.new("snickers", @run_context) - third_resource.action = :first_action - @run_context.resource_collection << third_resource + third_resource = Chef::Resource::Cat.new("snickers", run_context) + third_resource.action = :first_action + third_resource.provider = SnitchyProvider + run_context.resource_collection << third_resource - second_resource.notifies(:second_action, @first_resource, :delayed) - second_resource.notifies(:second_action, @first_resource, :delayed) + second_resource.notifies(:second_action, first_resource, :delayed) + second_resource.notifies(:second_action, first_resource, :delayed) - third_resource.notifies(:third_action, @first_resource, :delayed) - third_resource.notifies(:third_action, @first_resource, :delayed) + third_resource.notifies(:third_action, first_resource, :delayed) + third_resource.notifies(:third_action, first_resource, :delayed) - @runner.converge - SnitchyProvider.all_actions_called.should == [:first, :first, :second, :third] - end + runner.converge + expect(SnitchyProvider.all_actions_called).to eq([:first, :first, :second, :third]) + end - it "does not fire notifications if the resource was not updated by the last action executed" do - # REGRESSION TEST FOR CHEF-1452 - SnitchyProvider.clear_action_record + it "does not fire notifications if the resource was not updated by the last action executed" do + # REGRESSION TEST FOR CHEF-1452 + SnitchyProvider.clear_action_record - Chef::Platform.set( - :resource => :cat, - :provider => SnitchyProvider - ) + first_resource.action = :first_action + first_resource.provider = SnitchyProvider - @first_resource.action = :first_action + second_resource = Chef::Resource::Cat.new("peanut", run_context) + second_resource.action = :nothing + second_resource.provider = SnitchyProvider + run_context.resource_collection << second_resource - second_resource = Chef::Resource::Cat.new("peanut", @run_context) - second_resource.action = :nothing - @run_context.resource_collection << second_resource + third_resource = Chef::Resource::Cat.new("snickers", run_context) + third_resource.action = :nothing + third_resource.provider = SnitchyProvider + run_context.resource_collection << third_resource - third_resource = Chef::Resource::Cat.new("snickers", @run_context) - third_resource.action = :nothing - @run_context.resource_collection << third_resource + first_resource.notifies(:second_action, second_resource, :immediately) + second_resource.notifies(:third_action, third_resource, :immediately) - @first_resource.notifies(:second_action, second_resource, :immediately) - second_resource.notifies(:third_action, third_resource, :immediately) + runner.converge - @runner.converge + # All of the resources should only fire once: + expect(SnitchyProvider.all_actions_called).to eq([:first, :second, :third]) - # All of the resources should only fire once: - SnitchyProvider.all_actions_called.should == [:first, :second, :third] + # all of the resources should be marked as updated for reporting purposes + expect(first_resource).to be_updated + expect(second_resource).to be_updated + expect(third_resource).to be_updated + end - # all of the resources should be marked as updated for reporting purposes - @first_resource.should be_updated - second_resource.should be_updated - third_resource.should be_updated - end + it "should check a resource's only_if and not_if if notified by another resource" do + first_resource.action = :buy - it "should check a resource's only_if and not_if if notified by another resource" do - @first_resource.action = :buy + only_if_called_times = 0 + first_resource.only_if {only_if_called_times += 1; true} - only_if_called_times = 0 - @first_resource.only_if {only_if_called_times += 1; true} + not_if_called_times = 0 + first_resource.not_if {not_if_called_times += 1; false} - not_if_called_times = 0 - @first_resource.not_if {not_if_called_times += 1; false} + second_resource = Chef::Resource::Cat.new("carmel", run_context) + run_context.resource_collection << second_resource + second_resource.notifies(:purr, first_resource, :delayed) + second_resource.action = :purr - second_resource = Chef::Resource::Cat.new("carmel", @run_context) - @run_context.resource_collection << second_resource - second_resource.notifies(:purr, @first_resource, :delayed) - second_resource.action = :purr + # hits only_if first time when the resource is run in order, second on notify + runner.converge - # hits only_if first time when the resource is run in order, second on notify - @runner.converge + expect(only_if_called_times).to eq(2) + expect(not_if_called_times).to eq(2) + end - only_if_called_times.should == 2 - not_if_called_times.should == 2 - end + it "should resolve resource references in notifications when resources are defined lazily" do + first_resource.action = :nothing - it "should resolve resource references in notifications when resources are defined lazily" do - @first_resource.action = :nothing + lazy_resources = lambda { + last_resource = Chef::Resource::Cat.new("peanut", run_context) + run_context.resource_collection << last_resource + last_resource.notifies(:purr, first_resource.to_s, :delayed) + last_resource.action = :purr + } + second_resource = Chef::Resource::RubyBlock.new("myblock", run_context) + run_context.resource_collection << second_resource + second_resource.block { lazy_resources.call } - lazy_resources = lambda { - last_resource = Chef::Resource::Cat.new("peanut", @run_context) - @run_context.resource_collection << last_resource - last_resource.notifies(:purr, @first_resource.to_s, :delayed) - last_resource.action = :purr - } - second_resource = Chef::Resource::RubyBlock.new("myblock", @run_context) - @run_context.resource_collection << second_resource - second_resource.block { lazy_resources.call } + runner.converge - @runner.converge + expect(first_resource).to be_updated + end - @first_resource.should be_updated end - end - |