test-kitchen 2.2.5 → 2.3.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.
@@ -82,7 +82,7 @@ module Kitchen
82
82
  conn.execute(env_cmd(provisioner.run_command))
83
83
  info("Downloading files from #{instance.to_str}")
84
84
  provisioner[:downloads].to_h.each do |remotes, local|
85
- debug("Downloading #{Array(remotes).join(', ')} to #{local}")
85
+ debug("Downloading #{Array(remotes).join(", ")} to #{local}")
86
86
  conn.download(remotes, local)
87
87
  end
88
88
  debug("Download complete")
@@ -136,13 +136,12 @@ module Kitchen
136
136
  # Package an instance.
137
137
  #
138
138
  # (see Base#package)
139
- def package(state) # rubocop:disable Lint/UnusedMethodArgument
140
- end
139
+ def package(state); end
141
140
 
142
141
  # (see Base#login_command)
143
142
  def login_command(state)
144
143
  instance.transport.connection(backcompat_merged_state(state))
145
- .login_command
144
+ .login_command
146
145
  end
147
146
 
148
147
  # Executes an arbitrary command on an instance over an SSH connection.
@@ -179,8 +178,7 @@ module Kitchen
179
178
  #
180
179
  # @raise [UserError] if the driver will not be able to perform or if a
181
180
  # documented dependency is missing from the system
182
- def verify_dependencies
183
- end
181
+ def verify_dependencies; end
184
182
 
185
183
  class << self
186
184
  # @return [Array<Symbol>] an array of action method names that cannot
@@ -207,7 +205,7 @@ module Kitchen
207
205
  # @param methods [Array<Symbol>] one or more actions as symbols
208
206
  # @raise [ClientError] if any method is not a valid action method name
209
207
  def self.no_parallel_for(*methods)
210
- action_methods = [:create, :converge, :setup, :verify, :destroy]
208
+ action_methods = %i{create converge setup verify destroy}
211
209
 
212
210
  Array(methods).each do |meth|
213
211
  next if action_methods.include?(meth)
@@ -223,8 +221,7 @@ module Kitchen
223
221
  # that it can leverage it internally
224
222
  #
225
223
  # @return path [String] a path of the cache directory
226
- def cache_directory
227
- end
224
+ def cache_directory; end
228
225
 
229
226
  private
230
227
 
@@ -265,6 +262,7 @@ module Kitchen
265
262
  # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize
266
263
  def env_cmd(cmd)
267
264
  return if cmd.nil?
265
+
268
266
  env = "env"
269
267
  http_proxy = config[:http_proxy] || ENV["http_proxy"] ||
270
268
  ENV["HTTP_PROXY"]
@@ -343,7 +341,7 @@ module Kitchen
343
341
  pseudo_state.merge!(options)
344
342
 
345
343
  instance.transport.connection(backcompat_merged_state(pseudo_state))
346
- .wait_until_ready
344
+ .wait_until_ready
347
345
  end
348
346
 
349
347
  # Intercepts any bare #puts calls in subclasses and issues an INFO log
@@ -29,29 +29,29 @@ module Kitchen
29
29
  include Thor::Actions
30
30
 
31
31
  class_option :driver,
32
- type: :array,
33
- aliases: "-D",
34
- default: %w{kitchen-vagrant},
35
- desc: <<-D.gsub(/^\s+/, "").tr("\n", " ")
32
+ type: :array,
33
+ aliases: "-D",
34
+ default: %w{kitchen-vagrant},
35
+ desc: <<-D.gsub(/^\s+/, "").tr("\n", " ")
36
36
  One or more Kitchen Driver gems to be installed or added to a
37
37
  Gemfile
38
- D
38
+ D
39
39
 
40
40
  class_option :provisioner,
41
- type: :string,
42
- aliases: "-P",
43
- default: "chef_solo",
44
- desc: <<-D.gsub(/^\s+/, "").tr("\n", " ")
41
+ type: :string,
42
+ aliases: "-P",
43
+ default: "chef_solo",
44
+ desc: <<-D.gsub(/^\s+/, "").tr("\n", " ")
45
45
  The default Kitchen Provisioner to use
