test-kitchen 1.2.1 → 1.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.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +1 -1
  3. data/.rubocop.yml +3 -0
  4. data/.travis.yml +20 -9
  5. data/CHANGELOG.md +219 -108
  6. data/Gemfile +10 -6
  7. data/Guardfile +38 -9
  8. data/README.md +11 -1
  9. data/Rakefile +21 -37
  10. data/bin/kitchen +4 -4
  11. data/features/kitchen_action_commands.feature +161 -0
  12. data/features/kitchen_console_command.feature +34 -0
  13. data/features/kitchen_diagnose_command.feature +64 -0
  14. data/features/kitchen_init_command.feature +29 -17
  15. data/features/kitchen_list_command.feature +2 -2
  16. data/features/kitchen_login_command.feature +56 -0
  17. data/features/{sink_command.feature → kitchen_sink_command.feature} +0 -0
  18. data/features/kitchen_test_command.feature +88 -0
  19. data/features/step_definitions/gem_steps.rb +8 -6
  20. data/features/step_definitions/git_steps.rb +4 -2
  21. data/features/step_definitions/output_steps.rb +5 -0
  22. data/features/support/env.rb +12 -9
  23. data/lib/kitchen.rb +60 -38
  24. data/lib/kitchen/base64_stream.rb +55 -0
  25. data/lib/kitchen/busser.rb +124 -58
  26. data/lib/kitchen/cli.rb +121 -38
  27. data/lib/kitchen/collection.rb +3 -3
  28. data/lib/kitchen/color.rb +4 -4
  29. data/lib/kitchen/command.rb +78 -11
  30. data/lib/kitchen/command/action.rb +3 -2
  31. data/lib/kitchen/command/console.rb +12 -5
  32. data/lib/kitchen/command/diagnose.rb +17 -3
  33. data/lib/kitchen/command/driver_discover.rb +26 -7
  34. data/lib/kitchen/command/exec.rb +41 -0
  35. data/lib/kitchen/command/list.rb +44 -14
  36. data/lib/kitchen/command/login.rb +2 -1
  37. data/lib/kitchen/command/sink.rb +2 -1
  38. data/lib/kitchen/command/test.rb +5 -4
  39. data/lib/kitchen/config.rb +146 -14
  40. data/lib/kitchen/configurable.rb +314 -0
  41. data/lib/kitchen/data_munger.rb +522 -18
  42. data/lib/kitchen/diagnostic.rb +43 -4
  43. data/lib/kitchen/driver.rb +4 -4
  44. data/lib/kitchen/driver/base.rb +80 -115
  45. data/lib/kitchen/driver/dummy.rb +34 -6
  46. data/lib/kitchen/driver/proxy.rb +14 -3
  47. data/lib/kitchen/driver/ssh_base.rb +61 -7
  48. data/lib/kitchen/errors.rb +109 -9
  49. data/lib/kitchen/generator/driver_create.rb +39 -5
  50. data/lib/kitchen/generator/init.rb +130 -45
  51. data/lib/kitchen/instance.rb +162 -28
  52. data/lib/kitchen/lazy_hash.rb +79 -7
  53. data/lib/kitchen/loader/yaml.rb +159 -27
  54. data/lib/kitchen/logger.rb +267 -21
  55. data/lib/kitchen/logging.rb +30 -3
  56. data/lib/kitchen/login_command.rb +11 -2
  57. data/lib/kitchen/metadata_chopper.rb +2 -2
  58. data/lib/kitchen/provisioner.rb +4 -4
  59. data/lib/kitchen/provisioner/base.rb +107 -103
  60. data/lib/kitchen/provisioner/chef/berkshelf.rb +36 -8
  61. data/lib/kitchen/provisioner/chef/librarian.rb +40 -11
  62. data/lib/kitchen/provisioner/chef_base.rb +206 -167
  63. data/lib/kitchen/provisioner/chef_solo.rb +25 -7
  64. data/lib/kitchen/provisioner/chef_zero.rb +105 -29
  65. data/lib/kitchen/provisioner/dummy.rb +1 -1
  66. data/lib/kitchen/provisioner/shell.rb +21 -6
  67. data/lib/kitchen/rake_tasks.rb +8 -3
  68. data/lib/kitchen/shell_out.rb +15 -18
  69. data/lib/kitchen/ssh.rb +122 -27
  70. data/lib/kitchen/state_file.rb +24 -7
  71. data/lib/kitchen/thor_tasks.rb +9 -4
  72. data/lib/kitchen/util.rb +43 -118
  73. data/lib/kitchen/version.rb +1 -1
  74. data/lib/vendor/hash_recursive_merge.rb +10 -2
  75. data/spec/kitchen/base64_stream_spec.rb +77 -0
  76. data/spec/kitchen/busser_spec.rb +490 -0
  77. data/spec/kitchen/collection_spec.rb +10 -10
  78. data/spec/kitchen/color_spec.rb +2 -2
  79. data/spec/kitchen/config_spec.rb +234 -62
  80. data/spec/kitchen/configurable_spec.rb +490 -0
  81. data/spec/kitchen/data_munger_spec.rb +1070 -862
  82. data/spec/kitchen/diagnostic_spec.rb +79 -0
  83. data/spec/kitchen/driver/base_spec.rb +80 -85
  84. data/spec/kitchen/driver/dummy_spec.rb +43 -14
  85. data/spec/kitchen/driver/proxy_spec.rb +134 -0
  86. data/spec/kitchen/driver/ssh_base_spec.rb +644 -0
  87. data/spec/kitchen/driver_spec.rb +15 -15
  88. data/spec/kitchen/errors_spec.rb +309 -0
  89. data/spec/kitchen/instance_spec.rb +143 -46
  90. data/spec/kitchen/lazy_hash_spec.rb +36 -9
  91. data/spec/kitchen/loader/yaml_spec.rb +237 -226
  92. data/spec/kitchen/logger_spec.rb +419 -0
  93. data/spec/kitchen/logging_spec.rb +59 -0
  94. data/spec/kitchen/login_command_spec.rb +49 -0
  95. data/spec/kitchen/metadata_chopper_spec.rb +82 -0
  96. data/spec/kitchen/platform_spec.rb +4 -4
  97. data/spec/kitchen/provisioner/base_spec.rb +65 -125
  98. data/spec/kitchen/provisioner/chef_base_spec.rb +798 -0
  99. data/spec/kitchen/provisioner/chef_solo_spec.rb +316 -0
  100. data/spec/kitchen/provisioner/chef_zero_spec.rb +624 -0
  101. data/spec/kitchen/provisioner/shell_spec.rb +269 -0
  102. data/spec/kitchen/provisioner_spec.rb +6 -6
  103. data/spec/kitchen/shell_out_spec.rb +143 -0
  104. data/spec/kitchen/ssh_spec.rb +683 -0
  105. data/spec/kitchen/state_file_spec.rb +28 -21
  106. data/spec/kitchen/suite_spec.rb +7 -7
  107. data/spec/kitchen/util_spec.rb +68 -10
  108. data/spec/kitchen_spec.rb +107 -0
  109. data/spec/spec_helper.rb +18 -13
  110. data/support/chef-client-zero.rb +10 -9
  111. data/support/chef_helpers.sh +16 -0
  112. data/support/download_helpers.sh +109 -0
  113. data/test-kitchen.gemspec +42 -33
  114. metadata +107 -33
