test-kitchen-rsync 3.0.0.pre.1

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