test-kitchen 1.3.1 → 1.4.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +2 -0
  3. data/.gitignore +4 -0
  4. data/CHANGELOG.md +45 -0
  5. data/Rakefile +15 -0
  6. data/features/kitchen_action_commands.feature +12 -9
  7. data/features/kitchen_defaults.feature +38 -0
  8. data/features/kitchen_init_command.feature +0 -1
  9. data/features/kitchen_list_command.feature +2 -2
  10. data/features/kitchen_login_command.feature +7 -1
  11. data/features/kitchen_test_command.feature +4 -4
  12. data/lib/kitchen.rb +40 -11
  13. data/lib/kitchen/cli.rb +38 -22
  14. data/lib/kitchen/command/list.rb +5 -2
  15. data/lib/kitchen/config.rb +45 -18
  16. data/lib/kitchen/configurable.rb +137 -1
  17. data/lib/kitchen/data_munger.rb +248 -17
  18. data/lib/kitchen/driver.rb +1 -1
  19. data/lib/kitchen/driver/base.rb +1 -83
  20. data/lib/kitchen/driver/dummy.rb +0 -5
  21. data/lib/kitchen/driver/ssh_base.rb +177 -22
  22. data/lib/kitchen/instance.rb +140 -20
  23. data/lib/kitchen/logger.rb +43 -8
  24. data/lib/kitchen/login_command.rb +14 -5
  25. data/lib/kitchen/platform.rb +19 -0
  26. data/lib/kitchen/provisioner.rb +5 -3
  27. data/lib/kitchen/provisioner/base.rb +46 -48
  28. data/lib/kitchen/provisioner/chef/common_sandbox.rb +322 -0
  29. data/lib/kitchen/provisioner/chef_base.rb +179 -286
  30. data/lib/kitchen/provisioner/chef_solo.rb +11 -5
  31. data/lib/kitchen/provisioner/chef_zero.rb +108 -94
  32. data/lib/kitchen/provisioner/dummy.rb +47 -0
  33. data/lib/kitchen/provisioner/shell.rb +45 -12
  34. data/lib/kitchen/rake_tasks.rb +1 -1
  35. data/lib/kitchen/ssh.rb +1 -1
  36. data/lib/kitchen/thor_tasks.rb +1 -1
  37. data/lib/kitchen/transport.rb +54 -0
  38. data/lib/kitchen/transport/base.rb +146 -0
  39. data/lib/kitchen/transport/dummy.rb +75 -0
  40. data/lib/kitchen/transport/ssh.rb +325 -0
  41. data/lib/kitchen/transport/winrm.rb +508 -0
  42. data/lib/kitchen/transport/winrm/command_executor.rb +188 -0
  43. data/lib/kitchen/transport/winrm/file_transporter.rb +454 -0
  44. data/lib/kitchen/transport/winrm/logging.rb +50 -0
  45. data/lib/kitchen/transport/winrm/template.rb +74 -0
  46. data/lib/kitchen/transport/winrm/tmp_zip.rb +187 -0
  47. data/lib/kitchen/verifier.rb +55 -0
  48. data/lib/kitchen/verifier/base.rb +191 -0
  49. data/lib/kitchen/verifier/busser.rb +266 -0
  50. data/lib/kitchen/verifier/dummy.rb +75 -0
  51. data/lib/kitchen/version.rb +1 -1
  52. data/spec/kitchen/cli_spec.rb +56 -0
  53. data/spec/kitchen/config_spec.rb +61 -20
  54. data/spec/kitchen/configurable_spec.rb +327 -1
  55. data/spec/kitchen/data_munger_spec.rb +777 -14
  56. data/spec/kitchen/driver/base_spec.rb +7 -38
  57. data/spec/kitchen/driver/dummy_spec.rb +0 -29
  58. data/spec/kitchen/driver/ssh_base_spec.rb +580 -236
  59. data/spec/kitchen/driver_spec.rb +1 -0
  60. data/spec/kitchen/instance_spec.rb +383 -83
  61. data/spec/kitchen/login_command_spec.rb +29 -10
  62. data/spec/kitchen/platform_spec.rb +58 -2
  63. data/spec/kitchen/provisioner/base_spec.rb +170 -18
  64. data/spec/kitchen/provisioner/chef_base_spec.rb +454 -104
  65. data/spec/kitchen/provisioner/chef_solo_spec.rb +307 -104
  66. data/spec/kitchen/provisioner/chef_zero_spec.rb +561 -230
  67. data/spec/kitchen/provisioner/dummy_spec.rb +91 -0
  68. data/spec/kitchen/provisioner/shell_spec.rb +158 -56
  69. data/spec/kitchen/provisioner_spec.rb +37 -0
  70. data/spec/kitchen/ssh_spec.rb +19 -19
  71. data/spec/kitchen/transport/base_spec.rb +89 -0
  72. data/spec/kitchen/transport/ssh_spec.rb +1147 -0
  73. data/spec/kitchen/transport/winrm/command_executor_spec.rb +400 -0
  74. data/spec/kitchen/transport/winrm/file_transporter_spec.rb +876 -0
  75. data/spec/kitchen/transport/winrm/logging_spec.rb +92 -0
  76. data/spec/kitchen/transport/winrm/template_spec.rb +51 -0
  77. data/spec/kitchen/transport/winrm/tmp_zip_spec.rb +132 -0
  78. data/spec/kitchen/transport/winrm_spec.rb +1069 -0
  79. data/spec/kitchen/transport_spec.rb +112 -0
  80. data/spec/kitchen/verifier/base_spec.rb +310 -0
  81. data/spec/kitchen/verifier/busser_spec.rb +540 -0
  82. data/spec/kitchen/verifier/dummy_spec.rb +91 -0
  83. data/spec/kitchen/verifier_spec.rb +120 -0
  84. data/spec/kitchen_spec.rb +7 -0
  85. data/spec/spec_helper.rb +8 -0
  86. data/spec/support/powershell_max_size_spec.rb +40 -0
  87. data/support/busser_install_command.ps1 +14 -0
  88. data/support/busser_install_command.sh +15 -0
  89. data/support/check_files.ps1.erb +48 -0
  90. data/support/chef_base_init_command.ps1 +18 -0
  91. data/support/chef_base_init_command.sh +2 -0
  92. data/support/chef_base_install_command.ps1 +76 -0
  93. data/support/chef_base_install_command.sh +137 -0
  94. data/support/chef_zero_prepare_command_legacy.ps1 +9 -0
  95. data/support/chef_zero_prepare_command_legacy.sh +10 -0
  96. data/support/decode_files.ps1.erb +61 -0
  97. data/test-kitchen.gemspec +2 -0
  98. metadata +97 -8
  99. data/lib/kitchen/busser.rb +0 -316
  100. data/spec/kitchen/busser_spec.rb +0 -490
  101. data/support/chef_helpers.sh +0 -16