46
- D
46
+ D
47
47
 
48
48
  class_option :create_gemfile,
49
- type: :boolean,
50
- default: false,
51
- desc: <<-D.gsub(/^\s+/, "").tr("\n", " ")
49
+ type: :boolean,
50
+ default: false,
51
+ desc: <<-D.gsub(/^\s+/, "").tr("\n", " ")
52
52
  Whether or not to create a Gemfile if one does not exist.
53
53
  Default: false
54
- D
54
+ D
55
55
 
56
56
  # Invoke the command.
57
57
  def init
@@ -80,10 +80,9 @@ module Kitchen
80
80
  driver_plugin = Array(options[:driver]).first || "dummy"
81
81
 
82
82
  template("kitchen.yml.erb", "kitchen.yml",
83
- driver_plugin: driver_plugin.sub(/^kitchen-/, ""),
84
- provisioner: options[:provisioner],
85
- run_list: Array(run_list)
86
- )
83
+ driver_plugin: driver_plugin.sub(/^kitchen-/, ""),
84
+ provisioner: options[:provisioner],
85
+ run_list: Array(run_list))
87
86
  end
88
87
 
89
88
  # Creates the `chefignore` file.
@@ -217,7 +217,7 @@ module Kitchen
217
217
  transport.connection(state).login_command
218
218
  end
219
219
 