@@ -16,7 +16,7 @@
16
16
  # See the License for the specific language governing permissions and
17
17
  # limitations under the License.
18
18
 
19
- require 'vendor/hash_recursive_merge'
19
+ require "vendor/hash_recursive_merge"
20
20
 
21
21
  module Kitchen
22
22
 
@@ -26,9 +26,18 @@ module Kitchen
26
26
  # This object will mutate the data Hash passed into its constructor and so
27
27
  # should not be reused or shared across threads.
28
28
  #
29
+ # If you are squeamish or faint of heart, then you might want to skip this
30
+ # class. Just remember, you were warned. And if you made it this far, be
31
+ # sure to tweet at @fnichol and let him know your fear factor level.
32
+ #
29
33
  # @author Fletcher Nichol <fnichol@nichol.ca>
30
34
  class DataMunger
31
35
 
36
+ # Constructs a new DataMunger object.
37
+ #
38
+ # @param data [Hash] the incoming user data hash
39
+ # @param kitchen_config [Hash] the incoming Test Kitchen-provided
40
+ # configuration hash
32
41
  def initialize(data, kitchen_config = {})
33
42
  @data = data
34
43
  @kitchen_config = kitchen_config
@@ -38,6 +47,13 @@ module Kitchen
38
47
  move_chef_data_to_provisioner!
