test-kitchen 3.9.1 → 4.1.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +8 -5
  3. data/lib/kitchen/cli.rb +33 -8
  4. data/lib/kitchen/collection.rb +1 -1
  5. data/lib/kitchen/command/list.rb +49 -9
  6. data/lib/kitchen/command/logs.rb +185 -0
  7. data/lib/kitchen/command/test.rb +2 -1
  8. data/lib/kitchen/command.rb +22 -9
  9. data/lib/kitchen/config.rb +8 -0
  10. data/lib/kitchen/configurable.rb +4 -4
  11. data/lib/kitchen/data_munger.rb +54 -21
  12. data/lib/kitchen/driver/base.rb +19 -0
  13. data/lib/kitchen/driver/dummy.rb +27 -0
  14. data/lib/kitchen/driver/proxy.rb +11 -6
  15. data/lib/kitchen/errors.rb +1 -0
  16. data/lib/kitchen/generator/init.rb +1 -1
  17. data/lib/kitchen/instance.rb +248 -182
  18. data/lib/kitchen/lazy_hash.rb +1 -1
  19. data/lib/kitchen/lifecycle_hook/remote.rb +5 -2
  20. data/lib/kitchen/logger.rb +407 -74
  21. data/lib/kitchen/logging.rb +2 -2
  22. data/lib/kitchen/metadata_chopper.rb +1 -1
  23. data/lib/kitchen/provisioner/base.rb +27 -35
  24. data/lib/kitchen/provisioner/external.rb +420 -0
  25. data/lib/kitchen/provisioner.rb +1 -2
  26. data/lib/kitchen/shell_out.rb +1 -1
  27. data/lib/kitchen/state_file.rb +12 -2
  28. data/lib/kitchen/transport/base.rb +1 -1
  29. data/lib/kitchen/transport/ssh.rb +5 -5
  30. data/lib/kitchen/transport/winrm.rb +8 -8
  31. data/lib/kitchen/util.rb +1 -1
  32. data/lib/kitchen/verifier/base.rb +17 -5
  33. data/lib/kitchen/version.rb +1 -1
  34. data/lib/kitchen.rb +21 -4
  35. data/lib/vendor/hash_recursive_merge.rb +2 -2
  36. data/templates/driver/driver.rb.erb +1 -1
  37. data/test-kitchen.gemspec +5 -7
  38. metadata +36 -41
  39. data/lib/kitchen/driver/ssh_base.rb +0 -346
  40. data/lib/kitchen/provisioner/chef/berkshelf.rb +0 -116
  41. data/lib/kitchen/provisioner/chef/common_sandbox.rb +0 -352
  42. data/lib/kitchen/provisioner/chef/policyfile.rb +0 -173
  43. data/lib/kitchen/provisioner/chef_apply.rb +0 -121
  44. data/lib/kitchen/provisioner/chef_base.rb +0 -723
  45. data/lib/kitchen/provisioner/chef_infra.rb +0 -167
  46. data/lib/kitchen/provisioner/chef_solo.rb +0 -82
  47. data/lib/kitchen/provisioner/chef_target.rb +0 -130
  48. data/lib/kitchen/provisioner/chef_zero.rb +0 -12
  49. data/lib/kitchen/ssh.rb +0 -296
data/lib/kitchen.rb CHANGED
@@ -16,6 +16,7 @@
16
16
  # limitations under the License.
17
17
 
18
18
  require "pathname" unless defined?(Pathname)
19
+ require "securerandom" unless defined?(SecureRandom)
19
20
  require_relative "kitchen/errors"
20
21
  require_relative "kitchen/logger"
21
22
  require_relative "kitchen/logging"
@@ -31,7 +32,6 @@ require_relative "kitchen/config"
31
32
  require_relative "kitchen/data_munger"
32
33
  require_relative "kitchen/driver"
33
34
  require_relative "kitchen/driver/base"
34
- require_relative "kitchen/driver/ssh_base"
35
35
  require_relative "kitchen/driver/proxy"
36
36
  require_relative "kitchen/instance"
37
37
  require_relative "kitchen/lifecycle_hooks"
@@ -41,7 +41,6 @@ require_relative "kitchen/loader/yaml"
41
41
  require_relative "kitchen/metadata_chopper"
42
42
  require_relative "kitchen/platform"
