summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Edwards <adamed@opscode.com>2014-09-01 08:32:32 -0700
committerAdam Edwards <adamed@opscode.com>2014-09-01 08:32:32 -0700
commit6d2462faa543874b23064dceb950f50e019c760a (patch)
tree2ec7c330f738611005cace99c2db0025eded83a9
parent4f98c0027c59cd1faad25a3307fecec6942f9f6b (diff)
downloadchef-6d2462faa543874b23064dceb950f50e019c760a.tar.gz
Correctly handle dsc resource import failures
-rw-r--r--lib/chef/util/dsc/local_configuration_manager.rb52
-rw-r--r--lib/chef/util/powershell/cmdlet_result.rb4
-rw-r--r--spec/unit/util/dsc/lcm_output_parser_spec.rb4
-rw-r--r--spec/unit/util/dsc/local_configuration_manager_spec.rb134
4 files changed, 156 insertions, 38 deletions
diff --git a/lib/chef/util/dsc/local_configuration_manager.rb b/lib/chef/util/dsc/local_configuration_manager.rb
index d25f144095..e7e37718a7 100644
--- a/lib/chef/util/dsc/local_configuration_manager.rb
+++ b/lib/chef/util/dsc/local_configuration_manager.rb
@@ -27,24 +27,10 @@ class Chef::Util::DSC
clear_execution_time
end
- LCM_MODULE_NOT_INSTALLED_ERROR_CODE = 0x80131500
-
def test_configuration(configuration_document)
status = run_configuration_cmdlet(configuration_document)
- command_output = status.return_value
- unless status.succeeded?
- if status.exit_code == LCM_MODULE_NOT_INSTALLED_ERROR_CODE
- Chef::Log::warn('Unable to test configuration because a required DSC PowerShell module may not be installed.')
- command_output = ''
- end
- if status.stderr.gsub(/\s+/, ' ') =~ /A parameter cannot be found that matches parameter name 'Whatif'/
- # LCM returns an error if any of the resources do not support the opptional What-If
- Chef::Log::warn("Received error while testing configuration due to resource not supporting 'WhatIf'")
- else
- raise Chef::Exceptions::PowershellCmdletException, "Powershell Cmdlet failed: #{status.stderr.gsub(/\s+/, ' ')}"
- end
- end
- configuration_update_required?(command_output)
+ handle_what_if_exception!(status.stderr) unless status.succeeded?
+ configuration_update_required?(status.return_value)
end
def set_configuration(configuration_document)
@@ -88,25 +74,27 @@ class Chef::Util::DSC
def lcm_command_code(configuration_path, test_only_parameters)
<<-EOH
-try
-{
- $ProgressPreference = 'SilentlyContinue';start-dscconfiguration -path #{@configuration_path} -wait -force #{test_only_parameters} -erroraction 'Stop'
-}
-catch [Microsoft.Management.Infrastructure.CimException]
-{
- $exception = $_.Exception
- write-error -Exception $exception
- $StatusCode = 1
- if ( $exception.HResult -ne 0 )
- {
- $StatusCode = $exception.HResult
- }
- $exception | format-table -property * -force
- exit $StatusCode
-}
+$ProgressPreference = 'SilentlyContinue';start-dscconfiguration -path #{@configuration_path} -wait -force -erroraction 'stop' #{test_only_parameters}
EOH
end
+ def handle_what_if_exception!(what_if_exception_output)
+ if what_if_exception_output.gsub(/\s+/, ' ') =~ /A parameter cannot be found that matches parameter name 'Whatif'/i
+ # LCM returns an error if any of the resources do not support the opptional What-If
+ Chef::Log::warn("Received error while testing configuration due to resource not supporting 'WhatIf'")
+ elsif output_has_dsc_module_failure?(what_if_exception_output)
+ Chef::Log::warn('Received error while testing configuration due to a module for an imported resource possibly not being fully installed:\n#{what_if_exception_output.gsub(/\s+/, ' ')}')
+ else
+ raise Chef::Exceptions::PowershellCmdletException, "Powershell Cmdlet failed: #{what_if_exception_output.gsub(/\s+/, ' ')}"
+ end
+ end
+
+ def output_has_dsc_module_failure?(what_if_output)
+ !! (what_if_output.match(/\sCimException/) &&
+ what_if_output =~ /ProviderOperationExecutionFailure/ &&
+ what_if_output =~ /\smodule is installed/)
+ end
+
def configuration_update_required?(what_if_output)
Chef::Log.debug("DSC: DSC returned the following '-whatif' output from test operation:\n#{what_if_output}")
begin
diff --git a/lib/chef/util/powershell/cmdlet_result.rb b/lib/chef/util/powershell/cmdlet_result.rb
index 0e9ccc3523..696013e684 100644
--- a/lib/chef/util/powershell/cmdlet_result.rb
+++ b/lib/chef/util/powershell/cmdlet_result.rb
@@ -39,10 +39,6 @@ class Chef::Util::Powershell
end
end
- def exit_code
- @status.status.exitstatus
- end
-
def succeeded?
@succeeded = @status.status.exitstatus == 0
end
diff --git a/spec/unit/util/dsc/lcm_output_parser_spec.rb b/spec/unit/util/dsc/lcm_output_parser_spec.rb
index 802c0ea429..05aa072a94 100644
--- a/spec/unit/util/dsc/lcm_output_parser_spec.rb
+++ b/spec/unit/util/dsc/lcm_output_parser_spec.rb
@@ -20,11 +20,11 @@ require 'chef/util/dsc/lcm_output_parser'
describe Chef::Util::DSC::LocalConfigurationManager::Parser do
context 'empty input parameter' do
- it 'returns an emtpy array for a 0 length string' do
+ it 'returns an empty array for a 0 length string' do
Chef::Util::DSC::LocalConfigurationManager::Parser::parse('').should be_empty
end
- it 'returns an emtpy array for a nil input' do
+ it 'returns an empty array for a nil input' do
Chef::Util::DSC::LocalConfigurationManager::Parser::parse('').should be_empty
end
end
diff --git a/spec/unit/util/dsc/local_configuration_manager_spec.rb b/spec/unit/util/dsc/local_configuration_manager_spec.rb
new file mode 100644
index 0000000000..1b0d8b27db
--- /dev/null
+++ b/spec/unit/util/dsc/local_configuration_manager_spec.rb
@@ -0,0 +1,134 @@
+#
+# Author:: Adam Edwards <adamed@getchef.com>
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef'
+require 'chef/util/dsc/local_configuration_manager'
+
+describe Chef::Util::DSC::LocalConfigurationManager do
+
+ let(:lcm) { Chef::Util::DSC::LocalConfigurationManager.new(nil, 'tmp') }
+
+ let(:normal_lcm_output) { <<-EOH
+logtype: [machinename]: LCM: [ Start Set ]
+logtype: [machinename]: LCM: [ Start Resource ] [name]
+logtype: [machinename]: LCM: [ End Resource ] [name]
+logtype: [machinename]: LCM: [ End Set ]
+EOH
+ }
+
+ let(:no_whatif_lcm_output) { <<-EOH
+Start-DscConfiguration : A parameter cannot be found that matches parameter name 'whatif'.
+At line:1 char:123
++ run-somecommand -whatif
++ ~~~~~~~~
+ + CategoryInfo : InvalidArgument: (:) [Start-DscConfiguration], ParameterBindingException
+ + FullyQualifiedErrorId : NamedParameterNotFound,SomeCompany.SomeAssembly.Commands.RunSomeCommand
+EOH
+ }
+
+ let(:dsc_resource_import_failure_output) { <<-EOH
+PowerShell provider MSFT_xWebsite failed to execute Test-TargetResource functionality with error message: Please ensure that WebAdministration module is installed. + CategoryInfo : InvalidOperation: (:) [], CimException + FullyQualifiedErrorId : ProviderOperationExecutionFailure + PSComputerName : . PowerShell provider MSFT_xWebsite failed to execute Test-TargetResource functionality with error message: Please ensure that WebAdministration module is installed. + CategoryInfo : InvalidOperation: (:) [], CimException + FullyQualifiedErrorId : ProviderOperationExecutionFailure + PSComputerName : . The SendConfigurationApply function did not succeed. + CategoryInfo : NotSpecified: (root/Microsoft/...gurationManager:String) [], CimException + FullyQualifiedErrorId : MI RESULT 1 + PSComputerName : .
+EOH
+ }
+
+ let(:lcm_status) {
+ double("LCM cmdlet status", :stderr => lcm_standard_error, :return_value => lcm_standard_output, :succeeded? => lcm_cmdlet_success)
+ }
+
+ describe 'test_configuration method invocation' do
+ context 'when interacting with the LCM using a PowerShell cmdlet' do
+ before(:each) do
+ allow(lcm).to receive(:run_configuration_cmdlet).and_return(lcm_status)
+ end
+ context 'that returns successfully' do
+ before(:each) do
+ allow(lcm).to receive(:run_configuration_cmdlet).and_return(lcm_status)
+ end
+
+ let(:lcm_standard_output) { normal_lcm_output }
+ let(:lcm_standard_error) { nil }
+ let(:lcm_cmdlet_success) { true }
+
+ it 'should successfully return resource information for normally formatted output when cmdlet the cmdlet succeeds' do
+ test_configuration_result = lcm.test_configuration('config')
+ expect(test_configuration_result.class).to be(Array)
+ expect(test_configuration_result.length).to be > 0
+ expect(Chef::Log).not_to receive(:warn)
+ end
+ end
+
+ context 'that fails due to missing what-if switch in DSC resource cmdlet implementation' do
+ let(:lcm_standard_output) { '' }
+ let(:lcm_standard_error) { no_whatif_lcm_output }
+ let(:lcm_cmdlet_success) { false }
+
+ it 'should should return a (possibly empty) array of ResourceInfo instances' do
+ expect(Chef::Log).to receive(:warn)
+ test_configuration_result = nil
+ expect {test_configuration_result = lcm.test_configuration('config')}.not_to raise_error
+ expect(test_configuration_result.class).to be(Array)
+ end
+ end
+
+ context 'that fails due to a DSC resource not being imported before StartDSCConfiguration -whatif is executed' do
+ let(:lcm_standard_output) { '' }
+ let(:lcm_standard_error) { dsc_resource_import_failure_output }
+ let(:lcm_cmdlet_success) { false }
+
+ it 'should log a warning if the message is formatted as expected when a resource import failure occurs' do
+ expect(Chef::Log).to receive(:warn)
+ expect(lcm).to receive(:output_has_dsc_module_failure?).and_call_original
+ test_configuration_result = nil
+ expect {test_configuration_result = lcm.test_configuration('config')}.not_to raise_error
+ end
+
+ it 'should return a (possibly empty) array of ResourceInfo instances' do
+ expect(Chef::Log).to receive(:warn)
+ test_configuration_result = nil
+ expect {test_configuration_result = lcm.test_configuration('config')}.not_to raise_error
+ expect(test_configuration_result.class).to be(Array)
+ end
+ end
+
+ context 'that fails due to an PowerShell cmdlet error that cannot be handled' do
+ let(:lcm_standard_output) { 'some output' }
+ let(:lcm_standard_error) { 'Abort, Retry, Fail?' }
+ let(:lcm_cmdlet_success) { false }
+
+ it 'should raise a Chef::Exceptions::PowershellCmdletException' do
+ expect(Chef::Log).not_to receive(:warn)
+ expect(lcm).to receive(:output_has_dsc_module_failure?).and_call_original
+ expect {lcm.test_configuration('config')}.to raise_error(Chef::Exceptions::PowershellCmdletException)
+ end
+ end
+ end
+
+ it 'should identify a correctly formatted error message as a resource import failure' do
+ expect(lcm.send(:output_has_dsc_module_failure?, dsc_resource_import_failure_output)).to be(true)
+ end
+
+ it 'should not identify an incorrectly formatted error message as a resource import failure' do
+ expect(lcm.send(:output_has_dsc_module_failure?, dsc_resource_import_failure_output.gsub('module', 'gibberish'))).to be(false)
+ end
+
+ it 'should not identify a message without a CimException reference as a resource import failure' do
+ expect(lcm.send(:output_has_dsc_module_failure?, dsc_resource_import_failure_output.gsub('CimException', 'ArgumentException'))).to be(false)
+ end
+ end
+end
+