39
48
  end
40
49
 
50
+ # Generate a new Hash of configuration data that can be used to construct
51
+ # a new Busser object.
52
+ #
53
+ # @param suite [String] a suite name
54
+ # @param platform [String] a platform name
55
+ # @return [Hash] a new configuration Hash that can be used to construct a
56
+ # new Busser
41
57
  def busser_data_for(suite, platform)
42
58
  merged_data_for(:busser, suite, platform, :version).tap do |bdata|
43
59
  set_kitchen_config_at!(bdata, :kitchen_root)
@@ -46,6 +62,13 @@ module Kitchen
46
62
  end
47
63
  end
48
64
 
65
+ # Generate a new Hash of configuration data that can be used to construct
66
+ # a new Driver object.
67
+ #
68
+ # @param suite [String] a suite name
69
+ # @param platform [String] a platform name
70
+ # @return [Hash] a new configuration Hash that can be used to construct a
71
+ # new Driver
49
72
  def driver_data_for(suite, platform)
50
73
  merged_data_for(:driver, suite, platform).tap do |ddata|
51
74
  set_kitchen_config_at!(ddata, :kitchen_root)
@@ -54,10 +77,20 @@ module Kitchen
54
77
  end
55
78
  end
56
79
 
80
+ # Returns an Array of platform Hashes.
81
+ #
82
+ # @return [Array<Hash>] an Array of Hashes
57
83
  def platform_data
58
84
  data.fetch(:platforms, [])
59
85
  end
60
86
 
87
+ # Generate a new Hash of configuration data that can be used to construct
88
+ # a new Provisioner object.
89
+ #
90
+ # @param suite [String] a suite name
91
+ # @param platform [String] a platform name
92
+ # @return [Hash] a new configuration Hash that can be used to construct a
93
+ # new Provisioner
61
94
  def provisioner_data_for(suite, platform)
62
95
  merged_data_for(:provisioner, suite, platform).tap do |pdata|
63
96
  set_kitchen_config_at!(pdata, :kitchen_root)
@@ -67,31 +100,145 @@ module Kitchen
67
100
  end
68
101
  end
69
102
 
103
+ # Returns an Array of suite Hashes.
104
+ #
105
+ # @return [Array<Hash>] an Array of Hashes
70
106
  def suite_data
71
107
  data.fetch(:suites, [])
72
108
  end
73
109
 
74
110
  private
75
111
 
76
- attr_reader :data, :kitchen_config
112
+ # @return [Hash] the user data hash
113
+ # @api private
114
+ attr_reader :data
115
+
116
+ # @return [Hash] the Test Kitchen-provided configuration hash
117
+ # @api private
118
+ attr_reader :kitchen_config
77
119
 
78
120
  def combine_arrays!(root, key, *namespaces)
79
- if root.has_key?(key)
121
+ if root.key?(key)
80
122
  root[key] = namespaces.
81
123
  map { |namespace| root.fetch(key).fetch(namespace, []) }.flatten.
82
124
  compact
83
125
  end
84
126
  end
85
127
 
128
+ # Destructively moves Chef-related paths out of a suite hash and into the
129
+ # suite's provisioner sub-hash.
130
+ #
131
+ # This method converts the following:
132
+ #
133
+ # {
134
+ # :suites => [
135
+ # {
136
+ # :name => "alpha",
137
+ # :nodes_path => "/a/b/c"
138
+ # },
139
+ # {
140
+ # :name => "beta",
141
+ # :roles_path => "/tmp/roles",
142
+ # :data_bags_path => "/bags"
143
+ # },
144
+ # ]
145
+ # }
146
+ #
147
+ # into the following:
148
+ #
149
+ # {
150
+ # :suites => [
151
+ # {
152
+ # :name => "alpha",
153
+ # :provisioner => {
154
+ # :nodes_path => "/a/b/c"
155
+ # }
156
+ # },
157
+ # {
158
+ # :name => "beta",
159
+ # :provisioner => {
160
+ # :roles_path => "/tmp/roles",
161
+ # :data_bags_path => "/bags"
162
+ # }
163
+ # },
164
+ # ]
165
+ # }
166
+ #
167
+ # @deprecated The following Chef paths should no longer be created directly
168
+ # under a suite hash: [`data_path`, `data_bags_path`,
169
+ # `encrypted_data_bag_secret_key_path`, `environments_path`, `nodes_path`,
170
+ # `roles_path`]. Instead put these key/value pairs directly inside a
171
+ # `provisioner` hash.
172
+ # @api private
86
173
  def convert_legacy_chef_paths_format!