220
- debug(%{Login command: #{lc.command} #{lc.arguments.join(' ')} } \
220
+ debug(%{Login command: #{lc.command} #{lc.arguments.join(" ")} } \
221
221
  "(Options: #{lc.options})")
222
222
  Kernel.exec(*lc.exec_args)
223
223
  end
@@ -252,9 +252,9 @@ module Kitchen
252
252
  # @return [Hash] a diagnostic hash
253
253
  def diagnose
254
254
  result = {}
255
- [
256
- :platform, :state_file, :driver, :provisioner, :transport, :verifier
257
- ].each do |sym|
255
+ %i{
256
+ platform state_file driver provisioner transport verifier
257
+ }.each do |sym|
258
258
  obj = send(sym)
259
259
  result[sym] = obj.respond_to?(:diagnose) ? obj.diagnose : :unknown
260
260
  end
@@ -267,7 +267,7 @@ module Kitchen
267
267
  # @return [Hash] a diagnostic hash
268
268
  def diagnose_plugins
269
269
  result = {}
270
- [:driver, :provisioner, :verifier, :transport].each do |sym|
270
+ %i{driver provisioner verifier transport}.each do |sym|
271
271
  obj = send(sym)
272
272
  result[sym] = if obj.respond_to?(:diagnose_plugin)
273
273
  obj.diagnose_plugin
@@ -313,10 +313,10 @@ module Kitchen
313
313
  # @raise [ClientError] if any validations fail
314
314
  # @api private
315
315
  def validate_options(options)
316
- [
317
- :suite, :platform, :driver, :provisioner,
318
- :transport, :verifier, :state_file
319
- ].each do |k|
316
+ %i{
317
+ suite platform driver provisioner
318
+ transport verifier state_file
319
+ }.each do |k|
320
320
  next if options.key?(k)
321
321
 
322
322
  raise ClientError, "Instance#new requires option :#{k}"
@@ -521,12 +521,12 @@ module Kitchen
521
521
  state[:last_error] = e.class.name
522
522
  raise(InstanceFailure, failure_message(what) +
523
523
  " Please see .kitchen/logs/#{name}.log for more details",
524
- e.backtrace)
524
+ e.backtrace)
525
525
  rescue Exception => e # rubocop:disable Lint/RescueException
526
526
  log_failure(what, e)
527
527
  state[:last_error] = e.class.name
528
528
  raise ActionFailed,
529
- "Failed to complete ##{what} action: [#{e.message}]", e.backtrace
529
+ "Failed to complete ##{what} action: [#{e.message}]", e.backtrace
530
530
  ensure
531
531
  state_file.write(state)
532
532
  end
@@ -681,7 +681,7 @@ module Kitchen
681
681
  end
682
682
  end
683
683
 
684
- TRANSITIONS = [:destroy, :create, :converge, :setup, :verify].freeze
684
+ TRANSITIONS = %i{destroy create converge setup verify}.freeze
685
685
 
686
686
  # Determines the index of a state in the state lifecycle vector. Woah.
687
687
  #
@@ -61,6 +61,7 @@ module Kitchen
61
61
  # No hooks? We're outta here.
62
62
  hook_data = Array(config[hook_key])
63
63
  return if hook_data.empty?
64
+
64
65
  hook_data.each do |hook|
65
66
  # Coerce the common case of a bare string to be a local command. This
66
67
  # is to match the behavior of the old `pre_create_command` semi-hook.
@@ -352,7 +352,14 @@ module Kitchen
352
352
  def parse_yaml_string(string, file_name)
353
353
  return {} if string.nil? || string.empty?
354
354
 
355
- result = ::YAML.safe_load(string, [Symbol], [], true) || {}
355
+ result =
356
+ if Gem::Requirement.new(">= 3.1.0").satisfied_by?(Gem::Version.new(Psych::VERSION))
357
+ # ruby >= 2.6.0
358
+ ::YAML.safe_load(string, permitted_classes: [Symbol], permitted_symbols: [], aliases: true) || {}
359
+ else
360
+ # ruby < 2.6.0
361
+ ::YAML.safe_load(string, [Symbol], [], true) || {}
362
+ end
356
363
  unless result.is_a?(Hash)
357
364
  raise UserError, "Error parsing #{file_name} as YAML " \
358
365
  "(Result of parse was not a Hash, but was a #{result.class}).\n" \
@@ -65,13 +65,13 @@ module Kitchen
65
65
  # plugins of the given type
66
66
  def self.plugins_available(plugin_type)
67
67
  $LOAD_PATH.map { |load_path| Dir[File.expand_path("kitchen/#{plugin_type}/*.rb", load_path)] }
68
- .reject { |plugin_paths| plugin_paths.empty? }
69
- .flatten
70
- .uniq
71
- .select { |plugin_path| File.readlines(plugin_path).grep(/^\s*class \w* </).any? }
72
- .map { |plugin_path| File.basename(plugin_path).gsub(/\.rb$/, "") }
73
- .reject { |plugin_name| plugin_name == "base" }
74
- .sort
68
+ .reject(&:empty?)
69
+ .flatten
70
+ .uniq
71
+ .select { |plugin_path| File.readlines(plugin_path).grep(/^\s*class \w* </).any? }
72
+ .map { |plugin_path| File.basename(plugin_path).gsub(/\.rb$/, "") }
73
+ .reject { |plugin_name| plugin_name == "base" }
74
+ .sort
75
75
  end
76
76
  end
77
77
  end
@@ -86,7 +86,7 @@ module Kitchen
86
86
  )
87
87
  info("Downloading files from #{instance.to_str}")
88
88
  config[:downloads].to_h.each do |remotes, local|
89
- debug("Downloading #{Array(remotes).join(', ')} to #{local}")
89
+ debug("Downloading #{Array(remotes).join(", ")} to #{local}")
90
90
  conn.download(remotes, local)
91
91
  end
92
92
  debug("Download complete")
@@ -108,16 +108,14 @@ module Kitchen
108
108
  # Certain products that Test Kitchen uses to provision require accepting
109
109
  # a license to use. Overwrite this method in the specific provisioner
110
110
  # to implement this check.
111
- def check_license
112
- end
111
+ def check_license; end
113
112
 
114
113
  # Generates a command string which will install and configure the
115
114
  # provisioner software on an instance. If no work is required, then `nil`
116
115
  # will be returned.
117
116
  #
118
117
  # @return [String] a command string
119
- def install_command
120
- end
118
+ def install_command; end
121
119
 
122
120
  # Generates a command string which will perform any data initialization
123
121
  # or configuration required after the provisioner software is installed
@@ -125,8 +123,7 @@ module Kitchen
125
123
  # is required, then `nil` will be returned.
126
124
  #
127
125
  # @return [String] a command string
128
- def init_command
129
- end
126
+ def init_command; end
130
127
 
131
128
  # Generates a command string which will perform any commands or
132
129
  # configuration required just before the main provisioner run command but
@@ -134,16 +131,14 @@ module Kitchen
134
131
  # required, then `nil` will be returned.
135
132
  #
136
133
  # @return [String] a command string
137
- def prepare_command
138
- end
134
+ def prepare_command; end
139
135
 
140
136
  # Generates a command string which will invoke the main provisioner
141
137
  # command on the prepared instance. If no work is required, then `nil`
142
138
  # will be returned.
143
139
  #
144
140
  # @return [String] a command string
145
- def run_command
146
- end
141
+ def run_command; end
147
142
 
148
143
  # Creates a temporary directory on the local workstation into which
149
144
  # provisioner related files and directories can be copied or created. The
@@ -108,7 +108,7 @@ module Kitchen
108
108
  " `gem install berkshelf` or add the following to your" \
109
109
  " Gemfile if you are using Bundler: `gem 'berkshelf'`.")
