chef-dk 0.3.5 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +4 -4
  3. data/README.md +4 -4
  4. data/lib/chef-dk/builtin_commands.rb +4 -0
  5. data/lib/chef-dk/chef_runner.rb +7 -1
  6. data/lib/chef-dk/command/exec.rb +9 -0
  7. data/lib/chef-dk/command/export.rb +132 -0
  8. data/lib/chef-dk/command/generator_commands.rb +1 -1
  9. data/lib/chef-dk/command/generator_commands/app.rb +8 -0
  10. data/lib/chef-dk/command/generator_commands/base.rb +46 -4
  11. data/lib/chef-dk/command/generator_commands/cookbook.rb +8 -0
  12. data/lib/chef-dk/command/generator_commands/cookbook_code_file.rb +1 -0
  13. data/lib/chef-dk/command/push.rb +3 -6
  14. data/lib/chef-dk/command/shell_init.rb +28 -5
  15. data/lib/chef-dk/command/update.rb +106 -0
  16. data/lib/chef-dk/command/verify.rb +72 -0
  17. data/lib/chef-dk/component_test.rb +12 -1
  18. data/lib/chef-dk/configurable.rb +52 -0
  19. data/lib/chef-dk/cookbook_metadata.rb +10 -1
  20. data/lib/chef-dk/cookbook_profiler/git.rb +1 -1
  21. data/lib/chef-dk/exceptions.rb +17 -2
  22. data/lib/chef-dk/helpers.rb +2 -2
  23. data/lib/chef-dk/policyfile/community_cookbook_source.rb +1 -1
  24. data/lib/chef-dk/policyfile/dsl.rb +7 -0
  25. data/lib/chef-dk/policyfile/uploader.rb +25 -4
  26. data/lib/chef-dk/policyfile_compiler.rb +21 -1
  27. data/lib/chef-dk/policyfile_lock.rb +5 -0
  28. data/lib/chef-dk/policyfile_services/export_repo.rb +194 -0
  29. data/lib/chef-dk/policyfile_services/install.rb +8 -2
  30. data/lib/chef-dk/policyfile_services/push.rb +4 -1
  31. data/lib/chef-dk/service_exceptions.rb +6 -0
  32. data/lib/chef-dk/skeletons/code_generator/files/default/Berksfile +1 -1
  33. data/lib/chef-dk/skeletons/code_generator/files/default/repo/README.md +1 -1
  34. data/lib/chef-dk/skeletons/code_generator/files/default/repo/cookbooks/README.md +1 -1
  35. data/lib/chef-dk/skeletons/code_generator/files/default/repo/environments/README.md +2 -2
  36. data/lib/chef-dk/skeletons/code_generator/files/default/serverspec_spec_helper.rb +3 -0
  37. data/lib/chef-dk/skeletons/code_generator/files/default/spec_helper.rb +1 -7
  38. data/lib/chef-dk/skeletons/code_generator/metadata.rb +1 -1
  39. data/lib/chef-dk/skeletons/code_generator/recipes/app.rb +31 -1
  40. data/lib/chef-dk/skeletons/code_generator/recipes/cookbook.rb +32 -2
  41. data/lib/chef-dk/skeletons/code_generator/recipes/recipe.rb +18 -0
  42. data/lib/chef-dk/skeletons/code_generator/templates/default/recipe.rb.erb +5 -0
  43. data/lib/chef-dk/skeletons/code_generator/templates/default/recipe_spec.rb.erb +23 -0
  44. data/lib/chef-dk/skeletons/code_generator/templates/default/serverspec_default_spec.rb.erb +12 -0
  45. data/lib/chef-dk/version.rb +1 -1
  46. data/lib/kitchen/provisioner/policyfile_zero.rb +149 -0
  47. data/spec/shared/a_file_generator.rb +1 -0
  48. data/spec/shared/command_with_ui_object.rb +11 -0
  49. data/spec/shared/custom_generator_cookbook.rb +117 -0
  50. data/spec/unit/chef_runner_spec.rb +26 -0
  51. data/spec/unit/command/exec_spec.rb +46 -5
  52. data/spec/unit/command/export_spec.rb +176 -0
  53. data/spec/unit/command/generator_commands/app_spec.rb +38 -0
  54. data/spec/unit/command/generator_commands/cookbook_spec.rb +37 -28
  55. data/spec/unit/command/generator_commands/recipe_spec.rb +4 -2
  56. data/spec/unit/command/install_spec.rb +3 -6
  57. data/spec/unit/command/push_spec.rb +3 -6
  58. data/spec/unit/command/shell_init_spec.rb +77 -49
  59. data/spec/unit/command/update_spec.rb +155 -0
  60. data/spec/unit/command/verify_spec.rb +22 -7
  61. data/spec/unit/cookbook_metadata_spec.rb +44 -8
  62. data/spec/unit/cookbook_profiler/git_spec.rb +12 -0
  63. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/Berksfile +1 -1
  64. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/Berksfile +1 -1
  65. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/Berksfile +1 -1
  66. data/spec/unit/fixtures/cookbooks_api/small_universe.json +667 -667
  67. data/spec/unit/fixtures/cookbooks_api/universe.json +1 -1
  68. data/spec/unit/fixtures/cookbooks_api/update_fixtures.rb +1 -1
  69. data/spec/unit/fixtures/example_cookbook/Berksfile +1 -1
  70. data/spec/unit/fixtures/example_cookbook_metadata_json_only/.gitignore +17 -0
  71. data/spec/unit/fixtures/example_cookbook_metadata_json_only/.kitchen.yml +16 -0
  72. data/spec/unit/fixtures/example_cookbook_metadata_json_only/Berksfile +3 -0
  73. data/spec/unit/fixtures/example_cookbook_metadata_json_only/README.md +4 -0
  74. data/spec/unit/fixtures/example_cookbook_metadata_json_only/chefignore +96 -0
  75. data/spec/unit/fixtures/example_cookbook_metadata_json_only/metadata.json +5 -0
  76. data/spec/unit/fixtures/example_cookbook_metadata_json_only/recipes/default.rb +8 -0
  77. data/spec/unit/fixtures/example_cookbook_no_metadata/.gitignore +17 -0
  78. data/spec/unit/fixtures/example_cookbook_no_metadata/.kitchen.yml +16 -0
  79. data/spec/unit/fixtures/example_cookbook_no_metadata/Berksfile +3 -0
  80. data/spec/unit/fixtures/example_cookbook_no_metadata/README.md +4 -0
  81. data/spec/unit/fixtures/example_cookbook_no_metadata/chefignore +96 -0
  82. data/spec/unit/fixtures/example_cookbook_no_metadata/recipes/default.rb +8 -0
  83. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/Berksfile +1 -1
  84. data/spec/unit/policyfile/community_cookbook_source_spec.rb +2 -2
  85. data/spec/unit/policyfile/cookbook_location_specification_spec.rb +3 -3
  86. data/spec/unit/policyfile/uploader_spec.rb +61 -25
  87. data/spec/unit/policyfile_demands_spec.rb +47 -0
  88. data/spec/unit/policyfile_evaluation_spec.rb +1 -1
  89. data/spec/unit/policyfile_lock_build_spec.rb +60 -3
  90. data/spec/unit/policyfile_services/export_repo_spec.rb +321 -0
  91. data/spec/unit/policyfile_services/install_spec.rb +20 -1
  92. data/spec/unit/policyfile_services/push_spec.rb +36 -9
  93. metadata +53 -38
  94. data/lib/chef-dk/skeletons/code_generator/files/default/converge_spec.rb +0 -9
  95. data/lib/chef-dk/skeletons/code_generator/templates/default/default_recipe.rb.erb +0 -5
