test-kitchen 1.0.0.beta.4 → 1.0.0.rc.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -1
  3. data/Gemfile +1 -1
  4. data/README.md +18 -7
  5. data/Rakefile +8 -1
  6. data/features/kitchen_init_command.feature +90 -11
  7. data/features/step_definitions/git_steps.rb +3 -0
  8. data/lib/kitchen/busser.rb +79 -45
  9. data/lib/kitchen/cli.rb +14 -13
  10. data/lib/kitchen/config.rb +79 -138
  11. data/lib/kitchen/data_munger.rb +224 -0
  12. data/lib/kitchen/driver/base.rb +4 -31
  13. data/lib/kitchen/driver/ssh_base.rb +6 -16
  14. data/lib/kitchen/driver.rb +4 -0
  15. data/lib/kitchen/generator/init.rb +20 -9
  16. data/lib/kitchen/instance.rb +53 -58
  17. data/lib/kitchen/lazy_hash.rb +50 -0
  18. data/lib/kitchen/platform.rb +2 -31
  19. data/lib/kitchen/provisioner/base.rb +55 -9
  20. data/lib/kitchen/provisioner/chef/berkshelf.rb +76 -0
  21. data/lib/kitchen/provisioner/chef/librarian.rb +72 -0
  22. data/lib/kitchen/provisioner/chef_base.rb +159 -78
  23. data/lib/kitchen/provisioner/chef_solo.rb +6 -36
  24. data/lib/kitchen/provisioner/chef_zero.rb +70 -59
  25. data/lib/kitchen/provisioner/dummy.rb +28 -0
  26. data/lib/kitchen/provisioner.rb +6 -4
  27. data/lib/kitchen/shell_out.rb +2 -5
  28. data/lib/kitchen/ssh.rb +1 -1
  29. data/lib/kitchen/suite.rb +10 -79
  30. data/lib/kitchen/util.rb +2 -2
  31. data/lib/kitchen/version.rb +2 -2
  32. data/lib/kitchen.rb +5 -0
  33. data/spec/kitchen/config_spec.rb +84 -123
  34. data/spec/kitchen/data_munger_spec.rb +1412 -0
  35. data/spec/kitchen/driver/base_spec.rb +30 -0
  36. data/spec/kitchen/instance_spec.rb +868 -86
  37. data/spec/kitchen/lazy_hash_spec.rb +63 -0
  38. data/spec/kitchen/platform_spec.rb +0 -22
  39. data/spec/kitchen/provisioner/base_spec.rb +210 -0
  40. data/spec/kitchen/provisioner_spec.rb +70 -0
  41. data/spec/kitchen/suite_spec.rb +25 -38
  42. data/spec/spec_helper.rb +1 -0
  43. data/support/chef-client-zero.rb +51 -35
  44. data/support/dummy-validation.pem +27 -0
  45. data/templates/init/kitchen.yml.erb +10 -22
  46. data/test-kitchen.gemspec +1 -2
  47. metadata +20 -18
data/lib/kitchen/cli.rb CHANGED
@@ -43,7 +43,7 @@ module Kitchen
43
43
  Kitchen.logger = Kitchen.default_file_logger
44
44
  @config = Kitchen::Config.new(
45
45
  :loader => Kitchen::Loader::YAML.new(ENV['KITCHEN_YAML']),
46
- :log_level => ENV['KITCHEN_LOG'] && ENV['KITCHEN_LOG'].downcase.to_sym
46
+ :log_level => ENV.fetch('KITCHEN_LOG', "info").downcase.to_sym
47
47
  )
48
48
  end
49
49
 
@@ -52,7 +52,10 @@ module Kitchen
52
52
  :desc => "List the name of each instance only, one per line"
53
53
  method_option :debug, :aliases => "-d", :type => :boolean,
54
54
  :desc => "Show computed driver configuration for each instance"
55
+ method_option :log_level, :aliases => "-l",
56
+ :desc => "Set the log level (debug, info, warn, error, fatal)"
55
57
  def list(*args)
58
+ update_config!
56
59
  result = parse_subcommand(args.first)
57
60
  if options[:debug]
58
61
  Array(result).each { |i| debug_instance(i) }
