test-kitchen-rsync 3.0.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
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