test-kitchen 1.6.0 → 1.7.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 (180) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +8 -7
  3. data/.github/ISSUE_TEMPLATE.md +56 -0
  4. data/.gitignore +28 -27
  5. data/.kitchen.ci.yml +23 -0
  6. data/.kitchen.proxy.yml +27 -0
  7. data/.rubocop.yml +3 -3
  8. data/.travis.yml +70 -53
  9. data/.yardopts +3 -3
  10. data/Berksfile +3 -0
  11. data/CHANGELOG.md +1083 -1051
  12. data/CONTRIBUTING.md +14 -14
  13. data/Gemfile +19 -14
  14. data/Gemfile.proxy_tests +4 -5
  15. data/Guardfile +42 -42
  16. data/LICENSE +15 -15
  17. data/MAINTAINERS.md +23 -24
  18. data/README.md +135 -135
  19. data/Rakefile +61 -76
  20. data/appveyor.yml +44 -34
  21. data/features/kitchen_action_commands.feature +164 -164
  22. data/features/kitchen_command.feature +16 -16
  23. data/features/kitchen_console_command.feature +34 -34
  24. data/features/kitchen_defaults.feature +38 -38
  25. data/features/kitchen_diagnose_command.feature +96 -96
  26. data/features/kitchen_driver_create_command.feature +64 -64
  27. data/features/kitchen_driver_discover_command.feature +25 -25
  28. data/features/kitchen_help_command.feature +16 -16
  29. data/features/kitchen_init_command.feature +274 -274
  30. data/features/kitchen_list_command.feature +104 -104
  31. data/features/kitchen_login_command.feature +62 -62
  32. data/features/kitchen_sink_command.feature +30 -30
  33. data/features/kitchen_test_command.feature +88 -88
  34. data/features/step_definitions/gem_steps.rb +36 -36
  35. data/features/step_definitions/git_steps.rb +5 -5
  36. data/features/step_definitions/output_steps.rb +5 -5
  37. data/features/support/env.rb +75 -75
  38. data/lib/kitchen.rb +150 -150
  39. data/lib/kitchen/base64_stream.rb +55 -55
  40. data/lib/kitchen/cli.rb +419 -419
  41. data/lib/kitchen/collection.rb +55 -55
  42. data/lib/kitchen/color.rb +65 -65
  43. data/lib/kitchen/command.rb +185 -185
  44. data/lib/kitchen/command/action.rb +45 -45
  45. data/lib/kitchen/command/console.rb +58 -58
  46. data/lib/kitchen/command/diagnose.rb +92 -92
  47. data/lib/kitchen/command/driver_discover.rb +105 -105
  48. data/lib/kitchen/command/exec.rb +41 -41
  49. data/lib/kitchen/command/list.rb +119 -119
  50. data/lib/kitchen/command/login.rb +43 -43
  51. data/lib/kitchen/command/sink.rb +54 -54
  52. data/lib/kitchen/command/test.rb +51 -51
  53. data/lib/kitchen/config.rb +322 -322
  54. data/lib/kitchen/configurable.rb +529 -529
  55. data/lib/kitchen/data_munger.rb +959 -960
  56. data/lib/kitchen/diagnostic.rb +141 -141
  57. data/lib/kitchen/driver.rb +56 -56
  58. data/lib/kitchen/driver/base.rb +134 -134
  59. data/lib/kitchen/driver/dummy.rb +108 -108
  60. data/lib/kitchen/driver/proxy.rb +72 -72
  61. data/lib/kitchen/driver/ssh_base.rb +357 -357
  62. data/lib/kitchen/errors.rb +229 -229
  63. data/lib/kitchen/generator/driver_create.rb +177 -177
  64. data/lib/kitchen/generator/init.rb +296 -296
  65. data/lib/kitchen/instance.rb +662 -662
  66. data/lib/kitchen/lazy_hash.rb +142 -142
  67. data/lib/kitchen/loader/yaml.rb +349 -349
  68. data/lib/kitchen/logger.rb +423 -423
  69. data/lib/kitchen/logging.rb +56 -56
  70. data/lib/kitchen/login_command.rb +52 -52
  71. data/lib/kitchen/metadata_chopper.rb +52 -52
  72. data/lib/kitchen/platform.rb +67 -67
  73. data/lib/kitchen/provisioner.rb +54 -54
  74. data/lib/kitchen/provisioner/base.rb +236 -236
  75. data/lib/kitchen/provisioner/chef/berkshelf.rb +114 -114
  76. data/lib/kitchen/provisioner/chef/common_sandbox.rb +322 -322
  77. data/lib/kitchen/provisioner/chef/librarian.rb +112 -112
  78. data/lib/kitchen/provisioner/chef_apply.rb +124 -125
  79. data/lib/kitchen/provisioner/chef_base.rb +341 -294
  80. data/lib/kitchen/provisioner/chef_solo.rb +88 -89
  81. data/lib/kitchen/provisioner/chef_zero.rb +245 -245
  82. data/lib/kitchen/provisioner/dummy.rb +79 -79
  83. data/lib/kitchen/provisioner/shell.rb +138 -138
  84. data/lib/kitchen/rake_tasks.rb +63 -63
  85. data/lib/kitchen/shell_out.rb +93 -93
  86. data/lib/kitchen/ssh.rb +276 -276
  87. data/lib/kitchen/state_file.rb +120 -120
  88. data/lib/kitchen/suite.rb +51 -51
  89. data/lib/kitchen/thor_tasks.rb +66 -66
  90. data/lib/kitchen/transport.rb +54 -54
  91. data/lib/kitchen/transport/base.rb +176 -176
  92. data/lib/kitchen/transport/dummy.rb +79 -79
  93. data/lib/kitchen/transport/ssh.rb +364 -364
  94. data/lib/kitchen/transport/winrm.rb +486 -486
  95. data/lib/kitchen/util.rb +147 -147
  96. data/lib/kitchen/verifier.rb +55 -55
  97. data/lib/kitchen/verifier/base.rb +235 -235
  98. data/lib/kitchen/verifier/busser.rb +277 -277
  99. data/lib/kitchen/verifier/dummy.rb +79 -79
  100. data/lib/kitchen/verifier/shell.rb +101 -101
  101. data/lib/kitchen/version.rb +21 -21
  102. data/lib/vendor/hash_recursive_merge.rb +82 -82
  103. data/spec/kitchen/base64_stream_spec.rb +77 -77
  104. data/spec/kitchen/cli_spec.rb +56 -56
  105. data/spec/kitchen/collection_spec.rb +80 -80
  106. data/spec/kitchen/color_spec.rb +54 -54
  107. data/spec/kitchen/config_spec.rb +408 -408
  108. data/spec/kitchen/configurable_spec.rb +1095 -1062
  109. data/spec/kitchen/data_munger_spec.rb +2694 -2383
  110. data/spec/kitchen/diagnostic_spec.rb +129 -129
  111. data/spec/kitchen/driver/base_spec.rb +121 -121
  112. data/spec/kitchen/driver/dummy_spec.rb +199 -199
  113. data/spec/kitchen/driver/proxy_spec.rb +138 -138
  114. data/spec/kitchen/driver/ssh_base_spec.rb +1115 -1115
  115. data/spec/kitchen/driver_spec.rb +112 -112
  116. data/spec/kitchen/errors_spec.rb +309 -309
  117. data/spec/kitchen/instance_spec.rb +1419 -1419
  118. data/spec/kitchen/lazy_hash_spec.rb +117 -117
  119. data/spec/kitchen/loader/yaml_spec.rb +774 -774
  120. data/spec/kitchen/logger_spec.rb +429 -429
  121. data/spec/kitchen/logging_spec.rb +59 -59
  122. data/spec/kitchen/login_command_spec.rb +68 -68
  123. data/spec/kitchen/metadata_chopper_spec.rb +82 -82
  124. data/spec/kitchen/platform_spec.rb +89 -89
  125. data/spec/kitchen/provisioner/base_spec.rb +386 -386
  126. data/spec/kitchen/provisioner/chef_apply_spec.rb +136 -136
  127. data/spec/kitchen/provisioner/chef_base_spec.rb +1161 -1067
  128. data/spec/kitchen/provisioner/chef_solo_spec.rb +557 -557
  129. data/spec/kitchen/provisioner/chef_zero_spec.rb +1001 -1001
  130. data/spec/kitchen/provisioner/dummy_spec.rb +99 -99
  131. data/spec/kitchen/provisioner/shell_spec.rb +566 -566
  132. data/spec/kitchen/provisioner_spec.rb +107 -107
  133. data/spec/kitchen/shell_out_spec.rb +150 -150
  134. data/spec/kitchen/ssh_spec.rb +693 -693
  135. data/spec/kitchen/state_file_spec.rb +129 -129
  136. data/spec/kitchen/suite_spec.rb +62 -62
  137. data/spec/kitchen/transport/base_spec.rb +89 -89
  138. data/spec/kitchen/transport/ssh_spec.rb +1255 -1255
  139. data/spec/kitchen/transport/winrm_spec.rb +1143 -1143
  140. data/spec/kitchen/transport_spec.rb +112 -112
  141. data/spec/kitchen/util_spec.rb +165 -165
  142. data/spec/kitchen/verifier/base_spec.rb +362 -362
  143. data/spec/kitchen/verifier/busser_spec.rb +610 -610
  144. data/spec/kitchen/verifier/dummy_spec.rb +99 -99
  145. data/spec/kitchen/verifier/shell_spec.rb +160 -158
  146. data/spec/kitchen/verifier_spec.rb +120 -120
  147. data/spec/kitchen_spec.rb +114 -114
  148. data/spec/spec_helper.rb +85 -85
  149. data/spec/support/powershell_max_size_spec.rb +40 -40
  150. data/support/busser_install_command.ps1 +14 -14
  151. data/support/busser_install_command.sh +14 -14
  152. data/support/chef-client-zero.rb +77 -77
  153. data/support/chef_base_init_command.ps1 +18 -18
  154. data/support/chef_base_init_command.sh +2 -2
  155. data/support/chef_base_install_command.ps1 +85 -85
  156. data/support/chef_base_install_command.sh +229 -229
  157. data/support/chef_zero_prepare_command_legacy.ps1 +9 -9
  158. data/support/chef_zero_prepare_command_legacy.sh +10 -10
  159. data/support/download_helpers.sh +109 -109
  160. data/support/dummy-validation.pem +27 -27
  161. data/templates/driver/CHANGELOG.md.erb +3 -3
  162. data/templates/driver/Gemfile.erb +3 -3
  163. data/templates/driver/README.md.erb +64 -64
  164. data/templates/driver/Rakefile.erb +21 -21
  165. data/templates/driver/driver.rb.erb +23 -23
  166. data/templates/driver/gemspec.erb +29 -29
  167. data/templates/driver/gitignore.erb +17 -17
  168. data/templates/driver/license_apachev2.erb +15 -15
  169. data/templates/driver/license_lgplv3.erb +16 -16
  170. data/templates/driver/license_mit.erb +22 -22
  171. data/templates/driver/license_reserved.erb +5 -5
  172. data/templates/driver/tailor.erb +4 -4
  173. data/templates/driver/travis.yml.erb +11 -11
  174. data/templates/driver/version.rb.erb +12 -12
  175. data/templates/init/chefignore.erb +1 -1
  176. data/templates/init/kitchen.yml.erb +18 -18
  177. data/test-kitchen.gemspec +62 -62
  178. data/test/integration/default/default_spec.rb +3 -0
  179. data/testing_windows.md +37 -37
  180. metadata +23 -11