@@ -327,7 +330,7 @@ module Kitchen
327
330
  [
328
331
  color_pad(instance.name),
329
332
  color_pad(instance.driver.name),
330
- color_pad(format_provisioner(instance.driver[:provisioner])),
333
+ color_pad(instance.provisioner.name),
331
334
  format_last_action(instance.last_action)
332
335
  ]
333
336
  end
@@ -335,15 +338,17 @@ module Kitchen
335
338
  def debug_instance(instance)
336
339
  say "--------"
337
340
  say "Instance: #{instance.name}"
338
- say "Driver: #{instance.driver.name}"
339
- say "Driver Config:"
341
+ say "Driver (#{instance.driver.name}):"
340
342
  instance.driver.config_keys.sort.each do |key|
341
- say " #{key}: #{instance.driver[key]}"
343
+ say " #{key}: #{instance.driver[key].inspect}"
342
344
  end
343
- if instance.kind_of?(Instance::Cheflike)
344
- say "Chef Config:"
345
- say " attributes: #{instance.attributes.inspect}"
346
- say " run_list: #{instance.run_list.inspect}"
345
+ say "Provisioner (#{instance.provisioner.name}):"
346
+ instance.provisioner.config_keys.sort.each do |key|
347
+ say " #{key}: #{instance.provisioner[key].inspect}"
348
+ end
349
+ say "Busser (#{instance.busser.name}):"
350
+ instance.busser.config_keys.sort.each do |key|
351
+ say " #{key}: #{instance.busser[key].inspect}"
347
352
  end
348
353
  say ""
349
354
  end
@@ -363,10 +368,6 @@ module Kitchen
363
368
  end
364
369
  end
365
370
 
366
- def format_provisioner(name)
367
- name.split('_').map { |word| word.capitalize }.join(' ')
368
- end
369
-
370
371
  def update_config!
371
372
  if options[:log_level]
372
373
  level = options[:log_level].downcase.to_sym
@@ -16,8 +16,6 @@
16
16
  # See the License for the specific language governing permissions and
17
17
  # limitations under the License.
18
18
 
19
- require 'vendor/hash_recursive_merge'
20
-
21
19
  module Kitchen
22
20
 
23
21
  # Base configuration class for Kitchen. This class exposes configuration such
@@ -26,193 +24,136 @@ module Kitchen
26
24
  # @author Fletcher Nichol <fnichol@nichol.ca>
27
25
  class Config
28
26
 
29
- attr_accessor :kitchen_root
30
- attr_accessor :test_base_path
27
+ attr_reader :kitchen_root
28
+ attr_reader :log_root
29
+ attr_reader :test_base_path
30
+ attr_reader :loader
31
31
  attr_accessor :log_level
32
- attr_writer :platforms
33
- attr_writer :suites
34
-
35
- # Default driver plugin to use
36
- DEFAULT_DRIVER_PLUGIN = "dummy".freeze
37
-
38
- # Default provisioner to use
39
- DEFAULT_PROVISIONER = "chef_solo".freeze
40
32
 
41
33
  # Creates a new configuration.
42
34
  #
43
35
  # @param [Hash] options configuration
44
36
  # @option options [#read] :loader
45
37
  # @option options [String] :kitchen_root
38
+ # @option options [String] :log_root
46
39
  # @option options [String] :test_base_path
47
40
  # @option options [Symbol] :log_level
48
41
  def initialize(options = {})
49
- @loader = options[:loader] || Kitchen::Loader::YAML.new
50
- @kitchen_root = options[:kitchen_root] || Dir.pwd
51
- @test_base_path = options[:test_base_path] || default_test_base_path
52
- @log_level = options[:log_level] || Kitchen::DEFAULT_LOG_LEVEL
42
+ @loader = options.fetch(:loader) { Kitchen::Loader::YAML.new }
43
+ @kitchen_root = options.fetch(:kitchen_root) { Dir.pwd }
44
+ @log_level = options.fetch(:log_level) { Kitchen::DEFAULT_LOG_LEVEL }
45
+ @log_root = options.fetch(:log_root) { default_log_root }
46
+ @test_base_path = options.fetch(:test_base_path) { default_test_base_path }
47
+ end
48
+
49
+ # @return [Array<Instance>] all instances, resulting from all platform and
50
+ # suite combinations
51
+ def instances
52
+ @instances ||= Collection.new(build_instances)
53
53
  end
54
54
 
55
55
  # @return [Array<Platform>] all defined platforms which will be used in
