winrm 1.3.0.dev.2 → 1.3.0.dev.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +14 -6
- data/Rakefile +3 -1
- data/VERSION +1 -1
- data/Vagrantfile +8 -0
- data/bin/rwinrm +34 -70
- data/lib/winrm/exceptions/exceptions.rb +17 -1
- data/lib/winrm/helpers/powershell_script.rb +22 -0
- data/lib/winrm/http/response_handler.rb +15 -3
- data/lib/winrm/http/transport.rb +14 -1
- data/lib/winrm/output.rb +27 -0
- data/lib/winrm/winrm_service.rb +21 -11
- data/lib/winrm.rb +1 -1
- data/spec/cmd_spec.rb +32 -0
- data/spec/config-example.yml +1 -1
- data/spec/exception_spec.rb +17 -0
- data/spec/output_spec.rb +106 -0
- data/spec/powershell_spec.rb +34 -0
- data/spec/response_handler_spec.rb +14 -0
- data/spec/stubs/responses/wmi_error_v2.xml +41 -0
- data/spec/winrm_options_spec.rb +75 -0
- data/spec/winrm_primitives_spec.rb +0 -18
- data/winrm.gemspec +1 -3
- metadata +10 -38
- data/lib/winrm/file_transfer/remote_file.rb +0 -184
- data/lib/winrm/file_transfer/remote_zip_file.rb +0 -60
- data/lib/winrm/file_transfer.rb +0 -39
- data/spec/file_transfer/remote_file_spec.rb +0 -71
- data/spec/file_transfer/remote_zip_file_spec.rb +0 -51
- data/spec/file_transfer_spec.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 19e0ee9ca403a8fe7b5385edd2a055e040aa69fe
|
4
|
+
data.tar.gz: 7c66ac5aa2b9b02fa2245f52f1f61f770f929287
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2e8e8dddd4eb89fb2a19ff72ee2413cd39599c6c7198e1b8d3761f095739631ab73bb11467073d555a9fa059a723c081ce0d3d91188f2176143c65461770801
|
7
|
+
data.tar.gz: dcbd77c85f639859fdfbd60ba9a26b82ef7676139f9878853b8c4d64a85f8a1bff5e6051acb6f96d6c268025ed901ee7e7a054f4197886893e95954ed07de55a
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -73,21 +73,29 @@ WinRM::WinRMWebService.new(endpoint, :kerberos, :realm => 'MYREALM.COM')
|
|
73
73
|
```
|
74
74
|
|
75
75
|
### Uploading files
|
76
|
-
Files may be copied from the local machine to the winrm endpoint. Individual
|
76
|
+
Files may be copied from the local machine to the winrm endpoint. Individual
|
77
|
+
files or directories may be specified:
|
77
78
|
```ruby
|
78
|
-
WinRM::
|
79
|
+
service = WinRM::WinRMWebService.new(...
|
80
|
+
file_manager = WinRM::FileManager.new(service)
|
81
|
+
file_manager.upload('c:/dev/my_dir', '$env:AppData')
|
79
82
|
```
|
80
83
|
Or an array of several files and/or directories can be included:
|
81
84
|
```ruby
|
82
|
-
|
85
|
+
file_manager.upload(['c:/dev/file1.txt','c:/dev/dir1'], '$env:AppData')
|
83
86
|
```
|
84
|
-
|
87
|
+
|
88
|
+
#### Handling progress events
|
89
|
+
If you want to implemnt your own custom progress handling, you can pass a code
|
90
|
+
block and use the proggress data that `upload` yields to this block:
|
85
91
|
```ruby
|
86
|
-
|
92
|
+
file_manager.upload('c:/dev/my_dir', '$env:AppData') do |bytes_copied, total_bytes, local_path, remote_path|
|
93
|
+
puts "#{bytes_copied}bytes of #{total_bytes}bytes copied"
|
94
|
+
end
|
87
95
|
```
|
88
96
|
|
89
97
|
## Troubleshooting
|
90
|
-
You may have some errors like ```WinRM::
|
98
|
+
You may have some errors like ```WinRM::WinRMAuthorizationError```.
|
91
99
|
You can run the following commands on the server to try to solve the problem:
|
92
100
|
```
|
93
101
|
winrm set winrm/config/client/auth @{Basic="true"}
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.3.0.dev.
|
1
|
+
1.3.0.dev.3
|
data/Vagrantfile
ADDED
data/bin/rwinrm
CHANGED
@@ -17,82 +17,48 @@
|
|
17
17
|
$:.push File.expand_path('../../lib', __FILE__)
|
18
18
|
|
19
19
|
require 'readline'
|
20
|
-
require '
|
20
|
+
require 'io/console'
|
21
21
|
require 'winrm'
|
22
22
|
|
23
|
+
def help_msg
|
24
|
+
puts "Usage: rwinrm user@host"
|
25
|
+
puts ""
|
26
|
+
end
|
27
|
+
|
23
28
|
def parse_options
|
24
29
|
options = {}
|
30
|
+
raise "Missing required options" unless ARGV.length == 1
|
31
|
+
|
32
|
+
m = /^(?<user>\w+)@{1}(?<host>[a-zA-Z0-9\.]+)(?<port>:[0-9]+)?/.match(ARGV[0])
|
33
|
+
raise "#{ARGV[0]} is an invalid host" unless m
|
34
|
+
options[:user] = m[:user]
|
35
|
+
options[:endpoint] = "http://#{m[:host]}#{m[:port] || ':5985'}/wsman"
|
36
|
+
|
37
|
+
# Get the password
|
38
|
+
print "Password: "
|
39
|
+
options[:pass] = STDIN.noecho(&:gets).chomp
|
40
|
+
puts
|
41
|
+
|
42
|
+
# Set some defaults required by WinRM WS
|
25
43
|
options[:auth_type] = :plaintext
|
26
44
|
options[:basic_auth_only] = true
|
27
|
-
options[:mode] = :repl
|
28
|
-
|
29
|
-
optparse = OptionParser.new do |opts|
|
30
|
-
opts.banner = "Usage: rwinrm [command] [local_path] [remote_path] endpoint [options]"
|
31
|
-
opts.separator ""
|
32
|
-
opts.separator "Commands"
|
33
|
-
opts.separator " repl(default)"
|
34
|
-
opts.separator " upload"
|
35
|
-
opts.separator ""
|
36
|
-
opts.separator "Options"
|
37
|
-
|
38
|
-
opts.on('-u', '--user username', String, 'WinRM user name') do |v|
|
39
|
-
options[:user] = v
|
40
|
-
end
|
41
|
-
|
42
|
-
opts.on('-p', '--pass password', String, 'WinRM user password') do |v|
|
43
|
-
options[:pass] = v
|
44
|
-
end
|
45
|
-
|
46
|
-
opts.on('-h', '--help', 'Display this screen') do
|
47
|
-
puts optparse
|
48
|
-
exit
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
optparse.parse!
|
53
|
-
|
54
|
-
if ARGV[0] && ARGV[0].downcase !~ /\A#{URI::regexp(['http', 'https'])}\z/
|
55
|
-
options[:mode] = ARGV.shift.downcase.to_sym
|
56
|
-
|
57
|
-
case options[:mode]
|
58
|
-
when :upload
|
59
|
-
options[:local_path] = ARGV.shift
|
60
|
-
options[:remote_path] = ARGV.shift
|
61
|
-
|
62
|
-
raise OptionParser::MissingArgument.new(:local_path) if options[:local_path].nil?
|
63
|
-
raise OptionParser::MissingArgument.new(:remote_path) if options[:remote_path].nil?
|
64
|
-
when :repl
|
65
|
-
else
|
66
|
-
raise OptionParser::InvalidArgument.new(options[:mode])
|
67
|
-
end
|
68
|
-
end
|
69
|
-
options[:endpoint] = ARGV.shift
|
70
|
-
|
71
|
-
raise OptionParser::MissingArgument.new(:endpoint) if options[:endpoint].nil?
|
72
45
|
|
73
46
|
options
|
74
|
-
rescue
|
75
|
-
puts
|
76
|
-
|
47
|
+
rescue StandardError => e
|
48
|
+
puts e.message
|
49
|
+
help_msg
|
77
50
|
exit 1
|
78
51
|
end
|
79
52
|
|
80
|
-
def
|
81
|
-
WinRM::WinRMWebService.new(
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
end
|
53
|
+
def repl(options)
|
54
|
+
client = WinRM::WinRMWebService.new(
|
55
|
+
options[:endpoint],
|
56
|
+
options[:auth_type].to_sym,
|
57
|
+
options)
|
86
58
|
|
87
|
-
|
88
|
-
bytes = WinRM::FileTransfer.upload(client, options[:local_path], options[:remote_path])
|
59
|
+
client.set_timeout(3600)
|
89
60
|
shell_id = client.open_shell()
|
90
|
-
|
91
|
-
end
|
92
|
-
|
93
|
-
def repl(client, options)
|
94
|
-
shell_id = client.open_shell()
|
95
|
-
command_id = client.run_command(shell_id, 'cmd')
|
61
|
+
command_id = client.run_command(shell_id, 'cmd', "/K prompt [#{ARGV[0]}]$P$G")
|
96
62
|
|
97
63
|
read_thread = Thread.new do
|
98
64
|
client.get_command_output(shell_id, command_id) do |stdout, stderr|
|
@@ -100,6 +66,7 @@ def repl(client, options)
|
|
100
66
|
STDERR.write stderr
|
101
67
|
end
|
102
68
|
end
|
69
|
+
read_thread.abort_on_exception = true
|
103
70
|
|
104
71
|
while buf = Readline.readline('', true)
|
105
72
|
if buf =~ /^exit/
|
@@ -111,17 +78,14 @@ def repl(client, options)
|
|
111
78
|
client.write_stdin(shell_id, command_id, "#{buf}\r\n")
|
112
79
|
end
|
113
80
|
end
|
114
|
-
end
|
115
|
-
|
116
|
-
def run(options)
|
117
|
-
client = winrm_client(options)
|
118
|
-
method(options[:mode]).call(client, options)
|
119
|
-
exit 0
|
120
81
|
rescue Interrupt
|
121
82
|
# ctrl-c
|
83
|
+
rescue WinRM::WinRMAuthorizationError
|
84
|
+
puts "Authentication failed, bad user name or password"
|
85
|
+
exit 1
|
122
86
|
rescue StandardError => e
|
123
87
|
puts e.message
|
124
88
|
exit 1
|
125
89
|
end
|
126
90
|
|
127
|
-
|
91
|
+
repl(parse_options())
|
@@ -20,7 +20,11 @@ module WinRM
|
|
20
20
|
# Authorization Error
|
21
21
|
class WinRMAuthorizationError < WinRMError; end
|
22
22
|
|
23
|
-
|
23
|
+
# Error that occurs when a file upload fails
|
24
|
+
class WinRMUploadError < WinRMError; end
|
25
|
+
|
26
|
+
# Error that occurs when a file download fails
|
27
|
+
class WinRMDownloadError < WinRMError; end
|
24
28
|
|
25
29
|
# A Fault returned in the SOAP response. The XML node is a WSManFault
|
26
30
|
class WinRMWSManFault < WinRMError
|
@@ -34,6 +38,18 @@ module WinRM
|
|
34
38
|
end
|
35
39
|
end
|
36
40
|
|
41
|
+
# A Fault returned in the SOAP response. The XML node is a MSFT_WmiError
|
42
|
+
class WinRMWMIError < WinRMError
|
43
|
+
attr_reader :error_code
|
44
|
+
attr_reader :error
|
45
|
+
|
46
|
+
def initialize(error, error_code)
|
47
|
+
@error = error
|
48
|
+
@error_code = error_code
|
49
|
+
super("[WMI ERROR CODE: #{error_code}]: #{error}")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
37
53
|
# non-200 response without a SOAP fault
|
38
54
|
class WinRMHTTPTransportError < WinRMError
|
39
55
|
attr_reader :status_code
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module WinRM
|
2
|
+
class PowershellScript
|
3
|
+
|
4
|
+
attr_reader :text
|
5
|
+
|
6
|
+
# Creates a new PowershellScript object which can be used to encode
|
7
|
+
# PS scripts for safe transport over WinRM.
|
8
|
+
# @param [String] The PS script text content
|
9
|
+
def initialize(script)
|
10
|
+
@text = script
|
11
|
+
end
|
12
|
+
|
13
|
+
# Encodes the script so that it can be passed to the PowerShell
|
14
|
+
# --EncodedCommand argument.
|
15
|
+
# @return [String] The UTF-16LE base64 encoded script
|
16
|
+
def encoded()
|
17
|
+
encoded_script = text.encode('UTF-16LE', 'UTF-8')
|
18
|
+
Base64.strict_encode64(encoded_script)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -46,7 +46,8 @@ module WinRM
|
|
46
46
|
def raise_if_error()
|
47
47
|
return if @status_code == 200
|
48
48
|
raise_if_auth_error()
|
49
|
-
|
49
|
+
raise_if_wsman_fault()
|
50
|
+
raise_if_wmi_error()
|
50
51
|
raise_transport_error()
|
51
52
|
end
|
52
53
|
|
@@ -54,11 +55,22 @@ module WinRM
|
|
54
55
|
raise WinRMAuthorizationError.new if @status_code == 401
|
55
56
|
end
|
56
57
|
|
57
|
-
def
|
58
|
+
def raise_if_wsman_fault()
|
58
59
|
soap_errors = REXML::XPath.match(response_xml, "//#{NS_SOAP_ENV}:Body/#{NS_SOAP_ENV}:Fault/*")
|
59
60
|
if !soap_errors.empty?
|
60
61
|
fault = REXML::XPath.first(soap_errors, "//#{NS_WSMAN_FAULT}:WSManFault")
|
61
|
-
raise WinRMWSManFault.new(fault.to_s, fault.attributes['Code'])
|
62
|
+
raise WinRMWSManFault.new(fault.to_s, fault.attributes['Code']) unless fault.nil?
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def raise_if_wmi_error()
|
67
|
+
soap_errors = REXML::XPath.match(response_xml, "//#{NS_SOAP_ENV}:Body/#{NS_SOAP_ENV}:Fault/*")
|
68
|
+
if !soap_errors.empty?
|
69
|
+
error = REXML::XPath.first(soap_errors, "//#{NS_WSMAN_MSFT}:MSFT_WmiError")
|
70
|
+
if !error.nil?
|
71
|
+
error_code = REXML::XPath.first(error, "//#{NS_WSMAN_MSFT}:error_Code").text
|
72
|
+
raise WinRMWMIError.new(error.to_s, error_code)
|
73
|
+
end
|
62
74
|
end
|
63
75
|
end
|
64
76
|
|
data/lib/winrm/http/transport.rb
CHANGED
@@ -22,12 +22,15 @@ module WinRM
|
|
22
22
|
# is possible to use GSSAPI with Keep-Alive.
|
23
23
|
class HttpTransport
|
24
24
|
|
25
|
+
# Set this to an unreasonable amount because WinRM has its own timeouts
|
26
|
+
DEFAULT_RECEIVE_TIMEOUT = 3600
|
27
|
+
|
25
28
|
attr_reader :endpoint
|
26
29
|
|
27
30
|
def initialize(endpoint)
|
28
31
|
@endpoint = endpoint.is_a?(String) ? URI.parse(endpoint) : endpoint
|
29
32
|
@httpcli = HTTPClient.new(:agent_name => 'Ruby WinRM Client')
|
30
|
-
@httpcli.receive_timeout =
|
33
|
+
@httpcli.receive_timeout = DEFAULT_RECEIVE_TIMEOUT
|
31
34
|
@logger = Logging.logger[self]
|
32
35
|
end
|
33
36
|
|
@@ -62,6 +65,16 @@ module WinRM
|
|
62
65
|
@httpcli.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
63
66
|
end
|
64
67
|
|
68
|
+
# HTTP Client receive timeout. How long should a remote call wait for a
|
69
|
+
# for a response from WinRM?
|
70
|
+
def receive_timeout=(sec)
|
71
|
+
@httpcli.receive_timeout = sec
|
72
|
+
end
|
73
|
+
|
74
|
+
def receive_timeout()
|
75
|
+
@httpcli.receive_timeout
|
76
|
+
end
|
77
|
+
|
65
78
|
protected
|
66
79
|
|
67
80
|
def log_soap_message(message)
|
data/lib/winrm/output.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module WinRM
|
2
|
+
# This class holds raw output as a hash, and has convenience methods to parse.
|
3
|
+
class Output < Hash
|
4
|
+
def initialize
|
5
|
+
super
|
6
|
+
self[:data] = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def output
|
10
|
+
self[:data].flat_map do | line |
|
11
|
+
[line[:stdout], line[:stderr]]
|
12
|
+
end.compact.join
|
13
|
+
end
|
14
|
+
|
15
|
+
def stdout
|
16
|
+
self[:data].map do | line |
|
17
|
+
line[:stdout]
|
18
|
+
end.compact.join
|
19
|
+
end
|
20
|
+
|
21
|
+
def stderr
|
22
|
+
self[:data].map do | line |
|
23
|
+
line[:stderr]
|
24
|
+
end.compact.join
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/winrm/winrm_service.rb
CHANGED
@@ -14,6 +14,7 @@
|
|
14
14
|
|
15
15
|
require 'nori'
|
16
16
|
require 'rexml/document'
|
17
|
+
require_relative 'helpers/powershell_script'
|
17
18
|
|
18
19
|
module WinRM
|
19
20
|
# This is the main class that does the SOAP request/response logic. There are a few helper classes, but pretty
|
@@ -41,6 +42,7 @@ module WinRM
|
|
41
42
|
case transport
|
42
43
|
when :kerberos
|
43
44
|
require 'gssapi'
|
45
|
+
require 'gssapi/extensions'
|
44
46
|
@xfer = HTTP::HttpGSSAPI.new(endpoint, opts[:realm], opts[:service], opts[:keytab], opts)
|
45
47
|
when :plaintext
|
46
48
|
@xfer = HTTP::HttpPlaintext.new(endpoint, opts[:user], opts[:pass], opts)
|
@@ -51,11 +53,20 @@ module WinRM
|
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
54
|
-
# Operation timeout
|
56
|
+
# Operation timeout.
|
57
|
+
#
|
58
|
+
# Unless specified the client receive timeout will be 10s + the operation
|
59
|
+
# timeout.
|
60
|
+
#
|
55
61
|
# @see http://msdn.microsoft.com/en-us/library/ee916629(v=PROT.13).aspx
|
56
|
-
#
|
57
|
-
|
58
|
-
|
62
|
+
#
|
63
|
+
# @param [Fixnum] The number of seconds to set the WinRM operation timeout
|
64
|
+
# @param [Fixnum] The number of seconds to set the Ruby receive timeout
|
65
|
+
# @return [String] The ISO 8601 formatted operation timeout
|
66
|
+
def set_timeout(op_timeout_sec, receive_timeout_sec=nil)
|
67
|
+
@timeout = Iso8601Duration.sec_to_dur(op_timeout_sec)
|
68
|
+
@xfer.receive_timeout = receive_timeout_sec || op_timeout_sec + 10
|
69
|
+
@timeout
|
59
70
|
end
|
60
71
|
alias :op_timeout :set_timeout
|
61
72
|
|
@@ -151,12 +162,12 @@ module WinRM
|
|
151
162
|
|
152
163
|
# Grab the command element and unescape any single quotes - issue 69
|
153
164
|
xml = builder.target!
|
154
|
-
escaped_cmd = /<#{NS_WIN_SHELL}:Command>(.+)<\/#{NS_WIN_SHELL}:Command
|
165
|
+
escaped_cmd = /<#{NS_WIN_SHELL}:Command>(.+)<\/#{NS_WIN_SHELL}:Command>/m.match(xml)[1]
|
155
166
|
xml.sub!(escaped_cmd, escaped_cmd.gsub(/'/, "'"))
|
156
167
|
|
157
168
|
resp_doc = send_message(xml)
|
158
169
|
command_id = REXML::XPath.first(resp_doc, "//#{NS_WIN_SHELL}:CommandId").text
|
159
|
-
|
170
|
+
|
160
171
|
if block_given?
|
161
172
|
begin
|
162
173
|
yield command_id
|
@@ -211,7 +222,7 @@ module WinRM
|
|
211
222
|
end
|
212
223
|
|
213
224
|
resp_doc = send_message(builder.target!)
|
214
|
-
output =
|
225
|
+
output = Output.new
|
215
226
|
REXML::XPath.match(resp_doc, "//#{NS_WIN_SHELL}:Stream").each do |n|
|
216
227
|
next if n.text.nil? || n.text.empty?
|
217
228
|
stream = { n.attributes['Name'].to_sym => Base64.decode64(n.text) }
|
@@ -299,10 +310,9 @@ module WinRM
|
|
299
310
|
# @return [Hash] :stdout and :stderr
|
300
311
|
def run_powershell_script(script_file, &block)
|
301
312
|
# if an IO object is passed read it..otherwise assume the contents of the file were passed
|
302
|
-
|
303
|
-
script =
|
304
|
-
|
305
|
-
run_cmd("powershell -encodedCommand #{script}", &block)
|
313
|
+
script_text = script_file.kind_of?(IO) ? script_file.read : script_file
|
314
|
+
script = WinRM::PowershellScript.new(script_text)
|
315
|
+
run_cmd("powershell -encodedCommand #{script.encoded()}", &block)
|
306
316
|
end
|
307
317
|
alias :powershell :run_powershell_script
|
308
318
|
|
data/lib/winrm.rb
CHANGED
data/spec/cmd_spec.rb
CHANGED
@@ -24,6 +24,38 @@ describe 'winrm client cmd', :integration => true do
|
|
24
24
|
it { should have_no_stderr }
|
25
25
|
end
|
26
26
|
|
27
|
+
describe 'capturing output from stdout and stderr' do
|
28
|
+
subject(:output) do
|
29
|
+
# Note: Multiple lines doesn't work:
|
30
|
+
# script = <<-eos
|
31
|
+
# echo Hello
|
32
|
+
# echo , world! 1>&2
|
33
|
+
# eos
|
34
|
+
|
35
|
+
script = "echo Hello & echo , world! 1>&2"
|
36
|
+
|
37
|
+
@captured_stdout, @captured_stderr = "", ""
|
38
|
+
@winrm.cmd(script) do |stdout, stderr|
|
39
|
+
@captured_stdout << stdout if stdout
|
40
|
+
@captured_stderr << stderr if stderr
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should have stdout' do
|
45
|
+
expect(output.stdout).to eq("Hello \r\n")
|
46
|
+
expect(output.stdout).to eq(@captured_stdout)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should have stderr' do
|
50
|
+
expect(output.stderr).to eq(", world! \r\n")
|
51
|
+
expect(output.stderr).to eq(@captured_stderr)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should have output' do
|
55
|
+
expect(output.output).to eq("Hello \r\n, world! \r\n")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
27
59
|
describe 'ipconfig with /all argument' do
|
28
60
|
subject(:output) { @winrm.cmd('ipconfig', %w{/all}) }
|
29
61
|
it { should have_exit_code 0 }
|
data/spec/config-example.yml
CHANGED
data/spec/exception_spec.rb
CHANGED
@@ -32,4 +32,21 @@ describe "Exceptions", :unit => true do
|
|
32
32
|
expect(error).to be_kind_of(WinRM::WinRMError)
|
33
33
|
end
|
34
34
|
end
|
35
|
+
|
36
|
+
describe WinRM::WinRMWMIError do
|
37
|
+
|
38
|
+
let (:error) { WinRM::WinRMWMIError.new("message text", 77777) }
|
39
|
+
|
40
|
+
it 'exposes the error text as an attribute' do
|
41
|
+
expect(error.error).to eq('message text')
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'exposes the error code as an attribute' do
|
45
|
+
expect(error.error_code).to eq 77777
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'is a winrm error' do
|
49
|
+
expect(error).to be_kind_of(WinRM::WinRMError)
|
50
|
+
end
|
51
|
+
end
|
35
52
|
end
|
data/spec/output_spec.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
describe WinRM::Output, :unit => true do
|
2
|
+
subject { WinRM::Output.new() }
|
3
|
+
|
4
|
+
context "when there is no output" do
|
5
|
+
describe "#stdout" do
|
6
|
+
it "is empty" do
|
7
|
+
expect(subject.stdout).to be_empty
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#stderr" do
|
12
|
+
it "is empty" do
|
13
|
+
expect(subject.stderr).to be_empty
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#output" do
|
18
|
+
it "is empty" do
|
19
|
+
expect(subject.output).to be_empty
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "when there is only one line" do
|
25
|
+
describe "#stdout" do
|
26
|
+
it "is equal to that line" do
|
27
|
+
subject[:data] << { :stdout => "foo" }
|
28
|
+
expect(subject.stdout).to eq("foo")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#stderr" do
|
33
|
+
it "is equal to that line" do
|
34
|
+
subject[:data] << { :stderr => "foo" }
|
35
|
+
expect(subject.stderr).to eq("foo")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#output" do
|
40
|
+
it "is equal to stdout" do
|
41
|
+
subject[:data] << { :stdout => "foo" }
|
42
|
+
expect(subject.output).to eq("foo")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "is equal to stderr" do
|
46
|
+
subject[:data] << { :stderr => "foo" }
|
47
|
+
expect(subject.output).to eq("foo")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "when there is one line of each type" do
|
53
|
+
before(:each) do
|
54
|
+
subject[:data] << { :stdout => "foo" }
|
55
|
+
subject[:data] << { :stderr => "bar" }
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#stdout" do
|
59
|
+
it "is equal to that line" do
|
60
|
+
expect(subject.stdout).to eq("foo")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#stderr" do
|
65
|
+
it "is equal to that line" do
|
66
|
+
expect(subject.stderr).to eq("bar")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#output" do
|
71
|
+
it "is equal to stdout + stderr" do
|
72
|
+
expect(subject.output).to eq("foobar")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "when there are multiple lines" do
|
78
|
+
before(:each) do
|
79
|
+
subject[:data] << { :stdout => "I can have a newline\nanywhere, " }
|
80
|
+
subject[:data] << { :stderr => "I can also have stderr" }
|
81
|
+
subject[:data] << { :stdout => "or stdout", :stderr => " and stderr" }
|
82
|
+
subject[:data] << { }
|
83
|
+
subject[:data] << { :stdout => " or nothing! (above)" }
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#stdout" do
|
87
|
+
it "is equal to that line" do
|
88
|
+
expect(subject.stdout).to eq("I can have a newline\nanywhere, or stdout or nothing! (above)")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "#stderr" do
|
93
|
+
it "is equal to that line" do
|
94
|
+
expect(subject.stderr).to eq("I can also have stderr and stderr")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "#output" do
|
99
|
+
it "is equal to stdout + stderr" do
|
100
|
+
expect(subject.output).to eq("I can have a newline\nanywhere, I can also have stderror stdout and stderr or nothing! (above)")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
pending "parse CLIXML errors and convert to Strings and/or Exceptions"
|
106
|
+
end
|
data/spec/powershell_spec.rb
CHANGED
@@ -55,6 +55,40 @@ describe 'winrm client powershell', :integration => true do
|
|
55
55
|
end
|
56
56
|
it { should match(/Windows IP Configuration/) }
|
57
57
|
end
|
58
|
+
|
59
|
+
describe 'capturing output from Write-Host and Write-Error' do
|
60
|
+
subject(:output) do
|
61
|
+
script = <<-eos
|
62
|
+
Write-Host 'Hello'
|
63
|
+
$host.ui.WriteErrorLine(', world!')
|
64
|
+
eos
|
65
|
+
|
66
|
+
@captured_stdout, @captured_stderr = "", ""
|
67
|
+
@winrm.powershell(script) do |stdout, stderr|
|
68
|
+
@captured_stdout << stdout if stdout
|
69
|
+
@captured_stderr << stderr if stderr
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should have stdout' do
|
74
|
+
expect(output.stdout).to eq("Hello\n")
|
75
|
+
expect(output.stdout).to eq(@captured_stdout)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should have stderr' do
|
79
|
+
# TODO: Option to parse CLIXML
|
80
|
+
# expect(output.output).to eq("Hello\n, world!")
|
81
|
+
# expect(output.stderr).to eq(", world!")
|
82
|
+
expect(output.stderr).to eq("#< CLIXML\r\n<Objs Version=\"1.1.0.1\" xmlns=\"http://schemas.microsoft.com/powershell/2004/04\"><S S=\"Error\">, world!_x000D__x000A_</S></Objs>")
|
83
|
+
expect(output.stderr).to eq(@captured_stderr)
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'should have output' do
|
87
|
+
# TODO: Option to parse CLIXML
|
88
|
+
# expect(output.output).to eq("Hello\n, world!")
|
89
|
+
expect(output.output).to eq("Hello\n#< CLIXML\r\n<Objs Version=\"1.1.0.1\" xmlns=\"http://schemas.microsoft.com/powershell/2004/04\"><S S=\"Error\">, world!_x000D__x000A_</S></Objs>")
|
90
|
+
end
|
91
|
+
end
|
58
92
|
end
|
59
93
|
|
60
94
|
|