@@ -0,0 +1,106 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2014 Chef Software Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'chef-dk/command/base'
19
+ require 'chef-dk/ui'
20
+ require 'chef-dk/policyfile_services/install'
21
+
22
+ module ChefDK
23
+ module Command
24
+
25
+ class Update < Base
26
+
27
+ banner(<<-BANNER)
28
+ Usage: chef update [ POLICY_FILE ] [options]
29
+
30
+ `chef update` reads your `Policyfile.rb`, applies any changes, re-solves the
31
+ dependencies and emits an updated `Policyfile.lock.json`. The new locked policy
32
+ will reflect any changes to the `run_list` and pull in any cookbook updates
33
+ that are compatible with the version constraints stated in your `Policyfile.rb`.
34
+
35
+ NOTE: `chef update` does not yet support granular updates (e.g., just updating
36
+ the `run_list` or a specific cookbook version). Support will be added in a
37
+ future version.
38
+
39
+ The Policyfile feature is incomplete and beta quality. See our detailed README
40
+ for more information.
41
+
42
+ https://github.com/opscode/chef-dk/blob/master/POLICYFILE_README.md
43
+
44
+ Options:
45
+
46
+ BANNER
47
+
48
+ option :debug,
49
+ short: "-D",
50
+ long: "--debug",
51
+ description: "Enable stacktraces and other debug output",
52
+ default: false
53
+
54
+ attr_reader :policyfile_relative_path
55
+
56
+ attr_accessor :ui
57
+
58
+ def initialize(*args)
59
+ super
60
+ @ui = UI.new
61
+
62
+ @policyfile_relative_path = nil
63
+ @installer = nil
64
+ end
65
+
66
+ def run(params = [])
67
+ apply_params!(params)
68
+ installer.run
69
+ 0
70
+ rescue PolicyfileServiceError => e
71
+ handle_error(e)
72
+ 1
73
+ end
74
+
75
+ def installer
76
+ @installer ||= PolicyfileServices::Install.new(policyfile: policyfile_relative_path, ui: ui, root_dir: Dir.pwd, overwrite: true)
77
+ end
78
+
79
+ def debug?
80
+ !!config[:debug]
81
+ end
82
+
83
+ def handle_error(error)
84
+ ui.err("Error: #{error.message}")
85
+ if error.respond_to?(:reason)
86
+ ui.err("Reason: #{error.reason}")
87
+ ui.err("")
88
+ ui.err(error.extended_error_info) if debug?
89
+ ui.err(error.cause.backtrace.join("\n")) if debug?
90
+ end
91
+ end
92
+
93
+ def apply_params!(params)
94
+ remaining_args = parse_options(params)
95
+ if remaining_args.size > 1
96
+ ui.err(banner)
97
+ return 1
98
+ else
99
+ @policyfile_relative_path = remaining_args.first
100
+ end
101
+ end
102
+
103
+ end
104
+ end
105
+ end
106
+
@@ -143,6 +143,78 @@ require 'spec_helper'
143
143
  end
