test-kitchen 1.4.2 → 1.5.0

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.travis.yml +29 -33
  4. data/CHANGELOG.md +86 -2
  5. data/CONTRIBUTING.md +17 -0
  6. data/Gemfile.proxy_tests +5 -0
  7. data/MAINTAINERS.md +24 -0
  8. data/features/kitchen_driver_discover_command.feature +6 -0
  9. data/features/kitchen_init_command.feature +55 -7
  10. data/features/step_definitions/gem_steps.rb +12 -4
  11. data/features/step_definitions/git_steps.rb +1 -1
  12. data/features/step_definitions/output_steps.rb +1 -1
  13. data/features/support/env.rb +13 -9
  14. data/lib/kitchen/cli.rb +46 -5
  15. data/lib/kitchen/command/driver_discover.rb +8 -0
  16. data/lib/kitchen/command.rb +1 -0
  17. data/lib/kitchen/config.rb +4 -0
  18. data/lib/kitchen/configurable.rb +32 -0
  19. data/lib/kitchen/driver/ssh_base.rb +18 -4
  20. data/lib/kitchen/generator/driver_create.rb +2 -2
  21. data/lib/kitchen/generator/init.rb +4 -4
  22. data/lib/kitchen/instance.rb +7 -0
  23. data/lib/kitchen/lazy_hash.rb +20 -0
  24. data/lib/kitchen/logger.rb +4 -7
  25. data/lib/kitchen/provisioner/base.rb +16 -1
  26. data/lib/kitchen/provisioner/chef_base.rb +43 -98
  27. data/lib/kitchen/provisioner/chef_solo.rb +7 -4
  28. data/lib/kitchen/provisioner/chef_zero.rb +12 -5
  29. data/lib/kitchen/ssh.rb +1 -1
  30. data/lib/kitchen/transport/base.rb +7 -0
  31. data/lib/kitchen/transport/ssh.rb +13 -6
  32. data/lib/kitchen/transport/winrm.rb +168 -50
  33. data/lib/kitchen/verifier/base.rb +18 -1
  34. data/lib/kitchen/verifier/busser.rb +12 -5
  35. data/lib/kitchen/verifier/shell.rb +101 -0
  36. data/lib/kitchen/version.rb +1 -2
  37. data/spec/kitchen/collection_spec.rb +1 -1
  38. data/spec/kitchen/configurable_spec.rb +211 -2
  39. data/spec/kitchen/driver/ssh_base_spec.rb +131 -8
  40. data/spec/kitchen/lazy_hash_spec.rb +27 -0
  41. data/spec/kitchen/logger_spec.rb +10 -0
  42. data/spec/kitchen/provisioner/base_spec.rb +25 -0
  43. data/spec/kitchen/provisioner/chef_base_spec.rb +133 -252
  44. data/spec/kitchen/provisioner/chef_solo_spec.rb +30 -0
  45. data/spec/kitchen/provisioner/chef_zero_spec.rb +27 -2
  46. data/spec/kitchen/provisioner/shell_spec.rb +60 -12
  47. data/spec/kitchen/ssh_spec.rb +1 -1
  48. data/spec/kitchen/transport/ssh_spec.rb +2 -1
  49. data/spec/kitchen/transport/winrm_spec.rb +228 -48
  50. data/spec/kitchen/verifier/base_spec.rb +26 -0
  51. data/spec/kitchen/verifier/busser_spec.rb +69 -3
  52. data/spec/kitchen/verifier/shell_spec.rb +157 -0
  53. data/support/busser_install_command.sh +1 -2
  54. data/support/chef_base_install_command.ps1 +19 -12
  55. data/templates/driver/README.md.erb +1 -1
  56. data/test-kitchen.gemspec +12 -3
  57. metadata +106 -10
@@ -24,9 +24,7 @@ require "uri"
24
24
  require "kitchen"
25
25
 
26
26
  module Kitchen
27
-
28
27
  module Transport
29
-
30
28
  # Wrapped exception for any internally raised WinRM-related errors.
31
29
  #
32
30
  # @author Fletcher Nichol <fnichol@nichol.ca>
@@ -38,19 +36,43 @@ module Kitchen
38
36
  # @author Salim Afiune <salim@afiunemaya.com.mx>