56
56
  # convergence integration
57
57
  def platforms
58
58
  @platforms ||= Collection.new(
59
- Array(data[:platforms]).map { |hash| new_platform(hash) })
59
+ data.platform_data.map { |pdata| Platform.new(pdata) })
60
60
  end
61
61
 
62
62
  # @return [Array<Suite>] all defined suites which will be used in
63
63
  # convergence integration
64
64
  def suites
65
65
  @suites ||= Collection.new(
66
- Array(data[:suites]).map { |hash| new_suite(hash) })
67
- end
68
-
69
- # @return [Array<Instance>] all instances, resulting from all platform and
70
- # suite combinations
71
- def instances
72
- @instances ||= build_instances
66
+ data.suite_data.map { |sdata| Suite.new(sdata) })
73
67
  end
74
68
 
75
69
  private
76
70
 
77
- def new_suite(hash)
78
- path_hash = {
79
- :data_bags_path => calculate_path("data_bags", hash[:name], hash[:data_bags_path]),
80
- :roles_path => calculate_path("roles", hash[:name], hash[:roles_path]),
81
- :nodes_path => calculate_path("nodes", hash[:name], hash[:nodes_path]),
82
- }
83
-
84
- Suite.new(hash.rmerge(path_hash))
85
- end
86
-
87
- def new_platform(hash)
88
- Platform.new(hash)
89
- end
90
-
91
- def new_driver(hash)
92
- hash[:driver_config] ||= Hash.new
93
- hash[:driver_config][:kitchen_root] = kitchen_root
94
- hash[:driver_config][:provisioner] = hash[:provisioner]
95
-
96
- Driver.for_plugin(hash[:driver_plugin], hash[:driver_config])
97
- end
98
-
99
71
  def build_instances
100
- results = []
101
- filtered_instances = suites.product(platforms).delete_if do |arr|
102
- arr[0].excludes.include?(arr[1].name)
103
- end
104
- filtered_instances.each_with_index do |arr, index|
105
- results << new_instance(arr[0], arr[1], index)
72
+ filter_instances.map.with_index do |(suite, platform), index|
73
+ new_instance(suite, platform, index)
106
74
  end
107
- Collection.new(results)
108
- end
109
-
110
- def new_instance(suite, platform, index)
111
- platform_hash = platform_driver_hash(platform.name)
112
- platform_hash[:driver_config].rmerge!(suite.driver_config)
113
- driver = new_driver(merge_driver_hash(platform_hash))
114
- provisioner = driver[:provisioner]
115
-
116
- instance = Instance.new(
117
- :suite => extend_suite(suite, provisioner),
118
- :platform => extend_platform(platform, provisioner),
119
- :driver => driver,
120
- :logger => new_instance_logger(index)
121
- )
122
- extend_instance(instance, provisioner)
123
75
  end
124
76
 
125
- def extend_suite(suite, provisioner)
126
- case provisioner.to_s.downcase
127
- when /^chef_/ then suite.dup.extend(Suite::Cheflike)
128
- when /^puppet_/ then suite.dup.extend(Suite::Puppetlike)
129
- else suite.dup
130
- end
77
+ def data
78
+ @data ||= DataMunger.new(loader.read, kitchen_config)
131
79
  end
132
80
 
133
- def extend_platform(platform, provisioner)
134
- case provisioner.to_s.downcase
135
- when /^chef_/ then platform.dup.extend(Platform::Cheflike)
136
- else platform.dup
137
- end
81
+ def default_log_root
82
+ File.join(kitchen_root, Kitchen::DEFAULT_LOG_DIR)
138
83
  end
139
84
 
140
- def extend_instance(instance, provisioner)
141
- case provisioner.to_s.downcase
142
- when /^chef_/ then instance.extend(Instance::Cheflike)
143
- when /^puppet_/ then instance.extend(Instance::Puppetlike)
144
- else instance
85
+ def default_test_base_path
86
+ File.join(kitchen_root, Kitchen::DEFAULT_TEST_DIR)
87
+ end
88
+
89
+ def filter_instances
90
+ suites.product(platforms).select do |suite, platform|
91
+ if !suite.includes.empty?
92
+ suite.includes.include?(platform.name)
93
+ elsif !suite.excludes.empty?
94
+ !suite.excludes.include?(platform.name)
95
+ else
96
+ true
97
+ end
145
98
  end