144
144
  end
145
145
 
146
+ add_component "rubocop" do |c|
147
+ c.gem_base_dir = "rubocop"
148
+ c.smoke_test do
149
+ tmpdir do |cwd|
150
+ with_file(File.join(cwd, 'foo.rb')) do |f|
151
+ f.write <<-EOF
152
+ def foo
153
+ puts 'foo'
154
+ end
155
+ EOF
156
+ end
157
+ sh("rubocop foo.rb -l", cwd: cwd)
158
+ end
159
+ end
160
+ end
161
+
162
+ add_component "fauxhai" do |c|
163
+ c.gem_base_dir = "fauxhai"
164
+ c.smoke_test { sh("gem list fauxhai") }
165
+ end
166
+
167
+ add_component "knife-spork" do |c|
168
+ c.gem_base_dir = "knife-spork"
169
+ c.smoke_test { sh('knife spork info')}
170
+ end
171
+
172
+ add_component "kitchen-vagrant" do |c|
173
+ c.gem_base_dir = "kitchen-vagrant"
174
+ # The build is not passing in travis, so no tests
175
+ c.smoke_test { sh("gem list kitchen-vagrant") }
176
+ end
177
+
178
+ add_component "package installation" do |c|
179
+
180
+ c.base_dir = "chef-dk"
181
+
182
+ c.smoke_test do
183
+
184
+ if File.directory?("/usr/bin")
185
+ sh!("/usr/bin/berks -v")
186
+
187
+ sh!("/usr/bin/chef -v")
188
+
189
+ sh!("/usr/bin/chef-client -v")
190
+ sh!("/usr/bin/chef-solo -v")
191
+
192
+ # In `knife`, `knife -v` follows a different code path that skips
193
+ # command/plugin loading; `knife -h` loads commands and plugins, but
194
+ # it exits with code 1, which is the same as a load error. Running
195
+ # `knife exec` forces command loading to happen and this command
196
+ # exits 0, which runs most of the code.
197
+ #
198
+ # See also: https://github.com/opscode/chef-dk/issues/227
199
+ sh!("/usr/bin/knife exec -E true")
200
+
201
+ tmpdir do |dir|
202
+ # Kitchen tries to create a .kitchen dir even when just running
203
+ # `kitchen -v`:
204
+ sh!("/usr/bin/kitchen -v", cwd: dir)
205
+ end
206
+
207
+ sh!("/usr/bin/ohai -v")
208
+
209
+ sh!("/usr/bin/foodcritic -V")
210
+ end
211
+
212
+ # Test blocks are expected to return a Mixlib::ShellOut compatible
213
+ # object:
214
+ ComponentTest::NullTestResult.new
215
+ end
216
+ end
217
+
146
218
  attr_reader :verification_threads
