chef 11.14.6 → 11.16.0.rc.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/chef/exceptions.rb +2 -0
- data/lib/chef/mixin/windows_architecture_helper.rb +16 -0
- data/lib/chef/provider/dsc_script.rb +148 -0
- data/lib/chef/providers.rb +1 -0
- data/lib/chef/resource/dsc_script.rb +123 -0
- data/lib/chef/resources.rb +1 -0
- data/lib/chef/util/dsc/configuration_generator.rb +115 -0
- data/lib/chef/util/dsc/lcm_output_parser.rb +178 -0
- data/lib/chef/util/dsc/local_configuration_manager.rb +137 -0
- data/lib/chef/util/dsc/resource_info.rb +26 -0
- data/lib/chef/util/path_helper.rb +2 -2
- data/lib/chef/util/powershell/cmdlet.rb +136 -0
- data/lib/chef/util/powershell/cmdlet_result.rb +46 -0
- data/lib/chef/version.rb +1 -1
- data/spec/functional/resource/dsc_script_spec.rb +336 -0
- data/spec/functional/resource/group_spec.rb +5 -1
- data/spec/functional/util/powershell/cmdlet_spec.rb +114 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/platform_helpers.rb +24 -0
- data/spec/unit/provider/dsc_script_spec.rb +141 -0
- data/spec/unit/resource/dsc_script_spec.rb +94 -0
- data/spec/unit/util/dsc/configuration_generator_spec.rb +173 -0
- data/spec/unit/util/dsc/lcm_output_parser_spec.rb +178 -0
- data/spec/unit/util/dsc/local_configuration_manager_spec.rb +134 -0
- data/spec/unit/util/powershell/cmdlet_spec.rb +106 -0
- metadata +77 -61
@@ -0,0 +1,178 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Jay Mundrawala (<jdm@getchef.com>)
|
3
|
+
#
|
4
|
+
# Copyright:: 2014, Chef Software, Inc.
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'chef/log'
|
20
|
+
require 'chef/util/dsc/resource_info'
|
21
|
+
|
22
|
+
class Chef
|
23
|
+
class Util
|
24
|
+
class DSC
|
25
|
+
class LocalConfigurationManager
|
26
|
+
module Parser
|
27
|
+
class ParseException < RuntimeError; end
|
28
|
+
|
29
|
+
class Operation
|
30
|
+
attr_reader :op_type
|
31
|
+
attr_reader :resources
|
32
|
+
attr_reader :info
|
33
|
+
attr_reader :sets
|
34
|
+
attr_reader :tests
|
35
|
+
|
36
|
+
def initialize(op_type, info)
|
37
|
+
@op_type = op_type
|
38
|
+
@info = []
|
39
|
+
@sets = []
|
40
|
+
@tests = []
|
41
|
+
@resources = []
|
42
|
+
add_info(info)
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_info(info)
|
46
|
+
@info << info
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_set(set)
|
50
|
+
raise ParseException, "add_set is not allowed in this context. Found #{@op_type}" unless [:resource, :set].include?(@op_type)
|
51
|
+
@sets << set
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_test(test)
|
55
|
+
raise ParseException, "add_test is not allowed in this context. Found #{@op_type}" unless [:resource, :set].include?(@op_type)
|
56
|
+
@tests << test
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_resource(resource)
|
60
|
+
raise ParseException, 'add_resource is only allowed to be added to the set op_type' unless @op_type == :set
|
61
|
+
@resources << resource
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Parses the output from LCM and returns a list of Chef::Util::DSC::ResourceInfo objects
|
66
|
+
# that describe how the resources affected the system
|
67
|
+
#
|
68
|
+
# Example:
|
69
|
+
# parse <<-EOF
|
70
|
+
# What if: [Machine]: LCM: [Start Set ]
|
71
|
+
# What if: [Machine]: LCM: [Start Resource ] [[File]FileToNotBeThere]
|
72
|
+
# What if: [Machine]: LCM: [Start Set ] [[File]FileToNotBeThere]
|
73
|
+
# What if: [C:\ShouldNotExist.txt] removed
|
74
|
+
# What if: [Machine]: LCM: [End Set ] [[File]FileToNotBeThere] in 0.1 seconds
|
75
|
+
# What if: [Machine]: LCM: [End Resource ] [[File]FileToNotBeThere]
|
76
|
+
# What if: [Machine]: LCM: [End Set ]
|
77
|
+
# EOF
|
78
|
+
#
|
79
|
+
# would return
|
80
|
+
#
|
81
|
+
# [
|
82
|
+
# Chef::Util::DSC::ResourceInfo.new(
|
83
|
+
# '[[File]FileToNotBeThere]',
|
84
|
+
# true,
|
85
|
+
# [
|
86
|
+
# '[[File]FileToNotBeThere]',
|
87
|
+
# '[C:\Shouldnotexist.txt]',
|
88
|
+
# '[[File]FileToNotBeThere] in 0.1 seconds'
|
89
|
+
# ]
|
90
|
+
# )
|
91
|
+
# ]
|
92
|
+
#
|
93
|
+
def self.parse(lcm_output)
|
94
|
+
return [] unless lcm_output
|
95
|
+
|
96
|
+
stack = Array.new
|
97
|
+
popped_op = nil
|
98
|
+
lcm_output.lines.each do |line|
|
99
|
+
op_action, op_type, info = parse_line(line)
|
100
|
+
info.strip! # Because this was formatted for humans
|
101
|
+
|
102
|
+
# The rules:
|
103
|
+
# - For each `start` action, there must be a matching `end` action
|
104
|
+
# - `skip` actions do not not do anything (They don't add to the stack)
|
105
|
+
case op_action
|
106
|
+
when :start
|
107
|
+
new_op = Operation.new(op_type, info)
|
108
|
+
case op_type
|
109
|
+
when :set
|
110
|
+
stack[-1].add_set(new_op) if stack[-1]
|
111
|
+
when :test
|
112
|
+
stack[-1].add_test(new_op)
|
113
|
+
when :resource
|
114
|
+
while stack[-1].op_type != :set
|
115
|
+
Chef::Log.warn("Can't add resource to set...popping until it is allowed.")
|
116
|
+
popped_op = stack.pop
|
117
|
+
end
|
118
|
+
stack[-1].add_resource(new_op)
|
119
|
+
else
|
120
|
+
Chef::Log.warn("Unknown op_action #{op_action}: Read line #{line}")
|
121
|
+
end
|
122
|
+
stack.push(new_op)
|
123
|
+
when :end
|
124
|
+
popped_op = stack.pop
|
125
|
+
popped_op.add_info(info)
|
126
|
+
while popped_op.op_type != op_type
|
127
|
+
Chef::Log::warn("Unmatching end for op_type. Expected op_type=#{op_type}, found op_type=#{popped_op.op_type}. From output:\n#{lcm_output}")
|
128
|
+
popped_op = stack.pop
|
129
|
+
end
|
130
|
+
when :skip
|
131
|
+
# We don't really have anything to do here
|
132
|
+
when :info
|
133
|
+
stack[-1].add_info(info) if stack[-1]
|
134
|
+
else
|
135
|
+
stack[-1].add_info(line) if stack[-1]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
op_to_resource_infos(popped_op)
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.parse_line(line)
|
143
|
+
if match = line.match(/^.*?:.*?:\s*LCM:\s*\[(.*?)\](.*)/)
|
144
|
+
# If the line looks like
|
145
|
+
# x: [y]: LCM: [op_action op_type] message
|
146
|
+
# extract op_action, op_type, and message
|
147
|
+
operation, info = match.captures
|
148
|
+
op_action, op_type = operation.strip.split(' ').map {|m| m.downcase.to_sym}
|
149
|
+
else
|
150
|
+
# If the line looks like
|
151
|
+
# x: [y]: message
|
152
|
+
# extract message
|
153
|
+
match = line.match(/^.*?:.*?: \s+(.*)/)
|
154
|
+
op_action = op_type = :info
|
155
|
+
info = match.captures[0]
|
156
|
+
end
|
157
|
+
info.strip! # Because this was formatted for humans
|
158
|
+
return [op_action, op_type, info]
|
159
|
+
end
|
160
|
+
private_class_method :parse_line
|
161
|
+
|
162
|
+
def self.op_to_resource_infos(op)
|
163
|
+
resources = op ? op.resources : []
|
164
|
+
|
165
|
+
resources.map do |r|
|
166
|
+
name = r.info[0]
|
167
|
+
sets = r.sets.length > 0
|
168
|
+
change_log = r.sets[-1].info if sets
|
169
|
+
Chef::Util::DSC::ResourceInfo.new(name, sets, change_log)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
private_class_method :op_to_resource_infos
|
173
|
+
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Adam Edwards (<adamed@getchef.com>)
|
3
|
+
#
|
4
|
+
# Copyright:: 2014, Chef Software, Inc.
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'chef/util/powershell/cmdlet'
|
20
|
+
require 'chef/util/dsc/lcm_output_parser'
|
21
|
+
|
22
|
+
class Chef::Util::DSC
|
23
|
+
class LocalConfigurationManager
|
24
|
+
def initialize(node, configuration_path)
|
25
|
+
@node = node
|
26
|
+
@configuration_path = configuration_path
|
27
|
+
clear_execution_time
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_configuration(configuration_document)
|
31
|
+
status = run_configuration_cmdlet(configuration_document)
|
32
|
+
handle_what_if_exception!(status.stderr) unless status.succeeded?
|
33
|
+
configuration_update_required?(status.return_value)
|
34
|
+
end
|
35
|
+
|
36
|
+
def set_configuration(configuration_document)
|
37
|
+
run_configuration_cmdlet(configuration_document, true)
|
38
|
+
end
|
39
|
+
|
40
|
+
def last_operation_execution_time_seconds
|
41
|
+
if @operation_start_time && @operation_end_time
|
42
|
+
@operation_end_time - @operation_start_time
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def run_configuration_cmdlet(configuration_document, apply_configuration = false)
|
49
|
+
Chef::Log.debug("DSC: Calling DSC Local Config Manager to #{apply_configuration ? "set" : "test"} configuration document.")
|
50
|
+
test_only_parameters = ! apply_configuration ? '-whatif; if (! $?) { exit 1 }' : ''
|
51
|
+
|
52
|
+
start_operation_timing
|
53
|
+
command_code = lcm_command_code(@configuration_path, test_only_parameters)
|
54
|
+
status = nil
|
55
|
+
|
56
|
+
begin
|
57
|
+
save_configuration_document(configuration_document)
|
58
|
+
cmdlet = ::Chef::Util::Powershell::Cmdlet.new(@node, "#{command_code}")
|
59
|
+
if apply_configuration
|
60
|
+
status = cmdlet.run!
|
61
|
+
else
|
62
|
+
status = cmdlet.run
|
63
|
+
end
|
64
|
+
ensure
|
65
|
+
end_operation_timing
|
66
|
+
remove_configuration_document
|
67
|
+
if last_operation_execution_time_seconds
|
68
|
+
Chef::Log.debug("DSC: DSC operation completed in #{last_operation_execution_time_seconds} seconds.")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
Chef::Log.debug("DSC: Completed call to DSC Local Config Manager")
|
72
|
+
status
|
73
|
+
end
|
74
|
+
|
75
|
+
def lcm_command_code(configuration_path, test_only_parameters)
|
76
|
+
<<-EOH
|
77
|
+
$ProgressPreference = 'SilentlyContinue';start-dscconfiguration -path #{@configuration_path} -wait -erroraction 'continue' -force #{test_only_parameters}
|
78
|
+
EOH
|
79
|
+
end
|
80
|
+
|
81
|
+
def handle_what_if_exception!(what_if_exception_output)
|
82
|
+
if what_if_exception_output.gsub(/\s+/, ' ') =~ /A parameter cannot be found that matches parameter name 'Whatif'/i
|
83
|
+
# LCM returns an error if any of the resources do not support the opptional What-If
|
84
|
+
Chef::Log::warn("Received error while testing configuration due to resource not supporting 'WhatIf'")
|
85
|
+
elsif output_has_dsc_module_failure?(what_if_exception_output)
|
86
|
+
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+/, ' ')}")
|
87
|
+
else
|
88
|
+
raise Chef::Exceptions::PowershellCmdletException, "Powershell Cmdlet failed: #{what_if_exception_output.gsub(/\s+/, ' ')}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def output_has_dsc_module_failure?(what_if_output)
|
93
|
+
!! (what_if_output =~ /\sCimException/ &&
|
94
|
+
what_if_output =~ /ProviderOperationExecutionFailure/ &&
|
95
|
+
what_if_output =~ /\smodule\s+is\s+installed/)
|
96
|
+
end
|
97
|
+
|
98
|
+
def configuration_update_required?(what_if_output)
|
99
|
+
Chef::Log.debug("DSC: DSC returned the following '-whatif' output from test operation:\n#{what_if_output}")
|
100
|
+
begin
|
101
|
+
Parser::parse(what_if_output)
|
102
|
+
rescue Chef::Util::DSC::LocalConfigurationManager::Parser => e
|
103
|
+
Chef::Log::warn("Could not parse LCM output: #{e}")
|
104
|
+
[Chef::Util::DSC::ResourceInfo.new('Unknown DSC Resources', true, ['Unknown changes because LCM output was not parsable.'])]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def save_configuration_document(configuration_document)
|
109
|
+
::FileUtils.mkdir_p(@configuration_path)
|
110
|
+
::File.open(configuration_document_path, 'wb') do | file |
|
111
|
+
file.write(configuration_document)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def remove_configuration_document
|
116
|
+
::FileUtils.rm(configuration_document_path)
|
117
|
+
end
|
118
|
+
|
119
|
+
def configuration_document_path
|
120
|
+
File.join(@configuration_path,'..mof')
|
121
|
+
end
|
122
|
+
|
123
|
+
def clear_execution_time
|
124
|
+
@operation_start_time = nil
|
125
|
+
@operation_end_time = nil
|
126
|
+
end
|
127
|
+
|
128
|
+
def start_operation_timing
|
129
|
+
clear_execution_time
|
130
|
+
@operation_start_time = Time.now
|
131
|
+
end
|
132
|
+
|
133
|
+
def end_operation_timing
|
134
|
+
@operation_end_time = Time.now
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
class Chef
|
3
|
+
class Util
|
4
|
+
class DSC
|
5
|
+
class ResourceInfo
|
6
|
+
# The name is the text following [Start Set]
|
7
|
+
attr_reader :name
|
8
|
+
|
9
|
+
# A list of all log messages between [Start Set] and [End Set].
|
10
|
+
# Each line is an element in the list.
|
11
|
+
attr_reader :change_log
|
12
|
+
|
13
|
+
def initialize(name, sets, change_log)
|
14
|
+
@name = name
|
15
|
+
@sets = sets
|
16
|
+
@change_log = change_log || []
|
17
|
+
end
|
18
|
+
|
19
|
+
# Does this resource change the state of the system?
|
20
|
+
def changes_state?
|
21
|
+
@sets
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -32,7 +32,7 @@ class Chef
|
|
32
32
|
Chef::Log.error(msg)
|
33
33
|
raise Chef::Exceptions::ValidationFailed, msg
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
if windows_max_length_exceeded?(path)
|
37
37
|
Chef::Log.debug("Path '#{path}' is longer than #{WIN_MAX_PATH}, prefixing with'\\\\?\\'")
|
38
38
|
path.insert(0, "\\\\?\\")
|
@@ -50,7 +50,7 @@ class Chef
|
|
50
50
|
return true
|
51
51
|
end
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
false
|
55
55
|
end
|
56
56
|
|
@@ -0,0 +1,136 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Adam Edwards (<adamed@getchef.com>)
|
3
|
+
#
|
4
|
+
# Copyright:: 2014, Chef Software, Inc.
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'mixlib/shellout'
|
20
|
+
require 'chef/mixin/windows_architecture_helper'
|
21
|
+
require 'chef/util/powershell/cmdlet_result'
|
22
|
+
|
23
|
+
class Chef::Util::Powershell
|
24
|
+
class Cmdlet
|
25
|
+
def initialize(node, cmdlet, output_format=nil, output_format_options={})
|
26
|
+
@output_format = output_format
|
27
|
+
@node = node
|
28
|
+
|
29
|
+
case output_format
|
30
|
+
when nil
|
31
|
+
@json_format = false
|
32
|
+
when :json
|
33
|
+
@json_format = true
|
34
|
+
when :text
|
35
|
+
@json_format = false
|
36
|
+
when :object
|
37
|
+
@json_format = true
|
38
|
+
else
|
39
|
+
raise ArgumentError, "Invalid output format #{output_format.to_s} specified"
|
40
|
+
end
|
41
|
+
|
42
|
+
@cmdlet = cmdlet
|
43
|
+
@output_format_options = output_format_options
|
44
|
+
end
|
45
|
+
|
46
|
+
attr_reader :output_format
|
47
|
+
|
48
|
+
def run(switches={}, execution_options={}, *arguments)
|
49
|
+
arguments_string = arguments.join(' ')
|
50
|
+
|
51
|
+
switches_string = command_switches_string(switches)
|
52
|
+
|
53
|
+
json_depth = 5
|
54
|
+
|
55
|
+
if @json_format && @output_format_options.has_key?(:depth)
|
56
|
+
json_depth = @output_format_options[:depth]
|
57
|
+
end
|
58
|
+
|
59
|
+
json_command = @json_format ? " | convertto-json -compress -depth #{json_depth}" : ""
|
60
|
+
command_string = "powershell.exe -executionpolicy bypass -noprofile -noninteractive -command \"trap [Exception] {write-error -exception ($_.Exception.Message);exit 1};#{@cmdlet} #{switches_string} #{arguments_string}#{json_command}\";if ( ! $? ) { exit 1 }"
|
61
|
+
|
62
|
+
augmented_options = {:returns => [0], :live_stream => false}.merge(execution_options)
|
63
|
+
command = Mixlib::ShellOut.new(command_string, augmented_options)
|
64
|
+
|
65
|
+
os_architecture = "#{ENV['PROCESSOR_ARCHITEW6432']}" == 'AMD64' ? :x86_64 : :i386
|
66
|
+
|
67
|
+
status = nil
|
68
|
+
|
69
|
+
with_os_architecture(@node) do
|
70
|
+
status = command.run_command
|
71
|
+
end
|
72
|
+
|
73
|
+
CmdletResult.new(status, @output_format)
|
74
|
+
end
|
75
|
+
|
76
|
+
def run!(switches={}, execution_options={}, *arguments)
|
77
|
+
result = run(switches, execution_options, arguments)
|
78
|
+
|
79
|
+
if ! result.succeeded?
|
80
|
+
raise Chef::Exceptions::PowershellCmdletException, "Powershell Cmdlet failed: #{result.stderr}"
|
81
|
+
end
|
82
|
+
|
83
|
+
result
|
84
|
+
end
|
85
|
+
|
86
|
+
protected
|
87
|
+
|
88
|
+
include Chef::Mixin::WindowsArchitectureHelper
|
89
|
+
|
90
|
+
def validate_switch_name!(switch_parameter_name)
|
91
|
+
if !!(switch_parameter_name.to_s =~ /\A[A-Za-z]+[_a-zA-Z0-9]*\Z/) == false
|
92
|
+
raise ArgumentError, "`#{switch_parameter_name}` is not a valid PowerShell cmdlet switch parameter name"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def escape_parameter_value(parameter_value)
|
97
|
+
parameter_value.gsub(/(`|'|"|#)/,'`\1')
|
98
|
+
end
|
99
|
+
|
100
|
+
def escape_string_parameter_value(parameter_value)
|
101
|
+
"'#{escape_parameter_value(parameter_value)}'"
|
102
|
+
end
|
103
|
+
|
104
|
+
def command_switches_string(switches)
|
105
|
+
command_switches = switches.map do | switch_name, switch_value |
|
106
|
+
if switch_name.class != Symbol
|
107
|
+
raise ArgumentError, "Invalid type `#{switch_name} `for PowerShell switch '#{switch_name.to_s}'. The switch must be specified as a Symbol'"
|
108
|
+
end
|
109
|
+
|
110
|
+
validate_switch_name!(switch_name)
|
111
|
+
|
112
|
+
switch_argument = ''
|
113
|
+
switch_present = true
|
114
|
+
|
115
|
+
case switch_value
|
116
|
+
when Numeric
|
117
|
+
switch_argument = switch_value.to_s
|
118
|
+
when Float
|
119
|
+
switch_argument = switch_value.to_s
|
120
|
+
when FalseClass
|
121
|
+
switch_present = false
|
122
|
+
when TrueClass
|
123
|
+
when String
|
124
|
+
switch_argument = escape_string_parameter_value(switch_value)
|
125
|
+
else
|
126
|
+
raise ArgumentError, "Invalid argument type `#{switch_value.class}` specified for PowerShell switch `:#{switch_name.to_s}`. Arguments to PowerShell must be of type `String`, `Numeric`, `Float`, `FalseClass`, or `TrueClass`"
|
127
|
+
end
|
128
|
+
|
129
|
+
switch_present ? ["-#{switch_name.to_s.downcase}", switch_argument].join(' ').strip : ''
|
130
|
+
end
|
131
|
+
|
132
|
+
command_switches.join(' ')
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|