class Chef::CookbookLoader
This class is used by knife, cheffs and legacy chef-solo modes. It is not used by the server mode of chef-client or zolo/zero modes.
This class implements orchestration around producing a single cookbook_version for a cookbook or loading a Mash
of all cookbook_versions, using the cookbook_version_loader class, and doing lazy-access and memoization to only load each cookbook once on demand.
This implements a key-value style each which makes it appear to be a Hash of String
=> CookbookVersion
pairs where the String
is the cookbook name. The use of Enumerable combined with the Hash-style each is likely not entirely sane.
This object is also passed and injected into the CookbookCollection
object where it is converted to a Mash
that looks almost exactly like the cookbook_by_name Mash
in this object.
Attributes
@return [Array<String>] the array of repo paths containing cookbook dirs
Public Class Methods
@param repo_paths
[Array<String>] the array of repo paths containing cookbook dirs
# File lib/chef/cookbook_loader.rb, line 51 def initialize(*repo_paths) @repo_paths = repo_paths.flatten.map { |p| File.expand_path(p) } raise ArgumentError, "You must specify at least one cookbook repo path" if repo_paths.empty? end
Public Instance Methods
# File lib/chef/cookbook_loader.rb, line 103 def [](cookbook) load_cookbook(cookbook) end
# File lib/chef/cookbook_loader.rb, line 130 def cookbook_names cookbooks_by_name.keys.sort end
The primary function of this class is to build this Mash
mapping cookbook names as a string to the CookbookVersion
objects for them. Callers must call “load_cookbooks” first.
@return [Mash<String, Chef::CookbookVersion>]
# File lib/chef/cookbook_loader.rb, line 60 def cookbooks_by_name @cookbooks_by_name ||= Mash.new end
# File lib/chef/cookbook_loader.rb, line 116 def each cookbooks_by_name.keys.sort_by(&:to_s).each do |cname| yield(cname, cookbooks_by_name[cname]) end end
# File lib/chef/cookbook_loader.rb, line 122 def each_key(&block) cookbook_names.each(&block) end
# File lib/chef/cookbook_loader.rb, line 126 def each_value(&block) values.each(&block) end
# File lib/chef/cookbook_loader.rb, line 109 def has_key?(cookbook_name) not self[cookbook_name.to_sym].nil? end
Loads a single cookbook by its name.
@param [String] @return [Chef::CookbookVersion]
# File lib/chef/cookbook_loader.rb, line 86 def load_cookbook(cookbook_name) unless cookbook_version_loaders.key?(cookbook_name) raise Exceptions::CookbookNotFoundInRepo, "Cannot find a cookbook named #{cookbook_name}; did you forget to add metadata to a cookbook? (https://docs.chef.io/config_rb_metadata.html)" end return cookbooks_by_name[cookbook_name] if cookbooks_by_name.key?(cookbook_name) loader = cookbook_version_loaders[cookbook_name] loader.load cookbook_version = loader.cookbook_version cookbooks_by_name[cookbook_name] = cookbook_version metadata[cookbook_name] = cookbook_version.metadata cookbook_version end
Loads all cookbooks across all repo_paths
@return [Mash<String, Chef::CookbookVersion>] the cookbooks_by_name
Mash
# File lib/chef/cookbook_loader.rb, line 75 def load_cookbooks cookbook_version_loaders.each_key do |cookbook_name| load_cookbook(cookbook_name) end cookbooks_by_name end
This class also builds a mapping of cookbook names to their Metadata objects. Callers must call “load_cookbooks” first.
@return [Mash<String, Chef::Cookbook::Metadata>]
# File lib/chef/cookbook_loader.rb, line 68 def metadata @metadata ||= Mash.new end
# File lib/chef/cookbook_loader.rb, line 134 def values cookbooks_by_name.values end
Private Instance Methods
# File lib/chef/cookbook_loader.rb, line 152 def all_directories_in_repo_paths @all_directories_in_repo_paths ||= all_files_in_repo_paths.select { |path| File.directory?(path) } end
# File lib/chef/cookbook_loader.rb, line 157 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(Chef::Util::PathHelper.escape_glob_dir(repo_path), "*")] end end end
Helper method to lazily create and remember the chefignore object for a given repo_path.
@param [String] repo_path the full path to the cookbook directory of the repo @return [Chef::Cookbook::Chefignore] the chefignore object for the repo_path
# File lib/chef/cookbook_loader.rb, line 147 def chefignore(repo_path) @chefignores ||= {} @chefignores[repo_path] ||= Cookbook::Chefignore.new(repo_path) end
This method creates a Mash
of the CookbookVersionLoaders for each cookbook.
@return [Mash<String, Cookbook::CookbookVersionLoader>]
# File lib/chef/cookbook_loader.rb, line 169 def cookbook_version_loaders @cookbook_version_loaders ||= begin mash = Mash.new all_directories_in_repo_paths.each do |cookbook_path| loader = Cookbook::CookbookVersionLoader.new(cookbook_path, chefignore(File.dirname(cookbook_path))) cookbook_name = loader.cookbook_name if mash.key?(cookbook_name) raise Chef::Exceptions::CookbookMergingError, "Cookbook merging is no longer supported, the cookbook named #{cookbook_name} can only appear once in the cookbook_path" end mash[cookbook_name] = loader end mash end end