test-kitchen 1.3.1 → 1.4.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +2 -0
  3. data/.gitignore +4 -0
  4. data/CHANGELOG.md +45 -0
  5. data/Rakefile +15 -0
  6. data/features/kitchen_action_commands.feature +12 -9
  7. data/features/kitchen_defaults.feature +38 -0
  8. data/features/kitchen_init_command.feature +0 -1
  9. data/features/kitchen_list_command.feature +2 -2
  10. data/features/kitchen_login_command.feature +7 -1
  11. data/features/kitchen_test_command.feature +4 -4
  12. data/lib/kitchen.rb +40 -11
  13. data/lib/kitchen/cli.rb +38 -22
  14. data/lib/kitchen/command/list.rb +5 -2
  15. data/lib/kitchen/config.rb +45 -18
  16. data/lib/kitchen/configurable.rb +137 -1
  17. data/lib/kitchen/data_munger.rb +248 -17
  18. data/lib/kitchen/driver.rb +1 -1
  19. data/lib/kitchen/driver/base.rb +1 -83
  20. data/lib/kitchen/driver/dummy.rb +0 -5
  21. data/lib/kitchen/driver/ssh_base.rb +177 -22
  22. data/lib/kitchen/instance.rb +140 -20
  23. data/lib/kitchen/logger.rb +43 -8
  24. data/lib/kitchen/login_command.rb +14 -5
  25. data/lib/kitchen/platform.rb +19 -0
  26. data/lib/kitchen/provisioner.rb +5 -3
  27. data/lib/kitchen/provisioner/base.rb +46 -48
  28. data/lib/kitchen/provisioner/chef/common_sandbox.rb +322 -0
  29. data/lib/kitchen/provisioner/chef_base.rb +179 -286
  30. data/lib/kitchen/provisioner/chef_solo.rb +11 -5
  31. data/lib/kitchen/provisioner/chef_zero.rb +108 -94
  32. data/lib/kitchen/provisioner/dummy.rb +47 -0
  33. data/lib/kitchen/provisioner/shell.rb +45 -12
  34. data/lib/kitchen/rake_tasks.rb +1 -1
  35. data/lib/kitchen/ssh.rb +1 -1
  36. data/lib/kitchen/thor_tasks.rb +1 -1
  37. data/lib/kitchen/transport.rb +54 -0
  38. data/lib/kitchen/transport/base.rb +146 -0
  39. data/lib/kitchen/transport/dummy.rb +75 -0
  40. data/lib/kitchen/transport/ssh.rb +325 -0
  41. data/lib/kitchen/transport/winrm.rb +508 -0
  42. data/lib/kitchen/transport/winrm/command_executor.rb +188 -0
  43. data/lib/kitchen/transport/winrm/file_transporter.rb +454 -0
  44. data/lib/kitchen/transport/winrm/logging.rb +50 -0
  45. data/lib/kitchen/transport/winrm/template.rb +74 -0
  46. data/lib/kitchen/transport/winrm/tmp_zip.rb +187 -0
  47. data/lib/kitchen/verifier.rb +55 -0
  48. data/lib/kitchen/verifier/base.rb +191 -0
  49. data/lib/kitchen/verifier/busser.rb +266 -0
  50. data/lib/kitchen/verifier/dummy.rb +75 -0
  51. data/lib/kitchen/version.rb +1 -1
  52. data/spec/kitchen/cli_spec.rb +56 -0
  53. data/spec/kitchen/config_spec.rb +61 -20
  54. data/spec/kitchen/configurable_spec.rb +327 -1
  55. data/spec/kitchen/data_munger_spec.rb +777 -14
  56. data/spec/kitchen/driver/base_spec.rb +7 -38
  57. data/spec/kitchen/driver/dummy_spec.rb +0 -29
  58. data/spec/kitchen/driver/ssh_base_spec.rb +580 -236
  59. data/spec/kitchen/driver_spec.rb +1 -0
  60. data/spec/kitchen/instance_spec.rb +383 -83
  61. data/spec/kitchen/login_command_spec.rb +29 -10
  62. data/spec/kitchen/platform_spec.rb +58 -2
  63. data/spec/kitchen/provisioner/base_spec.rb +170 -18
  64. data/spec/kitchen/provisioner/chef_base_spec.rb +454 -104
  65. data/spec/kitchen/provisioner/chef_solo_spec.rb +307 -104
  66. data/spec/kitchen/provisioner/chef_zero_spec.rb +561 -230
  67. data/spec/kitchen/provisioner/dummy_spec.rb +91 -0
  68. data/spec/kitchen/provisioner/shell_spec.rb +158 -56
  69. data/spec/kitchen/provisioner_spec.rb +37 -0
  70. data/spec/kitchen/ssh_spec.rb +19 -19
  71. data/spec/kitchen/transport/base_spec.rb +89 -0
  72. data/spec/kitchen/transport/ssh_spec.rb +1147 -0
  73. data/spec/kitchen/transport/winrm/command_executor_spec.rb +400 -0
  74. data/spec/kitchen/transport/winrm/file_transporter_spec.rb +876 -0
  75. data/spec/kitchen/transport/winrm/logging_spec.rb +92 -0
  76. data/spec/kitchen/transport/winrm/template_spec.rb +51 -0
  77. data/spec/kitchen/transport/winrm/tmp_zip_spec.rb +132 -0
  78. data/spec/kitchen/transport/winrm_spec.rb +1069 -0
  79. data/spec/kitchen/transport_spec.rb +112 -0
  80. data/spec/kitchen/verifier/base_spec.rb +310 -0
  81. data/spec/kitchen/verifier/busser_spec.rb +540 -0
  82. data/spec/kitchen/verifier/dummy_spec.rb +91 -0
  83. data/spec/kitchen/verifier_spec.rb +120 -0
  84. data/spec/kitchen_spec.rb +7 -0
  85. data/spec/spec_helper.rb +8 -0
  86. data/spec/support/powershell_max_size_spec.rb +40 -0
  87. data/support/busser_install_command.ps1 +14 -0
  88. data/support/busser_install_command.sh +15 -0
  89. data/support/check_files.ps1.erb +48 -0
  90. data/support/chef_base_init_command.ps1 +18 -0
  91. data/support/chef_base_init_command.sh +2 -0
  92. data/support/chef_base_install_command.ps1 +76 -0
  93. data/support/chef_base_install_command.sh +137 -0
  94. data/support/chef_zero_prepare_command_legacy.ps1 +9 -0
  95. data/support/chef_zero_prepare_command_legacy.sh +10 -0
  96. data/support/decode_files.ps1.erb +61 -0
  97. data/test-kitchen.gemspec +2 -0
  98. metadata +97 -8
  99. data/lib/kitchen/busser.rb +0 -316
  100. data/spec/kitchen/busser_spec.rb +0 -490
  101. data/support/chef_helpers.sh +0 -16
