test-kitchen 1.7.0 → 1.7.1.dev

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 (181) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +8 -8
  3. data/.gitattributes +3 -0
  4. data/.github/ISSUE_TEMPLATE.md +55 -55
  5. data/.gitignore +28 -28
  6. data/.kitchen.ci.yml +23 -23
  7. data/.kitchen.proxy.yml +27 -27
  8. data/.rubocop.yml +3 -3
  9. data/.travis.yml +70 -70
  10. data/.yardopts +3 -3
  11. data/Berksfile +3 -3
  12. data/CHANGELOG.md +1090 -1083
  13. data/CONTRIBUTING.md +14 -14
  14. data/Gemfile +19 -19
  15. data/Gemfile.proxy_tests +4 -4
  16. data/Guardfile +42 -42
  17. data/LICENSE +15 -15
  18. data/MAINTAINERS.md +23 -23
  19. data/README.md +135 -135
  20. data/Rakefile +61 -61
  21. data/appveyor.yml +44 -44
  22. data/features/kitchen_action_commands.feature +164 -164
  23. data/features/kitchen_command.feature +16 -16
  24. data/features/kitchen_console_command.feature +34 -34
  25. data/features/kitchen_defaults.feature +38 -38
  26. data/features/kitchen_diagnose_command.feature +96 -96
  27. data/features/kitchen_driver_create_command.feature +64 -64
  28. data/features/kitchen_driver_discover_command.feature +25 -25
  29. data/features/kitchen_help_command.feature +16 -16
  30. data/features/kitchen_init_command.feature +274 -274
  31. data/features/kitchen_list_command.feature +104 -104
  32. data/features/kitchen_login_command.feature +62 -62
  33. data/features/kitchen_sink_command.feature +30 -30
  34. data/features/kitchen_test_command.feature +88 -88
  35. data/features/step_definitions/gem_steps.rb +36 -36
  36. data/features/step_definitions/git_steps.rb +5 -5
  37. data/features/step_definitions/output_steps.rb +5 -5
  38. data/features/support/env.rb +75 -75
  39. data/lib/kitchen.rb +150 -150
  40. data/lib/kitchen/base64_stream.rb +55 -55
  41. data/lib/kitchen/cli.rb +419 -419
  42. data/lib/kitchen/collection.rb +55 -55
  43. data/lib/kitchen/color.rb +65 -65
  44. data/lib/kitchen/command.rb +185 -185
  45. data/lib/kitchen/command/action.rb +45 -45
  46. data/lib/kitchen/command/console.rb +58 -58
  47. data/lib/kitchen/command/diagnose.rb +92 -92
  48. data/lib/kitchen/command/driver_discover.rb +105 -105
  49. data/lib/kitchen/command/exec.rb +41 -41
  50. data/lib/kitchen/command/list.rb +119 -119
  51. data/lib/kitchen/command/login.rb +43 -43
  52. data/lib/kitchen/command/sink.rb +54 -54
  53. data/lib/kitchen/command/test.rb +51 -51
  54. data/lib/kitchen/config.rb +322 -322
  55. data/lib/kitchen/configurable.rb +529 -529
  56. data/lib/kitchen/data_munger.rb +959 -959
  57. data/lib/kitchen/diagnostic.rb +141 -141
  58. data/lib/kitchen/driver.rb +56 -56
  59. data/lib/kitchen/driver/base.rb +134 -134
  60. data/lib/kitchen/driver/dummy.rb +108 -108
  61. data/lib/kitchen/driver/proxy.rb +72 -72
  62. data/lib/kitchen/driver/ssh_base.rb +357 -357
  63. data/lib/kitchen/errors.rb +229 -229
  64. data/lib/kitchen/generator/driver_create.rb +177 -177
  65. data/lib/kitchen/generator/init.rb +296 -296
  66. data/lib/kitchen/instance.rb +662 -662
  67. data/lib/kitchen/lazy_hash.rb +142 -142
  68. data/lib/kitchen/loader/yaml.rb +349 -349
  69. data/lib/kitchen/logger.rb +423 -423
  70. data/lib/kitchen/logging.rb +56 -56
  71. data/lib/kitchen/login_command.rb +52 -52
  72. data/lib/kitchen/metadata_chopper.rb +52 -52
  73. data/lib/kitchen/platform.rb +67 -67
  74. data/lib/kitchen/provisioner.rb +54 -54
  75. data/lib/kitchen/provisioner/base.rb +236 -236
  76. data/lib/kitchen/provisioner/chef/berkshelf.rb +114 -114
  77. data/lib/kitchen/provisioner/chef/common_sandbox.rb +322 -322
  78. data/lib/kitchen/provisioner/chef/librarian.rb +112 -112
  79. data/lib/kitchen/provisioner/chef_apply.rb +124 -124
  80. data/lib/kitchen/provisioner/chef_base.rb +341 -341
  81. data/lib/kitchen/provisioner/chef_solo.rb +88 -88
  82. data/lib/kitchen/provisioner/chef_zero.rb +245 -245
  83. data/lib/kitchen/provisioner/dummy.rb +79 -79
  84. data/lib/kitchen/provisioner/shell.rb +138 -138
  85. data/lib/kitchen/rake_tasks.rb +63 -63
  86. data/lib/kitchen/shell_out.rb +93 -93
  87. data/lib/kitchen/ssh.rb +276 -276
  88. data/lib/kitchen/state_file.rb +120 -120
  89. data/lib/kitchen/suite.rb +51 -51
  90. data/lib/kitchen/thor_tasks.rb +66 -66
  91. data/lib/kitchen/transport.rb +54 -54
  92. data/lib/kitchen/transport/base.rb +176 -176
  93. data/lib/kitchen/transport/dummy.rb +79 -79
  94. data/lib/kitchen/transport/ssh.rb +364 -364
  95. data/lib/kitchen/transport/winrm.rb +486 -486
  96. data/lib/kitchen/util.rb +147 -147
  97. data/lib/kitchen/verifier.rb +55 -55
  98. data/lib/kitchen/verifier/base.rb +235 -235
  99. data/lib/kitchen/verifier/busser.rb +277 -277
  100. data/lib/kitchen/verifier/dummy.rb +79 -79
  101. data/lib/kitchen/verifier/shell.rb +101 -101
  102. data/lib/kitchen/version.rb +21 -21
  103. data/lib/vendor/hash_recursive_merge.rb +82 -82
  104. data/spec/kitchen/base64_stream_spec.rb +77 -77
  105. data/spec/kitchen/cli_spec.rb +56 -56
  106. data/spec/kitchen/collection_spec.rb +80 -80
  107. data/spec/kitchen/color_spec.rb +54 -54
  108. data/spec/kitchen/config_spec.rb +408 -408
  109. data/spec/kitchen/configurable_spec.rb +1095 -1095
  110. data/spec/kitchen/data_munger_spec.rb +2694 -2694
  111. data/spec/kitchen/diagnostic_spec.rb +129 -129
  112. data/spec/kitchen/driver/base_spec.rb +121 -121
  113. data/spec/kitchen/driver/dummy_spec.rb +199 -199
  114. data/spec/kitchen/driver/proxy_spec.rb +138 -138
  115. data/spec/kitchen/driver/ssh_base_spec.rb +1115 -1115
  116. data/spec/kitchen/driver_spec.rb +112 -112
  117. data/spec/kitchen/errors_spec.rb +309 -309
  118. data/spec/kitchen/instance_spec.rb +1419 -1419
  119. data/spec/kitchen/lazy_hash_spec.rb +117 -117
  120. data/spec/kitchen/loader/yaml_spec.rb +774 -774
  121. data/spec/kitchen/logger_spec.rb +429 -429
  122. data/spec/kitchen/logging_spec.rb +59 -59
  123. data/spec/kitchen/login_command_spec.rb +68 -68
  124. data/spec/kitchen/metadata_chopper_spec.rb +82 -82
  125. data/spec/kitchen/platform_spec.rb +89 -89
  126. data/spec/kitchen/provisioner/base_spec.rb +386 -386
  127. data/spec/kitchen/provisioner/chef_apply_spec.rb +136 -136
  128. data/spec/kitchen/provisioner/chef_base_spec.rb +1161 -1161
  129. data/spec/kitchen/provisioner/chef_solo_spec.rb +557 -557
  130. data/spec/kitchen/provisioner/chef_zero_spec.rb +1001 -1001
  131. data/spec/kitchen/provisioner/dummy_spec.rb +99 -99
  132. data/spec/kitchen/provisioner/shell_spec.rb +566 -566
  133. data/spec/kitchen/provisioner_spec.rb +107 -107
  134. data/spec/kitchen/shell_out_spec.rb +150 -150
  135. data/spec/kitchen/ssh_spec.rb +693 -693
  136. data/spec/kitchen/state_file_spec.rb +129 -129
  137. data/spec/kitchen/suite_spec.rb +62 -62
  138. data/spec/kitchen/transport/base_spec.rb +89 -89
  139. data/spec/kitchen/transport/ssh_spec.rb +1255 -1255
  140. data/spec/kitchen/transport/winrm_spec.rb +1143 -1143
  141. data/spec/kitchen/transport_spec.rb +112 -112
  142. data/spec/kitchen/util_spec.rb +165 -165
  143. data/spec/kitchen/verifier/base_spec.rb +362 -362
  144. data/spec/kitchen/verifier/busser_spec.rb +610 -610
  145. data/spec/kitchen/verifier/dummy_spec.rb +99 -99
  146. data/spec/kitchen/verifier/shell_spec.rb +160 -160
  147. data/spec/kitchen/verifier_spec.rb +120 -120
  148. data/spec/kitchen_spec.rb +114 -114
  149. data/spec/spec_helper.rb +85 -85
  150. data/spec/support/powershell_max_size_spec.rb +40 -40
  151. data/support/busser_install_command.ps1 +14 -14
  152. data/support/busser_install_command.sh +14 -14
  153. data/support/chef-client-zero.rb +77 -77
  154. data/support/chef_base_init_command.ps1 +18 -18
  155. data/support/chef_base_init_command.sh +2 -2
  156. data/support/chef_base_install_command.ps1 +85 -85
  157. data/support/chef_base_install_command.sh +229 -229
  158. data/support/chef_zero_prepare_command_legacy.ps1 +9 -9
  159. data/support/chef_zero_prepare_command_legacy.sh +10 -10
  160. data/support/download_helpers.sh +109 -109
  161. data/support/dummy-validation.pem +27 -27
  162. data/templates/driver/CHANGELOG.md.erb +3 -3
  163. data/templates/driver/Gemfile.erb +3 -3
  164. data/templates/driver/README.md.erb +64 -64
  165. data/templates/driver/Rakefile.erb +21 -21
  166. data/templates/driver/driver.rb.erb +23 -23
  167. data/templates/driver/gemspec.erb +29 -29
  168. data/templates/driver/gitignore.erb +17 -17
  169. data/templates/driver/license_apachev2.erb +15 -15
  170. data/templates/driver/license_lgplv3.erb +16 -16
  171. data/templates/driver/license_mit.erb +22 -22
  172. data/templates/driver/license_reserved.erb +5 -5
  173. data/templates/driver/tailor.erb +4 -4
  174. data/templates/driver/travis.yml.erb +11 -11
  175. data/templates/driver/version.rb.erb +12 -12
  176. data/templates/init/chefignore.erb +1 -1
  177. data/templates/init/kitchen.yml.erb +18 -18
  178. data/test-kitchen.gemspec +62 -62
  179. data/test/integration/default/default_spec.rb +3 -3
  180. data/testing_windows.md +37 -37
  181. metadata +5 -4
@@ -1,959 +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
- 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
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