110
110
  raise UserError,
111
- "Could not load or activate Berkshelf (#{e.message})"
111
+ "Could not load or activate Berkshelf (#{e.message})"
112
112
  end
113
113
  end
114
114
  end
@@ -81,7 +81,7 @@ module Kitchen
81
81
  # @api private
82
82
  def all_files_in_cookbooks
83
83
  Util.list_directory(tmpbooks_dir, include_dot: true, recurse: true)
84
- .select { |fn| File.file?(fn) }
84
+ .select { |fn| File.file?(fn) }
85
85
  end
86
86
 
87
87
  # @return [String] an absolute path to a Policyfile, relative to the
@@ -89,14 +89,15 @@ module Kitchen
89
89
  # @api private
90
90
  def policyfile
91
91
  basename = config[:policyfile_path] || config[:policyfile] || "Policyfile.rb"
92
- File.join(config[:kitchen_root], basename)
92
+ File.expand_path(basename, config[:kitchen_root])
93
93
  end
94
94
 
95
95
  # @return [String] an absolute path to a Berksfile, relative to the
96
96
  # kitchen root
97
97
  # @api private
98
98
  def berksfile
99
- File.join(config[:kitchen_root], "Berksfile")
99
+ basename = config[:berksfile_path] || "Berksfile"
100
+ File.expand_path(basename, config[:kitchen_root])
100
101
  end
101
102
 
102
103
  # @return [String] an absolute path to a cookbooks/ directory, relative
@@ -140,8 +141,8 @@ module Kitchen
140
141
  debug("Using metadata.rb from #{metadata_rb}")
141
142
 
142
143
  cb_name = MetadataChopper.extract(metadata_rb).first || raise(UserError,
143
- "The metadata.rb does not define the 'name' key." \
144
- " Please add: `name '<cookbook_name>'` to metadata.rb and retry")
144
+ "The metadata.rb does not define the 'name' key." \
145
+ " Please add: `name '<cookbook_name>'` to metadata.rb and retry")
145
146
 
146
147
  cb_path = File.join(tmpbooks_dir, cb_name)
147
148
 
@@ -287,8 +288,8 @@ module Kitchen
287
288
  warn("Ignored run_list: #{config[:run_list].inspect}")
288
289
  end
289
290
  policy = Chef::Policyfile.new(policyfile, sandbox_path,
290
- logger: logger,
291
- always_update: config[:always_update_cookbooks])
291
+ logger: logger,
292
+ always_update: config[:always_update_cookbooks])
292
293
  Kitchen.mutex.synchronize do
293
294
  policy.compile
294
295
  end
@@ -303,8 +304,8 @@ module Kitchen
303
304
  def resolve_with_policyfile
304
305
  Kitchen.mutex.synchronize do
305
306
  Chef::Policyfile.new(policyfile, sandbox_path,
306
- logger: logger,
307
- always_update: config[:always_update_cookbooks]).resolve
307
+ logger: logger,
308
+ always_update: config[:always_update_cookbooks]).resolve
308
309
  end
309
310
  end
310
311
 
@@ -314,8 +315,8 @@ module Kitchen
314
315
  def resolve_with_berkshelf
