summaryrefslogtreecommitdiff
path: root/lib/chef/util
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chef/util')
-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
6 files changed, 78 insertions, 270 deletions
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