test-kitchen-rsync 3.0.0.pre.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 (108) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +21 -0
  3. data/LICENSE +15 -0
  4. data/Rakefile +53 -0
  5. data/bin/zl-kitchen +11 -0
  6. data/lib/kitchen/base64_stream.rb +48 -0
  7. data/lib/kitchen/chef_utils_wiring.rb +40 -0
  8. data/lib/kitchen/cli.rb +413 -0
  9. data/lib/kitchen/collection.rb +52 -0
  10. data/lib/kitchen/color.rb +63 -0
  11. data/lib/kitchen/command/action.rb +41 -0
  12. data/lib/kitchen/command/console.rb +54 -0
  13. data/lib/kitchen/command/diagnose.rb +84 -0
  14. data/lib/kitchen/command/doctor.rb +39 -0
  15. data/lib/kitchen/command/exec.rb +37 -0
  16. data/lib/kitchen/command/list.rb +148 -0
  17. data/lib/kitchen/command/login.rb +39 -0
  18. data/lib/kitchen/command/package.rb +32 -0
  19. data/lib/kitchen/command/sink.rb +50 -0
  20. data/lib/kitchen/command/test.rb +47 -0
  21. data/lib/kitchen/command.rb +207 -0
  22. data/lib/kitchen/config.rb +344 -0
  23. data/lib/kitchen/configurable.rb +616 -0
  24. data/lib/kitchen/data_munger.rb +1024 -0
  25. data/lib/kitchen/diagnostic.rb +138 -0
  26. data/lib/kitchen/driver/base.rb +133 -0
  27. data/lib/kitchen/driver/dummy.rb +105 -0
  28. data/lib/kitchen/driver/exec.rb +70 -0
  29. data/lib/kitchen/driver/proxy.rb +70 -0
  30. data/lib/kitchen/driver/ssh_base.rb +351 -0
  31. data/lib/kitchen/driver.rb +40 -0
  32. data/lib/kitchen/errors.rb +243 -0
  33. data/lib/kitchen/generator/init.rb +254 -0
  34. data/lib/kitchen/instance.rb +726 -0
  35. data/lib/kitchen/lazy_hash.rb +148 -0
  36. data/lib/kitchen/lifecycle_hook/base.rb +78 -0
  37. data/lib/kitchen/lifecycle_hook/local.rb +53 -0
  38. data/lib/kitchen/lifecycle_hook/remote.rb +39 -0
  39. data/lib/kitchen/lifecycle_hooks.rb +92 -0
  40. data/lib/kitchen/loader/yaml.rb +377 -0
  41. data/lib/kitchen/logger.rb +422 -0
  42. data/lib/kitchen/logging.rb +52 -0
  43. data/lib/kitchen/login_command.rb +49 -0
  44. data/lib/kitchen/metadata_chopper.rb +49 -0
  45. data/lib/kitchen/platform.rb +64 -0
  46. data/lib/kitchen/plugin.rb +76 -0
  47. data/lib/kitchen/plugin_base.rb +60 -0
  48. data/lib/kitchen/provisioner/base.rb +269 -0
  49. data/lib/kitchen/provisioner/chef/berkshelf.rb +116 -0
  50. data/lib/kitchen/provisioner/chef/common_sandbox.rb +350 -0
  51. data/lib/kitchen/provisioner/chef/policyfile.rb +163 -0
  52. data/lib/kitchen/provisioner/chef_apply.rb +121 -0
  53. data/lib/kitchen/provisioner/chef_base.rb +705 -0
  54. data/lib/kitchen/provisioner/chef_infra.rb +167 -0
  55. data/lib/kitchen/provisioner/chef_solo.rb +82 -0
  56. data/lib/kitchen/provisioner/chef_zero.rb +12 -0
  57. data/lib/kitchen/provisioner/dummy.rb +75 -0
  58. data/lib/kitchen/provisioner/shell.rb +157 -0
  59. data/lib/kitchen/provisioner.rb +42 -0
  60. data/lib/kitchen/rake_tasks.rb +80 -0
  61. data/lib/kitchen/shell_out.rb +90 -0
  62. data/lib/kitchen/ssh.rb +289 -0
  63. data/lib/kitchen/state_file.rb +112 -0
  64. data/lib/kitchen/suite.rb +48 -0
  65. data/lib/kitchen/thor_tasks.rb +63 -0
  66. data/lib/kitchen/transport/base.rb +236 -0
  67. data/lib/kitchen/transport/dummy.rb +78 -0
  68. data/lib/kitchen/transport/exec.rb +145 -0
  69. data/lib/kitchen/transport/ssh.rb +579 -0
  70. data/lib/kitchen/transport/winrm.rb +546 -0
  71. data/lib/kitchen/transport.rb +40 -0
  72. data/lib/kitchen/util.rb +229 -0
  73. data/lib/kitchen/verifier/base.rb +243 -0
  74. data/lib/kitchen/verifier/busser.rb +275 -0
  75. data/lib/kitchen/verifier/dummy.rb +75 -0
  76. data/lib/kitchen/verifier/shell.rb +99 -0
  77. data/lib/kitchen/verifier.rb +39 -0
  78. data/lib/kitchen/version.rb +20 -0
  79. data/lib/kitchen/which.rb +26 -0
  80. data/lib/kitchen.rb +152 -0
  81. data/lib/vendor/hash_recursive_merge.rb +79 -0
  82. data/support/busser_install_command.ps1 +14 -0
  83. data/support/busser_install_command.sh +21 -0
  84. data/support/chef-client-fail-if-update-handler.rb +15 -0
  85. data/support/chef_base_init_command.ps1 +18 -0
  86. data/support/chef_base_init_command.sh +1 -0
  87. data/support/chef_base_install_command.ps1 +85 -0
  88. data/support/chef_base_install_command.sh +229 -0
  89. data/support/download_helpers.sh +109 -0
  90. data/support/dummy-validation.pem +27 -0
  91. data/templates/driver/CHANGELOG.md.erb +3 -0
  92. data/templates/driver/Gemfile.erb +3 -0
  93. data/templates/driver/README.md.erb +64 -0
  94. data/templates/driver/Rakefile.erb +21 -0
  95. data/templates/driver/driver.rb.erb +23 -0
  96. data/templates/driver/gemspec.erb +29 -0
  97. data/templates/driver/gitignore.erb +17 -0
  98. data/templates/driver/license_apachev2.erb +15 -0
  99. data/templates/driver/license_lgplv3.erb +16 -0
  100. data/templates/driver/license_mit.erb +22 -0
  101. data/templates/driver/license_reserved.erb +5 -0
  102. data/templates/driver/tailor.erb +4 -0
  103. data/templates/driver/travis.yml.erb +11 -0
  104. data/templates/driver/version.rb.erb +12 -0
  105. data/templates/init/chefignore.erb +2 -0
  106. data/templates/init/kitchen.yml.erb +18 -0
  107. data/test-kitchen.gemspec +52 -0
  108. metadata +528 -0