87
174
  data.fetch(:suites, []).each do |suite|
88
- %w{data data_bags encrypted_data_bag_secret_key
89
- environments nodes roles}.each do |key|
90
- move_chef_data_to_provisioner_at!(suite, "#{key}_path".to_sym)
175
+ %w[
176
+ data data_bags encrypted_data_bag_secret_key
177
+ environments nodes roles
178
+ ].each do |key|
179
+ move_chef_data_to_provisioner_at!(suite, "#{key}_path".to_sym)
91
180
  end
92
181
  end
93
182
  end
94
183
 
184
+ # Destructively moves old-style `:driver_plugin` and `:driver_config`
185
+ # configuration hashes into the correct `:driver` hash.
186
+ #
187
+ # This method converts the following:
188
+ #
189
+ # {
190
+ # :driver_plugin => "foo",
191
+ # :driver_config => { :one => "two" },
192
+ #
193
+ # :platforms => [
194
+ # {
195
+ # :name => "ubuntu-12.04",
196
+ # :driver_plugin => "bar"
197
+ # }
198
+ # ],
199
+ #
200
+ # :suites => [
201
+ # {
202
+ # :name => "alpha",
203
+ # :driver_plugin => "baz"
204
+ # :driver_config => { :three => "four" }
205
+ # }
206
+ # ]
207
+ # }
208
+ #
209
+ # into the following:
210
+ #
211
+ # {
212
+ # :driver => {
213
+ # :name => "foo",
214
+ # :one => "two"
215
+ # }
216
+ #
217
+ # :platforms => [
218
+ # {
219
+ # :name => "ubuntu-12.04",
220
+ # :driver => {
221
+ # :name => "bar"
222
+ # }
223
+ # }
224
+ # ],
225
+ #
226
+ # :suites => [
227
+ # {
228
+ # :name => "alpha",
229
+ # :driver => {
230
+ # :name => "baz",
231
+ # :three => "four"
232
+ # }
233
+ # }
234
+ # ]
235
+ # }
236
+ #
237
+ # @deprecated The following configuration hashes should no longer be
238
+ # created in a `:platform`, `:suite`, or common location:
239
+ # [`:driver_plugin`, `:driver_config`]. Use a `:driver` hash block in
240
+ # their place.
241
+ # @api private
95
242
  def convert_legacy_driver_format!
96
243
  convert_legacy_driver_format_at!(data)
97
244
  data.fetch(:platforms, []).each do |platform|
@@ -102,20 +249,89 @@ module Kitchen
102
249
  end
103
250
  end
104
251
 
252
+ # Destructively moves old-style `:driver_plugin` and `:driver_config`
253
+ # configuration hashes into the correct `:driver` in the first level depth
254
+ # of a hash. This method has no knowledge of suites, platforms, or the
255
+ # like, just a vanilla hash.
256
+ #
257
+ # @param root [Hash] a hash to use as the root of the conversion
258
+ # @deprecated The following configuration hashes should no longer be
259
+ # created in a Test Kitche hash: [`:driver_plugin`, `:driver_config`].
260
+ # Use a `:driver` hash block in their place.
261
+ # @api private
105
262
  def convert_legacy_driver_format_at!(root)
106
- if root.has_key?(:driver_config)
263
+ if root.key?(:driver_config)
107
264
  ddata = root.fetch(:driver, Hash.new)
108
265
  ddata = { :name => ddata } if ddata.is_a?(String)
109
266
  root[:driver] = root.delete(:driver_config).rmerge(ddata)
110
267
  end
111
268
 
112
- if root.has_key?(:driver_plugin)
269
+ if root.key?(:driver_plugin)
113
270
  ddata = root.fetch(:driver, Hash.new)
114
271
  ddata = { :name => ddata } if ddata.is_a?(String)
115
272
  root[:driver] = { :name => root.delete(:driver_plugin) }.rmerge(ddata)
116
273
  end
117
274
  end
118
275
 
