test-kitchen 3.9.1 → 4.0.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.
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.0.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-01-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bcrypt_pbkdf
@@ -223,67 +223,65 @@ dependencies:
223
223
  - !ruby/object:Gem::Version
224
224
  version: '2.0'
225
225
  - !ruby/object:Gem::Dependency
226
- name: winrm
226
+ name: chef-winrm
227
227
  requirement: !ruby/object:Gem::Requirement
228
228
  requirements:
229
- - - "~>"
229
+ - - ">="
230
230
  - !ruby/object:Gem::Version
231
- version: '2.0'
231
+ version: 2.5.0
232
+ - - "<"
233
+ - !ruby/object:Gem::Version
234
+ version: '3.0'
232
235
  type: :runtime
233
236
  prerelease: false
234
237
  version_requirements: !ruby/object:Gem::Requirement
235
238
  requirements:
236
- - - "~>"
239
+ - - ">="
237
240
  - !ruby/object:Gem::Version
238
- version: '2.0'
241
+ version: 2.5.0
242
+ - - "<"
243
+ - !ruby/object:Gem::Version
244
+ version: '3.0'
239
245
  - !ruby/object:Gem::Dependency
240
- name: winrm-elevated
246
+ name: chef-winrm-elevated
241
247
  requirement: !ruby/object:Gem::Requirement
242
248
  requirements:
243
- - - "~>"
249
+ - - ">="
244
250
  - !ruby/object:Gem::Version
245
251
  version: '1.0'
252
+ - - "<"
253
+ - !ruby/object:Gem::Version
254
+ version: '2.0'
246
255
  type: :runtime
247
256
  prerelease: false
248
257
  version_requirements: !ruby/object:Gem::Requirement
249
258
  requirements:
250
- - - "~>"
259
+ - - ">="
251
260
  - !ruby/object:Gem::Version
252
261
  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
- - - "~>"
262
+ - - "<"
265
263
  - !ruby/object:Gem::Version
266
- version: '1.1'
264
+ version: '2.0'
267
265
  - !ruby/object:Gem::Dependency
268
- name: license-acceptance
266
+ name: chef-winrm-fs
269
267
  requirement: !ruby/object:Gem::Requirement
270
268
  requirements:
271
269
  - - ">="
272
270
  - !ruby/object:Gem::Version
273
- version: 1.0.11
271
+ version: '1.0'
274
272
  - - "<"
275
273
  - !ruby/object:Gem::Version
276
- version: '3.0'
274
+ version: '2.0'
277
275
  type: :runtime
278
276
  prerelease: false
279
277
  version_requirements: !ruby/object:Gem::Requirement
280
278
  requirements:
281
279
  - - ">="
282
280
  - !ruby/object:Gem::Version
283
- version: 1.0.11
281
+ version: '1.0'
284
282
  - - "<"
285
283
  - !ruby/object:Gem::Version
286
- version: '3.0'
284
+ version: '2.0'
287
285
  description: Test Kitchen is an integration tool for developing and testing infrastructure
288
286
  code and software on isolated target platforms.
289
287
  email:
@@ -323,7 +321,6 @@ files:
323
321
  - lib/kitchen/driver/dummy.rb
324
322
  - lib/kitchen/driver/exec.rb
325
323
  - lib/kitchen/driver/proxy.rb
326
- - lib/kitchen/driver/ssh_base.rb
327
324
  - lib/kitchen/errors.rb
328
325
  - lib/kitchen/generator/init.rb
329
326
  - lib/kitchen/instance.rb
@@ -343,20 +340,10 @@ files:
343
340
  - lib/kitchen/plugin_base.rb
344
341
  - lib/kitchen/provisioner.rb
345
342
  - 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
343
  - lib/kitchen/provisioner/dummy.rb
356
344
  - lib/kitchen/provisioner/shell.rb
357
345
  - lib/kitchen/rake_tasks.rb
