test-kitchen 1.7.0 → 1.7.1.dev

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