276
+ # Destructively moves a `:require_chef_omnibus` key/value pair from a
277
+ # `:driver` hash block to a `:provisioner` hash block.
278
+ #
279
+ # This method converts the following:
280
+ #
281
+ # {
282
+ # :driver => {
283
+ # :require_chef_omnibus => true
284
+ # }
285
+ #
286
+ # :platforms => [
287
+ # {
288
+ # :name => "ubuntu-12.04",
289
+ # :driver => {
290
+ # :require_chef_omnibus => "10.8.2"
291
+ # }
292
+ # }
293
+ # ],
294
+ #
295
+ # :suites => [
296
+ # {
297
+ # :name => "alpha",
298
+ # :driver => {
299
+ # :require_chef_omnibus => "11"
300
+ # }
301
+ # }
302
+ # ]
303
+ # }
304
+ #
305
+ # into the following:
306
+ #
307
+ # {
308
+ # :provisioner => {
309
+ # :require_chef_omnibus => true
310
+ # }
311
+ #
312
+ # :platforms => [
313
+ # {
314
+ # :name => "ubuntu-12.04",
315
+ # :provisioner => {
316
+ # :require_chef_omnibus => "10.8.2"
317
+ # }
318
+ # }
319
+ # ],
320
+ #
321
+ # :suites => [
322
+ # {
323
+ # :name => "alpha",
324
+ # :provisioner => {
325
+ # :require_chef_omnibus => "11"
326
+ # }
327
+ # }
328
+ # ]
329
+ # }
330
+ #
331
+ # @deprecated The `:require_chef_omnibus` key/value pair should no longer
332
+ # be created inside a `:driver` hash block. Put it in a `:provisioner`
333
+ # hash block instead.
334
+ # @api private
119
335
  def convert_legacy_require_chef_omnibus_format!
120
336
  convert_legacy_require_chef_omnibus_format_at!(data)
121
337
  data.fetch(:platforms, []).each do |platform|
@@ -126,11 +342,21 @@ module Kitchen
126
342
  end
127
343
  end
128
344
 
345
+ # Destructively moves a `:require_chef_omnibus` key/value pair from a
346
+ # `:driver` hash block to a `:provisioner` hash block in the first leve
347
+ # depth of a hash. This method has no knowledge of suites, platforms, or
348
+ # the like, just a vanilla haash.
349
+ #
350
+ # @param root [Hash] a hash to use as the root of the conversion
351
+ # @deprecated The `:require_chef_omnibus` key/value pair should no longer
352
+ # be created inside a `:driver` hash block. Put it in a `:provisioner`
353
+ # hash block instead.
354
+ # @api private
129
355
  def convert_legacy_require_chef_omnibus_format_at!(root)
130
356
  key = :require_chef_omnibus
131
357
  ddata = root.fetch(:driver, Hash.new)
132
358
 
133
- if ddata.is_a?(Hash) && ddata.has_key?(key)
359
+ if ddata.is_a?(Hash) && ddata.key?(key)
134
360
  pdata = root.fetch(:provisioner, Hash.new)
135
361
  pdata = { :name => pdata } if pdata.is_a?(String)
136
362
  root[:provisioner] =
@@ -138,6 +364,27 @@ module Kitchen
138
364
  end
139
365
  end
140
366
 
367
+ # Performs a prioritized recursive merge of several source Hashes and
368
+ # returns a new merged Hash. For these data sub-hash structures, there are
369
+ # 4 sources for configuration data:
370
+ #
371
+ # 1. defaults, provided by Test Kitchen code
372
+ # 2. user-provided in the common root-level of the incoming data hash
373
+ # 3. user-provided in a platform sub-hash
374
+ # 4. user-provided in a suite sub-hash
375
+ #
376
+ # The merge order is 4 -> 3 -> 2 -> 1, meaning that the highest number in
377
+ # the above list has merge precedence over any lower numbered source. Put
378
+ # another way, a key/value pair in a suite sub-hash will be used over the
379
+ # key/value pair in a platform sub-hash.
380
+ #
381
+ # @param key [Symbol] the data sub-hash(es) to merge
382
+ # @param suite [String] a suite name
383
+ # @param platform [String] a platform name
384
+ # @param default_key [Symbol] the default key to use when normalizing the
385
+ # data sub-hashes (default: `:name`)
386
+ # @return [Hash] a new merged Hash
387
+ # @api private
141
388
  def merged_data_for(key, suite, platform, default_key = :name)
142
389
  ddata = normalized_default_data(key, default_key)
143
390
  cdata = normalized_common_data(key, default_key)