39
37
  # @author Fletcher Nichol <fnichol@nichol.ca>
40
38
  class Winrm < Kitchen::Transport::Base
41
-
42
39
  kitchen_transport_api_version 1
43
40
 
44
41
  plugin_version Kitchen::VERSION
45
42
 
46
- default_config :port, 5985
47
43
  default_config :username, "administrator"
48
44
  default_config :password, nil
49
- default_config :endpoint_template, "http://%{hostname}:%{port}/wsman"
50
45
  default_config :rdp_port, 3389
51
46
  default_config :connection_retries, 5
52
47
  default_config :connection_retry_sleep, 1
53
48
  default_config :max_wait_until_ready, 600
49
+ default_config :winrm_transport, "plaintext"
50
+ default_config :port do |transport|
51
+ transport[:winrm_transport] == :ssl ? 5986 : 5985
52
+ end
53
+ default_config :endpoint_template do |transport|
54
+ scheme = transport[:winrm_transport] == :ssl ? "https" : "http"
55
+ "#{scheme}://%{hostname}:%{port}/wsman"
56
+ end
57
+
58
+ def finalize_config!(instance)
59
+ super
60
+
61
+ transport = config[:winrm_transport].to_sym
62
+ config[:winrm_transport] =
63
+ case transport
64
+ when :sspinegotiate
65
+ if host_os_windows?
66
+ transport
67
+ else
68
+ :plaintext
69
+ end
70
+ else
71
+ transport
72
+ end
73
+
74
+ self
75
+ end
54
76
 
55
77
  # (see Base#connection)
56
78
  def connection(state, &block)
@@ -70,7 +92,6 @@ module Kitchen
70
92
  #
71
93
  # @author Fletcher Nichol <fnichol@nichol.ca>
72
94
  class Connection < Kitchen::Transport::Base::Connection
73
-
74
95
  # (see Base::Connection#close)
75
96
  def close
76
97
  return if @session.nil?
@@ -87,6 +108,10 @@ module Kitchen
87
108
  def execute(command)
88
109
  return if command.nil?
89
110
  logger.debug("[WinRM] #{self} (#{command})")
111
+
112
+ if command.length > MAX_COMMAND_SIZE
113
+ command = run_from_file_command(command)
114
+ end
90
115
  exit_code, stderr = execute_with_exit_code(command)
91
116
 
92
117
  if logger.debug? && exit_code == 0
@@ -108,7 +133,7 @@ module Kitchen
108
133
  when /linux/
109
134
  login_command_for_linux
110
135
  else
111
- raise ActionFailed, "Remote login not supported in #{self.class} " \
136
+ fail ActionFailed, "Remote login not supported in #{self.class} " \
112
137
  "from host OS '#{RbConfig::CONFIG["host_os"]}'."
113
138
  end
114
139
  end
@@ -122,9 +147,9 @@ module Kitchen
122
147
  def wait_until_ready
123
148
  delay = 3
124
149
  session(
125
- :retries => max_wait_until_ready / delay,
126
- :delay => delay,
127
- :message => "Waiting for WinRM service on #{endpoint}, " \
150
+ :retries => max_wait_until_ready / delay,
151
+ :delay => delay,
152
+ :message => "Waiting for WinRM service on #{endpoint}, " \
128
153
  "retrying in #{delay} seconds"
129
154
  )
130
155
  execute(PING_COMMAND.dup)
@@ -134,9 +159,14 @@ module Kitchen
134
159
 
135
160
  PING_COMMAND = "Write-Host '[WinRM] Established\n'".freeze
136
161
 
162
+ # Maximum string to send to the transport to execute. WinRM has an 8000 character
163
+ # command line limit. The original command string is coverted to a base 64 encoded
164
+ # UTF-16 string which will double the string size.
165
+ MAX_COMMAND_SIZE = 3000
166
+
137
167
  RESCUE_EXCEPTIONS_ON_ESTABLISH = lambda do