146
99
  end
147
100
 
148
- def log_root
149
- File.expand_path(File.join(kitchen_root, ".kitchen", "logs"))
150
- end
151
-
152
- def platform_driver_hash(platform_name)
153
- h = data[:platforms].find { |p| p[:name] == platform_name } || Hash.new
154
- h[:driver_config] ||= {}
155
-
156
- h.select do |key, value|
157
- [:driver_plugin, :driver_config, :provisioner].include?(key)
158
- end
101
+ def instance_name(suite, platform)
102
+ Instance.name_for(suite, platform)
159
103
  end
160
104
 
161
- def new_instance_logger(index)
162
- level = Util.to_logger_level(self.log_level)
163
- color = Color::COLORS[index % Color::COLORS.size].to_sym
164
-
165
- lambda do |name|
166
- logfile = File.join(log_root, "#{name}.log")
167
-
168
- Logger.new(:stdout => STDOUT, :color => color, :logdev => logfile,
169
- :level => level, :progname => name)
170
- end
105
+ def kitchen_config
106
+ @kitchen_config ||= {
107
+ :defaults => {
108
+ :driver => Driver::DEFAULT_PLUGIN,
109
+ :provisioner => Provisioner::DEFAULT_PLUGIN
110
+ },
111
+ :kitchen_root => kitchen_root,
112
+ :test_base_path => test_base_path,
113
+ :log_level => log_level,
114
+ }
171
115
  end
172
116
 
173
- def data
174
- @data ||= @loader.read
117
+ def new_busser(suite, platform)
118
+ bdata = data.busser_data_for(suite.name, platform.name)
119
+ Busser.new(suite.name, bdata)
175
120
  end
176
121
 
177
- def merge_driver_hash(driver_hash)
178
- default_driver_hash.rmerge(common_driver_hash.rmerge(driver_hash))
122
+ def new_driver(suite, platform)
123
+ ddata = data.driver_data_for(suite.name, platform.name)
124
+ Driver.for_plugin(ddata[:name], ddata)
179
125
  end
180
126
 
181
- def calculate_path(path, suite_name, local_path)
182
- custom_path = File.join(kitchen_root, local_path) if local_path
183
- suite_path = File.join(test_base_path, suite_name, path)
184
- common_path = File.join(test_base_path, path)
185
- top_level_path = File.join(Dir.pwd, path)
186
-
187
- if custom_path and File.directory?(custom_path)
188
- custom_path
189
- elsif File.directory?(suite_path)
190
- suite_path
191
- elsif File.directory?(common_path)
192
- common_path
193
- elsif File.directory?(top_level_path)
194
- top_level_path
195
- else
196
- nil
197
- end
127
+ def new_instance(suite, platform, index)
128
+ Instance.new(
129
+ :busser => new_busser(suite, platform),
130
+ :driver => new_driver(suite, platform),
131
+ :logger => new_logger(suite, platform, index),
132
+ :suite => suite,
133
+ :platform => platform,
134
+ :provisioner => new_provisioner(suite, platform),
135
+ :state_file => new_state_file(suite, platform)
136
+ )
198
137
  end
199
138
 
200
- def default_driver_hash
201
- {
202
- :driver_plugin => DEFAULT_DRIVER_PLUGIN,
203
- :driver_config => {},
204
- :provisioner => DEFAULT_PROVISIONER
205
- }
139
+ def new_logger(suite, platform, index)
140
+ name = instance_name(suite, platform)
141
+ Logger.new(
142
+ :stdout => STDOUT,
143
+ :color => Color::COLORS[index % Color::COLORS.size].to_sym,
144
+ :logdev => File.join(log_root, "#{name}.log"),
145
+ :level => Util.to_logger_level(self.log_level),
146
+ :progname => name
147
+ )
206
148
  end
207
149
 
208
- def common_driver_hash
209
- data.select do |key, value|
210
- [:driver_plugin, :driver_config, :provisioner].include?(key)
211
- end
150
+ def new_provisioner(suite, platform)
151
+ pdata = data.provisioner_data_for(suite.name, platform.name)
152
+ Provisioner.for_plugin(pdata[:name], pdata)
212
153
  end
213
154
 
214
- def default_test_base_path
215
- File.join(kitchen_root, 'test/integration')
155
+ def new_state_file(suite, platform)
156
+ StateFile.new(kitchen_root, instance_name(suite, platform))
216
157
  end
