test-kitchen 1.4.0.beta.2 → 1.4.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -0
  3. data/features/kitchen_diagnose_command.feature +32 -0
  4. data/lib/kitchen/cli.rb +3 -0
  5. data/lib/kitchen/command/diagnose.rb +6 -1
  6. data/lib/kitchen/configurable.rb +48 -1
  7. data/lib/kitchen/diagnostic.rb +29 -0
  8. data/lib/kitchen/driver/base.rb +25 -0
  9. data/lib/kitchen/driver/dummy.rb +4 -0
  10. data/lib/kitchen/driver/proxy.rb +3 -0
  11. data/lib/kitchen/instance.rb +17 -0
  12. data/lib/kitchen/provisioner/base.rb +30 -1
  13. data/lib/kitchen/provisioner/chef_base.rb +7 -3
  14. data/lib/kitchen/provisioner/chef_solo.rb +4 -0
  15. data/lib/kitchen/provisioner/chef_zero.rb +5 -1
  16. data/lib/kitchen/provisioner/dummy.rb +4 -0
  17. data/lib/kitchen/provisioner/shell.rb +5 -0
  18. data/lib/kitchen/shell_out.rb +6 -2
  19. data/lib/kitchen/transport/base.rb +25 -0
  20. data/lib/kitchen/transport/dummy.rb +4 -0
  21. data/lib/kitchen/transport/ssh.rb +22 -1
  22. data/lib/kitchen/transport/winrm.rb +61 -90
  23. data/lib/kitchen/verifier/base.rb +30 -1
  24. data/lib/kitchen/verifier/busser.rb +4 -0
  25. data/lib/kitchen/verifier/dummy.rb +4 -0
  26. data/lib/kitchen/version.rb +1 -1
  27. data/spec/kitchen/configurable_spec.rb +35 -0
  28. data/spec/kitchen/diagnostic_spec.rb +53 -3
  29. data/spec/kitchen/driver/dummy_spec.rb +8 -0
  30. data/spec/kitchen/driver/proxy_spec.rb +4 -0
  31. data/spec/kitchen/driver/ssh_base_spec.rb +4 -0
  32. data/spec/kitchen/instance_spec.rb +75 -0
  33. data/spec/kitchen/provisioner/base_spec.rb +32 -6
  34. data/spec/kitchen/provisioner/chef_base_spec.rb +3 -2
  35. data/spec/kitchen/provisioner/chef_solo_spec.rb +10 -2
  36. data/spec/kitchen/provisioner/chef_zero_spec.rb +24 -2
  37. data/spec/kitchen/provisioner/dummy_spec.rb +8 -0
  38. data/spec/kitchen/provisioner/shell_spec.rb +10 -0
  39. data/spec/kitchen/shell_out_spec.rb +7 -0
  40. data/spec/kitchen/transport/ssh_spec.rb +90 -1
  41. data/spec/kitchen/transport/winrm_spec.rb +91 -11
  42. data/spec/kitchen/verifier/base_spec.rb +32 -6
  43. data/spec/kitchen/verifier/busser_spec.rb +8 -0
  44. data/spec/kitchen/verifier/dummy_spec.rb +8 -0
  45. data/support/chef_base_install_command.sh +183 -100
  46. data/test-kitchen.gemspec +1 -2
  47. metadata +11 -48
  48. data/lib/kitchen/transport/winrm/command_executor.rb +0 -188
  49. data/lib/kitchen/transport/winrm/file_transporter.rb +0 -454
  50. data/lib/kitchen/transport/winrm/logging.rb +0 -50
  51. data/lib/kitchen/transport/winrm/template.rb +0 -74
  52. data/lib/kitchen/transport/winrm/tmp_zip.rb +0 -187
  53. data/spec/kitchen/transport/winrm/command_executor_spec.rb +0 -400
  54. data/spec/kitchen/transport/winrm/file_transporter_spec.rb +0 -876
  55. data/spec/kitchen/transport/winrm/logging_spec.rb +0 -92
  56. data/spec/kitchen/transport/winrm/template_spec.rb +0 -51
  57. data/spec/kitchen/transport/winrm/tmp_zip_spec.rb +0 -132
  58. data/support/check_files.ps1.erb +0 -48
  59. data/support/decode_files.ps1.erb +0 -62