@@ -21,7 +21,7 @@ require "thor/util"
21
21
  module Kitchen
22
22
 
23
23
  # A driver is responsible for carrying out the lifecycle activities of an
24
- # instance, such as creating, converging, and destroying an instance.
24
+ # instance, such as creating and destroying an instance.
25
25
  #
26
26
  # @author Fletcher Nichol <fnichol@nichol.ca>
27
27
  module Driver
@@ -16,8 +16,6 @@
16
16
  # See the License for the specific language governing permissions and
17
17
  # limitations under the License.
18
18
 
19
- require "thor/util"
20
-
21
19
  require "kitchen/lazy_hash"
22
20
 
23
21
  module Kitchen
@@ -29,7 +27,6 @@ module Kitchen
29
27
  # @author Fletcher Nichol <fnichol@nichol.ca>
30
28
  class Base
31
29
 
32
- include ShellOut
33
30
  include Configurable
34
31
  include Logging
35
32
 
@@ -41,13 +38,6 @@ module Kitchen
41
38
  init_config(config)
42
39
  end
43
40
 
44
- # Returns the name of this driver, suitable for display in a CLI.
45
- #
46
- # @return [String] name of this driver
47
- def name
48
- self.class.name.split("::").last
49
- end
50
-
51
41
  # Creates an instance.
52
42
  #
53
43
  # @param state [Hash] mutable instance and driver state
@@ -55,27 +45,6 @@ module Kitchen
55
45
  def create(state) # rubocop:disable Lint/UnusedMethodArgument
56
46
  end
57
47
 
58
- # Converges a running instance.
59
- #
60
- # @param state [Hash] mutable instance and driver state
61
- # @raise [ActionFailed] if the action could not be completed
62
- def converge(state) # rubocop:disable Lint/UnusedMethodArgument
63
- end
64
-
65
- # Sets up an instance.
66
- #
67
- # @param state [Hash] mutable instance and driver state
68
- # @raise [ActionFailed] if the action could not be completed
69
- def setup(state) # rubocop:disable Lint/UnusedMethodArgument
70
- end
71
-
72
- # Verifies a converged instance.
73
- #
74
- # @param state [Hash] mutable instance and driver state
75
- # @raise [ActionFailed] if the action could not be completed
76
- def verify(state) # rubocop:disable Lint/UnusedMethodArgument
77
- end
78
-
79
48
  # Destroys an instance.