43
43
  require_relative "kitchen/state_file"
44
- require_relative "kitchen/ssh"
45
44
  require_relative "kitchen/suite"
46
45
  require_relative "kitchen/verifier"
47
46
  require_relative "kitchen/verifier/base"
@@ -61,6 +60,9 @@ module Kitchen
61
60
  # @return [Mutex] a mutex used for Dir.chdir coordination
62
61
  attr_accessor :mutex_chdir
63
62
 
63
+ # @return [String] identifier for the current Kitchen process invocation
64
+ attr_writer :run_id
65
+
64
66
  # Returns the root path of the Kitchen gem source code.
65
67
  #
66
68
  # @return [Pathname] root path of gem
@@ -72,7 +74,11 @@ module Kitchen
72
74
  #
73
75
  # @return [Logger] a logger
74
76
  def default_logger
75
- Logger.new(stdout: $stdout, level: Util.to_logger_level(env_log))
77
+ Logger.new(
78
+ stdout: $stdout,
79
+ level: Util.to_logger_level(env_log),
80
+ metadata: { kitchen_run_id: run_id }
81
+ )
76
82
  end
77
83
 
78
84
  # Returns a default file logger which emits on standard output and to a
@@ -86,15 +92,26 @@ module Kitchen
86
92
  log_overwrite = log_overwrite.nil? ? env_log_overwrite : log_overwrite
87
93
  log_location = File.expand_path(File.join(DEFAULT_LOG_DIR, "kitchen.log"))
88
94
  log_location = log_location.to_s
95
+ structured_log_location = File.expand_path(File.join(DEFAULT_LOG_DIR, "kitchen.ndjson"))
96
+ structured_log_location = structured_log_location.to_s
89
97
 
90
98
  Logger.new(
91
99
  stdout: $stdout,
92
100
  logdev: log_location,
101
+ structured_logdev: structured_log_location,
93
102
  level: Util.to_logger_level(level),
94
- log_overwrite:
103
+ log_overwrite:,
104
+ metadata: { kitchen_run_id: run_id }
95
105
  )
96
106
  end
97
107
 
108
+ # Returns the current Kitchen run identifier.
109
+ #
110
+ # @return [String] identifier for the current Kitchen process invocation
111
+ def run_id
112
+ @run_id ||= SecureRandom.uuid
113
+ end
114
+
98
115
  # Returns whether or not standard output is associated with a terminal
99
116
  # device (tty).
100
117
  #
@@ -48,8 +48,8 @@ module HashRecursiveMerge
48
48
  # When both +hsh+ and +other_hash+ contains an entry with the same key,
49
49
  # it merges and returns the values from both arrays.
50
50
  #
51
- # Compared with Hash#merge, this method provides a different approch
52
- # for merging nasted hashes.
51
+ # Compared with Hash#merge, this method provides a different approach
52
+ # for merging nested hashes.
53
53
  # If the value of a given key is an Hash and both +other_hash+ abd +hsh
54
54
  # includes the same key, the value is merged instead replaced with
55
55
  # +other_hash+ value.
@@ -11,7 +11,7 @@ module Kitchen
11
11
  # <%= config[:klass_name] %> driver for Kitchen.
12
12
  #
13
13
  # @author <%= config[:author] %> <<%= config[:email] %>>
14
- class <%= config[:klass_name] %> < Kitchen::Driver::SSHBase
14
+ class <%= config[:klass_name] %> < Kitchen::Driver::Base
15
15
 
16
16
  def create(state)
17
17
  end
data/test-kitchen.gemspec CHANGED
@@ -27,7 +27,7 @@ Gem::Specification.new do |gem|
27
27
  gem.add_dependency "csv", "~> 3.3"
28
28
  gem.add_dependency "ed25519", "~> 1.3" # ed25519 ssh key support
29
29
  gem.add_dependency "irb", "~> 1.15"
30
- gem.add_dependency "mixlib-install", "~> 3.6"
30
+ gem.add_dependency "mixlib-install", ">= 3.6", "< 5.0" # allow Cinc's mixlib-install 4.x fork
31
31
  gem.add_dependency "mixlib-shellout", ">= 1.2", "< 4.0"
32
32
  gem.add_dependency "net-scp", ">= 1.1", "< 5.0" # pinning until we can confirm 4+ works
