test-kitchen 1.18.0 → 1.19.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +59 -0
  3. data/ECOSYSTEM.md +6 -5
  4. data/lib/kitchen/cli.rb +24 -53
  5. data/lib/kitchen/command/doctor.rb +40 -0
  6. data/lib/kitchen/config.rb +6 -0
  7. data/lib/kitchen/configurable.rb +1 -1
  8. data/lib/kitchen/data_munger.rb +2 -0
  9. data/lib/kitchen/driver/base.rb +26 -1
  10. data/lib/kitchen/driver/dummy.rb +1 -0
  11. data/lib/kitchen/driver/exec.rb +71 -0
  12. data/lib/kitchen/driver/proxy.rb +2 -1
  13. data/lib/kitchen/instance.rb +13 -4
  14. data/lib/kitchen/provisioner/base.rb +8 -0
  15. data/lib/kitchen/provisioner/chef_base.rb +3 -1
  16. data/lib/kitchen/provisioner/shell.rb +21 -23
  17. data/lib/kitchen/transport/base.rb +8 -0
  18. data/lib/kitchen/transport/exec.rb +59 -0
  19. data/lib/kitchen/verifier/base.rb +8 -0
  20. data/lib/kitchen/version.rb +1 -1
  21. data/spec/kitchen/config_spec.rb +13 -0
  22. data/spec/kitchen/driver/base_spec.rb +18 -0
  23. data/spec/kitchen/driver/exec_spec.rb +75 -0
  24. data/spec/kitchen/provisioner/chef_base_spec.rb +9 -0
  25. data/spec/kitchen/provisioner/chef_solo_spec.rb +1 -1
  26. data/spec/kitchen/provisioner/chef_zero_spec.rb +1 -1
  27. data/spec/kitchen/provisioner/shell_spec.rb +22 -20
  28. data/spec/kitchen/ssh_spec.rb +0 -29
  29. data/spec/kitchen/transport/exec_spec.rb +79 -0
  30. data/spec/kitchen/transport/ssh_spec.rb +0 -29
  31. data/spec/spec_helper.rb +26 -0
  32. data/support/busser_install_command.sh +10 -3
  33. metadata +9 -8
  34. data/features/kitchen_driver_create_command.feature +0 -64
  35. data/features/kitchen_driver_discover_command.feature +0 -25
  36. data/lib/kitchen/command/driver_discover.rb +0 -102
  37. data/lib/kitchen/generator/driver_create.rb +0 -174
@@ -18,7 +18,7 @@
18
18
  # limitations under the License.
19
19
  #
20
20
 
21
- require "kitchen"
21
+ require "kitchen/driver/ssh_base"
22
22
  require "kitchen/version"
23
23
 
24
24
  module Kitchen
@@ -40,6 +40,7 @@ module Kitchen
40
40
 
41
41
  # (see Base#create)
42
42
  def create(state)
43
+ # TODO: Once this isn't using SSHBase, it should call `super` to support pre_create_command.
43
44
  state[:hostname] = config[:host]
44
45
  reset_instance(state)
45
46
  end
@@ -54,20 +54,20 @@ module Kitchen
54
54
 
55
55
  # @return [Driver::Base] driver object which will manage this instance's
56
56
  # lifecycle actions
57
- attr_reader :driver
57
+ attr_accessor :driver
58
58
 
59
59
  # @return [Provisioner::Base] provisioner object which will the setup
60
60
  # and invocation instructions for configuration management and other
61
61
  # automation tools
62
- attr_reader :provisioner
62
+ attr_accessor :provisioner
63
63
 
64
64
  # @return [Transport::Base] transport object which will communicate with
65
65
  # an instance.
66
- attr_reader :transport
66
+ attr_accessor :transport
67
67
 
68
68
  # @return [Verifier] verifier object for instance to manage the verifier
69
69
  # installation on this instance
70
- attr_reader :verifier
70
+ attr_accessor :verifier
71
71
 
72
72
  # @return [Logger] the logger for this instance
73
73
  attr_reader :logger
@@ -233,6 +233,15 @@ module Kitchen
233
233
  driver.package(state_file.read)
234
234
  end
235
235
 
