summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/chef/cookbook/cookbook_version_loader.rb3
-rw-r--r--lib/chef/cookbook_loader.rb69
-rw-r--r--spec/unit/cookbook_loader_spec.rb49
3 files changed, 94 insertions, 27 deletions
diff --git a/lib/chef/cookbook/cookbook_version_loader.rb b/lib/chef/cookbook/cookbook_version_loader.rb
index bff17dc4a4..750d886e84 100644
--- a/lib/chef/cookbook/cookbook_version_loader.rb
+++ b/lib/chef/cookbook/cookbook_version_loader.rb
@@ -24,6 +24,9 @@ class Chef
attr_reader :frozen
attr_reader :uploaded_cookbook_version_file
+ # TODO: use this in preference to @cookbook_path internally
+ attr_reader :cookbook_path
+
def initialize(path, chefignore=nil)
@cookbook_path = File.expand_path( path ) # cookbook_path from which this was loaded
# We keep a list of all cookbook paths that have been merged in
diff --git a/lib/chef/cookbook_loader.rb b/lib/chef/cookbook_loader.rb
index 27cf978acb..cac5548565 100644
--- a/lib/chef/cookbook_loader.rb
+++ b/lib/chef/cookbook_loader.rb
@@ -50,6 +50,9 @@ class Chef
repo_path = File.expand_path(repo_path)
end
+ @preloaded_cookbooks = false
+ @loaders_by_name = {}
+
# Used to track which cookbooks appear in multiple places in the cookbook repos
# and are merged in to a single cookbook by file shadowing. This behavior is
# deprecated, so users of this class may issue warnings to the user by checking
@@ -64,25 +67,25 @@ class Chef
end
def load_cookbooks
- @repo_paths.each do |repo_path|
- Dir[File.join(repo_path, "*")].each do |cookbook_path|
- load_cookbook(File.basename(cookbook_path), [repo_path])
- end
+ preload_cookbooks
+ @loaders_by_name.each do |cookbook_name, _loaders|
+ load_cookbook(cookbook_name)
end
@cookbooks_by_name
end
- def load_cookbook(cookbook_name, repo_paths=nil)
- repo_paths ||= @repo_paths
- repo_paths.each do |repo_path|
- @chefignores[repo_path] ||= Cookbook::Chefignore.new(repo_path)
- cookbook_path = File.join(repo_path, cookbook_name.to_s)
- next unless File.directory?(cookbook_path) and Dir[File.join(repo_path, "*")].include?(cookbook_path)
- loader = Cookbook::CookbookVersionLoader.new(cookbook_path, @chefignores[repo_path])
- loader.load_cookbooks
+ def load_cookbook(cookbook_name)
+ preload_cookbooks
+
+ return nil unless @loaders_by_name.key?(cookbook_name.to_s)
+
+ @loaders_by_name[cookbook_name.to_s].each do |loader|
+ loader.load
+
next if loader.empty?
- cookbook_name = loader.cookbook_name
- @cookbooks_paths[cookbook_name] << cookbook_path # for deprecation warnings
+
+ @cookbooks_paths[cookbook_name] << loader.cookbook_path # for deprecation warnings
+
if @loaded_cookbooks.key?(cookbook_name)
@merged_cookbooks << cookbook_name # for deprecation warnings
@loaded_cookbooks[cookbook_name].merge!(loader)
@@ -130,5 +133,43 @@ class Chef
end
alias :cookbooks :values
+ private
+
+ def preload_cookbooks
+ return false if @preloaded_cookbooks
+
+ all_directories_in_repo_paths.each do |cookbook_path|
+ preload_cookbook(cookbook_path)
+ end
+ @preloaded_cookbooks = true
+ true
+ end
+
+ def preload_cookbook(cookbook_path)
+ repo_path = File.dirname(cookbook_path)
+ @chefignores[repo_path] ||= Cookbook::Chefignore.new(repo_path)
+ loader = Cookbook::CookbookVersionLoader.new(cookbook_path, @chefignores[repo_path])
+
+ cookbook_name = loader.cookbook_name
+
+ # TODO: wrap @loaders_by_name so string keys are encapsulated
+ @loaders_by_name[cookbook_name.to_s] ||= []
+ @loaders_by_name[cookbook_name.to_s] << loader
+ end
+
+ def all_directories_in_repo_paths
+ @all_directories_in_repo_paths ||=
+ all_files_in_repo_paths.select { |path| File.directory?(path) }
+ end
+
+ def all_files_in_repo_paths
+ @all_files_in_repo_paths ||=
+ begin
+ @repo_paths.inject([]) do |all_children, repo_path|
+ all_children += Dir[File.join(repo_path, "*")]
+ end
+ end
+ end
+
end
end
diff --git a/spec/unit/cookbook_loader_spec.rb b/spec/unit/cookbook_loader_spec.rb
index 713347af7b..8c1a0886c2 100644
--- a/spec/unit/cookbook_loader_spec.rb
+++ b/spec/unit/cookbook_loader_spec.rb
@@ -29,7 +29,25 @@ describe Chef::CookbookLoader do
let(:cookbook_loader) { Chef::CookbookLoader.new(repo_paths) }
- describe "loading all cookbooks" do
+ it "checks each directory only once when loading (CHEF-3487)" do
+ cookbook_paths = []
+ repo_paths.each do |repo_path|
+ cookbook_paths |= Dir[File.join(repo_path, "*")]
+ end
+
+ cookbook_paths.delete_if { |path| File.basename(path) == "chefignore" }
+
+ cookbook_paths.each do |cookbook_path|
+ Chef::Cookbook::CookbookVersionLoader.should_receive(:new).
+ with(cookbook_path, anything).
+ once.
+ and_call_original
+ end
+ cookbook_loader.load_cookbooks
+ end
+
+
+ context "after loading all cookbooks" do
before(:each) do
cookbook_loader.load_cookbooks
end
@@ -77,7 +95,7 @@ describe Chef::CookbookLoader do
end
end
- describe "load_cookbooks" do
+ describe "referencing cookbook files" do
it "should find all the cookbooks in the cookbook path" do
cookbook_loader.load_cookbooks
cookbook_loader.should have_key(:openldap)
@@ -149,17 +167,7 @@ describe Chef::CookbookLoader do
cookbook_loader.metadata[:openldap].should be_a_kind_of(Chef::Cookbook::Metadata)
end
- it "should check each cookbook directory only once (CHEF-3487)" do
- cookbooks = []
- repo_paths.each do |repo_path|
- cookbooks |= Dir[File.join(repo_path, "*")]
- end
- cookbooks.each do |cookbook|
- File.should_receive(:directory?).with(cookbook).once;
- end
- cookbook_loader.load_cookbooks
- end
- end # load_cookbooks
+ end # after loading cookbooks
end # loading all cookbooks
@@ -209,6 +217,7 @@ describe Chef::CookbookLoader do
cookbook_loader["apache2"].should be_a_kind_of(Chef::CookbookVersion)
end
+
describe "loading all cookbooks after loading only one cookbook" do
before(:each) do
cookbook_loader.load_cookbooks
@@ -224,4 +233,18 @@ describe Chef::CookbookLoader do
end
end
end # loading only one cookbook
+
+ describe "loading a single cookbook with a different name than basename" do
+
+ before(:each) do
+ cookbook_loader.load_cookbook("name-mismatch")
+ end
+
+ it "loads the correct cookbook" do
+ cookbook_version = cookbook_loader["name-mismatch"]
+ cookbook_version.should be_a_kind_of(Chef::CookbookVersion)
+ cookbook_version.name.should == :"name-mismatch"
+ end
+
+ end
end