80
49
  #
81
50
  # @param state [Hash] mutable instance and driver state
@@ -83,26 +52,6 @@ module Kitchen
83
52
  def destroy(state) # rubocop:disable Lint/UnusedMethodArgument
84
53
  end
85
54
 
86
- # Returns the shell command that will log into an instance.
87
- #
88
- # @param state [Hash] mutable instance and driver state
89
- # @return [LoginCommand] an object containing the array of command line
90
- # tokens and exec options to be used in a fork/exec
91
- # @raise [ActionFailed] if the action could not be completed
92
- def login_command(state) # rubocop:disable Lint/UnusedMethodArgument
93
- raise ActionFailed, "Remote login is not supported in this driver."
94
- end
95
-
96
- # Performs whatever tests that may be required to ensure that this driver
97
- # will be able to function in the current environment. This may involve
98
- # checking for the presence of certain directories, software installed,
99
- # etc.
100
- #
101
- # @raise [UserError] if the driver will not be able to perform or if a
102
- # documented dependency is missing from the system
103
- def verify_dependencies
104
- end
105
-
106
55
  class << self
107
56
  # @return [Array<Symbol>] an array of action method names that cannot
108
57
  # be run concurrently and must be run in serial via a shared mutex
@@ -128,7 +77,7 @@ module Kitchen
128
77
  # @param methods [Array<Symbol>] one or more actions as symbols
129
78
  # @raise [ClientError] if any method is not a valid action method name
130
79
  def self.no_parallel_for(*methods)
131
- action_methods = [:create, :converge, :setup, :verify, :destroy]
80
+ action_methods = [:create, :setup, :verify, :destroy]
132
81
 
133
82
  Array(methods).each do |meth|
134
83
  next if action_methods.include?(meth)
@@ -142,13 +91,6 @@ module Kitchen
142
91
 
143
92
  private
144
93
 
145
- # Returns a suitable logger to use for output.
146
- #
147
- # @return [Kitchen::Logger] a logger
148
- def logger
149
- instance ? instance.logger : Kitchen.logger
150
- end
151
-
152
94
  # Intercepts any bare #puts calls in subclasses and issues an INFO log
153
95
  # event instead.
154
96
  #
@@ -164,30 +106,6 @@ module Kitchen
164
106
  def print(msg)
165
107
  info(msg)
166
108
  end
167
-
168
- # Delegates to Kitchen::ShellOut.run_command, overriding some default
169
- # options:
170
- #
171
- # * `:use_sudo` defaults to the value of `config[:use_sudo]` in the
172
- # Driver object
173
- # * `:log_subject` defaults to a String representation of the Driver's
174
- # class name
175
- #
176
- # @see ShellOut#run_command
177
- def run_command(cmd, options = {})
178
- base_options = {
179
- :use_sudo => config[:use_sudo],
180
- :log_subject => Thor::Util.snake_case(self.class.to_s)
181
- }.merge(options)
182
- super(cmd, base_options)
183
- end
184
-
185
- # Returns the Busser object associated with the driver.
186
- #
187
- # @return [Busser] a busser
188
- def busser
189
- instance.busser
190
- end
191
109
  end
192
110
  end
193
111
  end
@@ -39,11 +39,6 @@ module Kitchen
39
39
  report(:create, state)
40
40
  end
41
41
 
42
- # (see Base#converge)
43
- def converge(state)
44
- report(:converge, state)
45
- end
46
-
47
42
  # (see Base#setup)
48
43
  def setup(state)
49
44
  report(:setup, state)
@@ -16,56 +16,106 @@
16
16
  # See the License for the specific language governing permissions and
17
17
  # limitations under the License.
18
18
 
19
+ require "thor/util"
20
+
21
+ require "kitchen/lazy_hash"
22
+
19
23
  module Kitchen
20
24
 
21
25
  module Driver
22
26
 
23
- # Base class for a driver that uses SSH to communication with an instance.
27
+ # Legacy base class for a driver that uses SSH to communication with an
28
+ # instance. This class has been updated to use the Instance's Transport to
29
+ # issue commands and transfer files and no longer uses the `Kitchen:SSH`
30
+ # class directly.
31
+ #
32
+ # **NOTE:** Authors of new Drivers are encouraged to inherit from
33
+ # `Kitchen::Driver::Base` instead and existing Driver authors are
34
+ # encouraged to update their Driver class to inherit from
35
+ # `Kitchen::Driver::SSHBase`.
36
+ #
24
37
  # A subclass must implement the following methods:
25
38
  # * #create(state)
26
39
  # * #destroy(state)
27
40
  #
28
41
  # @author Fletcher Nichol <fnichol@nichol.ca>
29
- class SSHBase < Base
42
+ # @deprecated While all possible effort has been made to preserve the
43
+ # original behavior of this class, future improvements to the Driver,
44
+ # Transport, and Verifier subsystems may not be picked up in these
45
+ # Drivers. When legacy Driver::SSHBase support is removed, this class
46
+ # will no longer be available.
47
+ class SSHBase
48
+
49
+ include ShellOut
50
+ include Configurable
51
+ include Logging
30
52
 
31
53
  default_config :sudo, true
32
54
  default_config :port, 22
33
55
 
56
+ # Creates a new Driver object using the provided configuration data
57
+ # which will be merged with any default configuration.
58
+ #
59
+ # @param config [Hash] provided driver configuration
60
+ def initialize(config = {})
61
+ init_config(config)
62
+ end
63
+
34
64
  # (see Base#create)
35
65
  def create(state) # rubocop:disable Lint/UnusedMethodArgument
36
66
  raise ClientError, "#{self.class}#create must be implemented"
37
67
  end
38
68
 
39
69
  # (see Base#converge)
40
- def converge(state)
70
+ def converge(state) # rubocop:disable Metrics/AbcSize
41
71
  provisioner = instance.provisioner
42
72
  provisioner.create_sandbox
43
73
  sandbox_dirs = Dir.glob("#{provisioner.sandbox_path}/*")
44
74
 
45
- Kitchen::SSH.new(*build_ssh_args(state)) do |conn|
46
- run_remote(provisioner.install_command, conn)
47
- run_remote(provisioner.init_command, conn)
48
- transfer_path(sandbox_dirs, provisioner[:root_path], conn)
49
- run_remote(provisioner.prepare_command, conn)
50
- run_remote(provisioner.run_command, conn)
75
+ instance.transport.connection(backcompat_merged_state(state)) do |conn|
76
+ conn.execute(env_cmd(provisioner.install_command))
77
+ conn.execute(env_cmd(provisioner.init_command))
78
+ info("Transferring files to #{instance.to_str}")
79
+ conn.upload(sandbox_dirs, provisioner[:root_path])
80
+ debug("Transfer complete")
81
+ conn.execute(env_cmd(provisioner.prepare_command))
82
+ conn.execute(env_cmd(provisioner.run_command))
51
83
  end
84
+ rescue Kitchen::Transport::TransportFailed => ex
85
+ raise ActionFailed, ex.message
52
86
  ensure
53
- provisioner && provisioner.cleanup_sandbox
87
+ instance.provisioner.cleanup_sandbox
54
88
  end
55
89
 
56
90
  # (see Base#setup)
57
91
  def setup(state)
58
- Kitchen::SSH.new(*build_ssh_args(state)) do |conn|
59
- run_remote(busser.setup_cmd, conn)
92
+ verifier = instance.verifier
93
+
94
+ instance.transport.connection(backcompat_merged_state(state)) do |conn|
95
+ conn.execute(env_cmd(verifier.install_command))
60
96
  end
97
+ rescue Kitchen::Transport::TransportFailed => ex
98
+ raise ActionFailed, ex.message
61
99
  end
62
100
 
63
101
  # (see Base#verify)
64
- def verify(state)
65
- Kitchen::SSH.new(*build_ssh_args(state)) do |conn|
66
- run_remote(busser.sync_cmd, conn)
67
- run_remote(busser.run_cmd, conn)
102
+ def verify(state) # rubocop:disable Metrics/AbcSize
103
+ verifier = instance.verifier
104
+ verifier.create_sandbox
105
+ sandbox_dirs = Dir.glob(File.join(verifier.sandbox_path, "*"))
106
+
107
+ instance.transport.connection(backcompat_merged_state(state)) do |conn|
108
+ conn.execute(env_cmd(verifier.init_command))
109
+ info("Transferring files to #{instance.to_str}")
110
+ conn.upload(sandbox_dirs, verifier[:root_path])
111
+ debug("Transfer complete")
112
+ conn.execute(env_cmd(verifier.prepare_command))
113
+ conn.execute(env_cmd(verifier.run_command))
68
114
  end