@@ -147,6 +394,59 @@ module Kitchen
147
394
  ddata.rmerge(cdata.rmerge(pdata.rmerge(sdata)))
148
395
  end
149
396
 
397
+ # Destructively moves key Chef configuration key/value pairs from being
398
+ # directly under a suite or platform into a `:provisioner` sub-hash.
399
+ #
400
+ # There are two key Chef configuration key/value pairs:
401
+ #
402
+ # 1. `:attributes`
403
+ # 2. `:run_list`
404
+ #
405
+ # This method converts the following:
406
+ #
407
+ # {
408
+ # :platforms => [
409
+ # {
410
+ # :name => "ubuntu-12.04",
411
+ # :attributes => { :one => "two" },
412
+ # :run_list => ["alpha", "bravo"]
413
+ # }
414
+ # ],
415
+ #
416
+ # :suites => [
417
+ # {
418
+ # :name => "alpha",
419
+ # :attributes => { :three => "four" },
420
+ # :run_list => ["charlie", "delta"]
421
+ # }
422
+ # ]
423
+ # }
424
+ #
425
+ # into the following:
426
+ #
427
+ # {
428
+ # :platforms => [
429
+ # {
430
+ # :name => "ubuntu-12.04",
431
+ # :provisioner => {
432
+ # :attributes => { :one => "two" },
433
+ # :run_list => ["alpha", "bravo"]
434
+ # }
435
+ # }
436
+ # ],
437
+ #
438
+ # :suites => [
439
+ # {
440
+ # :name => "alpha",
441
+ # :provisioner => {
442
+ # :attributes => { :three => "four" },
443
+ # :run_list => ["charlie", "delta"]
444
+ # }
445
+ # }
446
+ # ]
447
+ # }
448
+ #
449
+ # @api private
150
450
  def move_chef_data_to_provisioner!
151
451
  data.fetch(:suites, []).each do |suite|
152
452
  move_chef_data_to_provisioner_at!(suite, :attributes)
@@ -159,20 +459,71 @@ module Kitchen
159
459
  end
160
460
  end
161
461
 
462
+ # Destructively moves key Chef configuration key/value pairs from being
463
+ # directly under a hash into a `:provisioner` sub-hash block. This method
464
+ # has no knowledge of suites, platforms, or the like, just a vanilla hash.
465
+ #
466
+ # @param root [Hash] a hash to use as the root of the conversion
467
+ # @param key [Symbol] a key in the root hash to move into a `:provisioner`
468
+ # sub-hash block
469
+ # @api private
162
470
  def move_chef_data_to_provisioner_at!(root, key)
163
- if root.has_key?(key)
471
+ if root.key?(key)
164
472
  pdata = root.fetch(:provisioner, Hash.new)
165
473
  pdata = { :name => pdata } if pdata.is_a?(String)
166
- if ! root.fetch(key, nil).nil?
167
- root[:provisioner] = pdata.rmerge({ key => root.delete(key) })
474
+ if !root.fetch(key, nil).nil?
475
+ root[:provisioner] = pdata.rmerge(key => root.delete(key))
168
476
  end
169
477
  end
170
478
  end
171
479
 
480
+ # Vicious hack to allow for Array-appending merge semantics. This method
481
+ # takes an array value and transforms it into a hash with a bucket name
482
+ # containing the original Array. This way semantic Hash merging will do
483
+ # its thing and another process can collapse the hash into a flat array
484
+ # afterwards, given a strategy (like use the array segmenet from one
485
+ # bucket first, then another one second). To anyone who made it this far,
486
+ # Fletcher appologizes.
487
+ #
488
+ # @param root [Hash] a hash to use as the root of the conversion
489
+ # @param key [Symbol] a key in the root hash that, if exists, has its
490
+ # value transformed into a sub-hash
491
+ # @param bucket [Symbol] a key to use for the sub-hash
492
+ # @api private
172
493
  def namespace_array!(root, key, bucket)
173
- root[key] = { bucket => root.fetch(key) } if root.has_key?(key)
494
+ root[key] = { bucket => root.fetch(key) } if root.key?(key)
174
495
  end
175
496
 
