summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormwrock <matt@mattwrock.com>2020-11-30 13:40:36 -0800
committermwrock <matt@mattwrock.com>2020-11-30 13:40:36 -0800
commit6408c925c03bb44ea3ae2194c064815177497d2e (patch)
treec3f043432e755fe29a66b2ea2f140556c8859d7d
parent3d7728a6ae3f7baed8c3d6bd4f1612607f6bce74 (diff)
downloadchef-6408c925c03bb44ea3ae2194c064815177497d2e.tar.gz
replace usages of Cmdlet class with powershell_exec
Signed-off-by: mwrock <matt@mattwrock.com>
-rw-r--r--distro/ruby_bin_folder/AMD64/Chef.PowerShell.Wrapper.dllbin171008 -> 171008 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/Chef.PowerShell.dllbin6144 -> 6144 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dllbin98304 -> 98304 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dllbin5120 -> 5632 bytes
-rw-r--r--distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdbbin11696 -> 11716 bytes
-rw-r--r--distro/ruby_bin_folder/x86/Chef.PowerShell.dllbin6656 -> 6656 bytes
-rw-r--r--distro/ruby_bin_folder/x86/Chef.Powershell.Wrapper.dllbin145920 -> 145920 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dllbin83456 -> 83456 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dllbin6144 -> 6144 bytes
-rw-r--r--distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdbbin11700 -> 11720 bytes
-rw-r--r--lib/chef/mixin/powershell_exec.rb4
-rw-r--r--lib/chef/platform/query_helpers.rb8
-rw-r--r--lib/chef/powershell.rb2
-rw-r--r--lib/chef/provider/dsc_resource.rb36
-rw-r--r--lib/chef/provider/dsc_script.rb37
-rw-r--r--lib/chef/util/dsc/configuration_generator.rb63
-rw-r--r--lib/chef/util/dsc/lcm_output_parser.rb8
-rw-r--r--lib/chef/util/dsc/local_configuration_manager.rb31
-rw-r--r--lib/chef/util/dsc/resource_store.rb16
-rw-r--r--lib/chef/util/powershell/cmdlet.rb169
-rw-r--r--lib/chef/util/powershell/cmdlet_result.rb61
-rw-r--r--spec/functional/resource/dsc_script_spec.rb4
-rw-r--r--spec/functional/util/powershell/cmdlet_spec.rb111
-rw-r--r--spec/spec_helper.rb2
-rw-r--r--spec/unit/mixin/powershell_exec_spec.rb2
-rw-r--r--spec/unit/platform/query_helpers_spec.rb23
-rw-r--r--spec/unit/provider/dsc_resource_spec.rb37
-rw-r--r--spec/unit/provider/dsc_script_spec.rb2
-rw-r--r--spec/unit/util/dsc/configuration_generator_spec.rb79
-rw-r--r--spec/unit/util/dsc/local_configuration_manager_spec.rb62
-rw-r--r--spec/unit/util/powershell/cmdlet_spec.rb106
31 files changed, 248 insertions, 615 deletions
diff --git a/distro/ruby_bin_folder/AMD64/Chef.PowerShell.Wrapper.dll b/distro/ruby_bin_folder/AMD64/Chef.PowerShell.Wrapper.dll
index c411388674..022591ffd1 100644
--- a/distro/ruby_bin_folder/AMD64/Chef.PowerShell.Wrapper.dll
+++ b/distro/ruby_bin_folder/AMD64/Chef.PowerShell.Wrapper.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/Chef.PowerShell.dll b/distro/ruby_bin_folder/AMD64/Chef.PowerShell.dll
index ec80d05b33..2316fffdb5 100644
--- a/distro/ruby_bin_folder/AMD64/Chef.PowerShell.dll
+++ b/distro/ruby_bin_folder/AMD64/Chef.PowerShell.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dll
index 5334ee315e..cb9a5c276b 100644
--- a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dll
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dll b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dll
index c74bec50a4..3f472f2c81 100644
--- a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dll
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdb b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdb
index 7ef67e5b43..03a0860086 100644
--- a/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdb
+++ b/distro/ruby_bin_folder/AMD64/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdb
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/Chef.PowerShell.dll b/distro/ruby_bin_folder/x86/Chef.PowerShell.dll
index 2ffbbea92c..8a528ad226 100644
--- a/distro/ruby_bin_folder/x86/Chef.PowerShell.dll
+++ b/distro/ruby_bin_folder/x86/Chef.PowerShell.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/Chef.Powershell.Wrapper.dll b/distro/ruby_bin_folder/x86/Chef.Powershell.Wrapper.dll
index b72d13c9c0..203d243b37 100644
--- a/distro/ruby_bin_folder/x86/Chef.Powershell.Wrapper.dll
+++ b/distro/ruby_bin_folder/x86/Chef.Powershell.Wrapper.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dll
index 09e0fa1333..879df9ab6e 100644
--- a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dll
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.PowerShell.Wrapper.Core.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dll b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dll
index 6827b2a64c..ff50126c80 100644
--- a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dll
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.dll
Binary files differ
diff --git a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdb b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdb
index 2546223f9d..5c979f1750 100644
--- a/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdb
+++ b/distro/ruby_bin_folder/x86/shared/Microsoft.NETCore.App/5.0.0/Chef.Powershell.Core.pdb
Binary files differ
diff --git a/lib/chef/mixin/powershell_exec.rb b/lib/chef/mixin/powershell_exec.rb
index 03b058edcf..bbf8ae1a69 100644
--- a/lib/chef/mixin/powershell_exec.rb
+++ b/lib/chef/mixin/powershell_exec.rb
@@ -23,10 +23,12 @@ require_relative "../pwsh"
# powershell_exec is initialized with a string that should be set to the script
# to run and also takes an optional interpreter argument which must be either
# :powershell (Windows PowerShell which is the default) or :pwsh (PowerShell
-# Core). It will return a Chef::PowerShell object that provides 4 methods:
+# Core). It will return a Chef::PowerShell object that provides 5 methods:
#
# .result - returns a hash representing the results returned by executing the
# PowerShell script block
+# .verbose - this is an array of string containing any messages written to the
+# PowerShell verbose stream during execution
# .errors - this is an array of string containing any messages written to the
# PowerShell error stream during execution
# .error? - returns true if there were error messages written to the PowerShell
diff --git a/lib/chef/platform/query_helpers.rb b/lib/chef/platform/query_helpers.rb
index 6188ce0cb5..bd0703d72a 100644
--- a/lib/chef/platform/query_helpers.rb
+++ b/lib/chef/platform/query_helpers.rb
@@ -58,10 +58,10 @@ class Chef
end
def dsc_refresh_mode_disabled?(node)
- require_relative "../util/powershell/cmdlet"
- cmdlet = Chef::Util::Powershell::Cmdlet.new(node, "Get-DscLocalConfigurationManager", :object)
- metadata = cmdlet.run!.return_value
- metadata["RefreshMode"] == "Disabled"
+ require_relative "../powershell"
+ exec = Chef::PowerShell.new("Get-DscLocalConfigurationManager")
+ exec.error!
+ exec.result["RefreshMode"] == "Disabled"
end
def supported_powershell_version?(node, version_string)
diff --git a/lib/chef/powershell.rb b/lib/chef/powershell.rb
index 6b925eabb6..b49d3c58e4 100644
--- a/lib/chef/powershell.rb
+++ b/lib/chef/powershell.rb
@@ -24,6 +24,7 @@ class Chef
attr_reader :result
attr_reader :errors
+ attr_reader :verbose
# Run a command under PowerShell via FFI
# This implementation requires the managed dll and native wrapper to be in the library search
@@ -72,6 +73,7 @@ class Chef
hashed_outcome = Chef::JSONCompat.parse(execution)
@result = Chef::JSONCompat.parse(hashed_outcome["result"])
@errors = hashed_outcome["errors"]
+ @verbose = hashed_outcome["verbose"]
end
end
end
diff --git a/lib/chef/provider/dsc_resource.rb b/lib/chef/provider/dsc_resource.rb
index 5f1f8ca8ac..a919d1deff 100644
--- a/lib/chef/provider/dsc_resource.rb
+++ b/lib/chef/provider/dsc_resource.rb
@@ -15,7 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-require_relative "../util/powershell/cmdlet"
+require "timeout" unless defined?(Timeout)
+require_relative "../mixin/powershell_exec"
require_relative "../util/dsc/local_configuration_manager"
require_relative "../mixin/powershell_type_coercions"
require_relative "../util/dsc/resource_store"
@@ -130,27 +131,27 @@ class Chef
def test_resource
result = invoke_resource(:test)
add_dsc_verbose_log(result)
- return_dsc_resource_result(result, "InDesiredState")
+ result.result["InDesiredState"]
end
def set_resource
result = invoke_resource(:set)
add_dsc_verbose_log(result)
- create_reboot_resource if return_dsc_resource_result(result, "RebootRequired")
- result.return_value
+ create_reboot_resource if result.result["RebootRequired"]
+ result
end
def add_dsc_verbose_log(result)
# We really want this information from the verbose stream,
# however in some versions of WMF, Invoke-DscResource is not correctly
# writing to that stream and instead just dumping to stdout
- verbose_output = result.stream(:verbose)
- verbose_output = result.stdout if verbose_output.empty?
+ verbose_output = result.verbose.join("\n")
+ verbose_output = result.result if verbose_output.empty?
if @converge_description.nil? || @converge_description.empty?
@converge_description = verbose_output
else
- @converge_description << "\n"
+ @converge_description << "\n\n"
@converge_description << verbose_output
end
end
@@ -159,26 +160,13 @@ class Chef
@module_version.nil? ? module_name : "@{ModuleName='#{module_name}';ModuleVersion='#{@module_version}'}"
end
- def invoke_resource(method, output_format = :object)
+ def invoke_resource(method)
properties = translate_type(new_resource.properties)
switches = "-Method #{method} -Name #{new_resource.resource}"\
" -Property #{properties} -Module #{module_info_object} -Verbose"
- cmdlet = Chef::Util::Powershell::Cmdlet.new(
- node,
- "Invoke-DscResource #{switches}",
- output_format
- )
- cmdlet.run!({}, { timeout: new_resource.timeout })
- end
-
- def return_dsc_resource_result(result, property_name)
- if result.return_value.is_a?(Array)
- # WMF Feb 2015 Preview
- result.return_value[0][property_name]
- else
- # WMF April 2015 Preview
- result.return_value[property_name]
- end
+ Timeout.timeout(new_resource.timeout) {
+ powershell_exec!("Invoke-DscResource #{switches}")
+ }
end
def create_reboot_resource
diff --git a/lib/chef/provider/dsc_script.rb b/lib/chef/provider/dsc_script.rb
index 8793a9274a..3e257492e7 100644
--- a/lib/chef/provider/dsc_script.rb
+++ b/lib/chef/provider/dsc_script.rb
@@ -16,7 +16,6 @@
# limitations under the License.
#
-require_relative "../util/powershell/cmdlet"
require_relative "../util/dsc/configuration_generator"
require_relative "../util/dsc/local_configuration_manager"
require_relative "../util/path_helper"
@@ -32,11 +31,11 @@ class Chef
@dsc_resource = dsc_resource
@resource_converged = false
@operations = {
- set: Proc.new do |config_manager, document, shellout_flags|
- config_manager.set_configuration(document, shellout_flags)
+ set: Proc.new do |config_manager, document|
+ config_manager.set_configuration(document)
end,
- test: Proc.new do |config_manager, document, shellout_flags|
- config_manager.test_configuration(document, shellout_flags)
+ test: Proc.new do |config_manager, document|
+ config_manager.test_configuration(document)
end }
end
@@ -85,20 +84,24 @@ class Chef
config_manager = Chef::Util::DSC::LocalConfigurationManager.new(@run_context.node, config_directory)
- shellout_flags = {
- cwd: @dsc_resource.cwd,
- environment: @dsc_resource.environment,
- timeout: @dsc_resource.timeout,
- }
+ cwd = @dsc_resource.cwd || Dir.pwd
+ original_env = ENV.to_hash
begin
- configuration_document = generate_configuration_document(config_directory, configuration_flags)
- @operations[operation].call(config_manager, configuration_document, shellout_flags)
+ ENV.update(@dsc_resource.environment || original_env)
+ Dir.chdir(cwd) do
+ Timeout.timeout(@dsc_resource.timeout) do
+ configuration_document = generate_configuration_document(config_directory, configuration_flags)
+ @operations[operation].call(config_manager, configuration_document)
+ end
+ end
rescue Exception => e
logger.error("DSC operation failed: #{e.message}")
raise e
ensure
::FileUtils.rm_rf(config_directory)
+ ENV.clear
+ ENV.update(original_env)
end
end
@@ -112,20 +115,14 @@ class Chef
end
def generate_configuration_document(config_directory, configuration_flags)
- shellout_flags = {
- cwd: @dsc_resource.cwd,
- environment: @dsc_resource.environment,
- timeout: @dsc_resource.timeout,
- }
-
generator = Chef::Util::DSC::ConfigurationGenerator.new(@run_context.node, config_directory)
if @dsc_resource.command
- generator.configuration_document_from_script_path(@dsc_resource.command, configuration_name, configuration_flags, shellout_flags)
+ generator.configuration_document_from_script_path(@dsc_resource.command, configuration_name, configuration_flags)
else
# If code is also not provided, we mimic what the other script resources do (execute nothing)
logger.warn("Neither code or command were provided for dsc_resource[#{@dsc_resource.name}].") unless @dsc_resource.code
- generator.configuration_document_from_script_code(@dsc_resource.code || "", configuration_flags, @dsc_resource.imports, shellout_flags)
+ generator.configuration_document_from_script_code(@dsc_resource.code || "", configuration_flags, @dsc_resource.imports)
end
end
diff --git a/lib/chef/util/dsc/configuration_generator.rb b/lib/chef/util/dsc/configuration_generator.rb
index 479e96dfa2..25b6110464 100644
--- a/lib/chef/util/dsc/configuration_generator.rb
+++ b/lib/chef/util/dsc/configuration_generator.rb
@@ -16,36 +16,34 @@
# limitations under the License.
#
-require_relative "../powershell/cmdlet"
+require_relative "../../mixin/powershell_exec"
class Chef::Util::DSC
class ConfigurationGenerator
+ include Chef::Mixin::PowershellExec
+
def initialize(node, config_directory)
@node = node
@config_directory = config_directory
end
- def configuration_document_from_script_code(code, configuration_flags, imports, shellout_flags)
+ def configuration_document_from_script_code(code, configuration_flags, imports)
Chef::Log.trace("DSC: DSC code:\n '#{code}'")
generated_script_path = write_document_generation_script(code, "chef_dsc", imports)
begin
- configuration_document_from_script_path(generated_script_path, "chef_dsc", configuration_flags, shellout_flags)
+ configuration_document_from_script_path(generated_script_path, "chef_dsc", configuration_flags)
ensure
::FileUtils.rm(generated_script_path)
end
end
- def configuration_document_from_script_path(script_path, configuration_name, configuration_flags, shellout_flags)
+ def configuration_document_from_script_path(script_path, configuration_name, configuration_flags)
validate_configuration_name!(configuration_name)
- document_generation_cmdlet = Chef::Util::Powershell::Cmdlet.new(
- @node,
- configuration_document_generation_code(script_path, configuration_name)
- )
-
- merged_configuration_flags = get_merged_configuration_flags!(configuration_flags, configuration_name)
+ config_generation_code = configuration_document_generation_code(script_path, configuration_name)
+ switches_string = command_switches_string(get_merged_configuration_flags!(configuration_flags, configuration_name))
- document_generation_cmdlet.run!(merged_configuration_flags, shellout_flags)
+ powershell_exec!("#{config_generation_code} #{switches_string}")
configuration_document_location = find_configuration_document(configuration_name)
unless configuration_document_location
@@ -59,6 +57,49 @@ class Chef::Util::DSC
protected
+ def validate_switch_name!(switch_parameter_name)
+ if !!(switch_parameter_name =~ /\A[A-Za-z]+[_a-zA-Z0-9]*\Z/) == false
+ raise ArgumentError, "`#{switch_parameter_name}` is not a valid PowerShell cmdlet switch parameter name"
+ end
+ end
+
+ def escape_parameter_value(parameter_value)
+ parameter_value.gsub(/(`|'|"|#)/, '`\1')
+ end
+
+ def escape_string_parameter_value(parameter_value)
+ "'#{escape_parameter_value(parameter_value)}'"
+ end
+
+ def command_switches_string(switches)
+ command_switches = switches.map do |switch_name, switch_value|
+ if switch_name.class != Symbol
+ raise ArgumentError, "Invalid type `#{switch_name} `for PowerShell switch '#{switch_name}'. The switch must be specified as a Symbol'"
+ end
+
+ validate_switch_name!(switch_name)
+
+ switch_argument = ""
+ switch_present = true
+
+ case switch_value
+ when Numeric, Float
+ switch_argument = switch_value.to_s
+ when FalseClass
+ switch_present = false
+ when TrueClass
+ when String
+ switch_argument = escape_string_parameter_value(switch_value)
+ else
+ raise ArgumentError, "Invalid argument type `#{switch_value.class}` specified for PowerShell switch `:#{switch_name}`. Arguments to PowerShell must be of type `String`, `Numeric`, `Float`, `FalseClass`, or `TrueClass`"
+ end
+
+ switch_present ? ["-#{switch_name.to_s.downcase}", switch_argument].join(" ").strip : ""
+ end
+
+ command_switches.join(" ")
+ end
+
# From PowerShell error help for the Configuration language element:
# Standard names may only contain letters (a-z, A-Z), numbers (0-9), and underscore (_).
# The name may not be null or empty, and should start with a letter.
diff --git a/lib/chef/util/dsc/lcm_output_parser.rb b/lib/chef/util/dsc/lcm_output_parser.rb
index 509f680bb4..cccf30c182 100644
--- a/lib/chef/util/dsc/lcm_output_parser.rb
+++ b/lib/chef/util/dsc/lcm_output_parser.rb
@@ -75,15 +75,16 @@ class Chef
#
def self.parse(lcm_output, test_dsc_configuration)
+ lcm_output ||= ""
+ lcm_output = lcm_output.split("\n")
test_dsc_configuration ? test_dsc_parser(lcm_output) : what_if_parser(lcm_output)
end
def self.test_dsc_parser(lcm_output)
- lcm_output ||= ""
current_resource = {}
resources = []
- lcm_output.lines.each do |line|
+ lcm_output.each do |line|
op_action , op_value = line.strip.split(":")
op_action&.strip!
case op_action
@@ -107,11 +108,10 @@ class Chef
end
def self.what_if_parser(lcm_output)
- lcm_output ||= ""
current_resource = {}
resources = []
- lcm_output.lines.each do |line|
+ lcm_output.each do |line|
op_action, op_type, info = parse_line(line)
case op_action
diff --git a/lib/chef/util/dsc/local_configuration_manager.rb b/lib/chef/util/dsc/local_configuration_manager.rb
index 431936574a..091d4aa426 100644
--- a/lib/chef/util/dsc/local_configuration_manager.rb
+++ b/lib/chef/util/dsc/local_configuration_manager.rb
@@ -16,25 +16,27 @@
# limitations under the License.
#
-require_relative "../powershell/cmdlet"
+require_relative "../../mixin/powershell_exec"
require_relative "lcm_output_parser"
class Chef::Util::DSC
class LocalConfigurationManager
+ include Chef::Mixin::PowershellExec
+
def initialize(node, configuration_path)
@node = node
@configuration_path = configuration_path
clear_execution_time
end
- def test_configuration(configuration_document, shellout_flags)
- status = run_configuration_cmdlet(configuration_document, false, shellout_flags)
- log_dsc_exception(status.stderr) unless status.succeeded?
- configuration_update_required?(status.return_value)
+ def test_configuration(configuration_document)
+ status = run_configuration_cmdlet(configuration_document, false)
+ log_dsc_exception(status.errors.join("\n")) if status.error?
+ configuration_update_required?(status.result)
end
- def set_configuration(configuration_document, shellout_flags)
- run_configuration_cmdlet(configuration_document, true, shellout_flags)
+ def set_configuration(configuration_document)
+ run_configuration_cmdlet(configuration_document, true)
end
def last_operation_execution_time_seconds
@@ -45,7 +47,7 @@ class Chef::Util::DSC
private
- def run_configuration_cmdlet(configuration_document, apply_configuration, shellout_flags)
+ def run_configuration_cmdlet(configuration_document, apply_configuration)
Chef::Log.trace("DSC: Calling DSC Local Config Manager to #{apply_configuration ? "set" : "test"} configuration document.")
start_operation_timing
@@ -53,11 +55,12 @@ class Chef::Util::DSC
begin
save_configuration_document(configuration_document)
- cmdlet = ::Chef::Util::Powershell::Cmdlet.new(@node, lcm_command(apply_configuration))
+ cmd = lcm_command(apply_configuration)
+ Chef::Log.trace("DSC: Calling DSC Local Config Manager with:\n#{cmd}")
+
+ status = powershell_exec(cmd)
if apply_configuration
- status = cmdlet.run!({}, shellout_flags)
- else
- status = cmdlet.run({}, shellout_flags)
+ status.error!
end
ensure
end_operation_timing
@@ -77,7 +80,7 @@ class Chef::Util::DSC
ps4_base_command
else
if ps_version_gte_5?
- "#{common_command_prefix} Test-DscConfiguration -path #{@configuration_path} | format-list"
+ "#{common_command_prefix} Test-DscConfiguration -path #{@configuration_path} | format-list | Out-String"
else
ps4_base_command + " -whatif; if (! $?) { exit 1 }"
end
@@ -100,7 +103,7 @@ class Chef::Util::DSC
end
def whatif_not_supported?(dsc_exception_output)
- !! (dsc_exception_output.gsub(/[\r\n]+/, "").gsub(/\s+/, " ") =~ /A parameter cannot be found that matches parameter name 'Whatif'/i)
+ !! (dsc_exception_output.gsub(/[\n]+/, "").gsub(/\s+/, " ") =~ /A parameter cannot be found that matches parameter name 'Whatif'/i)
end
def dsc_module_import_failure?(command_output)
diff --git a/lib/chef/util/dsc/resource_store.rb b/lib/chef/util/dsc/resource_store.rb
index 772bc82468..49ca46832a 100644
--- a/lib/chef/util/dsc/resource_store.rb
+++ b/lib/chef/util/dsc/resource_store.rb
@@ -16,14 +16,14 @@
# limitations under the License.
#
-require_relative "../powershell/cmdlet"
-require_relative "../powershell/cmdlet_result"
+require_relative "../../mixin/powershell_exec"
require_relative "../../exceptions"
class Chef
class Util
class DSC
class ResourceStore
+ include Chef::Mixin::PowershellExec
def self.instance
@@instance ||= ResourceStore.new.tap do |store|
@@ -83,19 +83,13 @@ class Chef
# Returns a list of dsc resources
def query_resources
- cmdlet = Chef::Util::Powershell::Cmdlet.new(nil, "get-dscresource",
- :object)
- result = cmdlet.run
- result.return_value
+ powershell_exec("get-dscresource").result
end
# Returns a list of dsc resources matching the provided name
def query_resource(resource_name)
- cmdlet = Chef::Util::Powershell::Cmdlet.new(nil, "get-dscresource #{resource_name}",
- :object)
- result = cmdlet.run
- ret_val = result.return_value
- if ret_val.nil?
+ ret_val = powershell_exec("get-dscresource #{resource_name}").result
+ if ret_val.empty?
[]
elsif ret_val.is_a? Array
ret_val
diff --git a/lib/chef/util/powershell/cmdlet.rb b/lib/chef/util/powershell/cmdlet.rb
deleted file mode 100644
index 1c728fa424..0000000000
--- a/lib/chef/util/powershell/cmdlet.rb
+++ /dev/null
@@ -1,169 +0,0 @@
-#
-# Author:: Adam Edwards (<adamed@chef.io>)
-#
-# Copyright:: Copyright (c) Chef Software Inc.
-#
-# 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.
-#
-
-module Mixlib
- autoload :ShellOut, "mixlib/shellout"
-end
-require_relative "../../mixin/windows_architecture_helper"
-require_relative "cmdlet_result"
-
-class Chef
- class Util
- class Powershell
- class Cmdlet
- def initialize(node, cmdlet, output_format = nil, output_format_options = {})
- @output_format = output_format
- @node = node
-
- case output_format
- when nil, :text
- @json_format = false
- when :json, :object
- @json_format = true
- else
- raise ArgumentError, "Invalid output format #{output_format} specified"
- end
-
- @cmdlet = cmdlet
- @output_format_options = output_format_options
- end
-
- attr_reader :output_format
-
- def run(switches = {}, execution_options = {}, *arguments)
- streams = { json: CmdletStream.new("json"),
- verbose: CmdletStream.new("verbose"),
- }
-
- arguments_string = arguments.join(" ")
-
- switches_string = command_switches_string(switches)
-
- json_depth = 5
-
- if @json_format && @output_format_options.key?(:depth)
- json_depth = @output_format_options[:depth]
- end
-
- json_command = if @json_format
- " | convertto-json -compress -depth #{json_depth} > #{streams[:json].path}"
- else
- ""
- end
- redirections = "4> '#{streams[:verbose].path}'"
- command_string = "powershell.exe -executionpolicy bypass -noprofile -noninteractive "\
- "-command \"trap [Exception] {write-error -exception "\
- "($_.Exception.Message);exit 1};#{@cmdlet} #{switches_string} "\
- "#{arguments_string} #{redirections}"\
- "#{json_command}\";if ( ! $? ) { exit 1 }"
-
- augmented_options = { returns: [0], live_stream: false }.merge(execution_options)
- command = Mixlib::ShellOut.new(command_string, augmented_options)
-
- status = nil
-
- with_os_architecture(@node) do
- status = command.run_command
- end
-
- CmdletResult.new(status, streams, @output_format)
- end
-
- def run!(switches = {}, execution_options = {}, *arguments)
- result = run(switches, execution_options, arguments)
-
- unless result.succeeded?
- raise Chef::Exceptions::PowershellCmdletException, "PowerShell Cmdlet failed: #{result.stderr}"
- end
-
- result
- end
-
- protected
-
- include Chef::Mixin::WindowsArchitectureHelper
-
- def validate_switch_name!(switch_parameter_name)
- if !!(switch_parameter_name =~ /\A[A-Za-z]+[_a-zA-Z0-9]*\Z/) == false
- raise ArgumentError, "`#{switch_parameter_name}` is not a valid PowerShell cmdlet switch parameter name"
- end
- end
-
- def escape_parameter_value(parameter_value)
- parameter_value.gsub(/(`|'|"|#)/, '`\1')
- end
-
- def escape_string_parameter_value(parameter_value)
- "'#{escape_parameter_value(parameter_value)}'"
- end
-
- def command_switches_string(switches)
- command_switches = switches.map do |switch_name, switch_value|
- if switch_name.class != Symbol
- raise ArgumentError, "Invalid type `#{switch_name} `for PowerShell switch '#{switch_name}'. The switch must be specified as a Symbol'"
- end
-
- validate_switch_name!(switch_name)
-
- switch_argument = ""
- switch_present = true
-
- case switch_value
- when Numeric, Float
- switch_argument = switch_value.to_s
- when FalseClass
- switch_present = false
- when TrueClass
- when String
- switch_argument = escape_string_parameter_value(switch_value)
- else
- raise ArgumentError, "Invalid argument type `#{switch_value.class}` specified for PowerShell switch `:#{switch_name}`. Arguments to PowerShell must be of type `String`, `Numeric`, `Float`, `FalseClass`, or `TrueClass`"
- end
-
- switch_present ? ["-#{switch_name.to_s.downcase}", switch_argument].join(" ").strip : ""
- end
-
- command_switches.join(" ")
- end
-
- class CmdletStream
- def initialize(name)
- @filename = Dir::Tmpname.create(name) {}
- ObjectSpace.define_finalizer(self, self.class.destroy(@filename))
- end
-
- def path
- @filename
- end
-
- def read
- if File.exist? @filename
- File.open(@filename, "rb:bom|UTF-16LE") do |f|
- f.read.encode("UTF-8")
- end
- end
- end
-
- def self.destroy(name)
- proc { File.delete(name) if File.exist? name }
- end
- end
- end
- end
- end
-end
diff --git a/lib/chef/util/powershell/cmdlet_result.rb b/lib/chef/util/powershell/cmdlet_result.rb
deleted file mode 100644
index 7aee2e8c4b..0000000000
--- a/lib/chef/util/powershell/cmdlet_result.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-#
-# Author:: Adam Edwards (<adamed@chef.io>)
-#
-# Copyright:: Copyright (c) Chef Software Inc.
-#
-# 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_relative "../../json_compat"
-
-class Chef
- class Util
- class Powershell
- class CmdletResult
- attr_reader :output_format
-
- def initialize(status, streams, output_format)
- @status = status
- @output_format = output_format
- @streams = streams
- end
-
- def stdout
- @status.stdout
- end
-
- def stderr
- @status.stderr
- end
-
- def stream(name)
- @streams[name].read
- end
-
- def return_value
- if output_format == :object
- Chef::JSONCompat.parse(stream(:json))
- elsif output_format == :json
- stream(:json)
- else
- @status.stdout
- end
- end
-
- def succeeded?
- @succeeded = @status.status.exitstatus == 0
- end
- end
- end
- end
-end
diff --git a/spec/functional/resource/dsc_script_spec.rb b/spec/functional/resource/dsc_script_spec.rb
index 9d18e2f85d..60197c8643 100644
--- a/spec/functional/resource/dsc_script_spec.rb
+++ b/spec/functional/resource/dsc_script_spec.rb
@@ -261,10 +261,10 @@ describe Chef::Resource::DscScript, :windows_powershell_dsc_only do
it "should raise an exception if the cwd is etc" do
dsc_test_resource.cwd(dsc_environment_fail_etc_directory)
- expect { dsc_test_resource.run_action(:run) }.to raise_error(Chef::Exceptions::PowershellCmdletException)
+ expect { dsc_test_resource.run_action(:run) }.to raise_error(Chef::PowerShell::CommandFailed)
begin
dsc_test_resource.run_action(:run)
- rescue Chef::Exceptions::PowershellCmdletException => e
+ rescue Chef::PowerShell::CommandFailed => e
expect(e.message).to match(exception_message_signature)
end
end
diff --git a/spec/functional/util/powershell/cmdlet_spec.rb b/spec/functional/util/powershell/cmdlet_spec.rb
deleted file mode 100644
index 565456f687..0000000000
--- a/spec/functional/util/powershell/cmdlet_spec.rb
+++ /dev/null
@@ -1,111 +0,0 @@
-#
-# Author:: Adam Edwards (<adamed@chef.io>)
-#
-# Copyright:: Copyright (c) Chef Software Inc.
-#
-# 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/json_compat"
-require "spec_helper"
-
-describe Chef::Util::Powershell::Cmdlet, :windows_powershell_dsc_only do
- before(:all) do
- @node = Chef::Node.new
- @node.consume_external_attrs(OHAI_SYSTEM.data, {})
- end
- let(:cmd_output_format) { :text }
- let(:simple_cmdlet) { Chef::Util::Powershell::Cmdlet.new(@node, "get-childitem", cmd_output_format, { depth: 2 }) }
- let(:invalid_cmdlet) { Chef::Util::Powershell::Cmdlet.new(@node, "get-idontexist", cmd_output_format) }
- let(:cmdlet_get_item_requires_switch_or_argument) { Chef::Util::Powershell::Cmdlet.new(@node, "get-item", cmd_output_format, { depth: 2 }) }
- let(:cmdlet_alias_requires_switch_or_argument) { Chef::Util::Powershell::Cmdlet.new(@node, "alias", cmd_output_format, { depth: 2 }) }
- let(:etc_directory) { "#{ENV["systemroot"]}\\system32\\drivers\\etc" }
- let(:architecture_cmdlet) { Chef::Util::Powershell::Cmdlet.new(@node, "$env:PROCESSOR_ARCHITECTURE") }
-
- it "executes a simple process" do
- result = simple_cmdlet.run
- expect(result.succeeded?).to eq(true)
- end
-
- it "#run does not raise a PowershellCmdletException exception if the command cannot be executed" do
- expect { invalid_cmdlet.run }.not_to raise_error
- end
-
- it "#run! raises a PowershellCmdletException exception if the command cannot be executed" do
- expect { invalid_cmdlet.run! }.to raise_error(Chef::Exceptions::PowershellCmdletException)
- end
-
- it "executes a 64-bit command on a 64-bit OS, 32-bit otherwise" do
- os_arch = ENV["PROCESSOR_ARCHITEW6432"]
- if os_arch.nil?
- os_arch = ENV["PROCESSOR_ARCHITECTURE"]
- end
-
- result = architecture_cmdlet.run
- execution_arch = result.return_value
- execution_arch.strip!
- expect(execution_arch).to eq(os_arch)
- end
-
- it "passes command line switches to the command" do
- result = cmdlet_alias_requires_switch_or_argument.run({ name: "ls" })
- expect(result.succeeded?).to eq(true)
- end
-
- it "passes command line arguments to the command" do
- result = cmdlet_alias_requires_switch_or_argument.run({}, {}, "ls")
- expect(result.succeeded?).to eq(true)
- end
-
- it "passes command line arguments and switches to the command" do
- result = cmdlet_get_item_requires_switch_or_argument.run({ path: etc_directory }, {}, " | select-object -property fullname | format-table -hidetableheaders")
- expect(result.succeeded?).to eq(true)
- returned_directory = result.return_value
- returned_directory.strip!
- expect(returned_directory).to eq(etc_directory)
- end
-
- it "passes execution options to the command" do
- result = cmdlet_get_item_requires_switch_or_argument.run({}, { cwd: etc_directory }, ". | select-object -property fullname | format-table -hidetableheaders")
- expect(result.succeeded?).to eq(true)
- returned_directory = result.return_value
- returned_directory.strip!
- expect(returned_directory).to eq(etc_directory)
- end
-
- context "when returning json" do
- let(:cmd_output_format) { :json }
- it "returns json format data" do
- result = cmdlet_alias_requires_switch_or_argument.run({}, {}, "ls")
- expect(result.succeeded?).to eq(true)
- expect { Chef::JSONCompat.parse(result.return_value) }.not_to raise_error
- end
- end
-
- context "when returning Ruby objects" do
- let(:cmd_output_format) { :object }
- it "returns object format data" do
- result = simple_cmdlet.run({}, { cwd: etc_directory }, "hosts")
- expect(result.succeeded?).to eq(true)
- data = result.return_value
- expect(data["Name"]).to eq("hosts")
- end
- end
-
- context "when constructor is given invalid arguments" do
- let(:cmd_output_format) { :invalid }
- it "throws an exception if an invalid format is passed to the constructor" do
- expect { simple_cmdlet }.to raise_error(ArgumentError)
- end
- end
-end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 4c925bace3..17ce1ab5b7 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -31,7 +31,7 @@ $LOAD_PATH.unshift File.expand_path("../chef-utils/lib", __dir__)
require "rubygems"
require "rspec/mocks"
-
+require "rexml/document"
require "webmock/rspec"
require "chef"
diff --git a/spec/unit/mixin/powershell_exec_spec.rb b/spec/unit/mixin/powershell_exec_spec.rb
index 245f681688..92e92dc2a1 100644
--- a/spec/unit/mixin/powershell_exec_spec.rb
+++ b/spec/unit/mixin/powershell_exec_spec.rb
@@ -66,7 +66,7 @@ describe Chef::Mixin::PowershellExec, :windows_only do
execution = object.powershell_exec("this-should-error")
expect(execution.errors).to be_a_kind_of(Array)
expect(execution.errors[0]).to be_a_kind_of(String)
- expect(execution.errors[0]).to include("Runtime exception: this-should-error")
+ expect(execution.errors[0]).to include("The term 'this-should-error' is not recognized")
end
it "raises an error if the interpreter is invalid" do
diff --git a/spec/unit/platform/query_helpers_spec.rb b/spec/unit/platform/query_helpers_spec.rb
index 728348e9af..0b4169810e 100644
--- a/spec/unit/platform/query_helpers_spec.rb
+++ b/spec/unit/platform/query_helpers_spec.rb
@@ -41,24 +41,23 @@ end
describe "Chef::Platform#dsc_refresh_mode_disabled?" do
let(:node) { instance_double("Chef::Node") }
- let(:cmdlet) { instance_double("Chef::Util::Powershell::Cmdlet") }
- let(:cmdlet_result) { instance_double("Chef::Util::Powershell::CmdletResult") }
+ let(:powershell) { instance_double("Chef::PowerShell") }
it "returns true when RefreshMode is Disabled" do
- expect(Chef::Util::Powershell::Cmdlet).to receive(:new)
- .with(node, "Get-DscLocalConfigurationManager", :object)
- .and_return(cmdlet)
- expect(cmdlet).to receive(:run!).and_return(cmdlet_result)
- expect(cmdlet_result).to receive(:return_value).and_return({ "RefreshMode" => "Disabled" })
+ expect(Chef::PowerShell).to receive(:new)
+ .with("Get-DscLocalConfigurationManager")
+ .and_return(powershell)
+ expect(powershell).to receive(:error!)
+ expect(powershell).to receive(:result).and_return({ "RefreshMode" => "Disabled" })
expect(Chef::Platform.dsc_refresh_mode_disabled?(node)).to be true
end
it "returns false when RefreshMode is not Disabled" do
- expect(Chef::Util::Powershell::Cmdlet).to receive(:new)
- .with(node, "Get-DscLocalConfigurationManager", :object)
- .and_return(cmdlet)
- expect(cmdlet).to receive(:run!).and_return(cmdlet_result)
- expect(cmdlet_result).to receive(:return_value).and_return({ "RefreshMode" => "LaLaLa" })
+ expect(Chef::PowerShell).to receive(:new)
+ .with("Get-DscLocalConfigurationManager")
+ .and_return(powershell)
+ expect(powershell).to receive(:error!)
+ expect(powershell).to receive(:result).and_return({ "RefreshMode" => "LaLaLa" })
expect(Chef::Platform.dsc_refresh_mode_disabled?(node)).to be false
end
end
diff --git a/spec/unit/provider/dsc_resource_spec.rb b/spec/unit/provider/dsc_resource_spec.rb
index 8613ce4af4..2540cb9df2 100644
--- a/spec/unit/provider/dsc_resource_spec.rb
+++ b/spec/unit/provider/dsc_resource_spec.rb
@@ -85,14 +85,13 @@ describe Chef::Provider::DscResource do
node.automatic[:languages][:powershell][:version] = "5.0.10018.0"
node
end
- let(:resource_result) { double("CmdletResult", return_value: { "InDesiredState" => true }, stream: "description") }
- let(:invoke_dsc_resource) { double("cmdlet", run!: resource_result) }
+ let(:resource_result) { double("PowerShell", result: { "InDesiredState" => true }, verbose: ["description"]) }
let(:store) { double("ResourceStore", find: resource_records) }
let(:resource_records) { [] }
before do
allow(Chef::Util::DSC::ResourceStore).to receive(:instance).and_return(store)
- allow(Chef::Util::Powershell::Cmdlet).to receive(:new).and_return(invoke_dsc_resource)
+ allow(provider).to receive(:powershell_exec!).and_return(resource_result)
allow(provider).to receive(:dsc_refresh_mode_disabled?).and_return(true)
end
@@ -112,9 +111,8 @@ describe Chef::Provider::DscResource do
it "flags the resource as reboot required when required" do
expect(provider).to receive(:test_resource).and_return(false)
expect(provider).to receive(:invoke_resource)
- .and_return(double(stdout: "", return_value: nil))
+ .and_return(double(result: { "RebootRequired" => true }))
expect(provider).to receive(:add_dsc_verbose_log)
- expect(provider).to receive(:return_dsc_resource_result).and_return(true)
expect(provider).to receive(:create_reboot_resource)
provider.run_action(:run)
end
@@ -122,9 +120,8 @@ describe Chef::Provider::DscResource do
it "does not flag the resource as reboot required when not required" do
expect(provider).to receive(:test_resource).and_return(false)
expect(provider).to receive(:invoke_resource)
- .and_return(double(stdout: "", return_value: nil))
+ .and_return(double(stdout: "", result: {}))
expect(provider).to receive(:add_dsc_verbose_log)
- expect(provider).to receive(:return_dsc_resource_result).and_return(false)
expect(provider).to_not receive(:create_reboot_resource)
provider.run_action(:run)
end
@@ -142,9 +139,7 @@ describe Chef::Provider::DscResource do
let(:resource_records) { [{}] }
it "returns the default dsc resource module" do
- expect(Chef::Util::Powershell::Cmdlet).to receive(:new) do |node, cmdlet, format|
- expect(cmdlet).to match(/Module PSDesiredStateConfiguration /)
- end.and_return(invoke_dsc_resource)
+ expect(provider).to receive(:powershell_exec!).with(/Module PSDesiredStateConfiguration /).and_return(resource_result)
provider.run_action(:run)
end
end
@@ -153,9 +148,7 @@ describe Chef::Provider::DscResource do
let(:resource_records) { [{ "Module" => { "Name" => "ModuleName" } }] }
it "returns the default dsc resource module" do
- expect(Chef::Util::Powershell::Cmdlet).to receive(:new) do |node, cmdlet, format|
- expect(cmdlet).to match(/Module ModuleName /)
- end.and_return(invoke_dsc_resource)
+ expect(provider).to receive(:powershell_exec!).with(/Module ModuleName /).and_return(resource_result)
provider.run_action(:run)
end
end
@@ -286,8 +279,6 @@ describe Chef::Provider::DscResource do
end
describe "invoke_resource" do
- let(:cmdlet) { double(run!: nil) }
-
before(:each) do
allow(provider).to receive(:translate_type).and_return("my_properties")
provider.instance_variable_set(:@new_resource, double(
@@ -301,12 +292,8 @@ describe Chef::Provider::DscResource do
end
it "invokes Invoke-DscResource command with module name" do
- expect(Chef::Util::Powershell::Cmdlet).to receive(:new).with(
- node,
- "Invoke-DscResource -Method my_method -Name my_resource -Property my_properties -Module my_module -Verbose",
- "my_output_format"
- ).and_return(cmdlet)
- provider.send(:invoke_resource, "my_method", "my_output_format")
+ expect(provider).to receive(:powershell_exec!).with("Invoke-DscResource -Method my_method -Name my_resource -Property my_properties -Module my_module -Verbose").and_return(nil)
+ provider.send(:invoke_resource, "my_method")
end
end
@@ -318,12 +305,8 @@ describe Chef::Provider::DscResource do
end
it "invokes Invoke-DscResource command with module info object" do
- expect(Chef::Util::Powershell::Cmdlet).to receive(:new).with(
- node,
- "Invoke-DscResource -Method my_method -Name my_resource -Property my_properties -Module @{ModuleName='my_module';ModuleVersion='my_module_version'} -Verbose",
- "my_output_format"
- ).and_return(cmdlet)
- provider.send(:invoke_resource, "my_method", "my_output_format")
+ expect(provider).to receive(:powershell_exec!).with("Invoke-DscResource -Method my_method -Name my_resource -Property my_properties -Module @{ModuleName='my_module';ModuleVersion='my_module_version'} -Verbose").and_return(nil)
+ provider.send(:invoke_resource, "my_method")
end
end
end
diff --git a/spec/unit/provider/dsc_script_spec.rb b/spec/unit/provider/dsc_script_spec.rb
index f0a63e0a5b..d59b6f2480 100644
--- a/spec/unit/provider/dsc_script_spec.rb
+++ b/spec/unit/provider/dsc_script_spec.rb
@@ -99,7 +99,7 @@ describe Chef::Provider::DscScript do
it "should noop if neither code or command are provided" do
allow(provider).to receive(:load_current_resource)
generator = double("Chef::Util::DSC::ConfigurationGenerator")
- expect(generator).to receive(:configuration_document_from_script_code).with("", anything, anything, anything)
+ expect(generator).to 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
diff --git a/spec/unit/util/dsc/configuration_generator_spec.rb b/spec/unit/util/dsc/configuration_generator_spec.rb
index eee6adbd07..981b99af0e 100644
--- a/spec/unit/util/dsc/configuration_generator_spec.rb
+++ b/spec/unit/util/dsc/configuration_generator_spec.rb
@@ -25,6 +25,85 @@ describe Chef::Util::DSC::ConfigurationGenerator do
Chef::Util::DSC::ConfigurationGenerator.new(node, "tmp")
end
+ describe "#validate_switch_name!" do
+ it "should not raise an error if a name contains all upper case letters" do
+ conf_man.send(:validate_switch_name!, "HELLO")
+ end
+
+ it "should not raise an error if the name contains all lower case letters" do
+ conf_man.send(:validate_switch_name!, "hello")
+ end
+
+ it "should not raise an error if no special characters are used except _" do
+ conf_man.send(:validate_switch_name!, "hello_world")
+ end
+
+ %w{! @ # $ % ^ & * & * ( ) - = + \{ \} . ? < > \\ /}.each do |sym|
+ it "raises an Argument error if it configuration name contains #{sym}" do
+ expect do
+ conf_man.send(:validate_switch_name!, "Hello#{sym}")
+ end.to raise_error(ArgumentError)
+ end
+ end
+ end
+
+ describe "#escape_parameter_value" do
+ # Is this list really complete?
+ %w{` " # '}.each do |c|
+ it "escapse #{c}" do
+ expect(conf_man.send(:escape_parameter_value, "stuff #{c}")).to eql("stuff `#{c}")
+ end
+ end
+
+ it "does not do anything to a string without special characters" do
+ expect(conf_man.send(:escape_parameter_value, "stuff")).to eql("stuff")
+ end
+ end
+
+ describe "#escape_string_parameter_value" do
+ it "surrounds a string with ''" do
+ expect(conf_man.send(:escape_string_parameter_value, "stuff")).to eql("'stuff'")
+ end
+ end
+
+ describe "#command_switches_string" do
+ it "raises an ArgumentError if the key is not a symbol" do
+ expect do
+ conf_man.send(:command_switches_string, { "foo" => "bar" })
+ end.to raise_error(ArgumentError)
+ end
+
+ it "does not allow invalid switch names" do
+ expect do
+ conf_man.send(:command_switches_string, { foo!: "bar" })
+ end.to raise_error(ArgumentError)
+ end
+
+ it "ignores switches with a false value" do
+ expect(conf_man.send(:command_switches_string, { foo: false })).to eql("")
+ end
+
+ it "should correctly handle a value type of string" do
+ expect(conf_man.send(:command_switches_string, { foo: "bar" })).to eql("-foo 'bar'")
+ end
+
+ it "should correctly handle a value type of string even when it is 0 length" do
+ expect(conf_man.send(:command_switches_string, { foo: "" })).to eql("-foo ''")
+ end
+
+ it "should not quote integers" do
+ expect(conf_man.send(:command_switches_string, { foo: 1 })).to eql("-foo 1")
+ end
+
+ it "should not quote floats" do
+ expect(conf_man.send(:command_switches_string, { foo: 1.0 })).to eql("-foo 1.0")
+ end
+
+ it "has just the switch when the value is true" do
+ expect(conf_man.send(:command_switches_string, { foo: true })).to eql("-foo")
+ end
+ end
+
describe "#validate_configuration_name!" do
it "should not raise an error if a name contains all upper case letters" do
conf_man.send(:validate_configuration_name!, "HELLO")
diff --git a/spec/unit/util/dsc/local_configuration_manager_spec.rb b/spec/unit/util/dsc/local_configuration_manager_spec.rb
index e0f7c4ddaf..8adf778949 100644
--- a/spec/unit/util/dsc/local_configuration_manager_spec.rb
+++ b/spec/unit/util/dsc/local_configuration_manager_spec.rb
@@ -49,23 +49,22 @@ describe Chef::Util::DSC::LocalConfigurationManager do
EOH
end
- let(:lcm_status) do
- double("LCM cmdlet status", stderr: lcm_standard_error, return_value: lcm_standard_output, succeeded?: lcm_cmdlet_success)
+ let(:powershell) do
+ double("Chef::PowerShell", errors: lcm_errors, error?: !lcm_errors.empty?, result: lcm_result)
end
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)
+ allow(lcm).to receive(:run_configuration_cmdlet).and_return(powershell)
allow(lcm).to receive(:ps_version_gte_5?).and_return(false)
end
context "that returns successfully" do
- let(:lcm_standard_output) { normal_lcm_output }
- let(:lcm_standard_error) { nil }
- let(:lcm_cmdlet_success) { true }
+ let(:lcm_result) { normal_lcm_output }
+ let(:lcm_errors) { [] }
it "successfully returns resource information for normally formatted output when cmdlet the cmdlet succeeds" do
- test_configuration_result = lcm.test_configuration("config", {})
+ 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)
@@ -73,13 +72,12 @@ describe Chef::Util::DSC::LocalConfigurationManager do
end
context "when running on PowerShell version 5" do
- let(:lcm_standard_output) { normal_lcm_output }
- let(:lcm_standard_error) { nil }
- let(:lcm_cmdlet_success) { true }
+ let(:lcm_result) { normal_lcm_output }
+ let(:lcm_errors) { [] }
it "successfully returns resource information for normally formatted output when cmdlet the cmdlet succeeds" do
allow(lcm).to receive(:ps_version_gte_5?).and_return(true)
- test_configuration_result = lcm.test_configuration("config", {})
+ 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)
@@ -87,13 +85,12 @@ describe Chef::Util::DSC::LocalConfigurationManager do
end
context "when running on PowerShell version less than 5" do
- let(:lcm_standard_output) { normal_lcm_output }
- let(:lcm_standard_error) { nil }
- let(:lcm_cmdlet_success) { true }
+ let(:lcm_result) { normal_lcm_output }
+ let(:lcm_errors) { [] }
it "successfully returns resource information for normally formatted output when cmdlet the cmdlet succeeds" do
allow(lcm).to receive(:ps_version_gte_5?).and_return(false)
- test_configuration_result = lcm.test_configuration("config", {})
+ 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)
@@ -104,10 +101,9 @@ describe Chef::Util::DSC::LocalConfigurationManager do
let(:common_command_prefix) { "$ProgressPreference = 'SilentlyContinue';" }
let(:ps4_base_command) { "#{common_command_prefix} Start-DscConfiguration -path tmp -wait -erroraction 'stop' -force" }
let(:lcm_command_ps4) { ps4_base_command + " -whatif; if (! $?) { exit 1 }" }
- let(:lcm_command_ps5) { "#{common_command_prefix} Test-DscConfiguration -path tmp | format-list" }
- let(:lcm_standard_output) { normal_lcm_output }
- let(:lcm_standard_error) { nil }
- let(:lcm_cmdlet_success) { true }
+ let(:lcm_command_ps5) { "#{common_command_prefix} Test-DscConfiguration -path tmp | format-list | Out-String" }
+ let(:lcm_result) { normal_lcm_output }
+ let(:lcm_errors) { [] }
it "successfully returns command when apply_configuration true" do
expect(lcm.send(:lcm_command, true)).to eq(ps4_base_command)
@@ -125,9 +121,8 @@ describe Chef::Util::DSC::LocalConfigurationManager do
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 }
+ let(:lcm_result) { "" }
+ let(:lcm_errors) { [no_whatif_lcm_output] }
it "returns true when passed to #whatif_not_supported?" do
expect(lcm.send(:whatif_not_supported?, no_whatif_lcm_output)).to be_truthy
@@ -137,40 +132,38 @@ describe Chef::Util::DSC::LocalConfigurationManager do
expect(Chef::Log).to receive(:warn).at_least(:once)
expect(lcm).to receive(:whatif_not_supported?).and_call_original
test_configuration_result = nil
- expect { test_configuration_result = lcm.test_configuration("config", {}) }.not_to raise_error
+ 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 }
+ let(:lcm_result) { "" }
+ let(:lcm_errors) { [dsc_resource_import_failure_output] }
it "logs a warning if the message is formatted as expected when a resource import failure occurs" do
expect(Chef::Log).to receive(:warn).at_least(:once)
expect(lcm).to receive(:dsc_module_import_failure?).and_call_original
test_configuration_result = nil
- expect { test_configuration_result = lcm.test_configuration("config", {}) }.not_to raise_error
+ expect { test_configuration_result = lcm.test_configuration("config") }.not_to raise_error
end
it "returns a (possibly empty) array of ResourceInfo instances" do
expect(Chef::Log).to receive(:warn).at_least(:once)
test_configuration_result = nil
- expect { test_configuration_result = lcm.test_configuration("config", {}) }.not_to raise_error
+ 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 unknown PowerShell cmdlet error" do
- let(:lcm_standard_output) { "some output" }
- let(:lcm_standard_error) { "Abort, Retry, Fail?" }
- let(:lcm_cmdlet_success) { false }
+ let(:lcm_result) { "some output" }
+ let(:lcm_errors) { ["Abort, Retry, Fail?"] }
it "logs a warning" do
expect(Chef::Log).to receive(:warn).at_least(:once)
expect(lcm).to receive(:dsc_module_import_failure?).and_call_original
- expect { lcm.test_configuration("config", {}) }.not_to raise_error
+ expect { lcm.test_configuration("config") }.not_to raise_error
end
end
end
@@ -188,12 +181,11 @@ describe Chef::Util::DSC::LocalConfigurationManager do
end
end
- describe "#run_configuration_cmdlet" do
+ describe "#run_configuration_cmdlet", :windows_powershell_dsc_only do
context "when invalid dsc script is given" do
it "raises exception" do
configuration_document = "invalid-config"
- shellout_flags = { cwd: nil, environment: nil, timeout: nil }
- expect { lcm.send(:run_configuration_cmdlet, configuration_document, true, shellout_flags) }.to raise_error(Chef::Exceptions::PowershellCmdletException)
+ expect { lcm.send(:run_configuration_cmdlet, configuration_document, true) }.to raise_error(Chef::PowerShell::CommandFailed)
end
end
end
diff --git a/spec/unit/util/powershell/cmdlet_spec.rb b/spec/unit/util/powershell/cmdlet_spec.rb
deleted file mode 100644
index 4dc6e2b85c..0000000000
--- a/spec/unit/util/powershell/cmdlet_spec.rb
+++ /dev/null
@@ -1,106 +0,0 @@
-#
-# Author:: Jay Mundrawala <jdm@chef.io>
-# Copyright:: Copyright (c) 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/powershell/cmdlet"
-
-describe Chef::Util::Powershell::Cmdlet do
- before (:all) do
- @node = Chef::Node.new
- @cmdlet = Chef::Util::Powershell::Cmdlet.new(@node, "Some-Commandlet")
- end
-
- describe "#validate_switch_name!" do
- it "should not raise an error if a name contains all upper case letters" do
- @cmdlet.send(:validate_switch_name!, "HELLO")
- end
-
- it "should not raise an error if the name contains all lower case letters" do
- @cmdlet.send(:validate_switch_name!, "hello")
- end
-
- it "should not raise an error if no special characters are used except _" do
- @cmdlet.send(:validate_switch_name!, "hello_world")
- end
-
- %w{! @ # $ % ^ & * & * ( ) - = + \{ \} . ? < > \\ /}.each do |sym|
- it "raises an Argument error if it configuration name contains #{sym}" do
- expect do
- @cmdlet.send(:validate_switch_name!, "Hello#{sym}")
- end.to raise_error(ArgumentError)
- end
- end
- end
-
- describe "#escape_parameter_value" do
- # Is this list really complete?
- %w{` " # '}.each do |c|
- it "escapse #{c}" do
- expect(@cmdlet.send(:escape_parameter_value, "stuff #{c}")).to eql("stuff `#{c}")
- end
- end
-
- it "does not do anything to a string without special characters" do
- expect(@cmdlet.send(:escape_parameter_value, "stuff")).to eql("stuff")
- end
- end
-
- describe "#escape_string_parameter_value" do
- it "surrounds a string with ''" do
- expect(@cmdlet.send(:escape_string_parameter_value, "stuff")).to eql("'stuff'")
- end
- end
-
- describe "#command_switches_string" do
- it "raises an ArgumentError if the key is not a symbol" do
- expect do
- @cmdlet.send(:command_switches_string, { "foo" => "bar" })
- end.to raise_error(ArgumentError)
- end
-
- it "does not allow invalid switch names" do
- expect do
- @cmdlet.send(:command_switches_string, { foo!: "bar" })
- end.to raise_error(ArgumentError)
- end
-
- it "ignores switches with a false value" do
- expect(@cmdlet.send(:command_switches_string, { foo: false })).to eql("")
- end
-
- it "should correctly handle a value type of string" do
- expect(@cmdlet.send(:command_switches_string, { foo: "bar" })).to eql("-foo 'bar'")
- end
-
- it "should correctly handle a value type of string even when it is 0 length" do
- expect(@cmdlet.send(:command_switches_string, { foo: "" })).to eql("-foo ''")
- end
-
- it "should not quote integers" do
- expect(@cmdlet.send(:command_switches_string, { foo: 1 })).to eql("-foo 1")
- end
-
- it "should not quote floats" do
- expect(@cmdlet.send(:command_switches_string, { foo: 1.0 })).to eql("-foo 1.0")
- end
-
- it "has just the switch when the value is true" do
- expect(@cmdlet.send(:command_switches_string, { foo: true })).to eql("-foo")
- end
- end
-end