138
168
  [
139
- Errno::EACCES, Errno::EADDRINUSE, Errno::ECONNREFUSED,
169
+ Errno::EACCES, Errno::EADDRINUSE, Errno::ECONNREFUSED, Errno::ETIMEDOUT,
140
170
  Errno::ECONNRESET, Errno::ENETUNREACH, Errno::EHOSTUNREACH,
141
171
  ::WinRM::WinRMHTTPTransportError, ::WinRM::WinRMAuthorizationError,
142
172
  HTTPClient::KeepAliveDisconnected,
@@ -290,9 +320,9 @@ module Kitchen
290
320
  # @return [LoginCommand] a login command
291
321
  # @api private
292
322
  def login_command_for_linux
293
- args = %W[ -u #{options[:user]} ]
294
- args += %W[ -p #{options[:pass]} ] if options.key?(:pass)
295
- args += %W[ #{URI.parse(endpoint).host}:#{rdp_port} ]
323
+ args = %W[-u #{options[:user]}]
324
+ args += %W[-p #{options[:pass]}] if options.key?(:pass)
325
+ args += %W[#{URI.parse(endpoint).host}:#{rdp_port}]
296
326
 
297
327
  LoginCommand.new("rdesktop", args)
298
328
  end
@@ -364,7 +394,7 @@ module Kitchen
364
394
  def session(retry_options = {})
365
395
  @session ||= establish_shell({
366
396
  :retries => connection_retries.to_i,
367
- :delay => connection_retry_sleep.to_i
397
+ :delay => connection_retry_sleep.to_i
368
398
  }.merge(retry_options))
369
399
  end
370
400
 
@@ -375,11 +405,36 @@ module Kitchen
375
405
  def to_s
376
406
  "#{winrm_transport}::#{endpoint}<#{options.inspect}>"
377
407
  end
408
+
409
+ # takes a long (greater than 3000 characters) command and saves it to a
410
+ # file and uploads it to the test instance.
411
+ #
412
+ # @param command [String] a long command to be saved and uploaded
413
+ # @return [String] a command that executes the uploaded script
414
+ # @api private
415
+ def run_from_file_command(command)
416
+ temp_dir = Dir.mktmpdir("kitchen-long-script")
417
+ begin
418
+ script_name = "#{instance_name}-long_script.ps1"
419
+ script_path = File.join(temp_dir, script_name)
420
+
421
+ File.open(script_path, "wb") do |file|
422
+ file.write(command)
423
+ end
424
+
425
+ target_path = File.join("$env:TEMP", "kitchen")
426
+ upload(script_path, target_path)
427
+
428
+ %{& "#{File.join(target_path, script_name)}"}
429
+ ensure
430
+ FileUtils.rmtree(temp_dir)
431
+ end
432
+ end
378
433
  end
379
434
 
380
435
  private
381
436
 
382
- WINRM_TRANSPORT_SPEC_VERSION = "~> 1.0".freeze
437
+ WINRM_TRANSPORT_SPEC_VERSION = ["~> 1.0", ">= 1.0.3"].freeze
383
438
 
384
439
  # Builds the hash of options needed by the Connection object on
385
440
  # construction.
@@ -389,24 +444,38 @@ module Kitchen
389
444
  # @api private
390
445
  def connection_options(data)
391
446
  opts = {
392
- :instance_name => instance.name,
393
- :kitchen_root => data[:kitchen_root],
394
- :logger => logger,
395
- :winrm_transport => :plaintext,
396
- :disable_sspi => true,
397
- :basic_auth_only => true,
398
- :endpoint => data[:endpoint_template] % data,
399
- :user => data[:username],
400
- :pass => data[:password],
401
- :rdp_port => data[:rdp_port],
402
- :connection_retries => data[:connection_retries],
447
+ :instance_name => instance.name,
448
+ :kitchen_root => data[:kitchen_root],
449
+ :logger => logger,
450
+ :endpoint => data[:endpoint_template] % data,
451
+ :user => data[:username],
452
+ :pass => data[:password],
453
+ :rdp_port => data[:rdp_port],
454
+ :connection_retries => data[:connection_retries],
403
455
  :connection_retry_sleep => data[:connection_retry_sleep],
404
- :max_wait_until_ready => data[:max_wait_until_ready]
456
+ :max_wait_until_ready => data[:max_wait_until_ready],
457
+ :winrm_transport => data[:winrm_transport]
405
458
  }
406
-
459
+ opts.merge!(additional_transport_args(opts[:winrm_transport]))
407
460
  opts
408
461
  end
409
462
 
463
+ def additional_transport_args(transport_type)
464
+ case transport_type
465
+ when :ssl, :sspinegotiate
466
+ {
467
+ :no_ssl_peer_verification => true,
468
+ :disable_sspi => false,
469
+ :basic_auth_only => false
470
+ }
471
+ when :plaintext
472
+ {
473
+ :disable_sspi => true,
474
+ :basic_auth_only => true
475
+ }
476
+ end
477
+ end
478
+
410
479
  # Creates a new WinRM Connection instance and save it for potential
411
480
  # future reuse.
412
481
  #
@@ -424,41 +493,90 @@ module Kitchen
424
493
  end
425
494
 
426
495
  # (see Base#load_needed_dependencies!)
427
- def load_needed_dependencies! # rubocop:disable Metrics/MethodLength
496
+ def load_needed_dependencies!
428
497
  super
429
- spec_version = WINRM_TRANSPORT_SPEC_VERSION.dup
430
- logger.debug("Winrm Transport requested," \
431
- " loading WinRM::Transport gem (#{spec_version})")
432
- gem "winrm-transport", spec_version
433
- first_load = require "winrm/transport/version"
434
498
  load_winrm_transport!
435
-
436
- version = ::WinRM::Transport::VERSION
437
- if first_load
438
- logger.debug("WinRM::Transport #{version} library loaded")
439
- else
440
- logger.debug("WinRM::Transport #{version} previously loaded")
441
- end
442
- rescue LoadError => e
443
- logger.fatal("The `winrm-transport' gem is missing and must" \
444
- " be installed or cannot be properly activated. Run" \
445
- " `gem install winrm-transport --version '#{spec_version}'`" \
446
- " or add the following to your Gemfile if you are using Bundler:" \
447
- " `gem 'winrm-transport', '#{spec_version}'`.")
448
- raise UserError,
449
- "Could not load or activate WinRM::Transport (#{e.message})"
499
+ load_winrm_s! if host_os_windows?
450
500
  end
451
501
 
452
502
  # Load WinRM::Transport code.
453
503
  #
454
504
  # @api private
455
505
  def load_winrm_transport!
506
+ spec_version = WINRM_TRANSPORT_SPEC_VERSION.dup
507
+ options = {
508
+ :load_msg => "Winrm Transport requested," \
509
+ " loading WinRM::Transport gem (#{spec_version})",
510
+ :success_msg => "WinRM::Transport library loaded",
511
+ :already_msg => "WinRM::Transport previously loaded",
512
+ :name => "winrm-transport",
513
+ :version => spec_version
514
+ }
515
+
516
+ load_with_rescue!(options) do
517
+ gem "winrm-transport", WINRM_TRANSPORT_SPEC_VERSION.dup
518
+ require "winrm/transport/version"
519
+ end
520
+
456
521
  silence_warnings { require "winrm" }
457
522
  require "winrm/transport/shell_closer"
458
523
  require "winrm/transport/command_executor"
459
524
  require "winrm/transport/file_transporter"
460
525
  end
461
526
 
527
+ def load_winrm_s!
528
+ options = {
529
+ :load_msg => "The winrm-s gem is being loaded" \
530
+ " to enable sspiauthentication.",
531
+ :success_msg => "winrm-s is loaded." \
532
+ " sspinegotiate auth is now available.",
533
+ :already_msg => "winrm-s was already loaded.",
534
+ :name => "winrm-s"
535
+ }
536
+
537
+ load_with_rescue!(options) { require "winrm-s" }
538
+ end
539
+
540
+ def load_with_rescue!(options = {}, &block)
541
+ logger.debug(options[:load_msg])
542
+ attempt_load = execute_block(&block)
543
+ if attempt_load
544
+ logger.debug(options[:success_msg])
545
+ else
546
+ logger.debug(options[:already_msg])
547
+ end
548
+ rescue LoadError => e
549
+ message = fail_to_load_gem_message(options[:name],
550
+ options[:version])
551
+ logger.fatal(message)
552
+ raise UserError,
553
+ "Could not load or activate #{options[:name]}. (#{e.message})"
554
+ end
555
+
556
+ def execute_block
557
+ yield if block_given?
558
+ end
559
+
560
+ def fail_to_load_gem_message(name, version = nil)
561
+ version_cmd = "--version '#{version}'" if version
562
+ version_file = "', '#{version}"
563
+
564
+ "The `#{name}` gem is missing and must" \
565
+ " be installed or cannot be properly activated. Run" \
566
+ " `gem install #{name} #{version_cmd}`" \
567
+ " or add the following to your Gemfile if you are using Bundler:" \
568
+ " `gem '#{name} #{version_file}'`."
569
+ end
570
+
571
+ def host_os_windows?
572
+ case RbConfig::CONFIG["host_os"]
573
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
574
+ true
575
+ else
576
+ false
577
+ end
578
+ end
579
+
462
580
  # Return the last saved WinRM connection instance.
463
581
  #
464
582
  # @return [Winrm::Connection] a WinRM Connection instance
@@ -33,8 +33,8 @@ module Kitchen
33
33
  include Logging
34
34
 
35
35
  default_config :http_proxy, nil
36
-
37
36
  default_config :https_proxy, nil
37
+ default_config :ftp_proxy, nil
38
38
 
39
39
  default_config :root_path do |verifier|
40
40
  verifier.windows_os? ? "$env:TEMP\\verifier" : "/tmp/verifier"
@@ -44,10 +44,14 @@ module Kitchen
44
44
  verifier.windows_os? ? nil : true
45
45
  end
46
46
 
47
+ default_config :chef_omnibus_root, "/opt/chef"
48
+
47
49
  default_config :sudo_command do |verifier|
48
50
  verifier.windows_os? ? nil : "sudo -E"
49
51
  end
50
52
 
53
+ default_config :command_prefix, nil
54
+
51
55
  default_config(:suite_name) { |busser| busser.instance.suite.name }
52
56
 
53
57
  # Creates a new Verifier object using the provided configuration data
@@ -213,6 +217,19 @@ module Kitchen
213
217
  def sudo(script)
214
218
  config[:sudo] ? "#{config[:sudo_command]} #{script}" : script
215
219
  end
220
+
221
+ # Conditionally prefixes a command with a command prefix.
222
+ # This should generally be done after a command has been
223
+ # conditionally prefixed by #sudo as certain platforms, such as
224
+ # Cisco Nexus, require all commands to be run with a prefix to
225
+ # obtain outbound network access.
226
+ #
227
+ # @param command [String] command to be prefixed
228
+ # @return [String] the command, conditionally prefixed with the configured prefix
229
+ # @api private
230
+ def prefix_command(script)
231
+ config[:command_prefix] ? "#{config[:command_prefix]} #{script}" : script
232
+ end
216
233
  end
217
234
  end
218
235
  end
@@ -46,7 +46,7 @@ module Kitchen
46
46
  if verifier.windows_os?
47
47
  "$env:systemdrive\\opscode\\chef\\embedded\\bin"
48
48
  else
49
- "/opt/chef/embedded/bin"
49
+ verifier.remote_path_join(%W[#{verifier[:chef_omnibus_root]} embedded bin])
50
50
  end
51
51
  end
52
52
 
@@ -76,7 +76,7 @@ module Kitchen
76
76
  cmd = sudo(config[:busser_bin]).dup.
77
77
  tap { |str| str.insert(0, "& ") if powershell_shell? }
78
78
 
79
- wrap_shell_code(Util.outdent!(<<-CMD))
79
+ prefix_command(wrap_shell_code(Util.outdent!(<<-CMD)))
80
80
  #{busser_env}
81
81
 
82
82
  #{cmd} suite cleanup
@@ -89,7 +89,7 @@ module Kitchen
89
89
 
90
90
  vars = install_command_vars
91
91
 
92
- shell_code_from_file(vars, "busser_install_command")
92
+ prefix_command(shell_code_from_file(vars, "busser_install_command"))
93
93
  end
94
94
 
95
95
  # (see Base#run_command)
@@ -99,7 +99,7 @@ module Kitchen
99
99
  cmd = sudo(config[:busser_bin]).dup.
100
100
  tap { |str| str.insert(0, "& ") if powershell_shell? }
101
101
 
102
- wrap_shell_code(Util.outdent!(<<-CMD))
102
+ prefix_command(wrap_shell_code(Util.outdent!(<<-CMD)))
103
103
  #{busser_env}
104
104
 
105
105
  #{cmd} test
@@ -173,9 +173,16 @@ module Kitchen
173
173
  gem, version = config[:version].split("@")
174
174
  gem, version = "busser", gem if gem =~ /^\d+\.\d+\.\d+/
175
175
 
176
+ root = config[:root_path]
177
+ gem_bin = remote_path_join(root, "bin")
178
+
179
+ # We don't want the gems to be installed in the home directory,
180
+ # this will force the bindir and the gem install location both
181
+ # to be under /tmp/verifier
176
182
  args = gem
177
183
  args += " --version #{version}" if version
178
- args += " --no-rdoc --no-ri"
184
+ args += " --no-rdoc --no-ri --no-format-executable -n #{gem_bin}"
185
+ args += " --no-user-install"
179
186
  args
180
187
  end
181
188
 
@@ -0,0 +1,101 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: SAWANOBORI Yukihiko (<sawanoboriyu@higanworks.com>)
4
+ #
5
+ # Copyright (C) 2015, HiganWorks LLC
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require "kitchen/verifier/base"
20
+
21
+ module Kitchen
22
+
23
+ module Verifier
24
+
25
+ # Shell verifier for Kitchen. This verifier just execute shell command from local.
26
+ #
27
+ # @author SAWANOBORI Yukihiko (<sawanoboriyu@higanworks.com>)
28
+ class Shell < Kitchen::Verifier::Base
29
+ require "mixlib/shellout"
30
+
31
+ kitchen_verifier_api_version 1
32
+
33
+ plugin_version Kitchen::VERSION
34
+
35
+ default_config :sleep, 0
36
+ default_config :command, "true"
37
+ default_config :shellout_opts, {}
38
+ default_config :live_stream, $stdout
39
+ default_config :remote_exec, false
40
+
41
+ # (see Base#call)
42
+ def call(state)
43
+ info("[#{name}] Verify on instance=#{instance} with state=#{state}")
44
+ sleep_if_set
45
+ merge_state_to_env(state)
46
+ if config[:remote_exec]
47
+ instance.transport.connection(state) do |conn|
48
+ conn.execute(config[:command])
49
+ end
50
+ else
51
+ shellout
52
+ end
53
+ debug("[#{name}] Verify completed.")
54
+ end
55
+
56
+ # for legacy drivers.
57
+ def run_command
58
+ if config[:remote_exec]
59
+ config[:command]
60
+ else
61
+ shellout
62
+ nil
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ # Sleep for a period of time, if a value is set in the config.
69
+ #
70
+ # @api private
71
+ def sleep_if_set
72
+ config[:sleep].to_i.times do
73
+ info(".")
74
+ sleep 1
75
+ end
76
+ end
77
+
78
+ def shellout
79
+ cmd = Mixlib::ShellOut.new(config[:command], config[:shellout_opts])
80
+ cmd.live_stream = config[:live_stream]
81
+ cmd.run_command
82
+ begin
83
+ cmd.error!
84
+ rescue Mixlib::ShellOut::ShellCommandFailed
85
+ raise ActionFailed, "Action #verify failed for #{instance.to_str}."
86
+ end
87
+ end
88
+
89
+ def merge_state_to_env(state)
90
+ env_state = { :environment => {} }
91
+ env_state[:environment]["KITCHEN_INSTANCE"] = instance.name
92
+ env_state[:environment]["KITCHEN_PLATFORM"] = instance.platform.name
93
+ env_state[:environment]["KITCHEN_SUITE"] = instance.suite.name
94
+ state.each_pair do |key, value|
95
+ env_state[:environment]["KITCHEN_" + key.to_s.upcase] = value
96
+ end
97
+ config[:shellout_opts].merge!(env_state)
98
+ end
99
+ end
100
+ end
101
+ end
@@ -17,6 +17,5 @@
17
17
  # limitations under the License.
18
18
 
19
19
  module Kitchen
20
-
21
- VERSION = "1.4.2"
20
+ VERSION = "1.5.0"
22
21
  end
@@ -58,7 +58,7 @@ describe Kitchen::Collection do
58
58
  result.get_all(/one/).size.must_equal 1
59
59
  end
60
60
 
61
- it "returns an empty Collection if on matches are found" do
61
+ it "returns an empty Collection if no matches are found" do
62
62
  result = collection.get_all(/noppa/)
63
63
  result.must_equal []
64
64
  result.get("nahuh").must_be_nil