33
33
  gem.add_dependency "net-ssh", ">= 2.9", "< 8.0" # pinning until we can confirm 8+ works
@@ -35,10 +35,8 @@ Gem::Specification.new do |gem|
35
35
  gem.add_dependency "ostruct", "~> 0.6"
36
36
  gem.add_dependency "syslog", "~> 0.3"
37
37
  gem.add_dependency "thor", ">= 0.19", "< 2.0"
38
- gem.add_dependency "winrm", "~> 2.0"
39
- gem.add_dependency "winrm-elevated", "~> 1.0"
40
- gem.add_dependency "winrm-fs", "~> 1.1"
41
- # Required to run the Chef provisioner local license check for remote systems
42
- # TK is not under Chef EULA
43
- gem.add_dependency "license-acceptance", ">= 1.0.11", "< 3.0" # pinning until we can confirm 3+ works
38
+ # Chef Forked versions with additional fixes since no one is maintining them upstream
39
+ gem.add_dependency "chef-winrm", ">= 2.5.0", "< 3.0"
40
+ gem.add_dependency "chef-winrm-elevated", ">= 1.0", "< 2.0"
41
+ gem.add_dependency "chef-winrm-fs", ">= 1.0", "< 2.0"
44
42
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: test-kitchen
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.9.1
4
+ version: 4.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fletcher Nichol
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-10-17 00:00:00.000000000 Z
11
+ date: 2026-07-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bcrypt_pbkdf
@@ -84,16 +84,22 @@ dependencies:
84
84
  name: mixlib-install
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: '3.6'
90
+ - - "<"
91
+ - !ruby/object:Gem::Version
92
+ version: '5.0'
90
93
  type: :runtime
91
94
  prerelease: false
92
95
  version_requirements: !ruby/object:Gem::Requirement
93
96
  requirements:
94
- - - "~>"
97
+ - - ">="
95
98
  - !ruby/object:Gem::Version
96
99
  version: '3.6'
100
+ - - "<"
101
+ - !ruby/object:Gem::Version
102
+ version: '5.0'
97
103
  - !ruby/object:Gem::Dependency
98
104
  name: mixlib-shellout
99
105
  requirement: !ruby/object:Gem::Requirement
@@ -223,67 +229,65 @@ dependencies:
223
229
  - !ruby/object:Gem::Version
224
230
  version: '2.0'
225
231
  - !ruby/object:Gem::Dependency
226
- name: winrm
232
+ name: chef-winrm
227
233
  requirement: !ruby/object:Gem::Requirement
228
234
  requirements:
229
- - - "~>"
235
+ - - ">="
230
236
  - !ruby/object:Gem::Version
231
- version: '2.0'
237
+ version: 2.5.0
238
+ - - "<"
239
+ - !ruby/object:Gem::Version
240
+ version: '3.0'
232
241
  type: :runtime
233
242
  prerelease: false
234
243
  version_requirements: !ruby/object:Gem::Requirement
235
244
  requirements:
236
- - - "~>"
245
+ - - ">="
237
246
  - !ruby/object:Gem::Version
238
- version: '2.0'
247
+ version: 2.5.0
248
+ - - "<"
249
+ - !ruby/object:Gem::Version
250
+ version: '3.0'
239
251
  - !ruby/object:Gem::Dependency
240
- name: winrm-elevated
252
+ name: chef-winrm-elevated
241
253
  requirement: !ruby/object:Gem::Requirement
242
254
  requirements:
243
- - - "~>"
255
+ - - ">="
244
256
  - !ruby/object:Gem::Version
245
257
  version: '1.0'
258
+ - - "<"
259
+ - !ruby/object:Gem::Version
260
+ version: '2.0'
246
261
  type: :runtime
247
262
  prerelease: false
248
263
  version_requirements: !ruby/object:Gem::Requirement
249
264
  requirements:
250
- - - "~>"
265
+ - - ">="
251
266
  - !ruby/object:Gem::Version
252
267
  version: '1.0'
253
- - !ruby/object:Gem::Dependency
254
- name: winrm-fs
255
- requirement: !ruby/object:Gem::Requirement
256
- requirements:
257
- - - "~>"
258
- - !ruby/object:Gem::Version
259
- version: '1.1'
260
- type: :runtime
261
- prerelease: false
262
- version_requirements: !ruby/object:Gem::Requirement
263
- requirements:
264
- - - "~>"
268
+ - - "<"
265
269
  - !ruby/object:Gem::Version