@@ -1,960 +1,959 @@
1
- # -*- encoding: utf-8 -*-
2
- #
3
- # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
- #
5
- # Copyright (C) 2013, Fletcher Nichol
6
- #
7
- # Licensed under the Apache License, Version 2.0 (the "License");
8
- # you may not use this file except in compliance with the License.
9
- # You may obtain a copy of the License at
10
- #
11
- # http://www.apache.org/licenses/LICENSE-2.0
12
- #
13
- # Unless required by applicable law or agreed to in writing, software
14
- # distributed under the License is distributed on an "AS IS" BASIS,
15
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- # See the License for the specific language governing permissions and
17
- # limitations under the License.
18
-
19
- require "vendor/hash_recursive_merge"
20
-
21
- module Kitchen
22
-
23
- # Class to handle recursive merging of configuration between platforms,
24
- # suites, and common data.
25
- #
26
- # This object will mutate the data Hash passed into its constructor and so
27
- # should not be reused or shared across threads.
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
- #
33
- # @author Fletcher Nichol <fnichol@nichol.ca>
34
- class DataMunger
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
41
- def initialize(data, kitchen_config = {})
42
- @data = data
43
- @kitchen_config = kitchen_config
44
- convert_legacy_driver_format!
45
- convert_legacy_chef_paths_format!
46
- convert_legacy_require_chef_omnibus_format!
47
- convert_legacy_busser_format!
48
- convert_legacy_driver_http_proxy_format!
49
- move_chef_data_to_provisioner!
50
- end
51
-
52
- # Generate a new Hash of configuration data that can be used to construct
53
- # a new Driver object.
54
- #
55
- # @param suite [String] a suite name
56
- # @param platform [String] a platform name
57
- # @return [Hash] a new configuration Hash that can be used to construct a
58
- # new Driver
59
- def driver_data_for(suite, platform)
60
- merged_data_for(:driver, suite, platform).tap do |ddata|
61
- set_kitchen_config_at!(ddata, :kitchen_root)
62
- set_kitchen_config_at!(ddata, :test_base_path)
63
- set_kitchen_config_at!(ddata, :log_level)
64
- end
65
- end
66
-
67
- # Returns an Array of platform Hashes.
68
- #
69
- # @return [Array<Hash>] an Array of Hashes
70
- def platform_data
71
- data.fetch(:platforms, [])
72
- end
73
-
74
- # Generate a new Hash of configuration data that can be used to construct
75
- # a new Provisioner object.
76
- #
77
- # @param suite [String] a suite name
78
- # @param platform [String] a platform name
79
- # @return [Hash] a new configuration Hash that can be used to construct a
80
- # new Provisioner
81
- def provisioner_data_for(suite, platform)
82
- merged_data_for(:provisioner, suite, platform).tap do |pdata|
83
- set_kitchen_config_at!(pdata, :kitchen_root)
84
- set_kitchen_config_at!(pdata, :test_base_path)
85
- set_kitchen_config_at!(pdata, :log_level)
86
- combine_arrays!(pdata, :run_list, :platform, :suite)
87
- end
88
- end
89
-
90
- # Returns an Array of suite Hashes.
91
- #
92
- # @return [Array<Hash>] an Array of Hashes
93
- def suite_data
94
- data.fetch(:suites, [])
95
- end
96
-
97
- # Generate a new Hash of configuration data that can be used to construct
98
- # a new Transport object.
99
- #
100
- # @param suite [String] a suite name
101
- # @param platform [String] a platform name
102
- # @return [Hash] a new configuration Hash that can be used to construct a
103
- # new Transport
104
- def transport_data_for(suite, platform)
105
- merged_data_for(:transport, suite, platform).tap do |tdata|
106
- set_kitchen_config_at!(tdata, :kitchen_root)
107
- set_kitchen_config_at!(tdata, :test_base_path)
108
- set_kitchen_config_at!(tdata, :log_level)
109
- end
110
- end
111
-
112
- # Generate a new Hash of configuration data that can be used to construct
113
- # a new Verifier object.
114
- #
115
- # @param suite [String] a suite name
116
- # @param platform [String] a platform name
117
- # @return [Hash] a new configuration Hash that can be used to construct a
118
- # new Verifier
119
- def verifier_data_for(suite, platform)
120
- merged_data_for(:verifier, suite, platform).tap do |vdata|
121
- set_kitchen_config_at!(vdata, :kitchen_root)
122
- set_kitchen_config_at!(vdata, :test_base_path)
123
- set_kitchen_config_at!(vdata, :log_level)
124
- end
125
- end
126
-
127
- private
128
-
129
- # @return [Hash] the user data hash
130
- # @api private
131
- attr_reader :data
132
-
133
- # @return [Hash] the Test Kitchen-provided configuration hash
134
- # @api private
135
- attr_reader :kitchen_config
136
-
137
- def combine_arrays!(root, key, *namespaces)
138
- if root.key?(key)
139
- root[key] = namespaces.
140
- map { |namespace| root.fetch(key).fetch(namespace, []) }.flatten.
141
- compact
142
- end
143
- end
144
-
145
- # Destructively moves old-style `:busser` configuration hashes into the
146
- # correct `:verifier` hash.
147
- #
148
- # This method converts the following:
149
- #
150
- # {
151
- # :busser => { :one => "two" },
152
- #
153
- # :platforms => [
154
- # {
155
- # :name => "ubuntu-12.04",
156
- # :busser => "bar"
157
- # }
158
- # ],
159
- #
160
- # :suites => [
161
- # {
162
- # :name => "alpha",
163
- # :busser => { :three => "four" }
164
- # }
165
- # ]
166
- # }
167
- #
168
- # into the following:
169
- #
170
- # {
171
- # :verifier => {
172
- # :name => "busser",
173
- # :one => "two"
174
- # }
175
- #
176
- # :platforms => [
177
- # {
178
- # :name => "ubuntu-12.04",
179
- # :verifier => {
180
- # :name => "busser",
181
- # :version => "bar
182
- # }
183
- # }
184
- # ],
185
- #
186
- # :suites => [
187
- # {
188
- # :name => "alpha",
189
- # :verifier => {
190
- # :name => "busser",
191
- # :three => "four"
192
- # }
193
- # }
194
- # ]
195
- # }
196
- #
197
- # @deprecated The following configuration hashes should no longer be
198
- # created in a `:platform`, `:suite`, or common location:
199
- # `:busser`. Use a `:verifier` hash block in their place.
200
- # @api private
201
- def convert_legacy_busser_format!
202
- convert_legacy_busser_format_at!(data)
203
- data.fetch(:platforms, []).each do |platform|
204
- convert_legacy_busser_format_at!(platform)
205
- end
206
- data.fetch(:suites, []).each do |suite|
207
- convert_legacy_busser_format_at!(suite)
208
- end
209
- end
210
-
211
- # Destructively moves old-style `:busser` configuration hashes into the
212
- # correct `:verifier` hashes. This method has no knowledge of suites,
213
- # platforms, or the like, just a vanilla hash.
214
- #
215
- # @param root [Hash] a hash to use as the root of the conversion
216
- # @deprecated The following configuration hashses should no longer be
217
- # created in a Test Kitchen hash: `:busser`. Use a `:verifier` hash
218
- # block in their place.
219
- # @api private
220
- def convert_legacy_busser_format_at!(root)
221
- if root.key?(:busser)
222
- bdata = root.delete(:busser)
223
- bdata = { :version => bdata } if bdata.is_a?(String)
224
- bdata[:name] = "busser" if bdata[:name].nil?
225
-
226
- vdata = root.fetch(:verifier, Hash.new)
227
- vdata = { :name => vdata } if vdata.is_a?(String)
228
- root[:verifier] = bdata.rmerge(vdata)
229
- end
230
- end
231
-
232
- # Destructively moves Chef-related paths out of a suite hash and into the
233
- # suite's provisioner sub-hash.
234
- #
235
- # This method converts the following:
236
- #
237
- # {
238
- # :suites => [
239
- # {
240
- # :name => "alpha",
241
- # :nodes_path => "/a/b/c"
242
- # },
243
- # {
244
- # :name => "beta",
245
- # :roles_path => "/tmp/roles",
246
- # :data_bags_path => "/bags"
247
- # },
248
- # ]
249
- # }
250
- #
251
- # into the following:
252
- #
253
- # {
254
- # :suites => [
255
- # {
256
- # :name => "alpha",
257
- # :provisioner => {
258
- # :nodes_path => "/a/b/c"
259
- # }
260
- # },
261
- # {
262
- # :name => "beta",
263
- # :provisioner => {
264
- # :roles_path => "/tmp/roles",
265
- # :data_bags_path => "/bags"
266
- # }
267
- # },
268
- # ]
269
- # }
270
- #
271
- # @deprecated The following Chef paths should no longer be created directly
272
- # under a suite hash: [`data_path`, `data_bags_path`,
273
- # `encrypted_data_bag_secret_key_path`, `environments_path`, `nodes_path`,
274
- # `roles_path`]. Instead put these key/value pairs directly inside a
275
- # `provisioner` hash.
276
- # @api private
277
- def convert_legacy_chef_paths_format!
278
- data.fetch(:suites, []).each do |suite|
279
- %w[
280
- data data_bags encrypted_data_bag_secret_key
281
- environments nodes roles
282
- ].each do |key|
283
- move_chef_data_to_provisioner_at!(suite, "#{key}_path".to_sym)
284
- end
285
- end
286
- end
287
-
288
- # Destructively moves old-style `:driver_plugin` and `:driver_config`
289
- # configuration hashes into the correct `:driver` hash.
290
- #
291
- # This method converts the following:
292
- #
293
- # {
294
- # :driver_plugin => "foo",
295
- # :driver_config => { :one => "two" },
296
- #
297
- # :platforms => [
298
- # {
299
- # :name => "ubuntu-12.04",
300
- # :driver_plugin => "bar"
301
- # }
302
- # ],
303
- #
304
- # :suites => [
305
- # {
306
- # :name => "alpha",
307
- # :driver_plugin => "baz"
308
- # :driver_config => { :three => "four" }
309
- # }
310
- # ]
311
- # }
312
- #
313
- # into the following:
314
- #
315
- # {
316
- # :driver => {
317
- # :name => "foo",
318
- # :one => "two"
319
- # }
320
- #
321
- # :platforms => [
322
- # {
323
- # :name => "ubuntu-12.04",
324
- # :driver => {
325
- # :name => "bar"
326
- # }
327
- # }
328
- # ],
329
- #
330
- # :suites => [
331
- # {
332
- # :name => "alpha",
333
- # :driver => {
334
- # :name => "baz",
335
- # :three => "four"
336
- # }
337
- # }
338
- # ]
339
- # }
340
- #
341
- # @deprecated The following configuration hashes should no longer be
342
- # created in a `:platform`, `:suite`, or common location:
343
- # [`:driver_plugin`, `:driver_config`]. Use a `:driver` hash block in
344
- # their place.
345
- # @api private
346
- def convert_legacy_driver_format!
347
- convert_legacy_driver_format_at!(data)
348
- data.fetch(:platforms, []).each do |platform|
349
- convert_legacy_driver_format_at!(platform)
350
- end
351
- data.fetch(:suites, []).each do |suite|
352
- convert_legacy_driver_format_at!(suite)
353
- end
354
- end
355
-
356
- # Destructively moves old-style `:driver_plugin` and `:driver_config`
357
- # configuration hashes into the correct `:driver` in the first level depth
358
- # of a hash. This method has no knowledge of suites, platforms, or the
359
- # like, just a vanilla hash.
360
- #
361
- # @param root [Hash] a hash to use as the root of the conversion
362
- # @deprecated The following configuration hashes should no longer be
363
- # created in a Test Kitche hash: [`:driver_plugin`, `:driver_config`].
364
- # Use a `:driver` hash block in their place.
365
- # @api private
366
- def convert_legacy_driver_format_at!(root)
367
- if root.key?(:driver_config)
368
- ddata = root.fetch(:driver, Hash.new)
369
- ddata = { :name => ddata } if ddata.is_a?(String)
370
- root[:driver] = root.delete(:driver_config).rmerge(ddata)
371
- end
372
-
373
- if root.key?(:driver_plugin)
374
- ddata = root.fetch(:driver, Hash.new)
375
- ddata = { :name => ddata } if ddata.is_a?(String)
376
- root[:driver] = { :name => root.delete(:driver_plugin) }.rmerge(ddata)
377
- end
378
- end
379
-
380
- # Copies `:http_proxy` and `:https_proxy` values in a driver hash into the
381
- # provisioner and verifier hashes. For backwards compatibility with legacy
382
- # Drivers (those inheriting directly from `SSHBase`), the original
383
- # values are maintained in the driver hash.
384
- #
385
- # This method converts the following:
386
- #
387
- # {
388
- # :driver => {
389
- # :http_proxy => "http://proxy",
390
- # :https_proxy => "https://proxy"
391
- # },
392
- #
393
- # :platforms => [
394
- # {
395
- # :name => "ubuntu-12.04",
396
- # :driver => {
397
- # :http_proxy => "foo"
398
- # }
399
- # }
400
- # ],
401
- #
402
- # :suites => [
403
- # {
404
- # :name => "alpha",
405
- # :driver => {
406
- # :https_proxy => "bar"
407
- # }
408
- # }
409
- # ]
410
- # }
411
- #
412
- # into the following:
413
- #
414
- # {
415
- # :driver => {
416
- # :http_proxy => "http://proxy",
417
- # :https_proxy => "https://proxy"
418
- # },
419
- #
420
- # :provisioner => {
421
- # :http_proxy => "http://proxy",
422
- # :https_proxy => "https://proxy"
423
- # },
424
- #
425
- # :verifier => {
426
- # :http_proxy => "http://proxy",
427
- # :https_proxy => "https://proxy"
428
- # },
429
- #
430
- # :platforms => [
431
- # {
432
- # :name => "ubuntu-12.04",
433
- # :driver => {
434
- # :http_proxy => "foo"
435
- # },
436
- # :provisioner => {
437
- # :http_proxy => "foo"
438
- # },
439
- # :verifier => {
440
- # :http_proxy => "foo"
441
- # }
442
- # }
443
- # ],
444
- #
445
- # :suites => [
446
- # {
447
- # :name => "alpha",
448
- # :driver => {
449
- # :https_proxy => "bar"
450
- # },
451
- # :provisioner => {
452
- # :https_proxy => "bar"
453
- # },
454
- # :verifier => {
455
- # :https_proxy => "bar"
456
- # }
457
- # }
458
- # ]
459
- # }
460
- #
461
- # @deprecated The `:http_proxy` and `:https_proxy` should no longer be
462
- # used in driver blocks, they should be added to the provisioner and
463
- # verifier blocks so that they can be independantly configured.
464
- # Provisioners and Verifiers are responsible for HTTP proxying and no
465
- # longer are Drivers responsible for this.
466
- # @api private
467
- def convert_legacy_driver_http_proxy_format!
468
- convert_legacy_driver_http_proxy_format_at!(data)
469
- data.fetch(:platforms, []).each do |platform|
470
- convert_legacy_driver_http_proxy_format_at!(platform)
471
- end
472
- data.fetch(:suites, []).each do |suite|
473
- convert_legacy_driver_http_proxy_format_at!(suite)
474
- end
475
- end
476
-
477
- # Copies `:http_proxy` and `:https_proxy` values in a driver hash into
478
- # the provisioner and verifier hashes. This method has no knowledge of
479
- # suites, platforms, or the like, just a vanilla hash.
480
- #
481
- # @param root [Hash] a hash to use as the root of the conversion
482
- # @deprecated The `:http_proxy` and `:https_proxy` should no longer be
483
- # used in driver blocks, they should be added to the provisioner and
484
- # verifier blocks so that they can be independantly configured.
485
- # Provisioners and Verifiers are responsible for HTTP proxying and no
486
- # longer are Drivers responsible for this.
487
- # @api private
488
- def convert_legacy_driver_http_proxy_format_at!(root)
489
- ddata = root.fetch(:driver, Hash.new)
490
-
491
- [:http_proxy, :https_proxy].each do |key|
492
- next unless ddata.is_a?(Hash) && ddata.key?(key)
493
-
494
- pdata = root.fetch(:provisioner, Hash.new)
495
- pdata = { :name => pdata } if pdata.is_a?(String)
496
- root[:provisioner] = { key => ddata.fetch(key) }.rmerge(pdata)
497
-
498
- vdata = root.fetch(:verifier, Hash.new)
499
- vdata = { :name => vdata } if vdata.is_a?(String)
500
- root[:verifier] = { key => ddata.fetch(key) }.rmerge(vdata)
501
- end
502
- end
503
-
504
- # Destructively moves a `:require_chef_omnibus` key/value pair from a
505
- # `:driver` hash block to a `:provisioner` hash block.
506
- #
507
- # This method converts the following:
508
- #
509
- # {
510
- # :driver => {
511
- # :require_chef_omnibus => true
512
- # }
513
- #
514
- # :platforms => [
515
- # {
516
- # :name => "ubuntu-12.04",
517
- # :driver => {
518
- # :require_chef_omnibus => "10.8.2"
519
- # }
520
- # }
521
- # ],
522
- #
523
- # :suites => [
524
- # {
525
- # :name => "alpha",
526
- # :driver => {
527
- # :require_chef_omnibus => "11"
528
- # }
529
- # }
530
- # ]
531
- # }
532
- #
533
- # into the following:
534
- #
535
- # {
536
- # :provisioner => {
537
- # :require_chef_omnibus => true
538
- # }
539
- #
540
- # :platforms => [
541
- # {
542
- # :name => "ubuntu-12.04",
543
- # :provisioner => {
544
- # :require_chef_omnibus => "10.8.2"
545
- # }
546
- # }
547
- # ],
548
- #
549
- # :suites => [
550
- # {
551
- # :name => "alpha",
552
- # :provisioner => {
553
- # :require_chef_omnibus => "11"
554
- # }
555
- # }
556
- # ]
557
- # }
558
- #
559
- # @deprecated The `:require_chef_omnibus` key/value pair should no longer
560
- # be created inside a `:driver` hash block. Put it in a `:provisioner`
561
- # hash block instead.
562
- # @api private
563
- def convert_legacy_require_chef_omnibus_format!
564
- convert_legacy_require_chef_omnibus_format_at!(data)
565
- data.fetch(:platforms, []).each do |platform|
566
- convert_legacy_require_chef_omnibus_format_at!(platform)
567
- end
568
- data.fetch(:suites, []).each do |suite|
569
- convert_legacy_require_chef_omnibus_format_at!(suite)
570
- end
571
- end
572
-
573
- # Destructively moves a `:require_chef_omnibus` key/value pair from a
574
- # `:driver` hash block to a `:provisioner` hash block in the first leve
575
- # depth of a hash. This method has no knowledge of suites, platforms, or
576
- # the like, just a vanilla haash.
577
- #
578
- # @param root [Hash] a hash to use as the root of the conversion
579
- # @deprecated The `:require_chef_omnibus` key/value pair should no longer
580
- # be created inside a `:driver` hash block. Put it in a `:provisioner`
581
- # hash block instead.
582
- # @api private
583
- def convert_legacy_require_chef_omnibus_format_at!(root)
584
- key = :require_chef_omnibus
585
- ddata = root.fetch(:driver, Hash.new)
586
-
587
- if ddata.is_a?(Hash) && ddata.key?(key)
588
- pdata = root.fetch(:provisioner, Hash.new)
589
- pdata = { :name => pdata } if pdata.is_a?(String)
590
- root[:provisioner] =
591
- { key => root.fetch(:driver).delete(key) }.rmerge(pdata)
592
- end
593
- end
594
-
595
- # Performs a prioritized recursive merge of several source Hashes and
596
- # returns a new merged Hash. For these data sub-hash structures, there are
597
- # 4 sources for configuration data:
598
- #
599
- # 1. defaults, provided by Test Kitchen code
600
- # 2. user-provided in the common root-level of the incoming data hash
601
- # 3. user-provided in a platform sub-hash
602
- # 4. user-provided in a suite sub-hash
603
- #
604
- # The merge order is 4 -> 3 -> 2 -> 1, meaning that the highest number in
605
- # the above list has merge precedence over any lower numbered source. Put
606
- # another way, a key/value pair in a suite sub-hash will be used over the
607
- # key/value pair in a platform sub-hash.
608
- #
609
- # @param key [Symbol] the data sub-hash(es) to merge
610
- # @param suite [String] a suite name
611
- # @param platform [String] a platform name
612
- # @param default_key [Symbol] the default key to use when normalizing the
613
- # data sub-hashes (default: `:name`)
614
- # @return [Hash] a new merged Hash
615
- # @api private
616
- def merged_data_for(key, suite, platform, default_key = :name)
617
- ddata = normalized_default_data(key, default_key, suite, platform)
618
- cdata = normalized_common_data(key, default_key)
619
- pdata = normalized_platform_data(key, default_key, platform)
620
- sdata = normalized_suite_data(key, default_key, suite)
621
-
622
- ddata.rmerge(cdata.rmerge(pdata.rmerge(sdata)))
623
- end
624
-
625
- # Destructively moves key Chef configuration key/value pairs from being
626
- # directly under a suite or platform into a `:provisioner` sub-hash.
627
- #
628
- # There are two key Chef configuration key/value pairs:
629
- #
630
- # 1. `:attributes`
631
- # 2. `:run_list`
632
- #
633
- # This method converts the following:
634
- #
635
- # {
636
- # :platforms => [
637
- # {
638
- # :name => "ubuntu-12.04",
639
- # :attributes => { :one => "two" },
640
- # :run_list => ["alpha", "bravo"]
641
- # }
642
- # ],
643
- #
644
- # :suites => [
645
- # {
646
- # :name => "alpha",
647
- # :attributes => { :three => "four" },
648
- # :run_list => ["charlie", "delta"]
649
- # }
650
- # ]
651
- # }
652
- #
653
- # into the following:
654
- #
655
- # {
656
- # :platforms => [
657
- # {
658
- # :name => "ubuntu-12.04",
659
- # :provisioner => {
660
- # :attributes => { :one => "two" },
661
- # :run_list => ["alpha", "bravo"]
662
- # }
663
- # }
664
- # ],
665
- #
666
- # :suites => [
667
- # {
668
- # :name => "alpha",
669
- # :provisioner => {
670
- # :attributes => { :three => "four" },
671
- # :run_list => ["charlie", "delta"]
672
- # }
673
- # }
674
- # ]
675
- # }
676
- #
677
- # @api private
678
- def move_chef_data_to_provisioner!
679
- data.fetch(:suites, []).each do |suite|
680
- move_chef_data_to_provisioner_at!(suite, :attributes)
681
- move_chef_data_to_provisioner_at!(suite, :run_list)
682
- end
683
-
684
- data.fetch(:platforms, []).each do |platform|
685
- move_chef_data_to_provisioner_at!(platform, :attributes)
686
- move_chef_data_to_provisioner_at!(platform, :run_list)
687
- end
688
- end
689
-
690
- # Destructively moves key Chef configuration key/value pairs from being
691
- # directly under a hash into a `:provisioner` sub-hash block. This method
692
- # has no knowledge of suites, platforms, or the like, just a vanilla hash.
693
- #
694
- # @param root [Hash] a hash to use as the root of the conversion
695
- # @param key [Symbol] a key in the root hash to move into a `:provisioner`
696
- # sub-hash block
697
- # @api private
698
- def move_chef_data_to_provisioner_at!(root, key)
699
- if root.key?(key)
700
- pdata = root.fetch(:provisioner, Hash.new)
701
- pdata = { :name => pdata } if pdata.is_a?(String)
702
- if !root.fetch(key, nil).nil?
703
- root[:provisioner] = pdata.rmerge(key => root.delete(key))
704
- end
705
- end
706
- end
707
-
708
- # Vicious hack to allow for Array-appending merge semantics. This method
709
- # takes an array value and transforms it into a hash with a bucket name
710
- # containing the original Array. This way semantic Hash merging will do
711
- # its thing and another process can collapse the hash into a flat array
712
- # afterwards, given a strategy (like use the array segmenet from one
713
- # bucket first, then another one second). To anyone who made it this far,
714
- # Fletcher appologizes.
715
- #
716
- # @param root [Hash] a hash to use as the root of the conversion
717
- # @param key [Symbol] a key in the root hash that, if exists, has its
718
- # value transformed into a sub-hash
719
- # @param bucket [Symbol] a key to use for the sub-hash
720
- # @api private
721
- def namespace_array!(root, key, bucket)
722
- root[key] = { bucket => root.fetch(key) } if root.key?(key)
723
- end
724
-
725
- # Normalizes a specific key in the root of the data hash to be a proper
726
- # sub-hash in all cases. Specifically handled are the following cases:
727
- #
728
- # * If the value for a key is set to `nil`, a new Hash will be put in
729
- # its place.
730
- # * If the value is a String, then convert the value to a new Hash with
731
- # a default key pointing to the original String
732
- #
733
- # Given a hash:
734
- #
735
- # { :driver => nil }
736
- #
737
- # this method (`normalized_common_data(:driver, :name)`) would return:
738
- #
739
- # {}
740
- #
741
- # Given a hash:
742
- #
743
- # { :driver => "coolbeans" }
744
- #
745
- # this method (`normalized_common_data(:driver, :name)`) would return:
746
- #
747
- # { :name => "coolbeans" }
748
- #
749
- # @param key [Symbol] the value to normalize
750
- # @param default_key [Symbol] the implicit default key if a String value
751
- # is given
752
- # @return [Hash] a shallow Hash copy of the original if not modified, or a
753
- # new Hash otherwise
754
- # @api private
755
- def normalized_common_data(key, default_key)
756
- cdata = data.fetch(key, Hash.new)
757
- cdata = cdata.nil? ? Hash.new : cdata.dup
758
- cdata = { default_key => cdata } if cdata.is_a?(String)
759
- cdata
760
- end
761
-
762
- # Normalizes a specific key in the `:defaults` data sub-hash to be a proper
763
- # sub-hash in all cases. Specifically handled are the following cases:
764
- #
765
- # * If the value for a key is not set, a new Hash will be put in its place
766
- # * If the value is a String, then convert the value to a new Hash with
767
- # a default key pointing to the original String
768
- #
769
- # Given a hash:
770
- #
771
- # {
772
- # :defaults => {}
773
- # }
774
- #
775
- # this method (`normalized_default_data(:driver, :name)`) would return:
776
- #
777
- # {}
778
- #
779
- # Given a hash:
780
- #
781
- # {
782
- # :defaults => {
783
- # :driver => "coolbeans"
784
- # }
785
- # }
786
- #
787
- # this method (`normalized_default_data(:driver, :name)`) would return:
788
- #
789
- # { :name => "coolbeans" }
790
- #
791
- # @param key [Symbol] the value to normalize
792
- # @param default_key [Symbol] the implicit default key if a String value
793
- # is given
794
- # @param suite [String] name of a suite
795
- # @param platform [String] name of a platform
796
- # @return [Hash] a shallow Hash copy of the original if not modified, or a
797
- # new Hash otherwise
798
- # @api private
799
- def normalized_default_data(key, default_key, suite, platform)
800
- ddata = kitchen_config.fetch(:defaults, Hash.new).fetch(key, Hash.new).dup
801
- ddata = { default_key => ddata.call(suite, platform) } if ddata.is_a?(Proc)
802
- ddata = { default_key => ddata } if ddata.is_a?(String)
803
- ddata
804
- end
805
-
806
- # Normalizes a specific key in a platform hash data sub-hash to be a proper
807
- # sub-hash in all cases. Specifically handled are the following cases:
808
- #
809
- # * If the value for a key is set to `nil`, a new Hash will be put in
810
- # its place.
811
- # * If the value is a String, then convert the value to a new Hash with
812
- # a default key pointing to the original String
813
- #
814
- # Given a hash:
815
- #
816
- # {
817
- # :platforms => [
818
- # {
819
- # :name => "alpha",
820
- # :driver => nil
821
- # }
822
- # ]
823
- # }
824
- #
825
- # this method (`normalized_platform_data(:driver, :name, "alpha)`) would
826
- # return:
827
- #
828
- # {}
829
- #
830
- # Given a hash:
831
- #
832
- # {
833
- # :platforms => [
834
- # {
835
- # :name => "alpha",
836
- # :driver => "coolbeans"
837
- # }
838
- # ]
839
- # }
840
- #
841
- # this method (`normalized_common_data(:driver, :name, "alpha")`) would
842
- # return:
843
- #
844
- # { :name => "coolbeans" }
845
- #
846
- # @param key [Symbol] the value to normalize
847
- # @param default_key [Symbol] the implicit default key if a String value
848
- # is given
849
- # @param platform [String] name of a platform
850
- # @return [Hash] a shallow Hash copy of the original if not modified, or a
851
- # new Hash otherwise
852
- # @api private
853
- def normalized_platform_data(key, default_key, platform)
854
- pdata = platform_data_for(platform).fetch(key, Hash.new)
855
- pdata = pdata.nil? ? Hash.new : pdata.dup
856
- pdata = { default_key => pdata } if pdata.is_a?(String)
857
- namespace_array!(pdata, :run_list, :platform)
858
- pdata
859
- end
860
-
861
- # Normalizes a specific key in a suite hash data sub-hash to be a proper
862
- # sub-hash in all cases. Specifically handled are the following cases:
863
- #
864
- # * If the value for a key is set to `nil`, a new Hash will be put in
865
- # its place.
866
- # * If the value is a String, then convert the value to a new Hash with
867
- # a default key pointing to the original String
868
- #
869
- # Given a hash:
870
- #
871
- # {
872
- # :suites => [
873
- # {
874
- # :name => "full",
875
- # :driver => nil
876
- # }
877
- # ]
878
- # }
879
- #
880
- # this method (`normalized_platform_data(:driver, :name, "full)`) would
881
- # return:
882
- #
883
- # {}
884
- #
885
- # Given a hash:
886
- #
887
- # {
888
- # :suites => [
889
- # {
890
- # :name => "full",
891
- # :driver => "coolbeans"
892
- # }
893
- # ]
894
- # }
895
- #
896
- # this method (`normalized_common_data(:driver, :name, "full")`) would
897
- # return:
898
- #
899
- # { :name => "coolbeans" }
900
- #
901
- # @param key [Symbol] the value to normalize
902
- # @param default_key [Symbol] the implicit default key if a String value
903
- # is given
904
- # @param suite [String] name of a suite
905
- # @return [Hash] a shallow Hash copy of the original if not modified, or a
906
- # new Hash otherwise
907
- # @api private
908
- def normalized_suite_data(key, default_key, suite)
909
- sdata = suite_data_for(suite).fetch(key, Hash.new)
910
- sdata = sdata.nil? ? Hash.new : sdata.dup
911
- sdata = { default_key => sdata } if sdata.is_a?(String)
912
- namespace_array!(sdata, :run_list, :suite)
913
- sdata
914
- end
915
-
916
- # Returns the hash for a platform by name, or an empty Hash if none
917
- # could be found.
918
- #
919
- # @param name [String] name of a platform
920
- # @return [Hash] the configuration hash for the platform, or an empty
921
- # Hash if not found
922
- # @api private
923
- def platform_data_for(name)
924
- data.fetch(:platforms, Hash.new).find(-> { Hash.new }) do |platform|
925
- platform.fetch(:name, nil) == name
926
- end
927
- end
928
-
929
- # Destructively sets a base kitchen config key/value pair at the root of
930
- # the given hash. If the key is present in the given Hash, it is deleted
931
- # and will not be used. If the key is found in the `kitchen_config` hash
932
- # (default values), then its value will be used and set. Finally, if
933
- # the key is found in `:kitchen` data sub-hash, then its value will be used
934
- # and set.
935
- #
936
- # @param root [Hash] a hash to use as the root of the conversion
937
- # @param key [Symbol] the key to search for
938
- # @api private
939
- def set_kitchen_config_at!(root, key)
940
- kdata = data.fetch(:kitchen, Hash.new)
941
-
942
- root.delete(key) if root.key?(key)
943
- root[key] = kitchen_config.fetch(key) if kitchen_config.key?(key)
944
- root[key] = kdata.fetch(key) if kdata.key?(key)
945
- end
946
-
947
- # Returns the hash for a suite by name, or an empty Hash if none
948
- # could be found.
949
- #
950
- # @param name [String] name of a suite
951
- # @return [Hash] the configuration hash for the suite, or an empty
952
- # Hash if not found
953
- # @api private
954
- def suite_data_for(name)
955
- data.fetch(:suites, Hash.new).find(-> { Hash.new }) do |suite|
956
- suite.fetch(:name, nil) == name
957
- end
958
- end
959
- end
960
- end
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2013, Fletcher Nichol
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require "vendor/hash_recursive_merge"
20
+
21
+ module Kitchen
22
+
23
+ # Class to handle recursive merging of configuration between platforms,
24
+ # suites, and common data.
25
+ #
26
+ # This object will mutate the data Hash passed into its constructor and so
27
+ # should not be reused or shared across threads.
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
+ #
33
+ # @author Fletcher Nichol <fnichol@nichol.ca>
34
+ class DataMunger
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
41
+ def initialize(data, kitchen_config = {})
42
+ @data = data
43
+ @kitchen_config = kitchen_config
44
+ convert_legacy_driver_format!
45
+ convert_legacy_chef_paths_format!
46
+ convert_legacy_require_chef_omnibus_format!
47
+ convert_legacy_busser_format!
48
+ convert_legacy_driver_http_proxy_format!
49
+ move_chef_data_to_provisioner!
50
+ end
51
+
52
+ # Generate a new Hash of configuration data that can be used to construct
53
+ # a new Driver object.
54
+ #
55
+ # @param suite [String] a suite name
56
+ # @param platform [String] a platform name
57
+ # @return [Hash] a new configuration Hash that can be used to construct a
58
+ # new Driver
59
+ def driver_data_for(suite, platform)
60
+ merged_data_for(:driver, suite, platform).tap do |ddata|
61
+ set_kitchen_config_at!(ddata, :kitchen_root)
62
+ set_kitchen_config_at!(ddata, :test_base_path)
63
+ set_kitchen_config_at!(ddata, :log_level)
64
+ end
65
+ end
66
+
67
+ # Returns an Array of platform Hashes.
68
+ #
69
+ # @return [Array<Hash>] an Array of Hashes
70
+ def platform_data
71
+ data.fetch(:platforms, [])
72
+ end
73
+
74
+ # Generate a new Hash of configuration data that can be used to construct
75
+ # a new Provisioner object.
76
+ #
77
+ # @param suite [String] a suite name
78
+ # @param platform [String] a platform name
79
+ # @return [Hash] a new configuration Hash that can be used to construct a
80
+ # new Provisioner
81
+ def provisioner_data_for(suite, platform)
82
+ merged_data_for(:provisioner, suite, platform).tap do |pdata|
83
+ set_kitchen_config_at!(pdata, :kitchen_root)
84
+ set_kitchen_config_at!(pdata, :test_base_path)
85
+ combine_arrays!(pdata, :run_list, :platform, :suite)
86
+ end
87
+ end
88
+
89
+ # Returns an Array of suite Hashes.
90
+ #
91
+ # @return [Array<Hash>] an Array of Hashes
92
+ def suite_data
93
+ data.fetch(:suites, [])
94
+ end
95
+
96
+ # Generate a new Hash of configuration data that can be used to construct
97
+ # a new Transport object.
98
+ #
99
+ # @param suite [String] a suite name
100
+ # @param platform [String] a platform name
101
+ # @return [Hash] a new configuration Hash that can be used to construct a
102
+ # new Transport
103
+ def transport_data_for(suite, platform)
104
+ merged_data_for(:transport, suite, platform).tap do |tdata|
105
+ set_kitchen_config_at!(tdata, :kitchen_root)
106
+ set_kitchen_config_at!(tdata, :test_base_path)
107
+ set_kitchen_config_at!(tdata, :log_level)
108
+ end
109
+ end
110
+
111
+ # Generate a new Hash of configuration data that can be used to construct
112
+ # a new Verifier object.
113
+ #
114
+ # @param suite [String] a suite name
115
+ # @param platform [String] a platform name
116
+ # @return [Hash] a new configuration Hash that can be used to construct a
117
+ # new Verifier
118
+ def verifier_data_for(suite, platform)
119
+ merged_data_for(:verifier, suite, platform).tap do |vdata|
120
+ set_kitchen_config_at!(vdata, :kitchen_root)
121
+ set_kitchen_config_at!(vdata, :test_base_path)
122
+ set_kitchen_config_at!(vdata, :log_level)
123
+ end
124
+ end
125
+
126
+ private
127
+
128
+ # @return [Hash] the user data hash
129
+ # @api private
130
+ attr_reader :data
131
+
132
+ # @return [Hash] the Test Kitchen-provided configuration hash
133
+ # @api private
134
+ attr_reader :kitchen_config
135
+
136
+ def combine_arrays!(root, key, *namespaces)
137
+ if root.key?(key)
138
+ root[key] = namespaces.
139
+ map { |namespace| root.fetch(key).fetch(namespace, []) }.flatten.
140
+ compact
141
+ end
142
+ end
143
+
144
+ # Destructively moves old-style `:busser` configuration hashes into the
145
+ # correct `:verifier` hash.
146
+ #
147
+ # This method converts the following:
148
+ #
149
+ # {
150
+ # :busser => { :one => "two" },
151
+ #
152
+ # :platforms => [
153
+ # {
154
+ # :name => "ubuntu-12.04",
155
+ # :busser => "bar"
156
+ # }
157
+ # ],
158
+ #
159
+ # :suites => [
160
+ # {
161
+ # :name => "alpha",
162
+ # :busser => { :three => "four" }
163
+ # }
164
+ # ]
165
+ # }
166
+ #
167
+ # into the following:
168
+ #
169
+ # {
170
+ # :verifier => {
171
+ # :name => "busser",
172
+ # :one => "two"
173
+ # }
174
+ #
175
+ # :platforms => [
176
+ # {
177
+ # :name => "ubuntu-12.04",
178
+ # :verifier => {
179
+ # :name => "busser",
180
+ # :version => "bar
181
+ # }
182
+ # }
183
+ # ],
184
+ #
185
+ # :suites => [
186
+ # {
187
+ # :name => "alpha",
188
+ # :verifier => {
189
+ # :name => "busser",
190
+ # :three => "four"
191
+ # }
192
+ # }
193
+ # ]
194
+ # }
195
+ #
196
+ # @deprecated The following configuration hashes should no longer be
197
+ # created in a `:platform`, `:suite`, or common location:
198
+ # `:busser`. Use a `:verifier` hash block in their place.
199
+ # @api private
200
+ def convert_legacy_busser_format!
201
+ convert_legacy_busser_format_at!(data)
202
+ data.fetch(:platforms, []).each do |platform|
203
+ convert_legacy_busser_format_at!(platform)
204
+ end
205
+ data.fetch(:suites, []).each do |suite|
206
+ convert_legacy_busser_format_at!(suite)
207
+ end
208
+ end
209
+
210
+ # Destructively moves old-style `:busser` configuration hashes into the
211
+ # correct `:verifier` hashes. This method has no knowledge of suites,
212
+ # platforms, or the like, just a vanilla hash.
213
+ #
214
+ # @param root [Hash] a hash to use as the root of the conversion
215
+ # @deprecated The following configuration hashses should no longer be
216
+ # created in a Test Kitchen hash: `:busser`. Use a `:verifier` hash
217
+ # block in their place.
218
+ # @api private
219
+ def convert_legacy_busser_format_at!(root)
220
+ if root.key?(:busser)
221
+ bdata = root.delete(:busser)
222
+ bdata = { :version => bdata } if bdata.is_a?(String)
223
+ bdata[:name] = "busser" if bdata[:name].nil?
224
+
225
+ vdata = root.fetch(:verifier, Hash.new)
226
+ vdata = { :name => vdata } if vdata.is_a?(String)
227
+ root[:verifier] = bdata.rmerge(vdata)
228
+ end
229
+ end
230
+
231
+ # Destructively moves Chef-related paths out of a suite hash and into the
232
+ # suite's provisioner sub-hash.
233
+ #
234
+ # This method converts the following:
235
+ #
236
+ # {
237
+ # :suites => [
238
+ # {
239
+ # :name => "alpha",
240
+ # :nodes_path => "/a/b/c"
241
+ # },
242
+ # {
243
+ # :name => "beta",
244
+ # :roles_path => "/tmp/roles",
245
+ # :data_bags_path => "/bags"
246
+ # },
247
+ # ]
248
+ # }
249
+ #
250
+ # into the following:
251
+ #
252
+ # {
253
+ # :suites => [
254
+ # {
255
+ # :name => "alpha",
256
+ # :provisioner => {
257
+ # :nodes_path => "/a/b/c"
258
+ # }
259
+ # },
260
+ # {
261
+ # :name => "beta",
262
+ # :provisioner => {
263
+ # :roles_path => "/tmp/roles",
264
+ # :data_bags_path => "/bags"
265
+ # }
266
+ # },
267
+ # ]
268
+ # }
269
+ #
270
+ # @deprecated The following Chef paths should no longer be created directly
271
+ # under a suite hash: [`data_path`, `data_bags_path`,
272
+ # `encrypted_data_bag_secret_key_path`, `environments_path`, `nodes_path`,
273
+ # `roles_path`]. Instead put these key/value pairs directly inside a
274
+ # `provisioner` hash.
275
+ # @api private
276
+ def convert_legacy_chef_paths_format!
277
+ data.fetch(:suites, []).each do |suite|
278
+ %w[
279
+ data data_bags encrypted_data_bag_secret_key
280
+ environments nodes roles
281
+ ].each do |key|
282
+ move_chef_data_to_provisioner_at!(suite, "#{key}_path".to_sym)
283
+ end
284
+ end
285
+ end
286
+
287
+ # Destructively moves old-style `:driver_plugin` and `:driver_config`
288
+ # configuration hashes into the correct `:driver` hash.
289
+ #
290
+ # This method converts the following:
291
+ #
292
+ # {
293
+ # :driver_plugin => "foo",
294
+ # :driver_config => { :one => "two" },
295
+ #
296
+ # :platforms => [
297
+ # {
298
+ # :name => "ubuntu-12.04",
299
+ # :driver_plugin => "bar"
300
+ # }
301
+ # ],
302
+ #
303
+ # :suites => [
304
+ # {
305
+ # :name => "alpha",
306
+ # :driver_plugin => "baz"
307
+ # :driver_config => { :three => "four" }
308
+ # }
309
+ # ]
310
+ # }
311
+ #
312
+ # into the following:
313
+ #
314
+ # {
315
+ # :driver => {
316
+ # :name => "foo",
317
+ # :one => "two"
318
+ # }
319
+ #
320
+ # :platforms => [
321
+ # {
322
+ # :name => "ubuntu-12.04",
323
+ # :driver => {
324
+ # :name => "bar"
325
+ # }
326
+ # }
327
+ # ],
328
+ #
329
+ # :suites => [
330
+ # {
331
+ # :name => "alpha",
332
+ # :driver => {
333
+ # :name => "baz",
334
+ # :three => "four"
335
+ # }
336
+ # }
337
+ # ]
338
+ # }
339
+ #
340
+ # @deprecated The following configuration hashes should no longer be
341
+ # created in a `:platform`, `:suite`, or common location:
342
+ # [`:driver_plugin`, `:driver_config`]. Use a `:driver` hash block in
343
+ # their place.
344
+ # @api private
345
+ def convert_legacy_driver_format!
346
+ convert_legacy_driver_format_at!(data)
347
+ data.fetch(:platforms, []).each do |platform|
348
+ convert_legacy_driver_format_at!(platform)
349
+ end
350
+ data.fetch(:suites, []).each do |suite|
351
+ convert_legacy_driver_format_at!(suite)
352
+ end
353
+ end
354
+
355
+ # Destructively moves old-style `:driver_plugin` and `:driver_config`
356
+ # configuration hashes into the correct `:driver` in the first level depth
357
+ # of a hash. This method has no knowledge of suites, platforms, or the
358
+ # like, just a vanilla hash.
359
+ #
360
+ # @param root [Hash] a hash to use as the root of the conversion
361
+ # @deprecated The following configuration hashes should no longer be
362
+ # created in a Test Kitche hash: [`:driver_plugin`, `:driver_config`].
363
+ # Use a `:driver` hash block in their place.
364
+ # @api private
365
+ def convert_legacy_driver_format_at!(root)
366
+ if root.key?(:driver_config)
367
+ ddata = root.fetch(:driver, Hash.new)
368
+ ddata = { :name => ddata } if ddata.is_a?(String)
369
+ root[:driver] = root.delete(:driver_config).rmerge(ddata)
370
+ end
371
+
372
+ if root.key?(:driver_plugin)
373
+ ddata = root.fetch(:driver, Hash.new)
374
+ ddata = { :name => ddata } if ddata.is_a?(String)
375
+ root[:driver] = { :name => root.delete(:driver_plugin) }.rmerge(ddata)
376
+ end
377
+ end
378
+
379
+ # Copies `:http_proxy` and `:https_proxy` values in a driver hash into the
380
+ # provisioner and verifier hashes. For backwards compatibility with legacy
381
+ # Drivers (those inheriting directly from `SSHBase`), the original
382
+ # values are maintained in the driver hash.
383
+ #
384
+ # This method converts the following:
385
+ #
386
+ # {
387
+ # :driver => {
388
+ # :http_proxy => "http://proxy",
389
+ # :https_proxy => "https://proxy"
390
+ # },
391
+ #
392
+ # :platforms => [
393
+ # {
394
+ # :name => "ubuntu-12.04",
395
+ # :driver => {
396
+ # :http_proxy => "foo"
397
+ # }
398
+ # }
399
+ # ],
400
+ #
401
+ # :suites => [
402
+ # {
403
+ # :name => "alpha",
404
+ # :driver => {
405
+ # :https_proxy => "bar"
406
+ # }
407
+ # }
408
+ # ]
409
+ # }
410
+ #
411
+ # into the following:
412
+ #
413
+ # {
414
+ # :driver => {
415
+ # :http_proxy => "http://proxy",
416
+ # :https_proxy => "https://proxy"
417
+ # },
418
+ #
419
+ # :provisioner => {
420
+ # :http_proxy => "http://proxy",
421
+ # :https_proxy => "https://proxy"
422
+ # },
423
+ #
424
+ # :verifier => {
425
+ # :http_proxy => "http://proxy",
426
+ # :https_proxy => "https://proxy"
427
+ # },
428
+ #
429
+ # :platforms => [
430
+ # {
431
+ # :name => "ubuntu-12.04",
432
+ # :driver => {
433
+ # :http_proxy => "foo"
434
+ # },
435
+ # :provisioner => {
436
+ # :http_proxy => "foo"
437
+ # },
438
+ # :verifier => {
439
+ # :http_proxy => "foo"
440
+ # }
441
+ # }
442
+ # ],
443
+ #
444
+ # :suites => [
445
+ # {
446
+ # :name => "alpha",
447
+ # :driver => {
448
+ # :https_proxy => "bar"
449
+ # },
450
+ # :provisioner => {
451
+ # :https_proxy => "bar"
452
+ # },
453
+ # :verifier => {
454
+ # :https_proxy => "bar"
455
+ # }
456
+ # }
457
+ # ]
458
+ # }
459
+ #
460
+ # @deprecated The `:http_proxy` and `:https_proxy` should no longer be
461
+ # used in driver blocks, they should be added to the provisioner and
462
+ # verifier blocks so that they can be independantly configured.
463
+ # Provisioners and Verifiers are responsible for HTTP proxying and no
464
+ # longer are Drivers responsible for this.
465
+ # @api private
466
+ def convert_legacy_driver_http_proxy_format!
467
+ convert_legacy_driver_http_proxy_format_at!(data)
468
+ data.fetch(:platforms, []).each do |platform|
469
+ convert_legacy_driver_http_proxy_format_at!(platform)
470
+ end
471
+ data.fetch(:suites, []).each do |suite|
472
+ convert_legacy_driver_http_proxy_format_at!(suite)
473
+ end
474
+ end
475
+
476
+ # Copies `:http_proxy` and `:https_proxy` values in a driver hash into
477
+ # the provisioner and verifier hashes. This method has no knowledge of
478
+ # suites, platforms, or the like, just a vanilla hash.
479
+ #
480
+ # @param root [Hash] a hash to use as the root of the conversion
481
+ # @deprecated The `:http_proxy` and `:https_proxy` should no longer be
482
+ # used in driver blocks, they should be added to the provisioner and
483
+ # verifier blocks so that they can be independantly configured.
484
+ # Provisioners and Verifiers are responsible for HTTP proxying and no
485
+ # longer are Drivers responsible for this.
486
+ # @api private
487
+ def convert_legacy_driver_http_proxy_format_at!(root)
488
+ ddata = root.fetch(:driver, Hash.new)
489
+
490
+ [:http_proxy, :https_proxy].each do |key|
491
+ next unless ddata.is_a?(Hash) && ddata.key?(key)
492
+
493
+ pdata = root.fetch(:provisioner, Hash.new)
494
+ pdata = { :name => pdata } if pdata.is_a?(String)
495
+ root[:provisioner] = { key => ddata.fetch(key) }.rmerge(pdata)
496
+
497
+ vdata = root.fetch(:verifier, Hash.new)
498
+ vdata = { :name => vdata } if vdata.is_a?(String)
499
+ root[:verifier] = { key => ddata.fetch(key) }.rmerge(vdata)
500
+ end
501
+ end
502
+
503
+ # Destructively moves a `:require_chef_omnibus` key/value pair from a
504
+ # `:driver` hash block to a `:provisioner` hash block.
505
+ #
506
+ # This method converts the following:
507
+ #
508
+ # {
509
+ # :driver => {
510
+ # :require_chef_omnibus => true
511
+ # }
512
+ #
513
+ # :platforms => [
514
+ # {
515
+ # :name => "ubuntu-12.04",
516
+ # :driver => {
517
+ # :require_chef_omnibus => "10.8.2"
518
+ # }
519
+ # }
520
+ # ],
521
+ #
522
+ # :suites => [
523
+ # {
524
+ # :name => "alpha",
525
+ # :driver => {
526
+ # :require_chef_omnibus => "11"
527
+ # }
528
+ # }
529
+ # ]
530
+ # }
531
+ #
532
+ # into the following:
533
+ #
534
+ # {
535
+ # :provisioner => {
536
+ # :require_chef_omnibus => true
537
+ # }
538
+ #
539
+ # :platforms => [
540
+ # {
541
+ # :name => "ubuntu-12.04",
542
+ # :provisioner => {
543
+ # :require_chef_omnibus => "10.8.2"
544
+ # }
545
+ # }
546
+ # ],
547
+ #
548
+ # :suites => [
549
+ # {
550
+ # :name => "alpha",
551
+ # :provisioner => {
552
+ # :require_chef_omnibus => "11"
553
+ # }
554
+ # }
555
+ # ]
556
+ # }
557
+ #
558
+ # @deprecated The `:require_chef_omnibus` key/value pair should no longer
559
+ # be created inside a `:driver` hash block. Put it in a `:provisioner`
560
+ # hash block instead.
561
+ # @api private
562
+ def convert_legacy_require_chef_omnibus_format!
563
+ convert_legacy_require_chef_omnibus_format_at!(data)
564
+ data.fetch(:platforms, []).each do |platform|
565
+ convert_legacy_require_chef_omnibus_format_at!(platform)
566
+ end
567
+ data.fetch(:suites, []).each do |suite|
568
+ convert_legacy_require_chef_omnibus_format_at!(suite)
569
+ end
570
+ end
571
+
572
+ # Destructively moves a `:require_chef_omnibus` key/value pair from a
573
+ # `:driver` hash block to a `:provisioner` hash block in the first leve
574
+ # depth of a hash. This method has no knowledge of suites, platforms, or
575
+ # the like, just a vanilla haash.
576
+ #
577
+ # @param root [Hash] a hash to use as the root of the conversion
578
+ # @deprecated The `:require_chef_omnibus` key/value pair should no longer
579
+ # be created inside a `:driver` hash block. Put it in a `:provisioner`
580
+ # hash block instead.
581
+ # @api private
582
+ def convert_legacy_require_chef_omnibus_format_at!(root)
583
+ key = :require_chef_omnibus
584
+ ddata = root.fetch(:driver, Hash.new)
585
+
586
+ if ddata.is_a?(Hash) && ddata.key?(key)
587
+ pdata = root.fetch(:provisioner, Hash.new)
588
+ pdata = { :name => pdata } if pdata.is_a?(String)
589
+ root[:provisioner] =
590
+ { key => root.fetch(:driver).delete(key) }.rmerge(pdata)
591
+ end
592
+ end
593
+
594
+ # Performs a prioritized recursive merge of several source Hashes and
595
+ # returns a new merged Hash. For these data sub-hash structures, there are
596
+ # 4 sources for configuration data:
597
+ #
598
+ # 1. defaults, provided by Test Kitchen code
599
+ # 2. user-provided in the common root-level of the incoming data hash
600
+ # 3. user-provided in a platform sub-hash
601
+ # 4. user-provided in a suite sub-hash
602
+ #
603
+ # The merge order is 4 -> 3 -> 2 -> 1, meaning that the highest number in
604
+ # the above list has merge precedence over any lower numbered source. Put
605
+ # another way, a key/value pair in a suite sub-hash will be used over the
606
+ # key/value pair in a platform sub-hash.
607
+ #
608
+ # @param key [Symbol] the data sub-hash(es) to merge
609
+ # @param suite [String] a suite name
610
+ # @param platform [String] a platform name
611
+ # @param default_key [Symbol] the default key to use when normalizing the
612
+ # data sub-hashes (default: `:name`)
613
+ # @return [Hash] a new merged Hash
614
+ # @api private
615
+ def merged_data_for(key, suite, platform, default_key = :name)
616
+ ddata = normalized_default_data(key, default_key, suite, platform)
617
+ cdata = normalized_common_data(key, default_key)
618
+ pdata = normalized_platform_data(key, default_key, platform)
619
+ sdata = normalized_suite_data(key, default_key, suite)
620
+
621
+ ddata.rmerge(cdata.rmerge(pdata.rmerge(sdata)))
622
+ end
623
+
624
+ # Destructively moves key Chef configuration key/value pairs from being
625
+ # directly under a suite or platform into a `:provisioner` sub-hash.
626
+ #
627
+ # There are two key Chef configuration key/value pairs:
628
+ #
629
+ # 1. `:attributes`
630
+ # 2. `:run_list`
631
+ #
632
+ # This method converts the following:
633
+ #
634
+ # {
635
+ # :platforms => [
636
+ # {
637
+ # :name => "ubuntu-12.04",
638
+ # :attributes => { :one => "two" },
639
+ # :run_list => ["alpha", "bravo"]
640
+ # }
641
+ # ],
642
+ #
643
+ # :suites => [
644
+ # {
645
+ # :name => "alpha",
646
+ # :attributes => { :three => "four" },
647
+ # :run_list => ["charlie", "delta"]
648
+ # }
649
+ # ]
650
+ # }
651
+ #
652
+ # into the following:
653
+ #
654
+ # {
655
+ # :platforms => [
656
+ # {
657
+ # :name => "ubuntu-12.04",
658
+ # :provisioner => {
659
+ # :attributes => { :one => "two" },
660
+ # :run_list => ["alpha", "bravo"]
661
+ # }
662
+ # }
663
+ # ],
664
+ #
665
+ # :suites => [
666
+ # {
667
+ # :name => "alpha",
668
+ # :provisioner => {
669
+ # :attributes => { :three => "four" },
670
+ # :run_list => ["charlie", "delta"]
671
+ # }
672
+ # }
673
+ # ]
674
+ # }
675
+ #
676
+ # @api private
677
+ def move_chef_data_to_provisioner!
678
+ data.fetch(:suites, []).each do |suite|
679
+ move_chef_data_to_provisioner_at!(suite, :attributes)
680
+ move_chef_data_to_provisioner_at!(suite, :run_list)
681
+ end
682
+
683
+ data.fetch(:platforms, []).each do |platform|
684
+ move_chef_data_to_provisioner_at!(platform, :attributes)
685
+ move_chef_data_to_provisioner_at!(platform, :run_list)
686
+ end
687
+ end
688
+
689
+ # Destructively moves key Chef configuration key/value pairs from being
690
+ # directly under a hash into a `:provisioner` sub-hash block. This method
691
+ # has no knowledge of suites, platforms, or the like, just a vanilla hash.
692
+ #
693
+ # @param root [Hash] a hash to use as the root of the conversion
694
+ # @param key [Symbol] a key in the root hash to move into a `:provisioner`
695
+ # sub-hash block
696
+ # @api private
697
+ def move_chef_data_to_provisioner_at!(root, key)
698
+ if root.key?(key)
699
+ pdata = root.fetch(:provisioner, Hash.new)
700
+ pdata = { :name => pdata } if pdata.is_a?(String)
701
+ if !root.fetch(key, nil).nil?
702
+ root[:provisioner] = pdata.rmerge(key => root.delete(key))
703
+ end
704
+ end
705
+ end
706
+
707
+ # Vicious hack to allow for Array-appending merge semantics. This method
708
+ # takes an array value and transforms it into a hash with a bucket name
709
+ # containing the original Array. This way semantic Hash merging will do
710
+ # its thing and another process can collapse the hash into a flat array
711
+ # afterwards, given a strategy (like use the array segmenet from one
712
+ # bucket first, then another one second). To anyone who made it this far,
713
+ # Fletcher appologizes.
714
+ #
715
+ # @param root [Hash] a hash to use as the root of the conversion
716
+ # @param key [Symbol] a key in the root hash that, if exists, has its
717
+ # value transformed into a sub-hash
718
+ # @param bucket [Symbol] a key to use for the sub-hash
719
+ # @api private
720
+ def namespace_array!(root, key, bucket)
721
+ root[key] = { bucket => root.fetch(key) } if root.key?(key)
722
+ end
723
+
724
+ # Normalizes a specific key in the root of the data hash to be a proper
725
+ # sub-hash in all cases. Specifically handled are the following cases:
726
+ #
727
+ # * If the value for a key is set to `nil`, a new Hash will be put in
728
+ # its place.
729
+ # * If the value is a String, then convert the value to a new Hash with
730
+ # a default key pointing to the original String
731
+ #
732
+ # Given a hash:
733
+ #
734
+ # { :driver => nil }
735
+ #
736
+ # this method (`normalized_common_data(:driver, :name)`) would return:
737
+ #
738
+ # {}
739
+ #
740
+ # Given a hash:
741
+ #
742
+ # { :driver => "coolbeans" }
743
+ #
744
+ # this method (`normalized_common_data(:driver, :name)`) would return:
745
+ #
746
+ # { :name => "coolbeans" }
747
+ #
748
+ # @param key [Symbol] the value to normalize
749
+ # @param default_key [Symbol] the implicit default key if a String value
750
+ # is given
751
+ # @return [Hash] a shallow Hash copy of the original if not modified, or a
752
+ # new Hash otherwise
753
+ # @api private
754
+ def normalized_common_data(key, default_key)
755
+ cdata = data.fetch(key, Hash.new)
756
+ cdata = cdata.nil? ? Hash.new : cdata.dup
757
+ cdata = { default_key => cdata } if cdata.is_a?(String)
758
+ cdata
759
+ end
760
+
761
+ # Normalizes a specific key in the `:defaults` data sub-hash to be a proper
762
+ # sub-hash in all cases. Specifically handled are the following cases:
763
+ #
764
+ # * If the value for a key is not set, a new Hash will be put in its place
765
+ # * If the value is a String, then convert the value to a new Hash with
766
+ # a default key pointing to the original String
767
+ #
768
+ # Given a hash:
769
+ #
770
+ # {
771
+ # :defaults => {}
772
+ # }
773
+ #
774
+ # this method (`normalized_default_data(:driver, :name)`) would return:
775
+ #
776
+ # {}
777
+ #
778
+ # Given a hash:
779
+ #
780
+ # {
781
+ # :defaults => {
782
+ # :driver => "coolbeans"
783
+ # }
784
+ # }
785
+ #
786
+ # this method (`normalized_default_data(:driver, :name)`) would return:
787
+ #
788
+ # { :name => "coolbeans" }
789
+ #
790
+ # @param key [Symbol] the value to normalize
791
+ # @param default_key [Symbol] the implicit default key if a String value
792
+ # is given
793
+ # @param suite [String] name of a suite
794
+ # @param platform [String] name of a platform
795
+ # @return [Hash] a shallow Hash copy of the original if not modified, or a
796
+ # new Hash otherwise
797
+ # @api private
798
+ def normalized_default_data(key, default_key, suite, platform)
799
+ ddata = kitchen_config.fetch(:defaults, Hash.new).fetch(key, Hash.new).dup
800
+ ddata = { default_key => ddata.call(suite, platform) } if ddata.is_a?(Proc)
801
+ ddata = { default_key => ddata } if ddata.is_a?(String)
802
+ ddata
803
+ end
804
+
805
+ # Normalizes a specific key in a platform hash data sub-hash to be a proper
806
+ # sub-hash in all cases. Specifically handled are the following cases:
807
+ #
808
+ # * If the value for a key is set to `nil`, a new Hash will be put in
809
+ # its place.
810
+ # * If the value is a String, then convert the value to a new Hash with
811
+ # a default key pointing to the original String
812
+ #
813
+ # Given a hash:
814
+ #
815
+ # {
816
+ # :platforms => [
817
+ # {
818
+ # :name => "alpha",
819
+ # :driver => nil
820
+ # }
821
+ # ]
822
+ # }
823
+ #
824
+ # this method (`normalized_platform_data(:driver, :name, "alpha)`) would
825
+ # return:
826
+ #
827
+ # {}
828
+ #
829
+ # Given a hash:
830
+ #
831
+ # {
832
+ # :platforms => [
833
+ # {
834
+ # :name => "alpha",
835
+ # :driver => "coolbeans"
836
+ # }
837
+ # ]
838
+ # }
839
+ #
840
+ # this method (`normalized_common_data(:driver, :name, "alpha")`) would
841
+ # return:
842
+ #
843
+ # { :name => "coolbeans" }
844
+ #
845
+ # @param key [Symbol] the value to normalize
846
+ # @param default_key [Symbol] the implicit default key if a String value
847
+ # is given
848
+ # @param platform [String] name of a platform
849
+ # @return [Hash] a shallow Hash copy of the original if not modified, or a
850
+ # new Hash otherwise
851
+ # @api private
852
+ def normalized_platform_data(key, default_key, platform)
853
+ pdata = platform_data_for(platform).fetch(key, Hash.new)
854
+ pdata = pdata.nil? ? Hash.new : pdata.dup
855
+ pdata = { default_key => pdata } if pdata.is_a?(String)
856
+ namespace_array!(pdata, :run_list, :platform)
857
+ pdata
858
+ end
859
+
860
+ # Normalizes a specific key in a suite hash data sub-hash to be a proper
861
+ # sub-hash in all cases. Specifically handled are the following cases:
862
+ #
863
+ # * If the value for a key is set to `nil`, a new Hash will be put in
864
+ # its place.
865
+ # * If the value is a String, then convert the value to a new Hash with
866
+ # a default key pointing to the original String
867
+ #
868
+ # Given a hash:
869
+ #
870
+ # {
871
+ # :suites => [
872
+ # {
873
+ # :name => "full",
874
+ # :driver => nil
875
+ # }
876
+ # ]
877
+ # }
878
+ #
879
+ # this method (`normalized_platform_data(:driver, :name, "full)`) would
880
+ # return:
881
+ #
882
+ # {}
883
+ #
884
+ # Given a hash:
885
+ #
886
+ # {
887
+ # :suites => [
888
+ # {
889
+ # :name => "full",
890
+ # :driver => "coolbeans"
891
+ # }
892
+ # ]
893
+ # }
894
+ #
895
+ # this method (`normalized_common_data(:driver, :name, "full")`) would
896
+ # return:
897
+ #
898
+ # { :name => "coolbeans" }
899
+ #
900
+ # @param key [Symbol] the value to normalize
901
+ # @param default_key [Symbol] the implicit default key if a String value
902
+ # is given
903
+ # @param suite [String] name of a suite
904
+ # @return [Hash] a shallow Hash copy of the original if not modified, or a
905
+ # new Hash otherwise
906
+ # @api private
907
+ def normalized_suite_data(key, default_key, suite)
908
+ sdata = suite_data_for(suite).fetch(key, Hash.new)
909
+ sdata = sdata.nil? ? Hash.new : sdata.dup
910
+ sdata = { default_key => sdata } if sdata.is_a?(String)
911
+ namespace_array!(sdata, :run_list, :suite)
912
+ sdata
913
+ end
914
+
915
+ # Returns the hash for a platform by name, or an empty Hash if none
916
+ # could be found.
917
+ #
918
+ # @param name [String] name of a platform
919
+ # @return [Hash] the configuration hash for the platform, or an empty
920
+ # Hash if not found
921
+ # @api private
922
+ def platform_data_for(name)
923
+ data.fetch(:platforms, Hash.new).find(-> { Hash.new }) do |platform|
924
+ platform.fetch(:name, nil) == name
925
+ end
926
+ end
927
+
928
+ # Destructively sets a base kitchen config key/value pair at the root of
929
+ # the given hash. If the key is present in the given Hash, it is deleted
930
+ # and will not be used. If the key is found in the `kitchen_config` hash
931
+ # (default values), then its value will be used and set. Finally, if
932
+ # the key is found in `:kitchen` data sub-hash, then its value will be used
933
+ # and set.
934
+ #
935
+ # @param root [Hash] a hash to use as the root of the conversion
936
+ # @param key [Symbol] the key to search for
937
+ # @api private
938
+ def set_kitchen_config_at!(root, key)
939
+ kdata = data.fetch(:kitchen, Hash.new)
940
+
941
+ root.delete(key) if root.key?(key)
942
+ root[key] = kitchen_config.fetch(key) if kitchen_config.key?(key)
943
+ root[key] = kdata.fetch(key) if kdata.key?(key)
944
+ end
945
+
946
+ # Returns the hash for a suite by name, or an empty Hash if none
947
+ # could be found.
948
+ #
949
+ # @param name [String] name of a suite
950
+ # @return [Hash] the configuration hash for the suite, or an empty
951
+ # Hash if not found
952
+ # @api private
953
+ def suite_data_for(name)
954
+ data.fetch(:suites, Hash.new).find(-> { Hash.new }) do |suite|
955
+ suite.fetch(:name, nil) == name
956
+ end
957
+ end
958
+ end
959
+ end