217
158
  end
218
159
  end
@@ -0,0 +1,224 @@
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
+ # @author Fletcher Nichol <fnichol@nichol.ca>
30
+ class DataMunger
31
+
32
+ def initialize(data, kitchen_config = {})
33
+ @data = data
34
+ @kitchen_config = kitchen_config
35
+ convert_legacy_driver_format!
36
+ convert_legacy_chef_paths_format!
37
+ convert_legacy_require_chef_omnibus_format!
38
+ move_chef_data_to_provisioner!
39
+ end
40
+
41
+ def busser_data_for(suite, platform)
42
+ merged_data_for(:busser, suite, platform, :version).tap do |bdata|
43
+ set_kitchen_config_at!(bdata, :kitchen_root)
44
+ set_kitchen_config_at!(bdata, :test_base_path)
45
+ set_kitchen_config_at!(bdata, :log_level)
46
+ end
47
+ end
48
+
49
+ def driver_data_for(suite, platform)
50
+ merged_data_for(:driver, suite, platform).tap do |ddata|
51
+ set_kitchen_config_at!(ddata, :kitchen_root)
52
+ set_kitchen_config_at!(ddata, :test_base_path)
53
+ set_kitchen_config_at!(ddata, :log_level)
54
+ end
55
+ end
56
+
57
+ def platform_data
58
+ data.fetch(:platforms, [])
59
+ end
60
+
61
+ def provisioner_data_for(suite, platform)
62
+ merged_data_for(:provisioner, suite, platform).tap do |pdata|
63
+ set_kitchen_config_at!(pdata, :kitchen_root)
64
+ set_kitchen_config_at!(pdata, :test_base_path)
65
+ set_kitchen_config_at!(pdata, :log_level)
66
+ combine_arrays!(pdata, :run_list, :platform, :suite)
67
+ end
68
+ end
69
+
70
+ def suite_data
71
+ data.fetch(:suites, [])
72
+ end
73
+
74
+ private
75
+
76
+ attr_reader :data, :kitchen_config
77
+
78
+ def combine_arrays!(root, key, *namespaces)
79
+ if root.has_key?(key)
80
+ root[key] = namespaces.
81
+ map { |namespace| root.fetch(key).fetch(namespace, []) }.flatten.
82
+ compact
83
+ end
84
+ end
85
+
86
+ def convert_legacy_chef_paths_format!
87
+ data.fetch(:suites, []).each do |suite|
88
+ %w{data data_bags environments nodes roles}.each do |key|
89
+ move_chef_data_to_provisioner_at!(suite, "#{key}_path".to_sym)
90
+ end
91
+ end
92
+ end
93
+
94
+ def convert_legacy_driver_format!
95
+ convert_legacy_driver_format_at!(data)
96
+ data.fetch(:platforms, []).each do |platform|
97
+ convert_legacy_driver_format_at!(platform)
98
+ end
99
+ data.fetch(:suites, []).each do |suite|
100
+ convert_legacy_driver_format_at!(suite)
101
+ end
102
+ end
103
+
104
+ def convert_legacy_driver_format_at!(root)
105
+ if root.has_key?(:driver_config)
106
+ ddata = root.fetch(:driver, Hash.new)
107
+ ddata = { :name => ddata } if ddata.is_a?(String)
108
+ root[:driver] = root.delete(:driver_config).rmerge(ddata)
109
+ end
110
+
111
+ if root.has_key?(:driver_plugin)
112
+ ddata = root.fetch(:driver, Hash.new)
113
+ ddata = { :name => ddata } if ddata.is_a?(String)
114
+ root[:driver] = { :name => root.delete(:driver_plugin) }.rmerge(ddata)
115
+ end
116
+ end
117
+
118
+ def convert_legacy_require_chef_omnibus_format!
119
+ convert_legacy_require_chef_omnibus_format_at!(data)
120
+ data.fetch(:platforms, []).each do |platform|
121
+ convert_legacy_require_chef_omnibus_format_at!(platform)
122
+ end
123
+ data.fetch(:suites, []).each do |suite|
124
+ convert_legacy_require_chef_omnibus_format_at!(suite)
125
+ end
126
+ end
127
+
128
+ def convert_legacy_require_chef_omnibus_format_at!(root)
129
+ key = :require_chef_omnibus
130
+ ddata = root.fetch(:driver, Hash.new)
131
+
132
+ if ddata.is_a?(Hash) && ddata.has_key?(key)
133
+ pdata = root.fetch(:provisioner, Hash.new)
134
+ pdata = { :name => pdata } if pdata.is_a?(String)
135
+ root[:provisioner] =
136
+ { key => root.fetch(:driver).delete(key) }.rmerge(pdata)
137
+ end
138
+ end
139
+
140
+ def merged_data_for(key, suite, platform, default_key = :name)
141
+ ddata = normalized_default_data(key, default_key)
142
+ cdata = normalized_common_data(key, default_key)
143
+ pdata = normalized_platform_data(key, default_key, platform)
144
+ sdata = normalized_suite_data(key, default_key, suite)
145
+
146
+ ddata.rmerge(cdata.rmerge(pdata.rmerge(sdata)))
147
+ end
148
+
149
+ def move_chef_data_to_provisioner!
150
+ data.fetch(:suites, []).each do |suite|
151
+ move_chef_data_to_provisioner_at!(suite, :attributes)
152
+ move_chef_data_to_provisioner_at!(suite, :run_list)
153
+ end
154
+
155
+ data.fetch(:platforms, []).each do |platform|
156
+ move_chef_data_to_provisioner_at!(platform, :attributes)
157
+ move_chef_data_to_provisioner_at!(platform, :run_list)
158
+ end
159
+ end
160
+
161
+ def move_chef_data_to_provisioner_at!(root, key)
162
+ if root.has_key?(key)
163
+ pdata = root.fetch(:provisioner, Hash.new)
164
+ pdata = { :name => pdata } if pdata.is_a?(String)
165
+ if ! root.fetch(key, nil).nil?
166
+ root[:provisioner] = pdata.rmerge({ key => root.delete(key) })
167
+ end
168
+ end
169
+ end
170
+
171
+ def namespace_array!(root, key, bucket)
172
+ root[key] = { bucket => root.fetch(key) } if root.has_key?(key)
173
+ end
174
+
175
+ def normalized_common_data(key, default_key)
176
+ cdata = data.fetch(key, Hash.new)
177
+ cdata = cdata.nil? ? Hash.new : cdata.dup
178
+ cdata = { default_key => cdata } if cdata.is_a?(String)
179
+ cdata
180
+ end
181
+
182
+ def normalized_default_data(key, default_key)
183
+ ddata = kitchen_config.fetch(:defaults, Hash.new).fetch(key, Hash.new).dup
184
+ ddata = { default_key => ddata } if ddata.is_a?(String)
185
+ ddata
186
+ end
187
+
188
+ def normalized_platform_data(key, default_key, platform)
189
+ pdata = platform_data_for(platform).fetch(key, Hash.new)
190
+ pdata = pdata.nil? ? Hash.new : pdata.dup
191
+ pdata = { default_key => pdata } if pdata.is_a?(String)
192
+ namespace_array!(pdata, :run_list, :platform)
193
+ pdata
194
+ end
195
+
196
+ def normalized_suite_data(key, default_key, suite)
197
+ sdata = suite_data_for(suite).fetch(key, Hash.new)
198
+ sdata = sdata.nil? ? Hash.new : sdata.dup
199
+ sdata = { default_key => sdata } if sdata.is_a?(String)
200
+ namespace_array!(sdata, :run_list, :suite)
201
+ sdata
202
+ end
203
+
204
+ def platform_data_for(name)
205
+ data.fetch(:platforms, Hash.new).find(lambda { Hash.new }) do |platform|
206
+ platform.fetch(:name, nil) == name
207
+ end
208
+ end
209
+
210
+ def set_kitchen_config_at!(root, key)
211
+ kdata = data.fetch(:kitchen, Hash.new)
212
+
213
+ root.delete(key) if root.has_key?(key)
214
+ root[key] = kitchen_config.fetch(key) if kitchen_config.has_key?(key)
215
+ root[key] = kdata.fetch(key) if kdata.has_key?(key)
216
+ end
217
+
218
+ def suite_data_for(name)
219
+ data.fetch(:suites, Hash.new).find(lambda { Hash.new }) do |suite|
220
+ suite.fetch(:name, nil) == name
221
+ end
222
+ end
223
+ end
224
+ end