266
- version: '1.1'
270
+ version: '2.0'
267
271
  - !ruby/object:Gem::Dependency
268
- name: license-acceptance
272
+ name: chef-winrm-fs
269
273
  requirement: !ruby/object:Gem::Requirement
270
274
  requirements:
271
275
  - - ">="
272
276
  - !ruby/object:Gem::Version
273
- version: 1.0.11
277
+ version: '1.0'
274
278
  - - "<"
275
279
  - !ruby/object:Gem::Version
276
- version: '3.0'
280
+ version: '2.0'
277
281
  type: :runtime
278
282
  prerelease: false
279
283
  version_requirements: !ruby/object:Gem::Requirement
280
284
  requirements:
281
285
  - - ">="
282
286
  - !ruby/object:Gem::Version
283
- version: 1.0.11
287
+ version: '1.0'
284
288
  - - "<"
285
289
  - !ruby/object:Gem::Version
286
- version: '3.0'
290
+ version: '2.0'
287
291
  description: Test Kitchen is an integration tool for developing and testing infrastructure
288
292
  code and software on isolated target platforms.
289
293
  email:
@@ -311,6 +315,7 @@ files:
311
315
  - lib/kitchen/command/exec.rb
312
316
  - lib/kitchen/command/list.rb
313
317
  - lib/kitchen/command/login.rb
318
+ - lib/kitchen/command/logs.rb
314
319
  - lib/kitchen/command/package.rb
315
320
  - lib/kitchen/command/sink.rb
316
321
  - lib/kitchen/command/test.rb
@@ -323,7 +328,6 @@ files:
323
328
  - lib/kitchen/driver/dummy.rb
324
329
  - lib/kitchen/driver/exec.rb
325
330
  - lib/kitchen/driver/proxy.rb
326
- - lib/kitchen/driver/ssh_base.rb
327
331
  - lib/kitchen/errors.rb
328
332
  - lib/kitchen/generator/init.rb
329
333
  - lib/kitchen/instance.rb
@@ -343,20 +347,11 @@ files:
343
347
  - lib/kitchen/plugin_base.rb
344
348
  - lib/kitchen/provisioner.rb
345
349
  - lib/kitchen/provisioner/base.rb
346
- - lib/kitchen/provisioner/chef/berkshelf.rb
347
- - lib/kitchen/provisioner/chef/common_sandbox.rb
348
- - lib/kitchen/provisioner/chef/policyfile.rb
349
- - lib/kitchen/provisioner/chef_apply.rb
350
- - lib/kitchen/provisioner/chef_base.rb
351
- - lib/kitchen/provisioner/chef_infra.rb
352
- - lib/kitchen/provisioner/chef_solo.rb
353
- - lib/kitchen/provisioner/chef_target.rb
354
- - lib/kitchen/provisioner/chef_zero.rb
355
350
  - lib/kitchen/provisioner/dummy.rb
351
+ - lib/kitchen/provisioner/external.rb
356
352
  - lib/kitchen/provisioner/shell.rb
357
353
  - lib/kitchen/rake_tasks.rb
358
354
  - lib/kitchen/shell_out.rb
359
- - lib/kitchen/ssh.rb
360
355
  - lib/kitchen/state_file.rb
361
356
  - lib/kitchen/suite.rb
362
357
  - lib/kitchen/thor_tasks.rb