@@ -27,6 +27,10 @@ module Kitchen
27
27
  # @author Fletcher Nichol <fnichol@nichol.ca>
28
28
  class ChefSolo < ChefBase
29
29
 
30
+ kitchen_provisioner_api_version 2
31
+
32
+ plugin_version Kitchen::VERSION
33
+
30
34
  default_config :solo_rb, {}
31
35
 
32
36
  default_config :chef_solo_path do |provisioner|
@@ -27,6 +27,10 @@ module Kitchen
27
27
  # @author Fletcher Nichol <fnichol@nichol.ca>
28
28
  class ChefZero < ChefBase
29
29
 
30
+ kitchen_provisioner_api_version 2
31
+
32
+ plugin_version Kitchen::VERSION
33
+
30
34
  default_config :client_rb, {}
31
35
  default_config :json_attributes, true
32
36
  default_config :chef_zero_host, nil
@@ -162,7 +166,7 @@ module Kitchen
162
166
  version = config[:require_chef_omnibus]
163
167
 
164
168
  case version
165
- when nil, false, true, "latest"
169
+ when nil, false, true, 11, "11", "latest"
166
170
  true
167
171
  else
168
172
  Gem::Version.new(version) >= Gem::Version.new("11.8.0") ? true : false
@@ -30,6 +30,10 @@ module Kitchen
30
30
  # @author Fletcher Nichol <fnichol@nichol.ca>
31
31
  class Dummy < Kitchen::Provisioner::Base
32
32
 
33
+ kitchen_provisioner_api_version 2
34
+
35
+ plugin_version Kitchen::VERSION
36
+
33
37
  default_config :sleep, 0
34
38
  default_config :random_failure, false
35
39
 
@@ -17,6 +17,7 @@
17
17
  # limitations under the License.
18
18
 
19
19
  require "kitchen/provisioner/base"
20
+ require "kitchen/version"
20
21
 
21
22
  module Kitchen
22
23
 
@@ -27,6 +28,10 @@ module Kitchen
27
28
  # @author Chris Lundquist (<chris.ludnquist@github.com>)
28
29
  class Shell < Base
29
30
 
31
+ kitchen_provisioner_api_version 2
32
+
33
+ plugin_version Kitchen::VERSION
34
+
30
35
  default_config :script do |provisioner|
31
36
  src = provisioner.powershell_shell? ? "bootstrap.ps1" : "bootstrap.sh"
32
37
  provisioner.calculate_path(src, :type => :file)
@@ -35,6 +35,8 @@ module Kitchen
35
35
  # @param options [Hash] additional configuration of command
36
36
  # @option options [TrueClass, FalseClass] :use_sudo whether or not to use
37
37
  # sudo
38
+ # @option options [String] :sudo_command custom sudo command to use.
39
+ # Default is "sudo -E".
38
40
  # @option options [String] :log_subject used in the output or log header
39
41
  # for clarity and context. Default is "local".
40
42
  # @option options [String] :cwd the directory to chdir to before running
@@ -56,7 +58,9 @@ module Kitchen
56
58
  # @raise [ShellCommandFailed] if the command fails
57
59
  # @raise [Error] for all other unexpected exceptions
58
60
  def run_command(cmd, options = {})
59
- cmd = "sudo -E #{cmd}" if options.fetch(:use_sudo, false)
61
+ if options.fetch(:use_sudo, false)
62
+ cmd = "#{options.fetch(:sudo_command, "sudo -E")} #{cmd}"
63
+ end
60
64
  subject = "[#{options.fetch(:log_subject, "local")} command]"
61
65
 
62
66
  debug("#{subject} BEGIN (#{cmd})")
@@ -81,7 +85,7 @@ module Kitchen
81
85
  # @api private
82
86
  def shell_opts(options)
83
87
  filtered_opts = options.reject do |key, _value|
84
- [:use_sudo, :log_subject, :quiet].include?(key)
88
+ [:use_sudo, :sudo_command, :log_subject, :quiet].include?(key)
85
89
  end
86
90
  { :live_stream => logger, :timeout => 60000 }.merge(filtered_opts)
87
91
  end
@@ -141,6 +141,31 @@ module Kitchen
141
141
  @logger = @options.delete(:logger) || Kitchen.logger