@@ -0,0 +1,546 @@
1
+ #
2
+ # Author:: Salim Afiune (<salim@afiunemaya.com.mx>)
3
+ # Author:: Matt Wrock (<matt@mattwrock.com>)
4
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
5
+ #
6
+ # Copyright (C) 2014, Salim Afiune
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+
20
+ require "rbconfig" unless defined?(RbConfig)
21
+ require "uri" unless defined?(URI)
22
+ require_relative "../../kitchen"
23
+ require "winrm" unless defined?(WinRM::Connection)
24
+
25
+ module Kitchen
26
+ module Transport
27
+ # Wrapped exception for any internally raised WinRM-related errors.
28
+ #
29
+ # @author Fletcher Nichol <fnichol@nichol.ca>
30
+ class WinrmFailed < TransportFailed; end
31
+
32
+ # A Transport which uses WinRM to execute commands and transfer files.
33
+ #
34
+ # @author Matt Wrock <matt@mattwrock.com>
35
+ # @author Salim Afiune <salim@afiunemaya.com.mx>
36
+ # @author Fletcher Nichol <fnichol@nichol.ca>
37
+ class Winrm < Kitchen::Transport::Base
38
+ kitchen_transport_api_version 1
39
+
40
+ plugin_version Kitchen::VERSION
41
+
42
+ default_config :username, "administrator"
43
+ default_config :password, nil
44
+ default_config :elevated, false
45
+ default_config :rdp_port, 3389
46
+ default_config :connection_retries, 5
47
+ default_config :connection_retry_sleep, 1
48
+ default_config :operation_timeout, 60
49
+ default_config :receive_timeout, 70
50
+ default_config :max_wait_until_ready, 600
51
+ default_config :winrm_transport, :negotiate
52
+ default_config :scheme do |transport|
53
+ transport[:winrm_transport] == :ssl ? "https" : "http"
54
+ end
55
+ default_config :port do |transport|
56
+ transport[:winrm_transport] == :ssl ? 5986 : 5985
57
+ end
58
+
59
+ def finalize_config!(instance)
60
+ super
61
+
62
+ config[:winrm_transport] = config[:winrm_transport].to_sym
63
+
64
+ self
65
+ end
66
+
67
+ # (see Base#connection)
68
+ def connection(state, &block)
69
+ options = connection_options(config.to_hash.merge(state))
70
+
71
+ if @connection && @connection_options == options
72
+ reuse_connection(&block)
73
+ else
74
+ create_new_connection(options, &block)
75
+ end
76
+ end
77
+
78
+ # A Connection instance can be generated and re-generated, given new
79
+ # connection details such as connection port, hostname, credentials, etc.
80
+ # This object is responsible for carrying out the actions on the remote
81
+ # host such as executing commands, transferring files, etc.
82
+ #
83
+ # @author Fletcher Nichol <fnichol@nichol.ca>
84
+ class Connection < Kitchen::Transport::Base::Connection
85
+ # (see Base::Connection#initialize)
86
+ def initialize(config = {})
87
+ super(config)
88
+ @unelevated_session = nil
89
+ @elevated_session = nil
90
+ end
91
+
92
+ # (see Base::Connection#close)
93
+ def close
94
+ @unelevated_session.close if @unelevated_session
95
+ @elevated_session.close if @elevated_session
96
+ ensure
97
+ @unelevated_session = nil
98
+ @elevated_session = nil
99
+ @file_transporter = nil
100
+ end
101
+
102
+ # (see Base::Connection#execute)
103
+ def execute(command)
104
+ return if command.nil?
105
+
106
+ logger.debug("[WinRM] #{self} (#{command})")
107
+
108
+ exit_code, stderr = execute_with_exit_code(command)
109
+
110
+ if logger.debug? && exit_code == 0
111
+ log_stderr_on_warn(stderr)
112
+ elsif exit_code != 0
113
+ log_stderr_on_warn(stderr)
114
+ raise Transport::WinrmFailed.new(
115
+ "WinRM exited (#{exit_code}) for command: [#{command}]",
116
+ exit_code
117
+ )
118
+ end
119
+ end
120
+
121
+ # (see Base::Connection#login_command)
122
+ def login_command
123
+ case RbConfig::CONFIG["host_os"]
124
+ when /darwin/
125
+ login_command_for_mac
126
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
127
+ login_command_for_windows
128
+ when /linux/
129
+ login_command_for_linux
130
+ else
131
+ raise ActionFailed, "Remote login not supported in #{self.class} " \
132
+ "from host OS '#{RbConfig::CONFIG["host_os"]}'."
133
+ end
134
+ end
135
+
136
+ # (see Base::Connection#upload)
137
+ def upload(locals, remote)
138
+ file_transporter.upload(locals, remote)
139
+ end
140
+
141
+ # (see Base::Connection#download)
142
+ def download(remotes, local)
143
+ # ensure the parent dir of the local target exists
144
+ FileUtils.mkdir_p(File.dirname(local))
145
+
146
+ Array(remotes).each do |remote|
147
+ file_manager.download(remote, local)
148
+ end
149
+ end
150
+
151
+ # @return [Winrm::FileManager] a file transporter
152
+ # @api private
153
+ def file_manager
154
+ @file_manager ||= WinRM::FS::FileManager.new(connection)
155
+ end
156
+
157
+ # (see Base::Connection#wait_until_ready)
158
+ def wait_until_ready
159
+ delay = 3
160
+ unelevated_session(
161
+ retry_limit: max_wait_until_ready / delay,
162
+ retry_delay: delay
163
+ )
164
+ execute(PING_COMMAND.dup)
165
+ rescue *RESCUE_EXCEPTIONS_ON_ESTABLISH => e
166
+ retries ||= connection_retries.to_i
167
+ raise e if (retries -= 1) < 0
168
+
169
+ logger.debug("[WinRM] PING_COMMAND failed. Retrying...")
170
+ logger.debug("#{e.class}::#{e.message}")
171
+ sleep(connection_retry_sleep.to_i)
172
+ retry
173
+ end
174
+
175
+ private
176
+
177
+ PING_COMMAND = "Write-Host '[WinRM] Established\n'".freeze
178
+
179
+ RESCUE_EXCEPTIONS_ON_ESTABLISH = [
180
+ Errno::EACCES, Errno::EALREADY, Errno::EADDRINUSE, Errno::ECONNREFUSED, Errno::ETIMEDOUT,
181
+ Errno::ECONNRESET, Errno::ENETUNREACH, Errno::EHOSTUNREACH, Errno::EPIPE,
182
+ OpenSSL::SSL::SSLError, WinRM::WinRMHTTPTransportError
183
+ ].freeze
184
+
185
+ # @return [Integer] how many times to retry when failing to execute
186
+ # a command or transfer files
187
+ # @api private
188
+ attr_reader :connection_retries
189
+
190
+ # @return [Float] how many seconds to wait before attempting a retry
191
+ # when failing to execute a command or transfer files
192
+ # @api private
193
+ attr_reader :connection_retry_sleep
194
+
195
+ # @return [String] display name for the associated instance
196
+ # @api private
197
+ attr_reader :instance_name
198
+
199
+ # @return [String] local path to the root of the project
200
+ # @api private
201
+ attr_reader :kitchen_root
202
+
203
+ # @return [Integer] how many times to retry when invoking
204
+ # `#wait_until_ready` before failing
205
+ # @api private
206
+ attr_reader :max_wait_until_ready
207
+
208
+ # @return [Integer] the TCP port number to use when connection to the
209
+ # remote WinRM host
210
+ # @api private
211
+ attr_reader :rdp_port
212
+
213
+ # @return [Boolean] whether to use winrm-elevated for running commands
214
+ # @api private
215
+ attr_reader :elevated
216
+
217
+ # Writes an RDP document to the local file system.
218
+ #
219
+ # @param opts [Hash] file options
220
+ # @option opts [true,false] :mac whether or not the document is for a
221
+ # Mac system
222
+ # @api private
223
+ def create_rdp_doc(opts = {})
224
+ content = Util.outdent!(<<-RDP)
225
+ full address:s:#{URI.parse(options[:endpoint]).host}:#{rdp_port}
226
+ prompt for credentials:i:1
227
+ username:s:#{options[:user]}
228
+ RDP
229
+ content.prepend("drivestoredirect:s:*\n") if opts[:mac]
230
+
231
+ File.open(rdp_doc_path, "wb") { |f| f.write(content) }
232
+
233
+ if logger.debug?
234
+ debug("Creating RDP document for #{instance_name} (#{rdp_doc_path})")
235
+ debug("------------")
236
+ IO.read(rdp_doc_path).each_line { |l| debug(l.chomp.to_s) }
237
+ debug("------------")
238
+ end
239
+ end
240
+
241
+ # Execute a Powershell script over WinRM and return the command's
242
+ # exit code and standard error.
243
+ #
244
+ # @param command [String] Powershell script to execute
245
+ # @return [[Integer,String]] an array containing the exit code of the
246
+ # script and the standard error stream
247
+ # @api private
248
+ def execute_with_exit_code(command)
249
+ if elevated
250
+ session = elevated_session
251
+ command = "$env:temp='#{unelevated_temp_dir}';#{command}"
252
+ else
253
+ session = unelevated_session
254
+ end
255
+
256
+ begin
257
+ response = session.run(command) do |stdout, _|
258
+ logger << stdout if stdout
259
+ end
260
+ [response.exitcode, response.stderr]
261
+ ensure
262
+ close
263
+ end
264
+ end
265
+
266
+ def unelevated_temp_dir
267
+ @unelevated_temp_dir ||= unelevated_session.run("$env:temp").stdout.chomp
268
+ end
269
+
270
+ # @return [Winrm::FileTransporter] a file transporter
271
+ # @api private
272
+ def file_transporter
273
+ @file_transporter ||= WinRM::FS::Core::FileTransporter.new(unelevated_session)
274
+ end
275
+
276
+ # (see Base#init_options)
277
+ def init_options(options)
278
+ super
279
+ @instance_name = @options.delete(:instance_name)
280
+ @kitchen_root = @options.delete(:kitchen_root)
281
+ @rdp_port = @options.delete(:rdp_port)
282
+ @connection_retries = @options.delete(:connection_retries)
283
+ @connection_retry_sleep = @options.delete(:connection_retry_sleep)
284
+ @operation_timeout = @options.delete(:operation_timeout)
285
+ @receive_timeout = @options.delete(:receive_timeout)
286
+ @max_wait_until_ready = @options.delete(:max_wait_until_ready)
287
+ @elevated = @options.delete(:elevated)
288
+ end
289
+
290
+ # Logs formatted standard error output at the warning level.
291
+ #
292
+ # @param stderr [String] standard error output
293
+ # @api private
294
+ def log_stderr_on_warn(stderr)
295
+ error_regexp = /<S S=\"Error\">/
296
+
297
+ if error_regexp.match(stderr)
298
+ stderr
299
+ .split(error_regexp)[1..-2]
300
+ .map! { |line| line.sub(%r{_x000D__x000A_</S>}, "").rstrip }
301
+ .each { |line| logger.warn(line) }
302
+ else
303
+ stderr
304
+ .split("\r\n")
305
+ .each { |line| logger.warn(line) }
306
+ end
307
+ end
308
+
309
+ # Builds a `LoginCommand` for use by Linux-based platforms.
310
+ #
311
+ # @return [LoginCommand] a login command
312
+ # @api private
313
+ def login_command_for_linux
314
+ xfreerdp = Util.command_exists? "xfreerdp"
315
+ unless xfreerdp
316
+ raise WinrmFailed, "xfreerdp binary not found. Please install freerdp2-x11 on Debian-based systems or freerdp on Redhat-based systems."
317
+ end
318
+
319
+ args = %W{/u:#{options[:user]}}
320
+ args += %W{/p:#{options[:password]}} if options.key?(:password)
321
+ args += %W{/v:#{URI.parse(options[:endpoint]).host}:#{rdp_port}}
322
+ args += %W{/cert-tofu} # always accept certificate
323
+
324
+ LoginCommand.new(xfreerdp, args)
325
+ end
326
+
327
+ # Builds a `LoginCommand` for use by Mac-based platforms.
328
+ #
329
+ # @return [LoginCommand] a login command
330
+ # @api private
331
+ def login_command_for_mac
332
+ create_rdp_doc(mac: true)
333
+
334
+ LoginCommand.new("open", rdp_doc_path)
335
+ end
336
+
337
+ # Builds a `LoginCommand` for use by Windows-based platforms.
338
+ #
339
+ # @return [LoginCommand] a login command
340
+ # @api private
341
+ def login_command_for_windows
342
+ create_rdp_doc
343
+
344
+ LoginCommand.new("mstsc", rdp_doc_path)
345
+ end
346
+
347
+ # @return [String] path to the local RDP document
348
+ # @api private
349
+ def rdp_doc_path
350
+ File.join(kitchen_root, ".kitchen", "#{instance_name}.rdp")
351
+ end
352
+
353
+ # Establishes a remote shell session, or establishes one when invoked
354
+ # the first time.
355
+ #
356
+ # @param retry_options [Hash] retry options for the initial connection
357
+ # @return [Winrm::Shells::Powershell] the command shell session
358
+ # @api private
359
+ def unelevated_session(retry_options = {})
360
+ @unelevated_session ||= connection(retry_options).shell(:powershell)
361
+ end
362
+
363
+ # Creates an elevated session for running commands via a scheduled task
364
+ #
365
+ # @return [Winrm::Shells::Elevated] the elevated shell
366
+ # @api private
367
+ def elevated_session(retry_options = {})
368
+ @elevated_session ||= connection(retry_options).shell(:elevated).tap do |shell|
369
+ shell.username = options[:elevated_username]
370
+ shell.password = options[:elevated_password]
371
+ end
372
+ end
373
+
374
+ # Creates a winrm Connection instance
375
+ #
376
+ # @param retry_options [Hash] retry options for the initial connection
377
+ # @return [Winrm::Connection] the winrm connection
378
+ # @api private
379
+ def connection(retry_options = {})
380
+ @connection ||= begin
381
+ opts = {
382
+ retry_limit: connection_retries.to_i,
383
+ retry_delay: connection_retry_sleep.to_i,
384
+ }.merge(retry_options)
385
+
386
+ ::WinRM::Connection.new(options.merge(opts)).tap do |conn|
387
+ conn.logger = logger
388
+ end
389
+ end
390
+ end
391
+
392
+ # String representation of object, reporting its connection details and
393
+ # configuration.
394
+ #
395
+ # @api private
396
+ def to_s
397
+ "<#{options.inspect}>"
398
+ end
399
+ end
400
+
401
+ private
402
+
403
+ WINRM_SPEC_VERSION = ["~> 2.0"].freeze
404
+ WINRM_FS_SPEC_VERSION = ["~> 1.0"].freeze
405
+ WINRM_ELEVATED_SPEC_VERSION = ["~> 1.0"].freeze
406
+
407
+ # Builds the hash of options needed by the Connection object on
408
+ # construction.
409
+ #
410
+ # @param data [Hash] merged configuration and mutable state data
411
+ # @return [Hash] hash of connection options
412
+ # @api private
413
+ def connection_options(data)
414
+ endpoint = URI::Generic.build(
415
+ scheme: data.fetch(:scheme),
416
+ host: data.fetch(:hostname),
417
+ port: data.fetch(:port),
418
+ path: "/wsman"
419
+ ).to_s
420
+
421
+ elevated_password = data[:password]
422
+ elevated_password = data[:elevated_password] if data.key?(:elevated_password)
423
+
424
+ opts = {
425
+ instance_name: instance.name,
426
+ kitchen_root: data[:kitchen_root],
427
+ logger: logger,
428
+ endpoint: endpoint,
429
+ user: data[:username],
430
+ password: data[:password],
431
+ rdp_port: data[:rdp_port],
432
+ connection_retries: data[:connection_retries],
433
+ connection_retry_sleep: data[:connection_retry_sleep],
434
+ operation_timeout: data[:operation_timeout],
435
+ receive_timeout: data[:receive_timeout],
436
+ max_wait_until_ready: data[:max_wait_until_ready],
437
+ transport: data[:winrm_transport],
438
+ elevated: data[:elevated],
439
+ elevated_username: data[:elevated_username] || data[:username],
440
+ elevated_password: elevated_password,
441
+ }
442
+ opts.merge!(additional_transport_args(opts[:transport]))
443
+ opts
444
+ end
445
+
446
+ def additional_transport_args(transport_type)
447
+ case transport_type.to_sym
448
+ when :ssl, :negotiate
449
+ {
450
+ no_ssl_peer_verification: true,
451
+ disable_sspi: false,
452
+ basic_auth_only: false,
453
+ }
454
+ when :plaintext
455
+ {
456
+ disable_sspi: true,
457
+ basic_auth_only: true,
458
+ }
459
+ else
460
+ {}
461
+ end
462
+ end
463
+
464
+ # Creates a new WinRM Connection instance and save it for potential
465
+ # future reuse.
466
+ #
467
+ # @param options [Hash] conneciton options
468
+ # @return [Ssh::Connection] a WinRM Connection instance
469
+ # @api private
470
+ def create_new_connection(options, &block)
471
+ if @connection
472
+ logger.debug("[WinRM] shutting previous connection #{@connection}")
473
+ @connection.close
474
+ end
475
+
476
+ @connection_options = options
477
+ @connection = Kitchen::Transport::Winrm::Connection.new(options, &block)
478
+ end
479
+
480
+ # (see Base#load_needed_dependencies!)
481
+ def load_needed_dependencies!
482
+ super
483
+ load_with_rescue!("winrm", WINRM_SPEC_VERSION.dup)
484
+ load_with_rescue!("winrm-fs", WINRM_FS_SPEC_VERSION.dup)
485
+ load_with_rescue!("winrm-elevated", WINRM_ELEVATED_SPEC_VERSION.dup) if config[:elevated]
486
+ end
487
+
488
+ def load_with_rescue!(gem_name, spec_version)
489
+ logger.debug("#{gem_name} requested," \
490
+ " loading #{gem_name} gem (#{spec_version})")
491
+ attempt_load = false
492
+ gem gem_name, spec_version
493
+ silence_warnings { attempt_load = require gem_name }
494
+ if attempt_load
495
+ logger.debug("#{gem_name} is loaded.")
496
+ else
497
+ logger.debug("#{gem_name} was already loaded.")
498
+ end
499
+ rescue LoadError => e
500
+ message = fail_to_load_gem_message(gem_name,
501
+ spec_version)
502
+ logger.fatal(message)
503
+ raise UserError,
504
+ "Could not load or activate #{gem_name}. (#{e.message})"
505
+ end
506
+
507
+ def fail_to_load_gem_message(name, version = nil)
508
+ version_cmd = "--version '#{version}'" if version
509
+ version_file = "', '#{version}"
510
+
511
+ "The `#{name}` gem is missing and must" \
512
+ " be installed or cannot be properly activated. Run" \
513
+ " `gem install #{name} #{version_cmd}`" \
514
+ " or add the following to your Gemfile if you are using Bundler:" \
515
+ " `gem '#{name} #{version_file}'`."
516
+ end
517
+
518
+ def host_os_windows?
519
+ case RbConfig::CONFIG["host_os"]
520
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
521
+ true
522
+ else
523
+ false
524
+ end
525
+ end
526
+
527
+ # Return the last saved WinRM connection instance.
528
+ #
529
+ # @return [Winrm::Connection] a WinRM Connection instance
530
+ # @api private
531
+ def reuse_connection
532
+ logger.debug("[WinRM] reusing existing connection #{@connection}")
533
+ yield @connection if block_given?
534
+ @connection
535
+ end
536
+
537
+ def silence_warnings
538
+ old_verbose = $VERBOSE
539
+ $VERBOSE = nil
540
+ yield
541
+ ensure
542
+ $VERBOSE = old_verbose
543
+ end
544
+ end
545
+ end
546
+ end
@@ -0,0 +1,40 @@
1
+ #
2
+ # Author:: Salim Afiune (<salim@afiunemaya.com.mx>)
3
+ #
4
+ # Copyright (C) 2014, Salim Afiune
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
+ require_relative "plugin"
19
+
20
+ module Kitchen
21
+ # A transport is responsible for the communication with an instance,
22
+ # that is remote comands and other actions such as file transfer,
23
+ # login, etc.
24
+ #
25
+ # @author Salim Afiune <salim@afiunemaya.com.mx>
26
+ module Transport
27
+ # Default transport to use
28
+ DEFAULT_PLUGIN = "ssh".freeze
29
+
30
+ # Returns an instance of a transport given a plugin type string.
31
+ #
32
+ # @param plugin [String] a transport plugin type, to be constantized
33
+ # @param config [Hash] a configuration hash to initialize the transport
34
+ # @return [Transport::Base] a transport instance
35
+ # @raise [ClientError] if a transport instance could not be created
36
+ def self.for_plugin(plugin, config)
37
+ Kitchen::Plugin.load(self, plugin, config)
38
+ end
39
+ end
40
+ end