test-kitchen 1.6.0 → 1.7.0

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