142
142
  end
143
143
  end
144
+
145
+ # Sets the API version for this transport. If the transport does not set
146
+ # this value, then `nil` will be used and reported.
147
+ #
148
+ # Sets the API version for this transport
149
+ #
150
+ # @example setting an API version
151
+ #
152
+ # module Kitchen
153
+ # module Transport
154
+ # class NewTransport < Kitchen::Transport::Base
155
+ #
156
+ # kitchen_transport_api_version 2
157
+ #
158
+ # end
159
+ # end
160
+ # end
161
+ #
162
+ # @param version [Integer,String] a version number
163
+ #
164
+ # rubocop:disable Style/TrivialAccessors
165
+ def self.kitchen_transport_api_version(version)
166
+ @api_version = version
167
+ end
168
+ # rubocop:enable Style/TrivialAccessors
144
169
  end
145
170
  end
146
171
  end
@@ -28,6 +28,10 @@ module Kitchen
28
28
  # plugins.
29
29
  class Dummy < Kitchen::Transport::Base
30
30
 
31
+ kitchen_transport_api_version 1
32
+
33
+ plugin_version Kitchen::VERSION
34
+
31
35
  default_config :sleep, 1
32
36
  default_config :random_exit_code, 0
33
37
 
@@ -36,6 +36,10 @@ module Kitchen
36
36
  # @author Fletcher Nichol <fnichol@nichol.ca>
37
37
  class Ssh < Kitchen::Transport::Base
38
38
 
39
+ kitchen_transport_api_version 1
40
+
41
+ plugin_version Kitchen::VERSION
42
+
39
43
  default_config :port, 22
40
44
  default_config :username, "root"
41
45
  default_config :keepalive, true
@@ -45,6 +49,21 @@ module Kitchen
45
49
  default_config :connection_retry_sleep, 1
46
50
  default_config :max_wait_until_ready, 600
47
51
 
52
+ default_config :ssh_key, nil
53
+ expand_path_for :ssh_key
54
+
55
+ default_config :compression, "zlib"
56
+ required_config :compression do |attr, value, transport|
57
+ if !%W[zlib none].include?(value)
58
+ raise UserError, "#{transport} :#{attr} value may only " \
59
+ "be set to `none' or `zlib'."
60
+ end
61
+ end
62
+
63
+ default_config :compression_level do |transport|
64
+ transport[:compression] == "none" ? 0 : 6
65
+ end
66
+
48
67
  # (see Base#connection)
49
68
  def connection(state, &block)
50
69
  options = connection_options(config.to_hash.merge(state))
@@ -271,7 +290,7 @@ module Kitchen
271
290
  # @param data [Hash] merged configuration and mutable state data
272
291
  # @return [Hash] hash of connection options
273
292
  # @api private