497
+ # Normalizes a specific key in the root of the data hash to be a proper
498
+ # sub-hash in all cases. Specifically handled are the following cases:
499
+ #
500
+ # * If the value for a key is set to `nil`, a new Hash will be put in
501
+ # its place.
502
+ # * If the value is a String, then convert the value to a new Hash with
503
+ # a default key pointing to the original String
504
+ #
505
+ # Given a hash:
506
+ #
507
+ # { :driver => nil }
508
+ #
509
+ # this method (`normalized_common_data(:driver, :name)`) would return:
510
+ #
511
+ # {}
512
+ #
513
+ # Given a hash:
514
+ #
515
+ # { :driver => "coolbeans" }
516
+ #
517
+ # this method (`normalized_common_data(:driver, :name)`) would return:
518
+ #
519
+ # { :name => "coolbeans" }
520
+ #
521
+ # @param key [Symbol] the value to normalize
522
+ # @param default_key [Symbol] the implicit default key if a String value
523
+ # is given
524
+ # @return [Hash] a shallow Hash copy of the original if not modified, or a
525
+ # new Hash otherwise
526
+ # @api private
176
527
  def normalized_common_data(key, default_key)
177
528
  cdata = data.fetch(key, Hash.new)
178
529
  cdata = cdata.nil? ? Hash.new : cdata.dup
@@ -180,12 +531,94 @@ module Kitchen
180
531
  cdata
181
532
  end
182
533
 
534
+ # Normalizes a specific key in the `:defaults` data sub-hash to be a proper
535
+ # sub-hash in all cases. Specifically handled are the following cases:
536
+ #
537
+ # * If the value for a key is not set, a new Hash will be put in its place
538
+ # * If the value is a String, then convert the value to a new Hash with
539
+ # a default key pointing to the original String
540
+ #
541
+ # Given a hash:
542
+ #
543
+ # {
544
+ # :defaults => {}
545
+ # }
546
+ #
547
+ # this method (`normalized_default_data(:driver, :name)`) would return:
548
+ #
549
+ # {}
550
+ #
551
+ # Given a hash:
552
+ #
553
+ # {
554
+ # :defaults => {
555
+ # :driver => "coolbeans"
556
+ # }
557
+ # }
558
+ #
559
+ # this method (`normalized_default_data(:driver, :name)`) would return:
560
+ #
561
+ # { :name => "coolbeans" }
562
+ #
563
+ # @param key [Symbol] the value to normalize
564
+ # @param default_key [Symbol] the implicit default key if a String value
565
+ # is given
566
+ # @return [Hash] a shallow Hash copy of the original if not modified, or a
567
+ # new Hash otherwise
568
+ # @api private
183
569
  def normalized_default_data(key, default_key)
184
570
  ddata = kitchen_config.fetch(:defaults, Hash.new).fetch(key, Hash.new).dup
185
571
  ddata = { default_key => ddata } if ddata.is_a?(String)
186
572
  ddata
187
573
  end
188
574
 
575
+ # Normalizes a specific key in a platform hash data sub-hash to be a proper
576
+ # sub-hash in all cases. Specifically handled are the following cases:
577
+ #
578
+ # * If the value for a key is set to `nil`, a new Hash will be put in
579
+ # its place.
580
+ # * If the value is a String, then convert the value to a new Hash with
581
+ # a default key pointing to the original String
582
+ #
583
+ # Given a hash:
584
+ #
585
+ # {
586
+ # :platforms => [
587
+ # {
588
+ # :name => "alpha",
589
+ # :driver => nil
590
+ # }
591
+ # ]
592
+ # }
593
+ #
594
+ # this method (`normalized_platform_data(:driver, :name, "alpha)`) would
595
+ # return:
596
+ #
597
+ # {}
598
+ #
599
+ # Given a hash:
600
+ #
601
+ # {
602
+ # :platforms => [
603
+ # {
604
+ # :name => "alpha",
605
+ # :driver => "coolbeans"
606
+ # }
607
+ # ]
608
+ # }
609
+ #
610
+ # this method (`normalized_common_data(:driver, :name, "alpha")`) would
611
+ # return:
612
+ #
613
+ # { :name => "coolbeans" }
614
+ #
615
+ # @param key [Symbol] the value to normalize
616
+ # @param default_key [Symbol] the implicit default key if a String value
617
+ # is given
618
+ # @param platform [String] name of a platform
619
+ # @return [Hash] a shallow Hash copy of the original if not modified, or a
620
+ # new Hash otherwise
621
+ # @api private
189
622
  def normalized_platform_data(key, default_key, platform)
190
623
  pdata = platform_data_for(platform).fetch(key, Hash.new)