147
219
  attr_reader :verification_results
148
220
  attr_reader :verification_status
@@ -88,6 +88,17 @@ module ChefDK
88
88
  system_command(command, combined_opts)
89
89
  end
90
90
 
91
+ # Just like #sh but raises an error if the the command returns an
92
+ # unexpected exit code.
93
+ #
94
+ # Most verification steps just run a single command, then
95
+ # ChefDK::Command::Verify#invoke_tests handles the results by inspecting
96
+ # the return value of the test block. For tests that run a lot of commands,
97
+ # this is inconvenient so you can use #sh! instead.
98
+ def sh!(*args)
99
+ sh(*args).tap { |result| result.error! }
100
+ end
101
+
91
102
  def run_in_tmpdir(command, options={})
92
103
  tmpdir do |dir|
93
104
  options[:cwd] = dir
@@ -103,7 +114,7 @@ module ChefDK
103
114
 
104
115
  def assert_present!
105
116
  unless File.exists?( component_path )
106
- raise MissingComponentError.new(name)
117
+ raise MissingComponentError.new(name, component_path)
107
118
  end
108
119
  end
109
120
 
@@ -0,0 +1,52 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2014 Chef Software Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'chef/config'
19
+ require 'chef/workstation_config_loader'
20
+
21
+ # Define a config context for ChefDK
22
+ class Chef::Config
23
+
24
+ default(:policy_document_native_api, false)
25
+
26
+ config_context(:chefdk) do
27
+
28
+ default(:generator_cookbook, File.expand_path("../skeletons/code_generator", __FILE__))
29
+
30
+ end
31
+ end
32
+
33
+ module ChefDK
34
+ module Configurable
35
+
36
+ def chef_config
37
+ return @chef_config if @chef_config
38
+ config_loader.load
39
+ @chef_config = Chef::Config
40
+ end
41
+
42
+ def chefdk_config
43
+ chef_config.chefdk
44
+ end
45
+
46
+ def config_loader
47
+ @config_loader ||= Chef::WorkstationConfigLoader.new(config[:config_file])
48
+ end
49
+
50
+ end
51
+ end
52
+
@@ -15,6 +15,7 @@
15
15
  # limitations under the License.
16
16
  #
17
17
 
18
+ require 'chef-dk/exceptions'
18
19
  require 'chef/cookbook/metadata'
19
20
 
20
21
  module ChefDK
@@ -24,8 +25,16 @@ module ChefDK
24
25
  class CookbookMetadata < Chef::Cookbook::Metadata
25
26
 
26
27
  def self.from_path(path)