274
- def connection_options(data)
293
+ def connection_options(data) # rubocop:disable Metrics/MethodLength
275
294
  opts = {
276
295
  :logger => logger,
277
296
  :user_known_hosts_file => "/dev/null",
@@ -279,6 +298,8 @@ module Kitchen
279
298
  :hostname => data[:hostname],
280
299
  :port => data[:port],
281
300
  :username => data[:username],
301
+ :compression => data[:compression],
302
+ :compression_level => data[:compression_level],
282
303
  :keepalive => data[:keepalive],
283
304
  :keepalive_interval => data[:keepalive_interval],
284
305
  :timeout => data[:connection_timeout],
@@ -18,21 +18,10 @@
18
18
  # See the License for the specific language governing permissions and
19
19
  # limitations under the License.
20
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
21
  require "rbconfig"
31
22
  require "uri"
32
23
 
33
24
  require "kitchen"
34
- require "kitchen/transport/winrm/command_executor"
35
- require "kitchen/transport/winrm/file_transporter"
36
25
 
37
26
  module Kitchen
38
27
 
@@ -50,6 +39,10 @@ module Kitchen
50
39
  # @author Fletcher Nichol <fnichol@nichol.ca>
51
40
  class Winrm < Kitchen::Transport::Base
52
41
 
42
+ kitchen_transport_api_version 1
43
+
44
+ plugin_version Kitchen::VERSION
45
+
53
46
  default_config :port, 5985
54
47
  default_config :username, ".\\administrator"
55
48
  default_config :password, nil
@@ -86,7 +79,6 @@ module Kitchen
86
79
  logger.debug("[WinRM] closing remote shell #{shell_id} on #{self}")
87
80
  session.close
88
81
  logger.debug("[WinRM] remote shell #{shell_id} closed")
89
- remove_finalizer
90
82
  ensure
91
83
  @session = nil
92
84
  end
@@ -142,12 +134,14 @@ module Kitchen
142
134
 
143
135
  PING_COMMAND = "Write-Host '[WinRM] Established\n'".freeze
144
136
 
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
137
+ RESCUE_EXCEPTIONS_ON_ESTABLISH = lambda do
138
+ [
139
+ Errno::EACCES, Errno::EADDRINUSE, Errno::ECONNREFUSED,
140
+ Errno::ECONNRESET, Errno::ENETUNREACH, Errno::EHOSTUNREACH,
141
+ ::WinRM::WinRMHTTPTransportError, ::WinRM::WinRMAuthorizationError,
142
+ HTTPClient::KeepAliveDisconnected
143
+ ].freeze
144
+ end
151
145
 
152
146
  # @return [Integer] how many times to retry when failing to execute
153
147
  # a command or transfer files
@@ -186,26 +180,6 @@ module Kitchen
186
180
  # @api private
187
181
  attr_reader :winrm_transport
188
182
 
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
183
  # Writes an RDP document to the local file system.
210
184
  #
211
185
  # @param opts [Hash] file options
@@ -242,15 +216,15 @@ module Kitchen
242
216
  # @return [Winrm::CommandExecutor] the command executor session
243
217
  # @api private
244
218
  def establish_shell(opts)
245
- @service = ::WinRM::WinRMWebService.new(
246
- endpoint, winrm_transport, options)
219
+ service_args = [endpoint, winrm_transport, options]
220
+ @service = ::WinRM::WinRMWebService.new(*service_args)
221
+ closer = WinRM::Transport::ShellCloser.new("#{self}", logger.debug?, service_args)
247
222
 
248
- executor = Winrm::CommandExecutor.new(@service, logger)
223
+ executor = WinRM::Transport::CommandExecutor.new(@service, logger, closer)
249
224
  retryable(opts) do
250
225
  logger.debug("[WinRM] opening remote shell on #{self}")
251
226
  shell_id = executor.open
252
227
  logger.debug("[WinRM] remote shell #{shell_id} is open on #{self}")
253
- add_finalizer(shell_id)
254
228
  end
255
229
  executor
256
230
  end
@@ -273,7 +247,7 @@ module Kitchen
273
247
  # @return [Winrm::FileTransporter] a file transporter
274
248
  # @api private
275
249
  def file_transporter
276
- @file_transporter ||= Winrm::FileTransporter.new(session, logger)
250
+ @file_transporter ||= WinRM::Transport::FileTransporter.new(session, logger)
277
251
  end
278
252
 
279
253
  # (see Base#init_options)
@@ -348,13 +322,6 @@ module Kitchen
348
322
  File.join(kitchen_root, ".kitchen", "#{instance_name}.rdp")
349
323
  end
350
324
 
351
- # Removes any finalizers for this connection.
352
- #
353
- # @api private
354
- def remove_finalizer
355
- ObjectSpace.undefine_finalizer(self)
356
- end
357
-
358
325
  # Yields to a block and reties the block if certain rescuable
359
326
  # exceptions are raised.
360
327
  #
@@ -369,7 +336,7 @@ module Kitchen
369
336
  # @api private
370
337
  def retryable(opts)
371
338
  yield
372
- rescue *RESCUE_EXCEPTIONS_ON_ESTABLISH => e
339
+ rescue *RESCUE_EXCEPTIONS_ON_ESTABLISH.call => e
373
340
  if (opts[:retries] -= 1) > 0
374
341
  message = if opts[:message]
375
342
  logger.debug("[WinRM] connection failed (#{e.inspect})")
@@ -411,6 +378,8 @@ module Kitchen
411
378
 
412
379
  private
413
380
 
381
+ WINRM_TRANSPORT_SPEC_VERSION = "~> 1.0".freeze
382
+
414
383
  # Builds the hash of options needed by the Connection object on
415
384
  # construction.
416
385
  #
@@ -453,6 +422,42 @@ module Kitchen
453
422
  @connection = Kitchen::Transport::Winrm::Connection.new(options, &block)
454
423
  end
455
424
 
425
+ # (see Base#load_needed_dependencies!)
426
+ def load_needed_dependencies! # rubocop:disable Metrics/MethodLength
427
+ super
428
+ spec_version = WINRM_TRANSPORT_SPEC_VERSION.dup
429
+ logger.debug("Winrm Transport requested," \
430
+ " loading WinRM::Transport gem (#{spec_version})")
431
+ gem "winrm-transport", spec_version
432
+ first_load = require "winrm/transport/version"
433
+ load_winrm_transport!
434
+
435
+ version = ::WinRM::Transport::VERSION
436
+ if first_load
437
+ logger.debug("WinRM::Transport #{version} library loaded")
438
+ else
439
+ logger.debug("WinRM::Transport #{version} previously loaded")
440
+ end
441
+ rescue LoadError => e
442
+ logger.fatal("The `winrm-transport' gem is missing and must" \
443
+ " be installed or cannot be properly activated. Run" \
444
+ " `gem install winrm-transport --version '#{spec_version}'`" \
445
+ " or add the following to your Gemfile if you are using Bundler:" \
446
+ " `gem 'winrm-transport', '#{spec_version}'`.")
447
+ raise UserError,
448
+ "Could not load or activate WinRM::Transport (#{e.message})"
449
+ end
450
+
451
+ # Load WinRM::Transport code.
452
+ #
453
+ # @api private
454
+ def load_winrm_transport!
455
+ silence_warnings { require "winrm" }
456
+ require "winrm/transport/shell_closer"
457
+ require "winrm/transport/command_executor"
458
+ require "winrm/transport/file_transporter"
459
+ end
460
+
456
461
  # Return the last saved WinRM connection instance.
457
462
  #
458
463
  # @return [Winrm::Connection] a WinRM Connection instance
@@ -463,45 +468,11 @@ module Kitchen
463
468
  @connection
464
469
  end
465
470
 
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
471
+ def silence_warnings
472
+ old_verbose, $VERBOSE = $VERBOSE, nil
473
+ yield
474
+ ensure
475
+ $VERBOSE = old_verbose
505
476
  end
506
477
  end
507
478
  end
@@ -44,6 +44,10 @@ module Kitchen
44
44
  verifier.windows_os? ? nil : true
45
45
  end
46
46
 
47
+ default_config :sudo_command do |verifier|
48
+ verifier.windows_os? ? nil : "sudo -E"
49
+ end
50
+
47
51
  default_config(:suite_name) { |busser| busser.instance.suite.name }
48
52
 
49
53
  # Creates a new Verifier object using the provided configuration data
@@ -158,6 +162,31 @@ module Kitchen
158
162
  "trying to access the path.")