115
+ rescue Kitchen::Transport::TransportFailed => ex
116
+ raise ActionFailed, ex.message
117
+ ensure
118
+ instance.verifier.cleanup_sandbox
69
119
  end
70
120
 
71
121
  # (see Base#destroy)
@@ -75,7 +125,8 @@ module Kitchen
75
125
 
76
126
  # (see Base#login_command)
77
127
  def login_command(state)
78
- SSH.new(*build_ssh_args(state)).login_command
128
+ instance.transport.connection(backcompat_merged_state(state)).
129
+ login_command
79
130
  end
80
131
 
81
132
  # Executes an arbitrary command on an instance over an SSH connection.
@@ -84,8 +135,8 @@ module Kitchen
84
135
  # @param command [String] the command to be executed
85
136
  # @raise [ActionFailed] if the command could not be successfully completed
86
137
  def remote_command(state, command)
87
- Kitchen::SSH.new(*build_ssh_args(state)) do |conn|
88
- run_remote(command, conn)
138
+ instance.transport.connection(backcompat_merged_state(state)) do |conn|
139
+ conn.execute(env_cmd(command))
89
140
  end
90
141
  end
91
142
 
@@ -96,13 +147,71 @@ module Kitchen
96
147
  # @deprecated This method should no longer be called directly and exists
97
148
  # to support very old drivers. This will be removed in the future.
98
149
  def ssh(ssh_args, command)
99
- Kitchen::SSH.new(*ssh_args) do |conn|
100
- run_remote(command, conn)
150
+ pseudo_state = { :hostname => ssh_args[0], :username => ssh_args[1] }
151
+ pseudo_state.merge!(ssh_args[2])
152
+ connection_state = backcompat_merged_state(pseudo_state)
153
+
154
+ instance.transport.connection(connection_state) do |conn|
155
+ conn.execute(env_cmd(command))
156
+ end
157
+ end
158
+
159
+ # Performs whatever tests that may be required to ensure that this driver
160
+ # will be able to function in the current environment. This may involve
161
+ # checking for the presence of certain directories, software installed,
162
+ # etc.
163
+ #
164
+ # @raise [UserError] if the driver will not be able to perform or if a
165
+ # documented dependency is missing from the system
166
+ def verify_dependencies
167
+ end
168
+
169
+ class << self
170
+ # @return [Array<Symbol>] an array of action method names that cannot
171
+ # be run concurrently and must be run in serial via a shared mutex
172
+ attr_reader :serial_actions
173
+ end
174
+
175
+ # Registers certain driver actions that cannot be safely run concurrently
176
+ # in threads across multiple instances. Typically this might be used
177
+ # for create or destroy actions that use an underlying resource that
178
+ # cannot be used at the same time.
179
+ #
180
+ # A shared mutex for this driver object will be used to synchronize all
181
+ # registered methods.
182
+ #
183
+ # @example a single action method that cannot be run concurrently
184
+ #
185
+ # no_parallel_for :create
186
+ #
187
+ # @example multiple action methods that cannot be run concurrently
188
+ #
189
+ # no_parallel_for :create, :destroy
190
+ #
191
+ # @param methods [Array<Symbol>] one or more actions as symbols
192
+ # @raise [ClientError] if any method is not a valid action method name
193
+ def self.no_parallel_for(*methods)
194
+ action_methods = [:create, :converge, :setup, :verify, :destroy]
195
+
196
+ Array(methods).each do |meth|
197
+ next if action_methods.include?(meth)
198
+
199
+ raise ClientError, "##{meth} is not a valid no_parallel_for method"
101
200
  end
201
+
202
+ @serial_actions ||= []
203
+ @serial_actions += methods
102
204
  end
103
205
 
104
206
  private
105
207
 
208
+ def backcompat_merged_state(state)
209
+ driver_ssh_keys = %w[
210
+ forward_agent hostname password port ssh_key username
211
+ ].map(&:to_sym)
212
+ config.select { |key, _| driver_ssh_keys.include?(key) }.rmerge(state)
213
+ end
214
+
106
215
  # Builds arguments for constructing a `Kitchen::SSH` instance.
107
216
  #
108
217
  # @param state [Hash] state hash
@@ -131,6 +240,7 @@ module Kitchen
131
240
  # @return [String] command string
132
241
  # @api private
133
242
  def env_cmd(cmd)
243
+ return if cmd.nil?
134
244
  env = "env"
135
245
  env << " http_proxy=#{config[:http_proxy]}" if config[:http_proxy]
