test-kitchen 1.0.0.beta.4 → 1.0.0.rc.1

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