315
316
  Kitchen.mutex.synchronize do
316
317
  Chef::Berkshelf.new(berksfile, tmpbooks_dir,
317
- logger: logger,
318
- always_update: config[:always_update_cookbooks]).resolve
318
+ logger: logger,
319
+ always_update: config[:always_update_cookbooks]).resolve
319
320
  end
320
321
  end
321
322
 
@@ -143,7 +143,7 @@ module Kitchen
143
143
  "from https://downloads.chef.io and that your PATH " \
144
144
  "setting includes the path to the `chef` comand.")
145
145
  raise UserError,
146
- "Could not find the chef executable in your PATH."
146
+ "Could not find the chef executable in your PATH."
147
147
  end
148
148
  end
149
149
  end
@@ -100,7 +100,7 @@ module Kitchen
100
100
  lines = []
101
101
  config[:run_list].map do |recipe|
102
102
  cmd = sudo(config[:chef_apply_path]).dup
103
- .tap { |str| str.insert(0, "& ") if powershell_shell? }
103
+ .tap { |str| str.insert(0, "& ") if powershell_shell? }
104
104
  args = [
105
105
  "apply/#{recipe}.rb",
106
106
  "--log_level #{level}",
@@ -59,6 +59,9 @@ module Kitchen
59
59
  # Will try to autodetect by searching for `Policyfile.rb` if not set.
60
60
  # If set, will error if the file doesn't exist.
61
61
  default_config :policyfile_path, nil
62
+ # Will try to autodetect by searching for `Berksfile` if not set.
63
+ # If set, will error if the file doesn't exist.
64
+ default_config :berksfile_path, nil
62
65
  # If set to true (which is the default from `chef generate`), try to update
63
66
  # backend cookbook downloader on every kitchen run.
64
67
  default_config :always_update_cookbooks, false
@@ -262,9 +265,24 @@ module Kitchen
262
265
  end
263
266
  end
264
267
 
268
+ # If the user has policyfiles we shell out to the `chef` executable, so need to ensure they have
269
+ # accepted the Chef Workstation license. Otherwise they just need the Chef Infra license.
270
+ #
271
+ # @return [String] license id to prompt for acceptance
272
+ def license_acceptance_id
273
+ case
274
+ when File.exist?(policyfile)
275
+ "chef-workstation"
276
+ when config[:product_name]
277
+ config[:product_name]
278
+ else
279
+ "chef"
280
+ end
281
+ end
282
+
265
283
  # (see Base#check_license)
266
284
  def check_license
267
- name = config[:product_name] || "chef"
285
+ name = license_acceptance_id
268
286
  version = product_version
269
287
  debug("Checking if we need to prompt for license acceptance on product: #{name} version: #{version}.")
270
288
 
@@ -309,6 +327,7 @@ module Kitchen
309
327
  def install_command
310
328
  return unless config[:require_chef_omnibus] || config[:product_name]
311
329
  return if config[:product_name] && config[:install_strategy] == "skip"
330
+
312
331
  prefix_command(sudo(install_script_contents))
313
332
  end
314
333
 
@@ -330,7 +349,7 @@ module Kitchen
330
349
  sudo_command: sudo_command,
331
350
  }.tap do |opts|
332
351
  opts[:root] = config[:chef_omnibus_root] if config.key? :chef_omnibus_root
333
- [:install_msi_url, :http_proxy, :https_proxy].each do |key|
352
+ %i{install_msi_url http_proxy https_proxy}.each do |key|
334
353
  opts[key] = config[key] if config.key? key
335
354
  end
336
355
  end
@@ -354,14 +373,15 @@ module Kitchen
354
373
  # @api private
355
374
  def policyfile
356
375
  policyfile_basename = config[:policyfile_path] || config[:policyfile] || "Policyfile.rb"
357
- File.join(config[:kitchen_root], policyfile_basename)
376
+ File.expand_path(policyfile_basename, config[:kitchen_root])
358
377
  end
359
378
 
360
379
  # @return [String] an absolute path to a Berksfile, relative to the
361
380
  # kitchen root
362
381
  # @api private