@@ -0,0 +1,508 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Salim Afiune (<salim@afiunemaya.com.mx>)
4
+ # Author:: Matt Wrock (<matt@mattwrock.com>)
5
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
6
+ #
7
+ # Copyright (C) 2014, Salim Afiune
8
+ #
9
+ # Licensed under the Apache License, Version 2.0 (the "License");
10
+ # you may not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+ #
15
+ # Unless required by applicable law or agreed to in writing, software
16
+ # distributed under the License is distributed on an "AS IS" BASIS,
17
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ # See the License for the specific language governing permissions and
19
+ # limitations under the License.
20
+
21
+ def silence_warnings
22
+ old_verbose, $VERBOSE = $VERBOSE, nil
23
+ yield
24
+ ensure
25
+ $VERBOSE = old_verbose
26
+ end
27
+
28
+ silence_warnings { require "winrm" }
29
+
30
+ require "rbconfig"
31
+ require "uri"
32
+
33
+ require "kitchen"
34
+ require "kitchen/transport/winrm/command_executor"
35
+ require "kitchen/transport/winrm/file_transporter"
36
+
37
+ module Kitchen
38
+
39
+ module Transport
40
+
41
+ # Wrapped exception for any internally raised WinRM-related errors.
42
+ #
43
+ # @author Fletcher Nichol <fnichol@nichol.ca>
44
+ class WinrmFailed < TransportFailed; end
45
+
46
+ # A Transport which uses WinRM to execute commands and transfer files.
47
+ #
48
+ # @author Matt Wrock <matt@mattwrock.com>
49
+ # @author Salim Afiune <salim@afiunemaya.com.mx>
50
+ # @author Fletcher Nichol <fnichol@nichol.ca>
51
+ class Winrm < Kitchen::Transport::Base
52
+
53
+ default_config :port, 5985
54
+ default_config :username, ".\\administrator"
55
+ default_config :password, nil
56
+ default_config :endpoint_template, "http://%{hostname}:%{port}/wsman"
57
+ default_config :rdp_port, 3389
58
+ default_config :connection_retries, 5
59
+ default_config :connection_retry_sleep, 1
60
+ default_config :max_wait_until_ready, 600
61
+
62
+ # (see Base#connection)
63
+ def connection(state, &block)
64
+ options = connection_options(config.to_hash.merge(state))
65
+
66
+ if @connection && @connection_options == options
67
+ reuse_connection(&block)
68
+ else
69
+ create_new_connection(options, &block)
70
+ end
71
+ end
72
+
73
+ # A Connection instance can be generated and re-generated, given new
74
+ # connection details such as connection port, hostname, credentials, etc.
75
+ # This object is responsible for carrying out the actions on the remote
76
+ # host such as executing commands, transferring files, etc.
77
+ #
78
+ # @author Fletcher Nichol <fnichol@nichol.ca>
79
+ class Connection < Kitchen::Transport::Base::Connection
80
+
81
+ # (see Base::Connection#close)
82
+ def close
83
+ return if @session.nil?
84
+
85
+ shell_id = session.shell
86
+ logger.debug("[WinRM] closing remote shell #{shell_id} on #{self}")
87
+ session.close
88
+ logger.debug("[WinRM] remote shell #{shell_id} closed")
89
+ remove_finalizer
90
+ ensure
91
+ @session = nil
92
+ end
93
+
94
+ # (see Base::Connection#execute)
95
+ def execute(command)
96
+ return if command.nil?
97
+ logger.debug("[WinRM] #{self} (#{command})")
98
+ exit_code, stderr = execute_with_exit_code(command)
99
+
100
+ if logger.debug? && exit_code == 0
101
+ log_stderr_on_warn(stderr)
102
+ elsif exit_code != 0
103
+ log_stderr_on_warn(stderr)
104
+ raise Transport::WinrmFailed,
105
+ "WinRM exited (#{exit_code}) for command: [#{command}]"
106
+ end
107
+ end
108
+
109
+ # (see Base::Connection#login_command)
110
+ def login_command
111
+ case RbConfig::CONFIG["host_os"]
112
+ when /darwin/
113
+ login_command_for_mac
114
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
115
+ login_command_for_windows
116
+ when /linux/
117
+ login_command_for_linux
118
+ else
119
+ raise ActionFailed, "Remote login not supported in #{self.class} " \
120
+ "from host OS '#{RbConfig::CONFIG["host_os"]}'."
121
+ end
122
+ end
123
+
124
+ # (see Base::Connection#upload)
125
+ def upload(locals, remote)
126
+ file_transporter.upload(locals, remote)
127
+ end
128
+
129
+ # (see Base::Connection#wait_until_ready)
130
+ def wait_until_ready
131
+ delay = 3
132
+ session(
133
+ :retries => max_wait_until_ready / delay,
134
+ :delay => delay,
135
+ :message => "Waiting for WinRM service on #{endpoint}, " \
136
+ "retrying in #{delay} seconds"
137
+ )
138
+ execute(PING_COMMAND.dup)
139
+ end
140
+
141
+ private
142
+
143
+ PING_COMMAND = "Write-Host '[WinRM] Established\n'".freeze
144
+
145
+ RESCUE_EXCEPTIONS_ON_ESTABLISH = [
146
+ Errno::EACCES, Errno::EADDRINUSE, Errno::ECONNREFUSED,
147
+ Errno::ECONNRESET, Errno::ENETUNREACH, Errno::EHOSTUNREACH,
148
+ ::WinRM::WinRMHTTPTransportError, ::WinRM::WinRMAuthorizationError,
149
+ HTTPClient::KeepAliveDisconnected
150
+ ].freeze
151
+
152
+ # @return [Integer] how many times to retry when failing to execute
153
+ # a command or transfer files
154
+ # @api private
155
+ attr_reader :connection_retries
156
+
157
+ # @return [Float] how many seconds to wait before attempting a retry
158
+ # when failing to execute a command or transfer files
159
+ # @api private
160
+ attr_reader :connection_retry_sleep
161
+
162
+ # @return [String] the endpoint URL of the remote WinRM host
163
+ # @api private
164
+ attr_reader :endpoint
165
+
166
+ # @return [String] display name for the associated instance
167
+ # @api private
168
+ attr_reader :instance_name
169
+
170
+ # @return [String] local path to the root of the project
171
+ # @api private
172
+ attr_reader :kitchen_root
173
+
174
+ # @return [Integer] how many times to retry when invoking
175
+ # `#wait_until_ready` before failing
176
+ # @api private
177
+ attr_reader :max_wait_until_ready
178
+
179
+ # @return [Integer] the TCP port number to use when connection to the
180
+ # remote WinRM host
181
+ # @api private
182
+ attr_reader :rdp_port
183
+
184
+ # @return [Symbol] the transport strategy to use when constructing a
185
+ # `WinRM::WinRMWebService`
186
+ # @api private
187
+ attr_reader :winrm_transport
188
+
189
+ # Creates a finalizer for this connection which will close the open
190
+ # remote shell session when the object is garabage collected or on
191
+ # Ruby VM shutdown.
192
+ #
193
+ # @param shell_id [String] the remote shell identifier
194
+ # @api private
195
+ def add_finalizer(shell_id)
196
+ ObjectSpace.define_finalizer(
197
+ self,
198
+ ShellCloser.new(
199
+ logger.debug?,
200
+ shell_id,
201
+ "#{self}",
202
+ endpoint,
203
+ winrm_transport,
204
+ options
205
+ )
206
+ )
207
+ end
208
+
209
+ # Writes an RDP document to the local file system.
210
+ #
211
+ # @param opts [Hash] file options
212
+ # @option opts [true,false] :mac whether or not the document is for a
213
+ # Mac system
214
+ # @api private
215
+ def create_rdp_doc(opts = {})
216
+ content = Util.outdent!(<<-RDP)
217
+ full address:s:#{URI.parse(endpoint).host}:#{rdp_port}
218
+ prompt for credentials:i:1
219
+ username:s:#{options[:user]}
220
+ RDP
221
+ content.prepend("drivestoredirect:s:*\n") if opts[:mac]
222
+
223
+ File.open(rdp_doc_path, "wb") { |f| f.write(content) }
224
+
225
+ if logger.debug?
226
+ debug("Creating RDP document for #{instance_name} (#{rdp_doc_path})")
227
+ debug("------------")
228
+ IO.read(rdp_doc_path).each_line { |l| debug("#{l.chomp}") }
229
+ debug("------------")
230
+ end
231
+ end
232
+
233
+ # Establish a remote shell session on the remote host.
234
+ #
235
+ # @param opts [Hash] retry options
236
+ # @option opts [Integer] :retries the number of times to retry before
237
+ # failing
238
+ # @option opts [Float] :delay the number of seconds to wait until
239
+ # attempting a retry
240
+ # @option opts [String] :message an optional message to be logged on
241
+ # debug (overriding the default) when a rescuable exception is raised
242
+ # @return [Winrm::CommandExecutor] the command executor session
243
+ # @api private
244
+ def establish_shell(opts)
245
+ @service = ::WinRM::WinRMWebService.new(
246
+ endpoint, winrm_transport, options)
247
+
248
+ executor = Winrm::CommandExecutor.new(@service, logger)
249
+ retryable(opts) do
250
+ logger.debug("[WinRM] opening remote shell on #{self}")
251
+ shell_id = executor.open
252
+ logger.debug("[WinRM] remote shell #{shell_id} is open on #{self}")
253
+ add_finalizer(shell_id)
254
+ end
255
+ executor
256
+ end
257
+
258
+ # Execute a Powershell script over WinRM and return the command's
259
+ # exit code and standard error.
260
+ #
261
+ # @param command [String] Powershell script to execute
262
+ # @return [[Integer,String]] an array containing the exit code of the
263
+ # script and the standard error stream
264
+ # @api private
265
+ def execute_with_exit_code(command)
266
+ response = session.run_powershell_script(command) do |stdout, _|
267
+ logger << stdout if stdout
268
+ end
269
+
270
+ [response[:exitcode], response.stderr]
271
+ end
272
+
273
+ # @return [Winrm::FileTransporter] a file transporter
274
+ # @api private
275
+ def file_transporter
276
+ @file_transporter ||= Winrm::FileTransporter.new(session, logger)
277
+ end
278
+
279
+ # (see Base#init_options)
280
+ def init_options(options)
281
+ super
282
+ @instance_name = @options.delete(:instance_name)
283
+ @kitchen_root = @options.delete(:kitchen_root)
284
+ @endpoint = @options.delete(:endpoint)
285
+ @rdp_port = @options.delete(:rdp_port)
286
+ @winrm_transport = @options.delete(:winrm_transport)
287
+ @connection_retries = @options.delete(:connection_retries)
288
+ @connection_retry_sleep = @options.delete(:connection_retry_sleep)
289
+ @max_wait_until_ready = @options.delete(:max_wait_until_ready)
290
+ end
291
+
292
+ # Logs formatted standard error output at the warning level.
293
+ #
294
+ # @param stderr [String] standard error output
295
+ # @api private
296
+ def log_stderr_on_warn(stderr)
297
+ error_regexp = /<S S=\"Error\">/
298
+
299
+ if error_regexp.match(stderr)
300
+ stderr.
301
+ split(error_regexp)[1..-2].
302
+ map! { |line| line.sub(/_x000D__x000A_<\/S>/, "").rstrip }.
303
+ each { |line| logger.warn(line) }
304
+ else
305
+ stderr.
306
+ split("\r\n").
307
+ each { |line| logger.warn(line) }
308
+ end
309
+ end
310
+
311
+ # Builds a `LoginCommand` for use by Linux-based platforms.
312
+ #
313
+ # TODO: determine whether or not `desktop` exists
314
+ #
315
+ # @return [LoginCommand] a login command
316
+ # @api private
317
+ def login_command_for_linux
318
+ args = %W[ -u #{options[:user]} ]
319
+ args += %W[ -p #{options[:pass]} ] if options.key?(:pass)
320
+ args += %W[ #{URI.parse(endpoint).host}:#{rdp_port} ]
321
+
322
+ LoginCommand.new("rdesktop", args)
323
+ end
324
+
325
+ # Builds a `LoginCommand` for use by Mac-based platforms.
326
+ #
327
+ # @return [LoginCommand] a login command
328
+ # @api private
329
+ def login_command_for_mac
330
+ create_rdp_doc(:mac => true)
331
+
332
+ LoginCommand.new("open", rdp_doc_path)
333
+ end
334
+
335
+ # Builds a `LoginCommand` for use by Windows-based platforms.
336
+ #
337
+ # @return [LoginCommand] a login command
338
+ # @api private
339
+ def login_command_for_windows
340
+ create_rdp_doc
341
+
342
+ LoginCommand.new("mstsc", rdp_doc_path)
343
+ end
344
+
345
+ # @return [String] path to the local RDP document
346
+ # @api private
347
+ def rdp_doc_path
348
+ File.join(kitchen_root, ".kitchen", "#{instance_name}.rdp")
349
+ end
350
+
351
+ # Removes any finalizers for this connection.
352
+ #
353
+ # @api private
354
+ def remove_finalizer
355
+ ObjectSpace.undefine_finalizer(self)
356
+ end
357
+
358
+ # Yields to a block and reties the block if certain rescuable
359
+ # exceptions are raised.
360
+ #
361
+ # @param opts [Hash] retry options
362
+ # @option opts [Integer] :retries the number of times to retry before
363
+ # failing
364
+ # @option opts [Float] :delay the number of seconds to wait until
365
+ # attempting a retry
366
+ # @option opts [String] :message an optional message to be logged on
367
+ # debug (overriding the default) when a rescuable exception is raised
368
+ # @return [Winrm::CommandExecutor] the command executor session
369
+ # @api private
370
+ def retryable(opts)
371
+ yield
372
+ rescue *RESCUE_EXCEPTIONS_ON_ESTABLISH => e
373
+ if (opts[:retries] -= 1) > 0
374
+ message = if opts[:message]
375
+ logger.debug("[WinRM] connection failed (#{e.inspect})")
376
+ opts[:message]
377
+ else
378
+ "[WinRM] connection failed, " \
379
+ "retrying in #{opts[:delay]} seconds (#{e.inspect})"
380
+ end
381
+ logger.info(message)
382
+ sleep(opts[:delay])
383
+ retry
384
+ else
385
+ logger.warn("[WinRM] connection failed, terminating (#{e.inspect})")
386
+ raise
387
+ end
388
+ end
389
+
390
+ # Establishes a remote shell session, or establishes one when invoked
391
+ # the first time.
392
+ #
393
+ # @param retry_options [Hash] retry options for the initial connection
394
+ # @return [Winrm::CommandExecutor] the command executor session
395
+ # @api private
396
+ def session(retry_options = {})
397
+ @session ||= establish_shell({
398
+ :retries => connection_retries.to_i,
399
+ :delay => connection_retry_sleep.to_i
400
+ }.merge(retry_options))
401
+ end
402
+
403
+ # String representation of object, reporting its connection details and
404
+ # configuration.
405
+ #
406
+ # @api private
407
+ def to_s
408
+ "#{winrm_transport}::#{endpoint}<#{options.inspect}>"
409
+ end
410
+ end
411
+
412
+ private
413
+
414
+ # Builds the hash of options needed by the Connection object on
415
+ # construction.
416
+ #
417
+ # @param data [Hash] merged configuration and mutable state data
418
+ # @return [Hash] hash of connection options
419
+ # @api private
420
+ def connection_options(data)
421
+ opts = {
422
+ :instance_name => instance.name,
423
+ :kitchen_root => data[:kitchen_root],
424
+ :logger => logger,
425
+ :winrm_transport => :plaintext,
426
+ :disable_sspi => true,
427
+ :basic_auth_only => true,
428
+ :endpoint => data[:endpoint_template] % data,
429
+ :user => data[:username],
430
+ :pass => data[:password],
431
+ :rdp_port => data[:rdp_port],
432
+ :connection_retries => data[:connection_retries],
433
+ :connection_retry_sleep => data[:connection_retry_sleep],
434
+ :max_wait_until_ready => data[:max_wait_until_ready]
435
+ }
436
+
437
+ opts
438
+ end
439
+
440
+ # Creates a new WinRM Connection instance and save it for potential
441
+ # future reuse.
442
+ #
443
+ # @param options [Hash] conneciton options
444
+ # @return [Ssh::Connection] a WinRM Connection instance
445
+ # @api private
446
+ def create_new_connection(options, &block)
447
+ if @connection
448
+ logger.debug("[WinRM] shutting previous connection #{@connection}")
449
+ @connection.close
450
+ end
451
+
452
+ @connection_options = options
453
+ @connection = Kitchen::Transport::Winrm::Connection.new(options, &block)
454
+ end
455
+
456
+ # Return the last saved WinRM connection instance.
457
+ #
458
+ # @return [Winrm::Connection] a WinRM Connection instance
459
+ # @api private
460
+ def reuse_connection
461
+ logger.debug("[WinRM] reusing existing connection #{@connection}")
462
+ yield @connection if block_given?
463
+ @connection
464
+ end
465
+
466
+ # An object that can close a remote shell session over WinRM.
467
+ #
468
+ # @author Fletcher Nichol <fnichol@nichol.ca>
469
+ # @api private
470
+ class ShellCloser
471
+
472
+ # Constructs a new ShellCloser.
473
+ #
474
+ # @param debug [true,false] whether or not debug messages should be
475
+ # output
476
+ # @param shell_id [String] the indentifier for the current open remote
477
+ # shell session
478
+ # @param info [String] a string representation of the connection
479
+ # @param args [Array] arguments to construct a `WinRM::WinRMWebService`
480
+ def initialize(debug, shell_id, info, *args)
481
+ @debug = debug
482
+ @shell_id = shell_id
483
+ @info = info
484
+ @args = args
485
+ end
486
+
487
+ # Closes the remote shell session.
488
+ def call(*)
489
+ debug("[WinRM] closing remote shell #{@shell_id} on #{@info}")
490
+ ::WinRM::WinRMWebService.new(*@args).close_shell(@shell_id)
491
+ debug("[WinRM] remote shell #{@shell_id} closed")
492
+ rescue => e
493
+ debug("Exception: #{e.inspect}")
494
+ end
495
+
496
+ private
497
+
498
+ # Writes a debug message, if debug mode is enabled.
499
+ #
500
+ # @param message [String] a message
501
+ # @api private
502
+ def debug(message)
503
+ $stdout.puts "D #{message}" if @debug
504
+ end
505
+ end
506
+ end
507
+ end
508
+ end