28
+ metadata_json_path = File.join(path, "metadata.json")
27
29
  metadata_rb_path = File.join(path, "metadata.rb")
28
- new.tap { |m| m.from_file(metadata_rb_path) }
30
+
31
+ if File.exist?(metadata_json_path)
32
+ new.tap { |m| m.from_json(File.read(metadata_json_path)) }
33
+ elsif File.exist?(metadata_rb_path)
34
+ new.tap { |m| m.from_file(metadata_rb_path) }
35
+ else
36
+ raise MalformedCookbook, "Cookbook at #{path} has neither metadata.json or metadata.rb"
37
+ end
29
38
  end
30
39
 
31
40
  def cookbook_name
@@ -74,7 +74,7 @@ module ChefDK
74
74
  end
75
75
 
76
76
  def have_remote?
77
- !remote_name.empty?
77
+ !remote_name.empty? && remote_name != '.'
78
78
  end
79
79
 
80
80
  def current_branch
@@ -39,8 +39,8 @@ module ChefDK
39
39
  end
40
40
 
41
41
  class MissingComponentError < RuntimeError
42
- def initialize(component_name)
43
- super("Component #{component_name} is missing.")
42
+ def initialize(component_name, path_checked)
43
+ super("Component #{component_name} is missing. \nReason: Could not find #{path_checked}.")
44
44
  end
45
45
  end
46
46
 
@@ -65,4 +65,19 @@ module ChefDK
65
65
  class InvalidPolicyfileFilename < StandardError
66
66
  end
67
67
 
68
+ class ChefRunnerError < StandardError
69
+
70
+ attr_reader :cause
71
+
72
+ def initialize(message, cause)
73
+ super(message)
74
+ @cause = cause
75
+ end
76
+
77
+ end
78
+
79
+ class CookbookNotFound < ChefRunnerError; end
80
+
81
+ class ChefConvergeError < ChefRunnerError; end
82
+
68
83
  end
@@ -96,9 +96,9 @@ module ChefDK
96
96
  user_bin_dir = File.expand_path(File.join(Gem.user_dir, 'bin'))
97
97
  {
98
98
  'PATH' => [ omnibus_bin_dir, user_bin_dir, omnibus_embedded_bin_dir, ENV['PATH'] ].join(File::PATH_SEPARATOR),
99
- 'GEM_ROOT' => Gem.default_dir.inspect,
99
+ 'GEM_ROOT' => Gem.default_dir,
100
100
  'GEM_HOME' => Gem.user_dir,
101
- 'GEM_PATH' => Gem.path.join(':'),
101
+ 'GEM_PATH' => Gem.path.join(File::PATH_SEPARATOR),
102
102
  }
103
103
  end
104
104
  end
@@ -31,7 +31,7 @@ module ChefDK
31
31
  attr_reader :uri
32
32
 
33
33
  def initialize(uri = nil)
34
- @uri = uri || "https://supermarket.getchef.com"
34
+ @uri = uri || "https://supermarket.chef.io"
35
35
  @http_connections = {}
36
36
  end
37
37
 
@@ -32,12 +32,15 @@ module ChefDK
32
32
  attr_reader :default_source
33
33
  attr_reader :cookbook_location_specs
34
34
 
35
+ attr_reader :named_run_lists
36
+
35
37
  attr_reader :storage_config
36
38
 
37
39
  def initialize(storage_config)
38
40
  @name = nil
39
41
  @errors = []
40
42
  @run_list = []
43
+ @named_run_lists = {}
41
44
  @default_source = NullCookbookSource.new
42
45
  @cookbook_location_specs = {}
43
46
  @storage_config = storage_config
@@ -56,6 +59,10 @@ module ChefDK
56
59
  @run_list
57
60
  end
58
61
 
62
+ def named_run_list(name, *run_list_items)
63
+ @named_run_lists[name] = run_list_items.flatten
64
+ end
65
+
59
66
  def default_source(source_type = nil, source_uri = nil)
60
67
  return @default_source if source_type.nil?
61
68
  case source_type