136
246
  env << " https_proxy=#{config[:https_proxy]}" if config[:https_proxy]
@@ -177,7 +287,52 @@ module Kitchen
177
287
  # @param options [Hash] configuration hash (default: `{}`)
178
288
  # @api private
179
289
  def wait_for_sshd(hostname, username = nil, options = {})
180
- SSH.new(hostname, username, { :logger => logger }.merge(options)).wait
290
+ pseudo_state = { :hostname => hostname }
291
+ pseudo_state[:username] = username if username
292
+ pseudo_state.merge!(options)
293
+
294
+ instance.transport.connection(backcompat_merged_state(pseudo_state)).
295
+ wait_until_ready
296
+ end
297
+
298
+ # Intercepts any bare #puts calls in subclasses and issues an INFO log
299
+ # event instead.
300
+ #
301
+ # @param msg [String] message string
302
+ def puts(msg)
303
+ info(msg)
304
+ end
305
+
306
+ # Intercepts any bare #print calls in subclasses and issues an INFO log
307
+ # event instead.
308
+ #
309
+ # @param msg [String] message string
310
+ def print(msg)
311
+ info(msg)
312
+ end
313
+
314
+ # Delegates to Kitchen::ShellOut.run_command, overriding some default
315
+ # options:
316
+ #
317
+ # * `:use_sudo` defaults to the value of `config[:use_sudo]` in the
318
+ # Driver object
319
+ # * `:log_subject` defaults to a String representation of the Driver's
320
+ # class name
321
+ #
322
+ # @see ShellOut#run_command
323
+ def run_command(cmd, options = {})
324
+ base_options = {
325
+ :use_sudo => config[:use_sudo],
326
+ :log_subject => Thor::Util.snake_case(self.class.to_s)
327
+ }.merge(options)
328
+ super(cmd, base_options)
329
+ end
330
+
331
+ # Returns the Busser object associated with the driver.
332
+ #
333
+ # @return [Busser] a busser
334
+ def busser
335
+ instance.verifier
181
336
  end
182
337
  end
183
338
  end
@@ -64,9 +64,13 @@ module Kitchen
64
64
  # automation tools
65
65
  attr_reader :provisioner
66
66
 
67
- # @return [Busser] busser object for instance to manage the busser
67
+ # @return [Transport::Base] transport object which will communicate with
68
+ # an instance.
69
+ attr_reader :transport
70
+
71
+ # @return [Verifier] verifier object for instance to manage the verifier
68
72
  # installation on this instance
69
- attr_reader :busser
73
+ attr_reader :verifier
70
74
 
71
75
  # @return [Logger] the logger for this instance
72
76
  attr_reader :logger
@@ -78,8 +82,9 @@ module Kitchen
78
82
  # @option options [Platform] :platform the platform (**Required**)
79
83
  # @option options [Driver::Base] :driver the driver (**Required**)
80
84
  # @option options [Provisioner::Base] :provisioner the provisioner
85
+ # @option options [Transport::Base] :transport the transport
81
86
  # (**Required**)
82
- # @option options [Busser] :busser the busser logger (**Required**)
87
+ # @option options [Verifier] :verifier the verifier logger (**Required**)
83
88
  # @option options [Logger] :logger the instance logger
84
89
  # (default: Kitchen.logger)
85
90
  # @option options [StateFile] :state_file the state file object to use
@@ -93,12 +98,15 @@ module Kitchen
93
98
  @name = self.class.name_for(@suite, @platform)
94
99
  @driver = options.fetch(:driver)
95
100
  @provisioner = options.fetch(:provisioner)
96
- @busser = options.fetch(:busser)
101
+ @transport = options.fetch(:transport)
102
+ @verifier = options.fetch(:verifier)
97
103
  @logger = options.fetch(:logger) { Kitchen.logger }
98
104
  @state_file = options.fetch(:state_file)
99
105
 
100
106
  setup_driver
101
107
  setup_provisioner
108
+ setup_transport
109
+ setup_verifier
102
110
  end
103
111
 
104
112
  # Returns a displayable representation of the instance.
@@ -121,7 +129,7 @@ module Kitchen
121
129
 
122
130
  # Converges this running instance.
123
131
  #
124
- # @see Driver::Base#converge
132
+ # @see Provisioner::Base#call
125
133
  # @return [self] this instance, used to chain actions
126
134
  #
127
135
  # @todo rescue Driver::ActionFailed and return some kind of null object
@@ -188,32 +196,37 @@ module Kitchen
188
196
  end