@@ -1,346 +0,0 @@
1
- # Licensed under the Apache License, Version 2.0 (the "License");
2
- # you may not use this file except in compliance with the License.
3
- # You may obtain a copy of the License at
4
- #
5
- # https://www.apache.org/licenses/LICENSE-2.0
6
- #
7
- # Unless required by applicable law or agreed to in writing, software
8
- # distributed under the License is distributed on an "AS IS" BASIS,
9
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
- # See the License for the specific language governing permissions and
11
- # limitations under the License.
12
-
13
- require "thor/util"
14
-
15
- require_relative "../lazy_hash"
16
- require_relative "../plugin_base"
17
- require "benchmark" unless defined?(Benchmark)
18
-
19
- module Kitchen
20
- module Driver
21
- # Legacy base class for a driver that uses SSH to communication with an
22
- # instance. This class has been updated to use the Instance's Transport to
23
- # issue commands and transfer files and no longer uses the `Kitchen:SSH`
24
- # class directly.
25
- #
26
- # **NOTE:** Authors of new Drivers are encouraged to inherit from
27
- # `Kitchen::Driver::Base` instead and existing Driver authors are
28
- # encouraged to update their Driver class to inherit from
29
- # `Kitchen::Driver::SSHBase`.
30
- #
31
- # A subclass must implement the following methods:
32
- # * #create(state)
33
- # * #destroy(state)
34
- #
35
- # @author Fletcher Nichol <fnichol@nichol.ca>
36
- # @deprecated While all possible effort has been made to preserve the
37
- # original behavior of this class, future improvements to the Driver,
38
- # Transport, and Verifier subsystems may not be picked up in these
39
- # Drivers. When legacy Driver::SSHBase support is removed, this class
40
- # will no longer be available.
41
- class SSHBase < Kitchen::Plugin::Base
42
- include ShellOut
43
- include Configurable
44
- include Logging
45
-
46
- default_config :sudo, true
47
- default_config :port, 22
48
- # needs to be one less than the configured sshd_config MaxSessions
49
- default_config :max_ssh_sessions, 9
50
-
51
- # Creates a new Driver object using the provided configuration data
52
- # which will be merged with any default configuration.
53
- #
54
- # @param config [Hash] provided driver configuration
55
- def initialize(config = {})
56
- init_config(config)
57
- end
58
-
59
- # (see Base#create)
60
- def create(state) # rubocop:disable Lint/UnusedMethodArgument
61
- raise ClientError, "#{self.class}#create must be implemented"
62
- end
63
-
64
- # (see Base#converge)
65
- def converge(state) # rubocop:disable Metrics/AbcSize
66
- provisioner = instance.provisioner
67
- provisioner.create_sandbox
68
- sandbox_dirs = provisioner.sandbox_dirs
69
-
70
- instance.transport.connection(backcompat_merged_state(state)) do |conn|
71
- conn.execute(env_cmd(provisioner.install_command))
72
- conn.execute(env_cmd(provisioner.init_command))
73
- info("Transferring files to #{instance.to_str}")
74
- conn.upload(sandbox_dirs, provisioner[:root_path])
75
- debug("Transfer complete")
76
- conn.execute(env_cmd(provisioner.prepare_command))
77
- conn.execute(env_cmd(provisioner.run_command))
78
- info("Downloading files from #{instance.to_str}")
79
- provisioner[:downloads].to_h.each do |remotes, local|
80
- debug("Downloading #{Array(remotes).join(", ")} to #{local}")
81
- conn.download(remotes, local)
82
- end
83
- debug("Download complete")
84
- end
85
- rescue Kitchen::Transport::TransportFailed => ex
86
- raise ActionFailed, ex.message
87
- ensure
88
- instance.provisioner.cleanup_sandbox
89
- end
90
-
91
- # (see Base#setup)
92
- def setup(state)
93
- verifier = instance.verifier
94
-
95
- instance.transport.connection(backcompat_merged_state(state)) do |conn|
96
- conn.execute(env_cmd(verifier.install_command))
97
- end
98
- rescue Kitchen::Transport::TransportFailed => ex
99
- raise ActionFailed, ex.message
100
- end
101
-
102
- # (see Base#verify)
103
- def verify(state) # rubocop:disable Metrics/AbcSize
104
- verifier = instance.verifier
105
- verifier.create_sandbox
106
- sandbox_dirs = Util.list_directory(verifier.sandbox_path)
107
-
108
- instance.transport.connection(backcompat_merged_state(state)) do |conn|
109
- conn.execute(env_cmd(verifier.init_command))
110
- info("Transferring files to #{instance.to_str}")
111
- conn.upload(sandbox_dirs, verifier[:root_path])
112
- debug("Transfer complete")
113
- conn.execute(env_cmd(verifier.prepare_command))
114
- conn.execute(env_cmd(verifier.run_command))
115
- end
116
- rescue Kitchen::Transport::TransportFailed => ex
117
- raise ActionFailed, ex.message
118
- ensure
119
- instance.verifier.cleanup_sandbox
120
- end
121
-
122
- # (see Base#destroy)
123
- def destroy(state) # rubocop:disable Lint/UnusedMethodArgument
124
- raise ClientError, "#{self.class}#destroy must be implemented"
125
- end
126
-
127
- def legacy_state(state)
128
- backcompat_merged_state(state)
129
- end
130
-
131
- # Package an instance.
132
- #
133
- # (see Base#package)
134
- def package(state); end
135
-
136
- # (see Base#login_command)
137
- def login_command(state)
138
- instance.transport.connection(backcompat_merged_state(state))
139
- .login_command
140
- end
141
-
142
- # Executes an arbitrary command on an instance over an SSH connection.
143
- #
144
- # @param state [Hash] mutable instance and driver state
145
- # @param command [String] the command to be executed
146
- # @raise [ActionFailed] if the command could not be successfully completed
147
- def remote_command(state, command)
148
- instance.transport.connection(backcompat_merged_state(state)) do |conn|
149
- conn.execute(env_cmd(command))
150
- end
151
- end
152
-
153
- # **(Deprecated)** Executes a remote command over SSH.
154
- #
155
- # @param ssh_args [Array] ssh arguments
156
- # @param command [String] remote command to invoke
157
- # @deprecated This method should no longer be called directly and exists
158
- # to support very old drivers. This will be removed in the future.
159
- def ssh(ssh_args, command)
160
- pseudo_state = { hostname: ssh_args[0], username: ssh_args[1] }
161
- pseudo_state.merge!(ssh_args[2])
162
- connection_state = backcompat_merged_state(pseudo_state)
163
-
164
- instance.transport.connection(connection_state) do |conn|
165
- conn.execute(env_cmd(command))
166
- end
167
- end
168
-
169
- # Performs whatever tests that may be required to ensure that this driver
170
- # will be able to function in the current environment. This may involve
171
- # checking for the presence of certain directories, software installed,
172
- # etc.
173
- #
174
- # @raise [UserError] if the driver will not be able to perform or if a
175
- # documented dependency is missing from the system
176
- def verify_dependencies; end
177
-
178
- # Cache directory that a driver could implement to inform the provisioner
179
- # that it can leverage it internally
180
- #
181
- # @return path [String] a path of the cache directory
182
- def cache_directory; end
183
-
184
- private
185
-
186
- def backcompat_merged_state(state)
187
- driver_ssh_keys = %w{
188
- forward_agent hostname password port ssh_key username
189
- }.map(&:to_sym)
190
- config.select { |key, _| driver_ssh_keys.include?(key) }.rmerge(state)
191
- end
192
-
193
- # Builds arguments for constructing a `Kitchen::SSH` instance.
194
- #
195
- # @param state [Hash] state hash
196
- # @return [Array] SSH constructor arguments
197
- # @api private
198
- def build_ssh_args(state)
199
- combined = config.to_hash.merge(state)
200
-
201
- opts = {}
202
- opts[:user_known_hosts_file] = "/dev/null"
203
- opts[:verify_host_key] = false
204
- opts[:keys_only] = true if combined[:ssh_key]
205
- opts[:password] = combined[:password] if combined[:password]
206
- opts[:forward_agent] = combined[:forward_agent] if combined.key? :forward_agent
207
- opts[:port] = combined[:port] if combined[:port]
208
- opts[:keys] = Array(combined[:ssh_key]) if combined[:ssh_key]
209
- opts[:logger] = logger
210
-
211
- [combined[:hostname], combined[:username], opts]
212
- end
213
-
214
- # Adds http, https and ftp proxy environment variables to a command, if
215
- # set in configuration data or on local workstation.
216
- #
217
- # @param cmd [String] command string
218
- # @return [String] command string
219
- # @api private
220
- # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize
221
- def env_cmd(cmd)
222
- return if cmd.nil?
223
-
224
- env = "env"
225
- http_proxy = config[:http_proxy] || ENV["http_proxy"] ||
226
- ENV["HTTP_PROXY"]
227
- https_proxy = config[:https_proxy] || ENV["https_proxy"] ||
228
- ENV["HTTPS_PROXY"]
229
- ftp_proxy = config[:ftp_proxy] || ENV["ftp_proxy"] ||
230
- ENV["FTP_PROXY"]
231
- no_proxy = if (!config[:http_proxy] && http_proxy) ||
232
- (!config[:https_proxy] && https_proxy) ||
233
- (!config[:ftp_proxy] && ftp_proxy)
234
- ENV["no_proxy"] || ENV["NO_PROXY"]
235
- end
236
- env << " http_proxy=#{http_proxy}" if http_proxy
237
- env << " https_proxy=#{https_proxy}" if https_proxy
238
- env << " ftp_proxy=#{ftp_proxy}" if ftp_proxy
239
- env << " no_proxy=#{no_proxy}" if no_proxy
240
-
241
- env == "env" ? cmd : "#{env} #{cmd}"
242
- end
243
-
244
- # Executes a remote command over SSH.
245
- #
246
- # @param command [String] remove command to run
247
- # @param connection [Kitchen::SSH] an SSH connection
248
- # @raise [ActionFailed] if an exception occurs
249
- # @api private
250
- def run_remote(command, connection)
251
- return if command.nil?
252
-
253
- connection.exec(env_cmd(command))
254
- rescue SSHFailed, Net::SSH::Exception => ex
255
- raise ActionFailed, ex.message
256
- end
257
-
258
- # Transfers one or more local paths over SSH.
259
- #
260
- # @param locals [Array<String>] array of local paths
261
- # @param remote [String] remote destination path
262
- # @param connection [Kitchen::SSH] an SSH connection
263
- # @raise [ActionFailed] if an exception occurs
264
- # @api private
265
- def transfer_path(locals, remote, connection)
266
- return if locals.nil? || Array(locals).empty?
267
-
268
- info("Transferring files to #{instance.to_str}")
269
- debug("TIMING: scp asynch upload (Kitchen::Driver::SSHBase)")
270
- elapsed = Benchmark.measure do
271
- transfer_path_async(locals, remote, connection)
272
- end
273
- delta = Util.duration(elapsed.real)
274
- debug("TIMING: scp async upload (Kitchen::Driver::SSHBase) took #{delta}")
275
- debug("Transfer complete")
276
- rescue SSHFailed, Net::SSH::Exception => ex
277
- raise ActionFailed, ex.message
278
- end
279
-
280
- def transfer_path_async(locals, remote, connection)
281
- waits = []
282
- locals.map do |local|
283
- waits.push connection.upload_path(local, remote)
284
- waits.shift.wait while waits.length >= config[:max_ssh_sessions]
285
- end
286
- waits.each(&:wait)
287
- end
288
-
289
- # Blocks until a TCP socket is available where a remote SSH server
290
- # should be listening.
291
- #
292
- # @param hostname [String] remote SSH server host
293
- # @param username [String] SSH username (default: `nil`)
294
- # @param options [Hash] configuration hash (default: `{}`)
295
- # @api private
296
- def wait_for_sshd(hostname, username = nil, options = {})
297
- pseudo_state = { hostname: }
298
- pseudo_state[:username] = username if username
299
- pseudo_state.merge!(options)
300
-
301
- instance.transport.connection(**backcompat_merged_state(pseudo_state))
302
- .wait_until_ready
303
- end
304
-
305
- # Intercepts any bare #puts calls in subclasses and issues an INFO log
306
- # event instead.
307
- #
308
- # @param msg [String] message string
309
- def puts(msg)
310
- info(msg)
311
- end
312
-
313
- # Intercepts any bare #print calls in subclasses and issues an INFO log
314
- # event instead.
315
- #
316
- # @param msg [String] message string
317
- def print(msg)
318
- info(msg)
319
- end
320
-
321
- # Delegates to Kitchen::ShellOut.run_command, overriding some default
322
- # options:
323
- #
324
- # * `:use_sudo` defaults to the value of `config[:use_sudo]` in the
325
- # Driver object
326
- # * `:log_subject` defaults to a String representation of the Driver's
327
- # class name
328
- #
329
- # @see ShellOut#run_command
330
- def run_command(cmd, options = {})
331
- base_options = {
332
- use_sudo: config[:use_sudo],
333
- log_subject: Thor::Util.snake_case(self.class.to_s),
334
- }.merge(options)
335
- super(cmd, base_options)
336
- end
337
-
338
- # Returns the Busser object associated with the driver.
339
- #
340
- # @return [Busser] a busser
341
- def busser
342
- instance.verifier
343
- end
344
- end
345
- end
346
- end