winrm 1.7.0 → 1.7.1

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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +10 -10
  3. data/.rspec +3 -3
  4. data/.rubocop.yml +12 -12
  5. data/.travis.yml +12 -12
  6. data/Gemfile +9 -9
  7. data/LICENSE +202 -202
  8. data/README.md +194 -194
  9. data/Rakefile +36 -36
  10. data/Vagrantfile +9 -9
  11. data/appveyor.yml +42 -42
  12. data/bin/rwinrm +97 -97
  13. data/changelog.md +74 -71
  14. data/lib/winrm.rb +42 -42
  15. data/lib/winrm/command_executor.rb +224 -224
  16. data/lib/winrm/exceptions/exceptions.rb +57 -57
  17. data/lib/winrm/helpers/iso8601_duration.rb +58 -58
  18. data/lib/winrm/helpers/powershell_script.rb +42 -42
  19. data/lib/winrm/http/response_handler.rb +82 -82
  20. data/lib/winrm/http/transport.rb +421 -421
  21. data/lib/winrm/output.rb +43 -43
  22. data/lib/winrm/soap_provider.rb +39 -39
  23. data/lib/winrm/version.rb +7 -7
  24. data/lib/winrm/winrm_service.rb +556 -556
  25. data/preamble +17 -17
  26. data/spec/auth_timeout_spec.rb +16 -16
  27. data/spec/cmd_spec.rb +102 -102
  28. data/spec/command_executor_spec.rb +429 -429
  29. data/spec/config-example.yml +19 -19
  30. data/spec/exception_spec.rb +50 -50
  31. data/spec/issue_184_spec.rb +67 -67
  32. data/spec/issue_59_spec.rb +23 -23
  33. data/spec/matchers.rb +74 -74
  34. data/spec/output_spec.rb +110 -110
  35. data/spec/powershell_spec.rb +97 -97
  36. data/spec/response_handler_spec.rb +59 -59
  37. data/spec/spec_helper.rb +73 -73
  38. data/spec/stubs/responses/get_command_output_response.xml.erb +13 -13
  39. data/spec/stubs/responses/open_shell_v1.xml +19 -19
  40. data/spec/stubs/responses/open_shell_v2.xml +20 -20
  41. data/spec/stubs/responses/soap_fault_v1.xml +36 -36
  42. data/spec/stubs/responses/soap_fault_v2.xml +42 -42
  43. data/spec/stubs/responses/wmi_error_v2.xml +41 -41
  44. data/spec/transport_spec.rb +124 -124
  45. data/spec/winrm_options_spec.rb +76 -76
  46. data/spec/winrm_primitives_spec.rb +51 -51
  47. data/spec/wql_spec.rb +14 -14
  48. data/winrm.gemspec +40 -40
  49. metadata +2 -2