189
197
 
190
198
  # Logs in to this instance by invoking a system command, provided by the
191
- # instance's driver. This could be an SSH command, telnet, or serial
199
+ # instance's transport. This could be an SSH command, telnet, or serial
192
200
  # console session.
193
201
  #
194
202
  # **Note** This method calls exec and will not return.
195
203
  #
196
- # @see Driver::LoginCommand
197
- # @see Driver::Base#login_command
204
+ # @see Kitchen::LoginCommand
205
+ # @see Transport::Base::Connection#login_command
198
206
  def login
199
207
  state = state_file.read
200
208
  if state[:last_action].nil?
201
209
  raise UserError, "Instance #{to_str} has not yet been created"
202
210
  end
203
211
 
204
- login_command = driver.login_command(state)
205
- cmd, *args = login_command.cmd_array
206
- options = login_command.options
212
+ lc = if legacy_ssh_base_driver?
213
+ legacy_ssh_base_login(state)
214
+ else
215
+ transport.connection(state).login_command
216
+ end
207
217
 
208
- debug(%{Login command: #{cmd} #{args.join(" ")} (Options: #{options})})
209
- Kernel.exec(cmd, *args, options)
218
+ debug(%{Login command: #{lc.command} #{lc.arguments.join(" ")} } \
219
+ "(Options: #{lc.options})")
220
+ Kernel.exec(*lc.exec_args)
210
221
  end
211
222
 
212
223
  # Executes an arbitrary command on this instance.
213
224
  #
214
225
  # @param command [String] a command string to execute
215
226
  def remote_exec(command)
216
- driver.remote_command(state_file.read, command)
227
+ transport.connection(state_file.read) do |conn|
228
+ conn.execute(command)
229
+ end
217
230
  end
218
231
 
219
232
  # Returns a Hash of configuration and other useful diagnostic information.
@@ -221,7 +234,9 @@ module Kitchen
221
234
  # @return [Hash] a diagnostic hash
222
235
  def diagnose
223
236
  result = Hash.new
224
- [:state_file, :driver, :provisioner, :busser].each do |sym|
237
+ [
238
+ :platform, :state_file, :driver, :provisioner, :transport, :verifier
239
+ ].each do |sym|
225
240
  obj = send(sym)
226
241
  result[sym] = obj.respond_to?(:diagnose) ? obj.diagnose : :unknown
227
242
  end
@@ -250,7 +265,8 @@ module Kitchen
250
265
  # @api private
251
266
  def validate_options(options)
252
267
  [
253
- :suite, :platform, :driver, :provisioner, :busser, :state_file
268
+ :suite, :platform, :driver, :provisioner,
269
+ :transport, :verifier, :state_file
254
270
  ].each do |k|
255
271
  next if options.key?(k)
256
272
 
@@ -281,6 +297,22 @@ module Kitchen
281
297
  @provisioner.finalize_config!(self)
282
298
  end
283
299
 
300
+ # Perform any final configuration or preparation needed for the transport
301
+ # object carry out its duties.
302
+ #
303
+ # @api private
304
+ def setup_transport
305
+ transport.finalize_config!(self)
306
+ end
307
+
308
+ # Perform any final configuration or preparation needed for the verifier
309
+ # object carry out its duties.
310
+ #
311
+ # @api private
312
+ def setup_verifier
313
+ verifier.finalize_config!(self)
314
+ end
315
+
284
316
  # Perform all actions in order from last state to desired state.
285
317
  #
286
318
  # @param desired [Symbol] a symbol representing the desired action state
@@ -305,11 +337,20 @@ module Kitchen
305
337
 
306
338
  # Perform the converge action.
307
339
  #
308
- # @see Driver::Base#converge
340
+ # @see Provisioner::Base#call
309
341
  # @return [self] this instance, used to chain actions
310
342
  # @api private
311
343
  def converge_action
312
- perform_action(:converge, "Converging")
344
+ banner "Converging #{to_str}..."
345
+ elapsed = action(:converge) do |state|
346
+ if legacy_ssh_base_driver?
347
+ legacy_ssh_base_converge(state)
348
+ else
349
+ provisioner.call(state)
350
+ end
351
+ end
352
+ info("Finished converging #{to_str} #{Util.duration(elapsed.real)}.")
353
+ self
313
354
  end
314
355
 
315
356
  # Perform the setup action.
@@ -318,7 +359,12 @@ module Kitchen
318
359
  # @return [self] this instance, used to chain actions
319
360
  # @api private
320
361
  def setup_action
321
- perform_action(:setup, "Setting up")
362
+ banner "Setting up #{to_str}..."
363
+ elapsed = action(:setup) do |state|
364
+ legacy_ssh_base_setup(state) if legacy_ssh_base_driver?
365
+ end
366
+ info("Finished setting up #{to_str} #{Util.duration(elapsed.real)}.")
367
+ self
322
368
  end
323
369
 
324
370
  # Perform the verify action.
@@ -327,7 +373,16 @@ module Kitchen
327
373
  # @return [self] this instance, used to chain actions
328
374
  # @api private
329
375
  def verify_action
330
- perform_action(:verify, "Verifying")
376
+ banner "Verifying #{to_str}..."
377
+ elapsed = action(:verify) do |state|
378
+ if legacy_ssh_base_driver?
379
+ legacy_ssh_base_verify(state)
380
+ else
381
+ verifier.call(state)
382
+ end
383
+ end
384
+ info("Finished verifying #{to_str} #{Util.duration(elapsed.real)}.")
385
+ self
331
386
  end
332
387
 
333
388
  # Perform the destroy action.
@@ -454,6 +509,71 @@ module Kitchen
454
509
  "#{what.capitalize} failed on instance #{to_str}."
455
510
  end
456
511
 
512
+ # Invokes `Driver#converge` on a legacy Driver, which inherits from
513
+ # `Kitchen::Driver::SSHBase`.
514
+ #
515
+ # @param state [Hash] mutable instance state
516
+ # @deprecated When legacy Driver::SSHBase support is removed, the
517
+ # `#converge` method will no longer be called on the Driver.
518
+ # @api private
519
+ def legacy_ssh_base_converge(state)
520
+ warn("Running legacy converge for '#{driver.name}' Driver")
521
+ # TODO: Document upgrade path and provide link
522
+ # warn("Driver authors: please read http://example.com for more details.")
523
+ driver.converge(state)
524
+ end
525
+
526
+ # @return [TrueClass,FalseClass] whether or not the Driver inherits from
527
+ # `Kitchen::Driver::SSHBase`
528
+ # @deprecated When legacy Driver::SSHBase support is removed, the
529
+ # `#converge` method will no longer be called on the Driver.
530
+ # @api private
531
+ def legacy_ssh_base_driver?
532
+ driver.class < Kitchen::Driver::SSHBase
533
+ end
534
+
535
+ # Invokes `Driver#login_command` on a legacy Driver, which inherits from
536
+ # `Kitchen::Driver::SSHBase`.
537
+ #
538
+ # @param state [Hash] mutable instance state
539
+ # @deprecated When legacy Driver::SSHBase support is removed, the
540
+ # `#login_command` method will no longer be called on the Driver.
541
+ # @api private
542
+ def legacy_ssh_base_login(state)
543
+ warn("Running legacy login for '#{driver.name}' Driver")
544
+ # TODO: Document upgrade path and provide link
545
+ # warn("Driver authors: please read http://example.com for more details.")
546
+ driver.login_command(state)
547
+ end
548
+
549
+ # Invokes `Driver#setup` on a legacy Driver, which inherits from
550
+ # `Kitchen::Driver::SSHBase`.
551
+ #
552
+ # @param state [Hash] mutable instance state
553
+ # @deprecated When legacy Driver::SSHBase support is removed, the
554
+ # `#setup` method will no longer be called on the Driver.
555
+ # @api private
556
+ def legacy_ssh_base_setup(state)
557
+ warn("Running legacy setup for '#{driver.name}' Driver")
558
+ # TODO: Document upgrade path and provide link
559
+ # warn("Driver authors: please read http://example.com for more details.")
560
+ driver.setup(state)
561
+ end
562
+
563
+ # Invokes `Driver#verify` on a legacy Driver, which inherits from
564
+ # `Kitchen::Driver::SSHBase`.
565
+ #
566
+ # @param state [Hash] mutable instance state
567
+ # @deprecated When legacy Driver::SSHBase support is removed, the
568
+ # `#verify` method will no longer be called on the Driver.
569
+ # @api private
570
+ def legacy_ssh_base_verify(state)
571
+ warn("Running legacy verify for '#{driver.name}' Driver")
572
+ # TODO: Document upgrade path and provide link
573
+ # warn("Driver authors: please read http://example.com for more details.")
574
+ driver.verify(state)
575
+ end
576
+
457
577
  # The simplest finite state machine pseudo-implementation needed to manage
458
578
  # an Instance.
459
579
  #