diff options
author | Jay Mundrawala <jdmundrawala@gmail.com> | 2014-09-10 11:19:17 -0700 |
---|---|---|
committer | Jay Mundrawala <jdmundrawala@gmail.com> | 2014-09-22 15:55:51 -0700 |
commit | b16b6157be731425f922b911d8810e9996c6887a (patch) | |
tree | cc5a93e47b374f0101b2ef757a877a253b7b1360 | |
parent | ed907ef4d64027212ceb0c082895b303844ff43f (diff) | |
download | chef-jdmundrawala/issue-2027-master.tar.gz |
We now check for powershell/dsc compat in provider.jdmundrawala/issue-2027-master
The problem with raising an exception when the resource is created
was that it was not possible to have a chef recipe that installs
the correct version of powershell and then runs the dsc_script
resource.
See https://github.com/opscode/chef/issues/2027
-rw-r--r-- | lib/chef/provider/dsc_script.rb | 34 | ||||
-rw-r--r-- | lib/chef/resource/dsc_script.rb | 18 | ||||
-rw-r--r-- | spec/unit/provider/dsc_script_spec.rb | 239 | ||||
-rw-r--r-- | spec/unit/resource/dsc_script_spec.rb | 29 |
4 files changed, 167 insertions, 153 deletions
diff --git a/lib/chef/provider/dsc_script.rb b/lib/chef/provider/dsc_script.rb index 5d7322842c..9bb2b5f364 100644 --- a/lib/chef/provider/dsc_script.rb +++ b/lib/chef/provider/dsc_script.rb @@ -46,9 +46,11 @@ class Chef end def load_current_resource - @dsc_resources_info = run_configuration(:test) - @resource_converged = @dsc_resources_info.all? do |resource| - !resource.changes_state? + if supports_dsc? + @dsc_resources_info = run_configuration(:test) + @resource_converged = @dsc_resources_info.all? do |resource| + !resource.changes_state? + end end end @@ -56,8 +58,26 @@ class Chef true end + def define_resource_requirements + requirements.assert(:run) do |a| + err = [ + 'Could not find Dsc on the system', + powershell_info_str, + "Powershell 4.0 or higher was not detected on your system and is required to use the dsc_script resource.", + ] + a.assertion { supports_dsc? } + a.failure_message Chef::Exceptions::NoProviderAvailable, err.join(' ') + a.whyrun err + ["Assuming a previous resource installs Powershell 4.0 or higher."] + a.block_action! + end + end + protected + def supports_dsc? + run_context && Chef::Platform.supports_dsc?(node) + end + def run_configuration(operation) config_directory = ::Dir.mktmpdir("chef-dsc-script") configuration_data_path = get_configuration_data_path(config_directory) @@ -143,6 +163,14 @@ class Chef end end end + + def powershell_info_str + if run_context && run_context.node[:languages] && run_context.node[:languages][:powershell] + install_info = "Powershell #{run_context.node[:languages][:powershell][:version]} was found on the system." + else + install_info = 'Powershell was not found.' + end + end end end end diff --git a/lib/chef/resource/dsc_script.rb b/lib/chef/resource/dsc_script.rb index 2972ace1aa..76ac6659d6 100644 --- a/lib/chef/resource/dsc_script.rb +++ b/lib/chef/resource/dsc_script.rb @@ -28,12 +28,8 @@ class Chef super @allowed_actions.push(:run) @action = :run - if(run_context && Chef::Platform.supports_dsc?(run_context.node)) - @provider = Chef::Provider::DscScript - else - raise Chef::Exceptions::NoProviderAvailable, - "#{powershell_info_str(run_context)}\nPowershell 4.0 or higher was not detected on your system and is required to use the dsc_script resource." - end + @provider = Chef::Provider::DscScript + @resource_name = :dsc_script end def code(arg=nil) @@ -125,16 +121,6 @@ class Chef :kind_of => [ Integer ] ) end - - private - - def powershell_info_str(run_context) - if run_context && run_context.node[:languages] && run_context.node[:languages][:powershell] - install_info = "Powershell #{run_context.node[:languages][:powershell][:version]} was found on the system." - else - install_info = 'Powershell was not found.' - end - end end end end diff --git a/spec/unit/provider/dsc_script_spec.rb b/spec/unit/provider/dsc_script_spec.rb index 8a7a7b5c6a..d8fbee3ff9 100644 --- a/spec/unit/provider/dsc_script_spec.rb +++ b/spec/unit/provider/dsc_script_spec.rb @@ -22,123 +22,152 @@ require 'chef/util/dsc/resource_info' require 'spec_helper' describe Chef::Provider::DscScript do - let (:node) { - node = Chef::Node.new - node.automatic[:languages][:powershell][:version] = '4.0' - node - } - let (:events) { Chef::EventDispatch::Dispatcher.new } - let (:run_context) { Chef::RunContext.new(node, {}, events) } - let (:resource) { Chef::Resource::DscScript.new("script", run_context) } - let (:provider) do - Chef::Provider::DscScript.new(resource, run_context) - end - - describe '#load_current_resource' do - it "describes the resource as converged if there were 0 DSC resources" do - allow(provider).to receive(:run_configuration).with(:test).and_return([]) - provider.load_current_resource - provider.instance_variable_get('@resource_converged').should be_true + context 'when DSC is available' do + let (:node) { + node = Chef::Node.new + node.automatic[:languages][:powershell][:version] = '4.0' + node + } + let (:events) { Chef::EventDispatch::Dispatcher.new } + let (:run_context) { Chef::RunContext.new(node, {}, events) } + let (:resource) { Chef::Resource::DscScript.new("script", run_context) } + let (:provider) do + Chef::Provider::DscScript.new(resource, run_context) end - it "describes the resource as not converged if there is 1 DSC resources that is converged" do - dsc_resource_info = Chef::Util::DSC::ResourceInfo.new('resource', false, ['nothing will change something']) - allow(provider).to receive(:run_configuration).with(:test).and_return([dsc_resource_info]) - provider.load_current_resource - provider.instance_variable_get('@resource_converged').should be_true + describe '#load_current_resource' do + it "describes the resource as converged if there were 0 DSC resources" do + allow(provider).to receive(:run_configuration).with(:test).and_return([]) + provider.load_current_resource + provider.instance_variable_get('@resource_converged').should be_true + end + + it "describes the resource as not converged if there is 1 DSC resources that is converged" do + dsc_resource_info = Chef::Util::DSC::ResourceInfo.new('resource', false, ['nothing will change something']) + allow(provider).to receive(:run_configuration).with(:test).and_return([dsc_resource_info]) + provider.load_current_resource + provider.instance_variable_get('@resource_converged').should be_true + end + + it "describes the resource as not converged if there is 1 DSC resources that is not converged" do + dsc_resource_info = Chef::Util::DSC::ResourceInfo.new('resource', true, ['will change something']) + allow(provider).to receive(:run_configuration).with(:test).and_return([dsc_resource_info]) + provider.load_current_resource + provider.instance_variable_get('@resource_converged').should be_false + end + + it "describes the resource as not converged if there are any DSC resources that are not converged" do + dsc_resource_info1 = Chef::Util::DSC::ResourceInfo.new('resource', true, ['will change something']) + dsc_resource_info2 = Chef::Util::DSC::ResourceInfo.new('resource', false, ['nothing will change something']) + + allow(provider).to receive(:run_configuration).with(:test).and_return([dsc_resource_info1, dsc_resource_info2]) + provider.load_current_resource + provider.instance_variable_get('@resource_converged').should be_false + end + + it "describes the resource as converged if all DSC resources that are converged" do + dsc_resource_info1 = Chef::Util::DSC::ResourceInfo.new('resource', false, ['nothing will change something']) + dsc_resource_info2 = Chef::Util::DSC::ResourceInfo.new('resource', false, ['nothing will change something']) + + allow(provider).to receive(:run_configuration).with(:test).and_return([dsc_resource_info1, dsc_resource_info2]) + provider.load_current_resource + provider.instance_variable_get('@resource_converged').should be_true + end end - it "describes the resource as not converged if there is 1 DSC resources that is not converged" do - dsc_resource_info = Chef::Util::DSC::ResourceInfo.new('resource', true, ['will change something']) - allow(provider).to receive(:run_configuration).with(:test).and_return([dsc_resource_info]) - provider.load_current_resource - provider.instance_variable_get('@resource_converged').should be_false + describe '#generate_configuration_document' do + # I think integration tests should cover these cases + + it 'uses configuration_document_from_script_path when a dsc script file is given' do + allow(provider).to receive(:load_current_resource) + resource.command("path_to_script") + generator = double('Chef::Util::DSC::ConfigurationGenerator') + generator.should_receive(:configuration_document_from_script_path) + allow(Chef::Util::DSC::ConfigurationGenerator).to receive(:new).and_return(generator) + provider.send(:generate_configuration_document, 'tmp', nil) + end + + it 'uses configuration_document_from_script_code when a the dsc resource is given' do + allow(provider).to receive(:load_current_resource) + resource.code("ImADSCResource{}") + generator = double('Chef::Util::DSC::ConfigurationGenerator') + generator.should_receive(:configuration_document_from_script_code) + allow(Chef::Util::DSC::ConfigurationGenerator).to receive(:new).and_return(generator) + provider.send(:generate_configuration_document, 'tmp', nil) + end + + it 'should noop if neither code or command are provided' do + allow(provider).to receive(:load_current_resource) + generator = double('Chef::Util::DSC::ConfigurationGenerator') + generator.should_receive(:configuration_document_from_script_code).with('', anything(), anything()) + allow(Chef::Util::DSC::ConfigurationGenerator).to receive(:new).and_return(generator) + provider.send(:generate_configuration_document, 'tmp', nil) + end end - it "describes the resource as not converged if there are any DSC resources that are not converged" do - dsc_resource_info1 = Chef::Util::DSC::ResourceInfo.new('resource', true, ['will change something']) - dsc_resource_info2 = Chef::Util::DSC::ResourceInfo.new('resource', false, ['nothing will change something']) - - allow(provider).to receive(:run_configuration).with(:test).and_return([dsc_resource_info1, dsc_resource_info2]) - provider.load_current_resource - provider.instance_variable_get('@resource_converged').should be_false + describe 'action_run' do + it 'should converge the script if it is not converged' do + dsc_resource_info = Chef::Util::DSC::ResourceInfo.new('resource', true, ['will change something']) + allow(provider).to receive(:run_configuration).with(:test).and_return([dsc_resource_info]) + allow(provider).to receive(:run_configuration).with(:set) + + provider.run_action(:run) + resource.should be_updated + end + + it 'should not converge if the script is already converged' do + allow(provider).to receive(:run_configuration).with(:test).and_return([]) + + provider.run_action(:run) + resource.should_not be_updated + end end - it "describes the resource as converged if all DSC resources that are converged" do - dsc_resource_info1 = Chef::Util::DSC::ResourceInfo.new('resource', false, ['nothing will change something']) - dsc_resource_info2 = Chef::Util::DSC::ResourceInfo.new('resource', false, ['nothing will change something']) - - allow(provider).to receive(:run_configuration).with(:test).and_return([dsc_resource_info1, dsc_resource_info2]) - provider.load_current_resource - provider.instance_variable_get('@resource_converged').should be_true + describe '#generate_description' do + it 'removes the resource name from the beginning of any log line from the LCM' do + dsc_resource_info = Chef::Util::DSC::ResourceInfo.new('resourcename', true, ['resourcename doing something', 'lastline']) + provider.instance_variable_set('@dsc_resources_info', [dsc_resource_info]) + provider.send(:generate_description)[1].should match(/converge DSC resource resourcename by doing something/) + end + + it 'ignores the last line' do + dsc_resource_info = Chef::Util::DSC::ResourceInfo.new('resourcename', true, ['resourcename doing something', 'lastline']) + provider.instance_variable_set('@dsc_resources_info', [dsc_resource_info]) + provider.send(:generate_description)[1].should_not match(/lastline/) + end + + it 'reports a dsc resource has not been changed if the LCM reported no change was required' do + dsc_resource_info = Chef::Util::DSC::ResourceInfo.new('resourcename', false, ['resourcename does nothing', 'lastline']) + provider.instance_variable_set('@dsc_resources_info', [dsc_resource_info]) + provider.send(:generate_description)[1].should match(/converge DSC resource resourcename by doing nothing/) + end end end - describe '#generate_configuration_document' do - # I think integration tests should cover these cases - - it 'uses configuration_document_from_script_path when a dsc script file is given' do - allow(provider).to receive(:load_current_resource) - resource.command("path_to_script") - generator = double('Chef::Util::DSC::ConfigurationGenerator') - generator.should_receive(:configuration_document_from_script_path) - allow(Chef::Util::DSC::ConfigurationGenerator).to receive(:new).and_return(generator) - provider.send(:generate_configuration_document, 'tmp', nil) - end - - it 'uses configuration_document_from_script_code when a the dsc resource is given' do - allow(provider).to receive(:load_current_resource) - resource.code("ImADSCResource{}") - generator = double('Chef::Util::DSC::ConfigurationGenerator') - generator.should_receive(:configuration_document_from_script_code) - allow(Chef::Util::DSC::ConfigurationGenerator).to receive(:new).and_return(generator) - provider.send(:generate_configuration_document, 'tmp', nil) - end - - it 'should noop if neither code or command are provided' do - allow(provider).to receive(:load_current_resource) - generator = double('Chef::Util::DSC::ConfigurationGenerator') - generator.should_receive(:configuration_document_from_script_code).with('', anything(), anything()) - allow(Chef::Util::DSC::ConfigurationGenerator).to receive(:new).and_return(generator) - provider.send(:generate_configuration_document, 'tmp', nil) - end - end - - describe 'action_run' do - it 'should converge the script if it is not converged' do - dsc_resource_info = Chef::Util::DSC::ResourceInfo.new('resource', true, ['will change something']) - allow(provider).to receive(:run_configuration).with(:test).and_return([dsc_resource_info]) - allow(provider).to receive(:run_configuration).with(:set) - - provider.run_action(:run) - resource.should be_updated - end - - it 'should not converge if the script is already converged' do - allow(provider).to receive(:run_configuration).with(:test).and_return([]) - - provider.run_action(:run) - resource.should_not be_updated - end - end - - describe '#generate_description' do - it 'removes the resource name from the beginning of any log line from the LCM' do - dsc_resource_info = Chef::Util::DSC::ResourceInfo.new('resourcename', true, ['resourcename doing something', 'lastline']) - provider.instance_variable_set('@dsc_resources_info', [dsc_resource_info]) - provider.send(:generate_description)[1].should match(/converge DSC resource resourcename by doing something/) - end - - it 'ignores the last line' do - dsc_resource_info = Chef::Util::DSC::ResourceInfo.new('resourcename', true, ['resourcename doing something', 'lastline']) - provider.instance_variable_set('@dsc_resources_info', [dsc_resource_info]) - provider.send(:generate_description)[1].should_not match(/lastline/) - end + context 'when Dsc is not available' do + let (:node) { Chef::Node.new } + let (:events) { Chef::EventDispatch::Dispatcher.new } + let (:run_context) { Chef::RunContext.new(node, {}, events) } + let (:resource) { Chef::Resource::DscScript.new('script', run_context) } + let (:provider) { Chef::Provider::DscScript.new(resource, run_context) } + + describe 'action_run' do + ['1.0', '2.0', '3.0'].each do |version| + it "raises an exception for powershell version '#{version}'" do + node.automatic[:languages][:powershell][:version] = version + + expect { + provider.run_action(:run) + }.to raise_error(Chef::Exceptions::NoProviderAvailable) + end + end + + it 'raises an exception if Powershell is not present' do + expect { + provider.run_action(:run) + }.to raise_error(Chef::Exceptions::NoProviderAvailable) + end - it 'reports a dsc resource has not been changed if the LCM reported no change was required' do - dsc_resource_info = Chef::Util::DSC::ResourceInfo.new('resourcename', false, ['resourcename does nothing', 'lastline']) - provider.instance_variable_set('@dsc_resources_info', [dsc_resource_info]) - provider.send(:generate_description)[1].should match(/converge DSC resource resourcename by doing nothing/) end end end diff --git a/spec/unit/resource/dsc_script_spec.rb b/spec/unit/resource/dsc_script_spec.rb index cbd502a61c..eb9d19e553 100644 --- a/spec/unit/resource/dsc_script_spec.rb +++ b/spec/unit/resource/dsc_script_spec.rb @@ -95,33 +95,4 @@ describe Chef::Resource::DscScript do expect { dsc_test_resource.configuration_data_script(configuration_data_script) }.to raise_error(ArgumentError) end end - - context 'when Powershell does not supported Dsc' do - ['1.0', '2.0', '3.0'].each do |version| - it "raises an exception for powershell version '#{version}'" do - node = Chef::Node.new - node.automatic[:languages][:powershell][:version] = version - empty_events = Chef::EventDispatch::Dispatcher.new - dsc_test_run_context = Chef::RunContext.new(node, {}, empty_events) - - expect { - Chef::Resource::DscScript.new(dsc_test_resource_name, dsc_test_run_context) - }.to raise_error(Chef::Exceptions::NoProviderAvailable) - end - end - end - - context 'when Powershell is not present' do - let (:dsc_test_run_context) { - node = Chef::Node.new - empty_events = Chef::EventDispatch::Dispatcher.new - dsc_test_run_context = Chef::RunContext.new(node, {}, empty_events) - } - - it 'raises an exception if powershell is not present' do - expect { - Chef::Resource::DscScript.new(dsc_test_resource_name, dsc_test_run_context) - }.to raise_error(Chef::Exceptions::NoProviderAvailable) - end - end end |