@@ -1,224 +1,224 @@
1
- # -*- encoding: utf-8 -*-
2
- #
3
- # Copyright 2015 Shawn Neal <sneal@sneal.net>
4
- # Copyright 2015 Matt Wrock <matt@mattwrock.com>
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
- module WinRM
19
- # Object which can execute multiple commands and Powershell scripts in
20
- # one shared remote shell session. The maximum number of commands per
21
- # shell is determined by interrogating the remote host when the session
22
- # is opened and the remote shell is automatically recycled before the
23
- # threshold is reached.
24
- #
25
- # @author Shawn Neal <sneal@sneal.net>
26
- # @author Matt Wrock <matt@mattwrock.com>
27
- # @author Fletcher Nichol <fnichol@nichol.ca>
28
- class CommandExecutor
29
- # Closes an open remote shell session left open
30
- # after a command executor is garbage collecyted.
31
- #
32
- # @param shell_id [String] the remote shell identifier
33
- # @param service [WinRM::WinRMWebService] a winrm web service object
34
- def self.finalize(shell_id, service)
35
- proc { service.close_shell(shell_id) }
36
- end
37
-
38
- # @return [WinRM::WinRMWebService] a WinRM web service object
39
- attr_reader :service
40
-
41
- # @return [String,nil] the identifier for the current open remote
42
- # shell session, or `nil` if the session is not open
43
- attr_reader :shell
44
-
45
- # Creates a CommandExecutor given a `WinRM::WinRMWebService` object.
46
- #
47
- # @param service [WinRM::WinRMWebService] a winrm web service object
48
- # responds to `#debug` and `#info` (default: `nil`)
49
- def initialize(service)
50
- @service = service
51
- @command_count = 0
52
- end
53
-
54
- # Closes the open remote shell session. This method can be called
55
- # multiple times, even if there is no open session.
56
- def close
57
- return if shell.nil?
58
-
59
- service.close_shell(shell)
60
- remove_finalizer
61
- @shell = nil
62
- end
63
-
64
- # Opens a remote shell session for reuse. The maxiumum
65
- # command-per-shell threshold is also determined the first time this
66
- # method is invoked and cached for later invocations.
67
- #
68
- # @return [String] the remote shell session indentifier
69
- def open
70
- close
71
- retryable(service.retry_limit, service.retry_delay) do
72
- @shell = service.open_shell(codepage: code_page)
73
- end
74
- add_finalizer(shell)
75
- @command_count = 0
76
- shell
77
- end
78
-
79
- # Runs a CMD command.
80
- #
81
- # @param command [String] the command to run on the remote system
82
- # @param arguments [Array<String>] arguments to the command
83
- # @yield [stdout, stderr] yields more live access the standard
84
- # output and standard error streams as they are returns, if
85
- # streaming behavior is desired
86
- # @return [WinRM::Output] output object with stdout, stderr, and
87
- # exit code
88
- def run_cmd(command, arguments = [], &block)
89
- reset if command_count > max_commands
90
- ensure_open_shell!
91
-
92
- @command_count += 1
93
- result = nil
94
- service.run_command(shell, command, arguments) do |command_id|
95
- result = service.get_command_output(shell, command_id, &block)
96
- end
97
- result
98
- end
99
-
100
- # Run a Powershell script that resides on the local box.
101
- #
102
- # @param script_file [IO,String] an IO reference for reading the
103
- # Powershell script or the actual file contents
104
- # @yield [stdout, stderr] yields more live access the standard
105
- # output and standard error streams as they are returns, if
106
- # streaming behavior is desired
107
- # @return [WinRM::Output] output object with stdout, stderr, and
108
- # exit code
109
- def run_powershell_script(script_file, &block)
110
- # this code looks overly compact in an attempt to limit local
111
- # variable assignments that may contain large strings and
112
- # consequently bloat the Ruby VM
113
- run_cmd(
114
- 'powershell',
115
- [
116
- '-encodedCommand',
117
- ::WinRM::PowershellScript.new(
118
- script_file.is_a?(IO) ? script_file.read : script_file
119
- ).encoded
120
- ],
121
- &block
122
- )
123
- end
124
-
125
- # Code page appropriate to os version. utf-8 (65001) is buggy pre win7/2k8r2
126
- # So send MS-DOS (437) for earlier versions
127
- #
128
- # @return [Integer] code page in use
129
- def code_page
130
- @code_page ||= os_version < '6.1' ? 437 : 65_001
131
- end
132
-
133
- # @return [Integer] the safe maximum number of commands that can
134
- # be executed in one remote shell session
135
- def max_commands
136
- @max_commands ||= (os_version < '6.2' ? 15 : 1500) - 2
137
- end
138
-
139
- private
140
-
141
- # @return [Integer] the number of executed commands on the remote
142
- # shell session
143
- # @api private
144
- attr_accessor :command_count
145
-
146
- # Creates a finalizer for this connection which will close the open
147
- # remote shell session when the object is garabage collected or on
148
- # Ruby VM shutdown.
149
- #
150
- # @param shell_id [String] the remote shell identifier
151
- # @api private
152
- def add_finalizer(shell_id)
153
- ObjectSpace.define_finalizer(self, self.class.finalize(shell_id, service))
154
- end
155
-
156
- # Ensures that there is an open remote shell session.
157
- #
158
- # @raise [WinRM::WinRMError] if there is no open shell
159
- # @api private
160
- def ensure_open_shell!
161
- fail ::WinRM::WinRMError, "#{self.class}#open must be called " \
162
- 'before any run methods are invoked' if shell.nil?
163
- end
164
-
165
- # Fetches the OS build bersion of the remote endpoint
166
- #
167
- # @api private
168
- def os_version
169
- @os_version ||= begin
170
- version = nil
171
- wql = service.run_wql('select version from Win32_OperatingSystem')
172
- if wql[:xml_fragment]
173
- version = wql[:xml_fragment].first[:version] if wql[:xml_fragment].first[:version]
174
- end
175
- fail ::WinRM::WinRMError, 'Unable to determine endpoint os version' if version.nil?
176
- version
177
- end
178
- end
179
-
180
- # Removes any finalizers for this connection.
181
- #
182
- # @api private
183
- def remove_finalizer
184
- ObjectSpace.undefine_finalizer(self)
185
- end
186
-
187
- # Closes the remote shell session and opens a new one.
188
- #
189
- # @api private
190
- def reset
191
- service.logger.debug("Resetting WinRM shell (Max command limit is #{max_commands})")
192
- open
193
- end
194
-
195
- # Yields to a block and reties the block if certain rescuable
196
- # exceptions are raised.
197
- #
198
- # @param retries [Integer] the number of times to retry before failing
199
- # @option delay [Float] the number of seconds to wait until
200
- # attempting a retry
201
- # @api private
202
- def retryable(retries, delay)
203
- yield
204
- rescue *RESCUE_EXCEPTIONS_ON_ESTABLISH.call => e
205
- if (retries -= 1) > 0
206
- service.logger.info("[WinRM] connection failed. retrying in #{delay} seconds: #{e.inspect}")
207
- sleep(delay)
208
- retry
209
- else
210
- service.logger.warn("[WinRM] connection failed, terminating (#{e.inspect})")
211
- raise
212
- end
213
- end
214
-
215
- RESCUE_EXCEPTIONS_ON_ESTABLISH = lambda do
216
- [
217
- Errno::EACCES, Errno::EADDRINUSE, Errno::ECONNREFUSED, Errno::ETIMEDOUT,
218
- Errno::ECONNRESET, Errno::ENETUNREACH, Errno::EHOSTUNREACH,
219
- ::WinRM::WinRMHTTPTransportError, ::WinRM::WinRMAuthorizationError,
220
- HTTPClient::KeepAliveDisconnected, HTTPClient::ConnectTimeoutError
221
- ].freeze
222
- end
223
- end
224
- end
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Copyright 2015 Shawn Neal <sneal@sneal.net>
4
+ # Copyright 2015 Matt Wrock <matt@mattwrock.com>
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
+ module WinRM
19
+ # Object which can execute multiple commands and Powershell scripts in
20
+ # one shared remote shell session. The maximum number of commands per
21
+ # shell is determined by interrogating the remote host when the session
22
+ # is opened and the remote shell is automatically recycled before the
23
+ # threshold is reached.
24
+ #
25
+ # @author Shawn Neal <sneal@sneal.net>
26
+ # @author Matt Wrock <matt@mattwrock.com>
27
+ # @author Fletcher Nichol <fnichol@nichol.ca>
28
+ class CommandExecutor
29
+ # Closes an open remote shell session left open
30
+ # after a command executor is garbage collecyted.
31
+ #
32
+ # @param shell_id [String] the remote shell identifier
33
+ # @param service [WinRM::WinRMWebService] a winrm web service object
34
+ def self.finalize(shell_id, service)
35
+ proc { service.close_shell(shell_id) }
36
+ end
37
+
38
+ # @return [WinRM::WinRMWebService] a WinRM web service object
39
+ attr_reader :service
40
+
41
+ # @return [String,nil] the identifier for the current open remote
42
+ # shell session, or `nil` if the session is not open
43
+ attr_reader :shell
44
+
45
+ # Creates a CommandExecutor given a `WinRM::WinRMWebService` object.
46
+ #
47
+ # @param service [WinRM::WinRMWebService] a winrm web service object
48
+ # responds to `#debug` and `#info` (default: `nil`)
49
+ def initialize(service)
50
+ @service = service
51
+ @command_count = 0
52
+ end
53
+
54
+ # Closes the open remote shell session. This method can be called
55
+ # multiple times, even if there is no open session.
56
+ def close
57
+ return if shell.nil?
58
+
59
+ service.close_shell(shell)
60
+ remove_finalizer
61
+ @shell = nil
62
+ end
63
+
64
+ # Opens a remote shell session for reuse. The maxiumum
65
+ # command-per-shell threshold is also determined the first time this
66
+ # method is invoked and cached for later invocations.
67
+ #
68
+ # @return [String] the remote shell session indentifier
69
+ def open
70
+ close
71
+ retryable(service.retry_limit, service.retry_delay) do
72
+ @shell = service.open_shell(codepage: code_page)
73
+ end
74
+ add_finalizer(shell)
75
+ @command_count = 0
76
+ shell
77
+ end
78
+
79
+ # Runs a CMD command.
80
+ #
81
+ # @param command [String] the command to run on the remote system
82
+ # @param arguments [Array<String>] arguments to the command
83
+ # @yield [stdout, stderr] yields more live access the standard
84
+ # output and standard error streams as they are returns, if
85
+ # streaming behavior is desired
86
+ # @return [WinRM::Output] output object with stdout, stderr, and
87
+ # exit code
88
+ def run_cmd(command, arguments = [], &block)
89
+ reset if command_count > max_commands
90
+ ensure_open_shell!
91
+
92
+ @command_count += 1
93
+ result = nil
94
+ service.run_command(shell, command, arguments) do |command_id|
95
+ result = service.get_command_output(shell, command_id, &block)
96
+ end
97
+ result
98
+ end
99
+
100
+ # Run a Powershell script that resides on the local box.
101
+ #
102
+ # @param script_file [IO,String] an IO reference for reading the
103
+ # Powershell script or the actual file contents
104
+ # @yield [stdout, stderr] yields more live access the standard
105
+ # output and standard error streams as they are returns, if
106
+ # streaming behavior is desired
107
+ # @return [WinRM::Output] output object with stdout, stderr, and
108
+ # exit code
109
+ def run_powershell_script(script_file, &block)
110
+ # this code looks overly compact in an attempt to limit local
111
+ # variable assignments that may contain large strings and
112
+ # consequently bloat the Ruby VM
113
+ run_cmd(
114
+ 'powershell',
115
+ [
116
+ '-encodedCommand',
117
+ ::WinRM::PowershellScript.new(
118
+ script_file.is_a?(IO) ? script_file.read : script_file
119
+ ).encoded
120
+ ],
121
+ &block
122
+ )
123
+ end
124
+
125
+ # Code page appropriate to os version. utf-8 (65001) is buggy pre win7/2k8r2
126
+ # So send MS-DOS (437) for earlier versions
127
+ #
128
+ # @return [Integer] code page in use
129
+ def code_page
130
+ @code_page ||= os_version < Gem::Version.new('6.1') ? 437 : 65_001
131
+ end
132
+
133
+ # @return [Integer] the safe maximum number of commands that can
134
+ # be executed in one remote shell session
135
+ def max_commands
136
+ @max_commands ||= (os_version < Gem::Version.new('6.2') ? 15 : 1500) - 2
137
+ end
138
+
139
+ private
140
+
141
+ # @return [Integer] the number of executed commands on the remote
142
+ # shell session
143
+ # @api private
144
+ attr_accessor :command_count
145
+
146
+ # Creates a finalizer for this connection which will close the open
147
+ # remote shell session when the object is garabage collected or on
148
+ # Ruby VM shutdown.
149
+ #
150
+ # @param shell_id [String] the remote shell identifier
151
+ # @api private
152
+ def add_finalizer(shell_id)
153
+ ObjectSpace.define_finalizer(self, self.class.finalize(shell_id, service))
154
+ end
155
+
156
+ # Ensures that there is an open remote shell session.
157
+ #
158
+ # @raise [WinRM::WinRMError] if there is no open shell
159
+ # @api private
160
+ def ensure_open_shell!
161
+ fail ::WinRM::WinRMError, "#{self.class}#open must be called " \
162
+ 'before any run methods are invoked' if shell.nil?
163
+ end
164
+
165
+ # Fetches the OS build bersion of the remote endpoint
166
+ #
167
+ # @api private
168
+ def os_version
169
+ @os_version ||= begin
170
+ version = nil
171
+ wql = service.run_wql('select version from Win32_OperatingSystem')
172
+ if wql[:xml_fragment]
173
+ version = wql[:xml_fragment].first[:version] if wql[:xml_fragment].first[:version]
174
+ end
175
+ fail ::WinRM::WinRMError, 'Unable to determine endpoint os version' if version.nil?
176
+ Gem::Version.new(version)
177
+ end
178
+ end
179
+
180
+ # Removes any finalizers for this connection.
181
+ #
182
+ # @api private
183
+ def remove_finalizer
184
+ ObjectSpace.undefine_finalizer(self)
185
+ end
186
+
187
+ # Closes the remote shell session and opens a new one.
188
+ #
189
+ # @api private
190
+ def reset
191
+ service.logger.debug("Resetting WinRM shell (Max command limit is #{max_commands})")
192
+ open
193
+ end
194
+
195
+ # Yields to a block and reties the block if certain rescuable
196
+ # exceptions are raised.
197
+ #
198
+ # @param retries [Integer] the number of times to retry before failing
199
+ # @option delay [Float] the number of seconds to wait until
200
+ # attempting a retry
201
+ # @api private
202
+ def retryable(retries, delay)
203
+ yield
204
+ rescue *RESCUE_EXCEPTIONS_ON_ESTABLISH.call => e
205
+ if (retries -= 1) > 0
206
+ service.logger.info("[WinRM] connection failed. retrying in #{delay} seconds: #{e.inspect}")
207
+ sleep(delay)
208
+ retry
209
+ else
210
+ service.logger.warn("[WinRM] connection failed, terminating (#{e.inspect})")
211
+ raise
212
+ end
213
+ end
214
+
215
+ RESCUE_EXCEPTIONS_ON_ESTABLISH = lambda do
216
+ [
217
+ Errno::EACCES, Errno::EADDRINUSE, Errno::ECONNREFUSED, Errno::ETIMEDOUT,
218
+ Errno::ECONNRESET, Errno::ENETUNREACH, Errno::EHOSTUNREACH,
219
+ ::WinRM::WinRMHTTPTransportError, ::WinRM::WinRMAuthorizationError,
220
+ HTTPClient::KeepAliveDisconnected, HTTPClient::ConnectTimeoutError
221
+ ].freeze
222
+ end
223
+ end
224
+ end
@@ -1,57 +1,57 @@
1
- # encoding: UTF-8
2
- #
3
- # Copyright 2010 Dan Wanek <dan.wanek@gmail.com>
4
- #
5
- # Licensed under the Apache License, Version 2.0 (the "License");
6
- # you may not use this file except in compliance with the License.
7
- # You may obtain a copy of the License at
8
- #
9
- # http://www.apache.org/licenses/LICENSE-2.0
10
- #
11
- # Unless required by applicable law or agreed to in writing, software
12
- # distributed under the License is distributed on an "AS IS" BASIS,
13
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- # See the License for the specific language governing permissions and
15
- # limitations under the License.
16
-
17
- module WinRM
18
- # WinRM base class for errors
19
- class WinRMError < StandardError; end
20
-
21
- # Authorization Error
22
- class WinRMAuthorizationError < WinRMError; end
23
-
24
- # A Fault returned in the SOAP response. The XML node is a WSManFault
25
- class WinRMWSManFault < WinRMError
26
- attr_reader :fault_code
27
- attr_reader :fault_description
28
-
29
- def initialize(fault_description, fault_code)
30
- @fault_description = fault_description
31
- @fault_code = fault_code
32
- super("[WSMAN ERROR CODE: #{fault_code}]: #{fault_description}")
33
- end
34
- end
35
-
36
- # A Fault returned in the SOAP response. The XML node is a MSFT_WmiError
37
- class WinRMWMIError < WinRMError
38
- attr_reader :error_code
39
- attr_reader :error
40
-
41
- def initialize(error, error_code)
42
- @error = error
43
- @error_code = error_code
44
- super("[WMI ERROR CODE: #{error_code}]: #{error}")
45
- end
46
- end
47
-
48
- # non-200 response without a SOAP fault
49
- class WinRMHTTPTransportError < WinRMError
50
- attr_reader :status_code
51
-
52
- def initialize(msg, status_code)
53
- @status_code = status_code
54
- super(msg + " (#{status_code}).")
55
- end
56
- end
57
- end
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright 2010 Dan Wanek <dan.wanek@gmail.com>
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module WinRM
18
+ # WinRM base class for errors
19
+ class WinRMError < StandardError; end
20
+
21
+ # Authorization Error
22
+ class WinRMAuthorizationError < WinRMError; end
23
+
24
+ # A Fault returned in the SOAP response. The XML node is a WSManFault
25
+ class WinRMWSManFault < WinRMError
26
+ attr_reader :fault_code
27
+ attr_reader :fault_description
28
+
29
+ def initialize(fault_description, fault_code)
30
+ @fault_description = fault_description
31
+ @fault_code = fault_code
32
+ super("[WSMAN ERROR CODE: #{fault_code}]: #{fault_description}")
33
+ end
34
+ end
35
+
36
+ # A Fault returned in the SOAP response. The XML node is a MSFT_WmiError
37
+ class WinRMWMIError < WinRMError
38
+ attr_reader :error_code
39
+ attr_reader :error
40
+
41
+ def initialize(error, error_code)
42
+ @error = error
43
+ @error_code = error_code
44
+ super("[WMI ERROR CODE: #{error_code}]: #{error}")
45
+ end
46
+ end
47
+
48
+ # non-200 response without a SOAP fault
49
+ class WinRMHTTPTransportError < WinRMError
50
+ attr_reader :status_code
51
+
52
+ def initialize(msg, status_code)
53
+ @status_code = status_code
54
+ super(msg + " (#{status_code}).")
55
+ end
56
+ end
57
+ end