test-kitchen 2.2.5 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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