159
163
  end
160
164
 
165
+ # Sets the API version for this verifier. If the verifier does not set
166
+ # this value, then `nil` will be used and reported.
167
+ #
168
+ # Sets the API version for this verifier
169
+ #
170
+ # @example setting an API version
171
+ #
172
+ # module Kitchen
173
+ # module Verifier
174
+ # class NewVerifier < Kitchen::Verifier::Base
175
+ #
176
+ # kitchen_verifier_api_version 2
177
+ #
178
+ # end
179
+ # end
180
+ # end
181
+ #
182
+ # @param version [Integer,String] a version number
183
+ #
184
+ # rubocop:disable Style/TrivialAccessors
185
+ def self.kitchen_verifier_api_version(version)
186
+ @api_version = version
187
+ end
188
+ # rubocop:enable Style/TrivialAccessors
189
+
161
190
  private
162
191
 
163
192
  # Builds a complete command given a variables String preamble and a file
@@ -184,7 +213,7 @@ module Kitchen
184
213
  # @return [String] the command, conditionaly prefixed with sudo
185
214
  # @api private
186
215
  def sudo(script)
187
- config[:sudo] ? "sudo -E #{script}" : script
216
+ config[:sudo] ? "#{config[:sudo_command]} #{script}" : script
188
217
  end
189
218
  end
190
219
  end