358
346
  - lib/kitchen/shell_out.rb
359
- - lib/kitchen/ssh.rb
360
347
  - lib/kitchen/state_file.rb
361
348
  - lib/kitchen/suite.rb
362
349
  - 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
@@ -1,116 +0,0 @@
1
- #
2
- # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
3
- #
4
- # Copyright (C) 2013, Fletcher Nichol
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # https://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
-
18
- require_relative "../../errors"
19
- require_relative "../../logging"
20
-
21
- module Kitchen
22
- module Provisioner
23
- module Chef
24
- # Chef cookbook resolver that uses Berkshelf and a Berksfile to calculate
25
- # dependencies.
26
- #
27
- # @author Fletcher Nichol <fnichol@nichol.ca>
28
- class Berkshelf
29
- include Logging
30
-
31
- # Creates a new cookbook resolver.
32
- #
33
- # @param berksfile [String] path to a Berksfile
34
- # @param path [String] path in which to vendor the resulting
35
- # cookbooks
36
- # @param logger [Kitchen::Logger] a logger to use for output, defaults
37
- # to `Kitchen.logger`
38
- def initialize(berksfile, path, logger: Kitchen.logger, always_update: false)
39
- @berksfile = berksfile
40
- @path = path
41
- @logger = logger
42
- @always_update = always_update
43
- end
44
-
45
- # Loads the library code required to use the resolver.
46
- #
47
- # @param logger [Kitchen::Logger] a logger to use for output, defaults
48
- # to `Kitchen.logger`
49
- def self.load!(logger: Kitchen.logger)
50
- load_berkshelf!(logger)
51
- end
52
-
53
- # Performs the cookbook resolution and vendors the resulting cookbooks
54
- # in the desired path.
55
- def resolve
56
- version = ::Berkshelf::VERSION
57
- info("Resolving cookbook dependencies with Berkshelf #{version}...")
58
- debug("Using Berksfile from #{berksfile}")
59
-
60
- ::Berkshelf.ui.mute do
61
- berksfile_obj = ::Berkshelf::Berksfile.from_file(berksfile)
62
- berksfile_obj.update if always_update && berksfile_obj.lockfile.present?
63
- # Berkshelf requires the directory to not exist
64
- FileUtils.rm_rf(path)
65
- berksfile_obj.vendor(path)
66
- end
67
- end
68
-
69
- private
70
-
71
- # @return [String] path to a Berksfile
72
- # @api private
73
- attr_reader :berksfile
74
-
75
- # @return [String] path in which to vendor the resulting cookbooks
76
- # @api private
77
- attr_reader :path
78
-
79
- # @return [Kitchen::Logger] a logger to use for output
80
- # @api private
81
- attr_reader :logger
82
-
83
- # @return [Boolean] If true, always update cookbooks in Berkshelf.
84
- # @api private
85
- attr_reader :always_update
86
-
87
- class << self
88
- private
89
-
90
- # Load the Berkshelf-specific library code.
91
- #
92
- # @param logger [Kitchen::Logger] the logger to use
93
- # @raise [UserError] if the library couldn't be loaded
94
- # @api private
95
- def load_berkshelf!(logger)
96
- first_load = require "berkshelf"
97
-
98
- version = ::Berkshelf::VERSION
99
- if first_load
100
- logger.debug("Berkshelf #{version} library loaded")
101
- else
102
- logger.debug("Berkshelf #{version} previously loaded")
103
- end
104
- rescue LoadError => e
105
- logger.fatal("The `berkshelf' gem is missing and must be installed" \
106
- " or cannot be properly activated. Run" \
107
- " `gem install berkshelf` or add the following to your" \
108
- " Gemfile if you are using Bundler: `gem 'berkshelf'`.")
109
- raise UserError,
110
- "Could not load or activate Berkshelf (#{e.message})"
111
- end
112
- end
113
- end
114
- end
115
- end
116
- end