191
624
  pdata = pdata.nil? ? Hash.new : pdata.dup
@@ -194,6 +627,53 @@ module Kitchen
194
627
  pdata
195
628
  end
196
629
 
630
+ # Normalizes a specific key in a suite hash data sub-hash to be a proper
631
+ # sub-hash in all cases. Specifically handled are the following cases:
632
+ #
633
+ # * If the value for a key is set to `nil`, a new Hash will be put in
634
+ # its place.
635
+ # * If the value is a String, then convert the value to a new Hash with
636
+ # a default key pointing to the original String
637
+ #
638
+ # Given a hash:
639
+ #
640
+ # {
641
+ # :suites => [
642
+ # {
643
+ # :name => "full",
644
+ # :driver => nil
645
+ # }
646
+ # ]
647
+ # }
648
+ #
649
+ # this method (`normalized_platform_data(:driver, :name, "full)`) would
650
+ # return:
651
+ #
652
+ # {}
653
+ #
654
+ # Given a hash:
655
+ #
656
+ # {
657
+ # :suites => [
658
+ # {
659
+ # :name => "full",
660
+ # :driver => "coolbeans"
661
+ # }
662
+ # ]
663
+ # }
664
+ #
665
+ # this method (`normalized_common_data(:driver, :name, "full")`) would
666
+ # return:
667
+ #
668
+ # { :name => "coolbeans" }
669
+ #
670
+ # @param key [Symbol] the value to normalize
671
+ # @param default_key [Symbol] the implicit default key if a String value
672
+ # is given
673
+ # @param suite [String] name of a suite
674
+ # @return [Hash] a shallow Hash copy of the original if not modified, or a
675
+ # new Hash otherwise
676
+ # @api private
197
677
  def normalized_suite_data(key, default_key, suite)
198
678
  sdata = suite_data_for(suite).fetch(key, Hash.new)
199
679
  sdata = sdata.nil? ? Hash.new : sdata.dup
@@ -202,22 +682,46 @@ module Kitchen
202
682
  sdata
203
683
  end
204
684
 
685
+ # Returns the hash for a platform by name, or an empty Hash if none
686
+ # could be found.
687
+ #
688
+ # @param name [String] name of a platform
689
+ # @return [Hash] the configuration hash for the platform, or an empty
690
+ # Hash if not found
691
+ # @api private
205
692
  def platform_data_for(name)
206
- data.fetch(:platforms, Hash.new).find(lambda { Hash.new }) do |platform|
693
+ data.fetch(:platforms, Hash.new).find(-> { Hash.new }) do |platform|
207
694
  platform.fetch(:name, nil) == name
208
695
  end
209
696
  end
210
697
 
698
+ # Destructively sets a base kitchen config key/value pair at the root of
699
+ # the given hash. If the key is present in the given Hash, it is deleted
700
+ # and will not be used. If the key is found in the `kitchen_config` hash
701
+ # (default values), then its value will be used and set. Finally, if
702
+ # the key is found in `:kitchen` data sub-hash, then its value will be used
703
+ # and set.
704
+ #
705
+ # @param root [Hash] a hash to use as the root of the conversion
706
+ # @param key [Symbol] the key to search for
707
+ # @api private
211
708
  def set_kitchen_config_at!(root, key)
212
709
  kdata = data.fetch(:kitchen, Hash.new)
213
710
 
214
- root.delete(key) if root.has_key?(key)
215
- root[key] = kitchen_config.fetch(key) if kitchen_config.has_key?(key)
216
- root[key] = kdata.fetch(key) if kdata.has_key?(key)
711
+ root.delete(key) if root.key?(key)
712
+ root[key] = kitchen_config.fetch(key) if kitchen_config.key?(key)
713
+ root[key] = kdata.fetch(key) if kdata.key?(key)
217
714
  end
218
715
 
716
+ # Returns the hash for a suite by name, or an empty Hash if none
717
+ # could be found.
718
+ #
719
+ # @param name [String] name of a suite
720
+ # @return [Hash] the configuration hash for the suite, or an empty
721
+ # Hash if not found
722
+ # @api private
219
723
  def suite_data_for(name)
220
- data.fetch(:suites, Hash.new).find(lambda { Hash.new }) do |suite|
724
+ data.fetch(:suites, Hash.new).find(-> { Hash.new }) do |suite|
221
725
  suite.fetch(:name, nil) == name
222
726
  end
223
727
  end