363
382
  def berksfile
364
- File.join(config[:kitchen_root], "Berksfile")
383
+ berksfile_basename = config[:berksfile_path] || config[:berksfile] || "Berksfile"
384
+ File.expand_path(berksfile_basename, config[:kitchen_root])
365
385
  end
366
386
 
367
387
  # Generates a Hash with default values for a solo.rb or client.rb Chef
@@ -423,7 +443,7 @@ module Kitchen
423
443
  elsif obj.is_a?(String)
424
444
  %{"#{obj.gsub(/\\/, '\\\\\\\\')}"}
425
445
  elsif obj.is_a?(Array)
426
- %{[#{obj.map { |i| format_value(i) }.join(', ')}]}
446
+ %{[#{obj.map { |i| format_value(i) }.join(", ")}]}
427
447
  else
428
448
  obj.inspect
429
449
  end
@@ -449,7 +469,7 @@ module Kitchen
449
469
  # @api private
450
470
  def init_command_vars_for_powershell(dirs)
451
471
  [
452
- %{$dirs = @(#{dirs.map { |d| %{"#{d}"} }.join(', ')})},
472
+ %{$dirs = @(#{dirs.map { |d| %{"#{d}"} }.join(", ")})},
453
473
  shell_var("root_path", config[:root_path]),
454
474
  ].join("\n")
455
475
  end
@@ -492,7 +512,7 @@ module Kitchen
492
512
  },
493
513
  }.tap do |opts|
494
514
  opts[:shell_type] = :ps1 if powershell_shell?
495
- [:platform, :platform_version, :architecture].each do |key|
515
+ %i{platform platform_version architecture}.each do |key|
496
516
  opts[key] = config[key] if config[key]
497
517
  end
498
518
 
@@ -507,12 +527,12 @@ module Kitchen
507
527
  end
508
528
 
509
529
  proxies = {}.tap do |prox|
510
- [:http_proxy, :https_proxy, :ftp_proxy, :no_proxy].each do |key|
530
+ %i{http_proxy https_proxy ftp_proxy no_proxy}.each do |key|
511
531
  prox[key] = config[key] if config[key]
512
532
  end
513
533
 
514
534
  # install.ps1 only supports http_proxy
515
- prox.delete_if { |p| [:https_proxy, :ftp_proxy, :no_proxy].include?(p) } if powershell_shell?
535
+ prox.delete_if { |p| %i{https_proxy ftp_proxy no_proxy}.include?(p) } if powershell_shell?
516
536
  end
517
537
 
518
538
  opts[:install_command_options].merge!(proxies)
@@ -546,7 +566,8 @@ module Kitchen
546
566
  # @api private
547
567
  def script_for_omnibus_version
548
568
  installer = Mixlib::Install::ScriptGenerator.new(
549
- config[:require_chef_omnibus], powershell_shell?, install_options)
569
+ config[:require_chef_omnibus], powershell_shell?, install_options
570
+ )
550
571
  config[:chef_omnibus_root] = installer.root
551
572
  installer.install_command
552
573
  end
@@ -567,13 +588,18 @@ module Kitchen
567
588
  if (config[:policyfile_path] || config[:policyfile]) && !File.exist?(policyfile)
568
589
  raise UserError, "policyfile_path set in config "\
569
590
  "(#{config[:policyfile_path]} could not be found. " \
570
- "Expected to find it at full path #{policyfile} " \
591
+ "Expected to find it at full path #{policyfile}."
592
+ end
593
+ if config[:berksfile_path] && !File.exist?(berksfile)
594
+ raise UserError, "berksfile_path set in config "\
595
+ "(#{config[:berksfile_path]} could not be found. " \
596
+ "Expected to find it at full path #{berksfile}."
571
597
  end
572
598
  if File.exist?(policyfile) && !supports_policyfile?
573
599
  raise UserError, "policyfile detected, but provisioner " \
574
600
  "#{self.class.name} doesn't support Policyfiles. " \
575
601
  "Either use a different provisioner, or delete/rename " \
576
- "#{policyfile}"
602
+ "#{policyfile}."
577
603
  end
578
604
  end
579
605