@@ -34,21 +34,38 @@ module ChefDK
34
34
  attr_reader :http_client
35
35
  attr_reader :ui
36
36
 
37
- def initialize(policyfile_lock, policy_group, ui: nil, http_client: nil)
37
+ def initialize(policyfile_lock, policy_group, ui: nil, http_client: nil, policy_document_native_api: false)
38
38
  @policyfile_lock = policyfile_lock
39
39
  @policy_group = policy_group
40
40
  @http_client = http_client
41
41
  @ui = ui || UI.null
42
+ @policy_document_native_api = policy_document_native_api
42
43
 
43
44
  @cookbook_versions_for_policy = nil
44
45
  end
45
46
 
47
+ def policy_name
48
+ policyfile_lock.name
49
+ end
50
+
46
51
  def upload
47
52
  ui.msg("WARN: Uploading policy to policy group #{policy_group} in compatibility mode")
48
53
 
49
54
  upload_cookbooks
50
- data_bag_create
51
- data_bag_item_create
55
+ upload_policy
56
+ end
57
+
58
+ def upload_policy
59
+ if using_policy_document_native_api?
60
+ upload_policy_native
61
+ else
62
+ data_bag_create
63
+ data_bag_item_create
64
+ end
65
+ end
66
+
67
+ def upload_policy_native
68
+ http_client.put("/policies/#{policy_group}/#{policy_name}", policyfile_lock.to_lock)
52
69
  end
53
70
 
54
71
  def data_bag_create
@@ -58,7 +75,7 @@ module ChefDK
58
75
  end
59
76
 
60
77
  def data_bag_item_create
61
- policy_id = "#{policyfile_lock.name}-#{policy_group}"
78
+ policy_id = "#{policy_name}-#{policy_group}"
62
79
  lock_data = policyfile_lock.to_lock.dup
63
80
 
64
81
  lock_data["id"] = policy_id
@@ -114,6 +131,10 @@ module ChefDK
114
131
  end
115
132
  end
116
133
 
134
+ def using_policy_document_native_api?
135
+ @policy_document_native_api
136
+ end
137
+
117
138
  private
118
139
 
119
140
  def upload_cookbooks
@@ -44,6 +44,8 @@ module ChefDK
44
44
 
45
45
  def_delegator :@dsl, :name
46
46
  def_delegator :@dsl, :run_list
47
+ def_delegator :@dsl, :named_run_list
48
+ def_delegator :@dsl, :named_run_lists
47
49
  def_delegator :@dsl, :errors
48
50
  def_delegator :@dsl, :default_source
49
51
  def_delegator :@dsl, :cookbook_location_specs
@@ -81,6 +83,20 @@ module ChefDK
81
83
  expanded_run_list.map { |i| normalize_recipe(i) }
82
84
  end
83
85
 
86
+ def expanded_named_run_lists
87
+ named_run_lists.inject({}) do |expanded, (name, run_list_items)|
88
+ expanded[name] = Chef::RunList.new(*run_list_items)
89
+ expanded
90
+ end
91
+ end
92
+
93
+ def normalized_named_run_lists
94
+ expanded_named_run_lists.inject({}) do |normalized,(name, run_list)|
95
+ normalized[name] = run_list.map { |i| normalize_recipe(i) }
96
+ normalized
97
+ end
98
+ end
99
+
84
100
  def lock
85
101
  @policyfile_lock ||= PolicyfileLock.build_from_compiler(self, storage_config)
86
102
  end
@@ -205,7 +221,11 @@ module ChefDK
205
221
  end
206
222
 
207
223
  def cookbooks_in_run_list
208
- recipes = expanded_run_list.map {|recipe| recipe.name }
224
+ combined_run_lists = expanded_named_run_lists.values.inject(expanded_run_list.to_a) do |accum_run_lists, run_list|
225
+ accum_run_lists |= run_list.to_a
226
+ end
227
+
228
+ recipes = combined_run_lists.map {|recipe| recipe.name }
209
229
  recipes.map { |r| r[/^([^:]+)/, 1] }
210
230
  end
211
231