236
+ # Check system and configuration for common errors.
237
+ #
238
+ def doctor_action
239
+ banner "The doctor is in"
240
+ [driver, provisioner, transport, verifier].any? do |obj|
241
+ obj.doctor(state_file.read)
242
+ end
243
+ end
244
+
236
245
  # Returns a Hash of configuration and other useful diagnostic information.
237
246
  #
238
247
  # @return [Hash] a diagnostic hash
@@ -85,6 +85,14 @@ module Kitchen
85
85
  cleanup_sandbox
86
86
  end
87
87
 
88
+ # Check system and configuration for common errors.
89
+ #
90
+ # @param state [Hash] mutable instance state
91
+ # @returns [Boolean] Return true if a problem is found.
92
+ def doctor(state)
93
+ false
94
+ end
95
+
88
96
  # Generates a command string which will install and configure the
89
97
  # provisioner software on an instance. If no work is required, then `nil`
90
98
  # will be returned.
@@ -49,7 +49,9 @@ module Kitchen
49
49
  default_config :attributes, {}
50
50
  default_config :config_path, nil
51
51
  default_config :log_file, nil
52
- default_config :log_level, "auto"
52
+ default_config :log_level do |provisioner|
53
+ provisioner[:debug] ? "debug" : "auto"
54
+ end
53
55
  default_config :profile_ruby, false
54
56
  # The older policyfile_zero used `policyfile` so support it for compat.
55
57
  default_config :policyfile, nil
@@ -16,6 +16,8 @@
16
16
  # See the License for the specific language governing permissions and
17
17
  # limitations under the License.
18
18
 
19
+ require "shellwords"
20
+
19
21
  require "kitchen/provisioner/base"
20
22
  require "kitchen/version"
21
23
 
@@ -35,6 +37,12 @@ module Kitchen
35
37
  end
36
38
  expand_path_for :script
37
39
 
40
+ # Run a single command instead of managing and running a script.
41
+ default_config :command, nil
42
+
43
+ # Add extra arguments to the converge script.
44
+ default_config :arguments, []
45
+
38
46
  default_config :data_path do |provisioner|
39
47
  provisioner.calculate_path("data")
40
48
  end
@@ -49,6 +57,7 @@ module Kitchen
49
57
 
50
58
  # (see Base#init_command)
51
59
  def init_command
60
+ return nil if config[:command]
52
61
  root = config[:root_path]
53
62
  data = remote_path_join(root, "data")
54
63
 
@@ -65,18 +74,24 @@ module Kitchen
65
74
  "#{sudo('rm')} -rf #{data} ; mkdir -p #{root}"
66
75
  end
67
76
 
68
- wrap_shell_code(code)
77
+ prefix_command(wrap_shell_code(code))
69
78
  end
70
79
 
71
80
  # (see Base#run_command)
72
81
  def run_command
82
+ return prefix_command(wrap_shell_code(config[:command])) if config[:command]
83
+ return unless config[:script]
73
84
  script = remote_path_join(
74
85
  config[:root_path],
75
86
  File.basename(config[:script])
76
87
  )
77
88
 
78
- if config[:arguments]
79
- script.concat(" ").concat(config[:arguments])
89
+ if config[:arguments] && !config[:arguments].empty?
90
+ if config[:arguments].is_a?(Array)
91
+ script = Shellwords.join([script] + config[:arguments])
92
+ else
93
+ script.concat(" ").concat(config[:arguments].to_s)
94
+ end
80
95
  end
81
96
 
82
97
  code = powershell_shell? ? %{& "#{script}"} : sudo(script)
@@ -111,30 +126,13 @@ module Kitchen
111
126
  if config[:script]
112
127
  debug("Using script from #{config[:script]}")
113
128
  FileUtils.cp_r(config[:script], sandbox_path)
129
+ FileUtils.chmod(0755,
130
+ File.join(sandbox_path, File.basename(config[:script])))
114
131
  else
115
- prepare_stubbed_script
132
+ info("No provisioner script file specified, skipping")
116
133
  end
117
-
118
- FileUtils.chmod(0755,
119
- File.join(sandbox_path, File.basename(config[:script])))
120
134
  end
121
135
 
122
- # Creates a minimal, no-op script in the sandbox path.
123
- #
124
- # @api private
125
- def prepare_stubbed_script
126
- base = powershell_shell? ? "bootstrap.ps1" : "bootstrap.sh"
127
- config[:script] = File.join(sandbox_path, base)
128
- info("#{File.basename(config[:script])} not found " \
129
- "so Kitchen will run a stubbed script. Is this intended?")
130
- File.open(config[:script], "wb") do |file|
131
- if powershell_shell?
132
- file.write(%{Write-Host "NO BOOTSTRAP SCRIPT PRESENT`n"\n})
133
- else
134
- file.write(%{#!/bin/sh\necho "NO BOOTSTRAP SCRIPT PRESENT"\n})
135
- end
136
- end
137
- end
138
136
  end
139
137
  end
140
138
  end
@@ -64,6 +64,14 @@ module Kitchen
64
64
  raise ClientError, "#{self.class}#connection must be implemented"
65
65
  end
66
66
 
67
+ # Check system and configuration for common errors.
68
+ #
69
+ # @param state [Hash] mutable instance state
70
+ # @returns [Boolean] Return true if a problem is found.
71
+ def doctor(state)
72
+ false
73
+ end
74
+
67
75
  # Closes the connection, if it is still active.
68
76
  #
69
77
  # @return [void]
@@ -0,0 +1,59 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require "fileutils"
16
+
17
+ require "kitchen/shell_out"
18
+ require "kitchen/transport/base"
19
+ require "kitchen/version"
20
+
21
+ module Kitchen
22
+ module Transport
23
+ # Exec transport for Kitchen. This transport runs all commands locally.
24
+ #
25
+ # @since 1.19
26
+ class Exec < Kitchen::Transport::Base
27
+ kitchen_transport_api_version 1
28
+
29
+ plugin_version Kitchen::VERSION
30
+
31
+ def connection(state, &block)
32
+ options = config.to_hash.merge(state)
33
+ Kitchen::Transport::Exec::Connection.new(options, &block)
34
+ end
35
+
36
+ # Fake connection which just does local operations.
37
+ class Connection < Kitchen::Transport::Base::Connection
38
+ include ShellOut
39
+
40
+ # (see Base#execute)
41
+ def execute(command)
42
+ return if command.nil?
43
+ run_command(command)
44
+ end
45
+
46
+ # "Upload" the files by copying them locally.
47
+ #
48
+ # @see Base#upload
49
+ def upload(locals, remote)
50
+ FileUtils.mkdir_p(remote)
51
+ Array(locals).each do |local|
52
+ FileUtils.cp_r(local, remote)
53
+ end
54
+ end
55
+
56
+ end
57
+ end
58
+ end
59
+ end
@@ -82,6 +82,14 @@ module Kitchen
82
82
  cleanup_sandbox
83
83
  end
84
84
 
85
+ # Check system and configuration for common errors.
86
+ #
87
+ # @param state [Hash] mutable instance state
88
+ # @returns [Boolean] Return true if a problem is found.
89
+ def doctor(state)
90
+ false
91
+ end
92
+
85
93
  # Deletes the sandbox path. Without calling this method, the sandbox path
86
94
  # will persist after the process terminates. In other words, cleanup is
87
95
  # explicit. This method is safe to call multiple times.
@@ -17,5 +17,5 @@
17
17
  # limitations under the License.
18
18
 
19
19
  module Kitchen
20
- VERSION = "1.18.0".freeze
20
+ VERSION = "1.19.0".freeze
21
21
  end
@@ -63,6 +63,7 @@ describe Kitchen::Config do
63
63
  log_level: :debug,
64
64
  log_overwrite: false,
65
65
  colorize: false,
66
+ debug: true,
66
67
  }
67
68
  end
68
69
 
@@ -166,6 +167,18 @@ describe Kitchen::Config do
166
167
  end
167
168
  end
168
169
 
170
+ describe "#debug" do
171
+ it "returns its debug" do
172
+ config.debug.must_equal true
173
+ end
174
+
175
+ it "uses false by default" do
176
+ opts.delete(:debug)
177
+
178
+ config.debug.must_equal false
179
+ end
180
+ end
181
+
169
182
  describe "#platforms" do
170
183
  before do
171
184
  Kitchen::DataMunger.stubs(:new).returns(munger)
@@ -88,6 +88,24 @@ describe Kitchen::Driver::Base do
88
88
  end
89
89
  end
90
90
 
91
+ describe ".pre_create_command" do
92
+ it "run command" do
93
+ Kitchen::Driver::Base.any_instance.stubs(:run_command).returns(true)
94
+
95
+ config[:pre_create_command] = "echo works 2&>1 > /dev/null"
96
+ driver.expects(:run_command).with("echo works 2&>1 > /dev/null")
97
+ driver.send(:pre_create_command)
98
+ end
99
+
100
+ it "error if cannot run" do
101
+ class ShellCommandFailed < Kitchen::ShellOut::ShellCommandFailed; end
102
+ Kitchen::Driver::Base.any_instance.stubs(:run_command).raises(ShellCommandFailed, "Expected process to exit with [0], but received '1'")
103
+
104
+ config[:pre_create_command] = "touch /this/dir/does/not/exist 2&>1 > /dev/null"
105
+ proc { driver.send(:pre_create_command) }.must_raise Kitchen::ActionFailed
106
+ end
107
+ end
108
+
91
109
  describe ".no_parallel_for" do
92
110
  it "registers no serial actions when none are declared" do
93
111
  Kitchen::Driver::Speedy.serial_actions.must_be_nil
@@ -0,0 +1,75 @@
1
+ #
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ #
14
+
15
+ require_relative "../../spec_helper"
16
+
17
+ require "kitchen/driver/exec"
18
+
19
+ describe Kitchen::Driver::Exec do
20
+ let(:logged_output) { StringIO.new }
21
+ let(:logger) { Logger.new(logged_output) }
22
+ let(:state) { Hash.new }
23
+
24
+ let(:config) do
25
+ { reset_command: "mulligan" }
26
+ end
27
+
28
+ let(:instance) do
29
+ stub(name: "coolbeans", logger: logger, to_str: "instance", :"transport=" => nil)
30
+ end
31
+
32
+ let(:driver) do
33
+ Kitchen::Driver::Exec.new(config).finalize_config!(instance)
34
+ end
35
+
36
+ it "plugin_version is set to Kitchen::VERSION" do
37
+ driver.diagnose_plugin[:version].must_equal Kitchen::VERSION
38
+ end
39
+
40
+ it "sets the transport to exec" do
41
+ instance.expects(:"transport=").with { |v| v.is_a?(Kitchen::Transport::Exec) }
42
+ driver
43
+ end
44
+
45
+ describe "#create" do
46
+ it "runs the reset command" do
47
+ driver.expects(:run_command).with("mulligan")
48
+
49
+ driver.create(state)
50
+ end
51
+
52
+ it "skips the reset command if :reset_command is falsey" do
53
+ config[:reset_command] = false
54
+ driver.expects(:run_command).never
55
+
56
+ driver.create(state)
57
+ end
58
+ end
59
+
60
+ describe "#destroy" do
61
+ it "calls the reset command" do
62
+ driver.expects(:run_command).with("mulligan")
63
+
64
+ driver.destroy(state)
65
+ end
66
+
67
+ it "skips reset command if :reset_command is falsey" do
68
+ config[:reset_command] = false
69
+ driver.expects(:run_command).never
70
+
71
+ driver.destroy(state)
72
+ end
73
+ end
74
+
75
+ end
@@ -90,6 +90,15 @@ describe Kitchen::Provisioner::ChefBase do
90
90
  provisioner[:attributes].must_equal Hash.new
91
91
  end
92
92
 
93
+ it ":log_level defaults to auto" do
94
+ provisioner[:log_level].must_equal "auto"
95
+ end
96
+
97
+ it ":log_level is debug when in debug mode" do
98
+ config[:debug] = true
99
+ provisioner[:log_level].must_equal "debug"
100
+ end
101
+
93
102
  it ":log_file defaults to nil" do
94
103
  provisioner[:log_file].must_be_nil
95
104
  end
@@ -285,7 +285,7 @@ describe Kitchen::Provisioner::ChefSolo do
285
285
  config[:enforce_idempotency] = true
286
286
  provisioner.create_sandbox
287
287
 
288
- file_no_updated_resources.join.must_match /handler_file =.*chef-client-fail-if-update-handler.rb/
288
+ file_no_updated_resources.join.must_match(/handler_file =.*chef-client-fail-if-update-handler.rb/)
289
289
  end
290
290
  end
291
291
 
@@ -310,7 +310,7 @@ describe Kitchen::Provisioner::ChefZero do
310
310
  config[:enforce_idempotency] = true
311
311
  provisioner.create_sandbox
312
312
 
313
- file_no_updated_resources.join.must_match /handler_file =.*chef-client-fail-if-update-handler.rb/
313
+ file_no_updated_resources.join.must_match(/handler_file =.*chef-client-fail-if-update-handler.rb/)
314
314
  end
315
315
  end
316
316
 
@@ -163,6 +163,12 @@ describe Kitchen::Provisioner::Shell do
163
163
 
164
164
  cmd.must_match regexify("mkdir -p /root/path", :partial_line)
165
165
  end
166
+
167
+ it "respects command" do
168
+ config[:command] = "asdf"
169
+
170
+ cmd.must_be_nil
171
+ end
166
172
  end
167
173
 
168
174
  describe "for powershell shells on windows os types" do
@@ -319,6 +325,12 @@ describe Kitchen::Provisioner::Shell do
319
325
  config[:arguments] = "--php 70 --mysql 57"
320
326
  cmd.must_match(/--php 70 --mysql 57/)
321
327
  end
328
+
329
+ it "respects command" do
330
+ config[:command] = "dothingy.rb"
331
+
332
+ cmd.must_match regexify("dothingy.rb", :partial_line)
333
+ end
322
334
  end
323
335
 
324
336
  describe "for powershell shells on windows os types" do
@@ -469,6 +481,10 @@ describe Kitchen::Provisioner::Shell do
469
481
  describe "with no :script file" do
470
482
  before { config[:script] = nil }
471
483
 
484
+ it "has no run command" do
485
+ provisioner.run_command.must_be_nil
486
+ end
487
+
472
488
  describe "for bourne shells" do
473
489
  before { platform.stubs(:shell_type).returns("bourne") }
474
490
 
@@ -482,20 +498,13 @@ describe Kitchen::Provisioner::Shell do
482
498
  provisioner.create_sandbox
483
499
 
484
500
  logged_output.string.must_match info_line(
485
- "bootstrap.sh not found so Kitchen will run a stubbed script. " \
486
- "Is this intended?")
501
+ "No provisioner script file specified, skipping")
487
502
  end
488
503
 
489
- it "creates a file in the sandbox directory" do
504
+ it "does not create a file in the sandbox directory" do
490
505
  provisioner.create_sandbox
491
506
 
492
- sandbox_path("bootstrap.sh").file?.must_equal true
493
- unless running_tests_on_windows?
494
- # Windows doesn't have the concept of executable
495
- sandbox_path("bootstrap.sh").executable?.must_equal true
496
- end
497
- IO.read(sandbox_path("bootstrap.sh"))
498
- .must_match(/NO BOOTSTRAP SCRIPT PRESENT/)
507
+ sandbox_path("bootstrap.sh").file?.must_equal false
499
508
  end
500
509
  end
501
510
 
@@ -512,20 +521,13 @@ describe Kitchen::Provisioner::Shell do
512
521
  provisioner.create_sandbox
513
522
 
514
523
  logged_output.string.must_match info_line(
515
- "bootstrap.ps1 not found so Kitchen will run a stubbed script. " \
516
- "Is this intended?")
524
+ "No provisioner script file specified, skipping")
517
525
  end
518
526
 
519
- it "creates a file in the sandbox directory" do
527
+ it "does not create a file in the sandbox directory" do
520
528
  provisioner.create_sandbox
521
529
 
522
- sandbox_path("bootstrap.ps1").file?.must_equal true
523
- unless running_tests_on_windows?
524
- # Windows doesn't have the concept of executable
525
- sandbox_path("bootstrap.ps1").executable?.must_equal true
526
- end
527
- IO.read(sandbox_path("bootstrap.ps1"))
528
- .must_match(/Write-Host "NO BOOTSTRAP SCRIPT PRESENT`n"/)
530
+ sandbox_path("bootstrap.ps1").file?.must_equal false
529
531
  end
530
532
  end
531
533
  end