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

repo_paths[R]

@return [Array<String>] the array of repo paths containing cookbook dirs

Public Class Methods

copy_to_tmp_dir_from_array(cookbooks) { |tmp_cookbook_loader| ... } click to toggle source

This method creates tmp directory and copies all cookbooks into it and creates cookbook loader object which points to tmp directory

# File lib/chef/cookbook_loader.rb, line 139
def self.copy_to_tmp_dir_from_array(cookbooks)
  Dir.mktmpdir do |tmp_dir|
    cookbooks.each do |cookbook|
      checksums_to_on_disk_paths = cookbook.checksums
      cookbook.each_file do |manifest_record|
        path_in_cookbook = manifest_record[:path]
        on_disk_path = checksums_to_on_disk_paths[manifest_record[:checksum]]
        dest = File.join(tmp_dir, cookbook.name.to_s, path_in_cookbook)
        FileUtils.mkdir_p(File.dirname(dest))
        FileUtils.cp_r(on_disk_path, dest)
      end
    end
    tmp_cookbook_loader ||= begin
      Chef::Cookbook::FileVendor.fetch_from_disk(tmp_dir)
      CookbookLoader.new(tmp_dir)
    end
    yield tmp_cookbook_loader
  end
end
new(*repo_paths) click to toggle source

@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.compact.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

[](cookbook) click to toggle source
# File lib/chef/cookbook_loader.rb, line 103
def [](cookbook)
  load_cookbook(cookbook)
end
Also aliased as: fetch
compile_metadata() click to toggle source

generates metadata.json adds it in the manifest

# File lib/chef/cookbook_loader.rb, line 160
def compile_metadata
  each do |cookbook_name, cookbook|
    compiled_metadata = cookbook.compile_metadata
    if compiled_metadata
      cookbook.all_files << compiled_metadata
      cookbook.cookbook_manifest.send(:generate_manifest)
    end
  end
end
cookbook_exists?(cookbook_name)
Alias for: has_key?
cookbook_names() click to toggle source
# File lib/chef/cookbook_loader.rb, line 130
def cookbook_names
  cookbooks_by_name.keys.sort
end
cookbooks()
Alias for: values
cookbooks_by_name() click to toggle source

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
each() { |cname, cookbooks_by_name| ... } click to toggle source
# 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
each_key(&block) click to toggle source
# File lib/chef/cookbook_loader.rb, line 122
def each_key(&block)
  cookbook_names.each(&block)
end
each_value(&block) click to toggle source
# File lib/chef/cookbook_loader.rb, line 126
def each_value(&block)
  values.each(&block)
end
fetch(cookbook)
Alias for: []
freeze_versions() click to toggle source

freeze versions of all the cookbooks

# File lib/chef/cookbook_loader.rb, line 171
def freeze_versions
  each do |cookbook_name, cookbook|
    cookbook.freeze_version
  end
end
has_key?(cookbook_name) click to toggle source
# File lib/chef/cookbook_loader.rb, line 109
def has_key?(cookbook_name)
  not self[cookbook_name.to_sym].nil?
end
Also aliased as: cookbook_exists?, key?
key?(cookbook_name)
Alias for: has_key?
load_cookbook(cookbook_name) click to toggle source

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/)"
  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 unless cookbook_version.nil?
  cookbook_version
end
load_cookbooks() click to toggle source

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
metadata() click to toggle source

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
values() click to toggle source
# File lib/chef/cookbook_loader.rb, line 134
def values
  cookbooks_by_name.values
end
Also aliased as: cookbooks

Private Instance Methods

all_directories_in_repo_paths() click to toggle source
# File lib/chef/cookbook_loader.rb, line 191
def all_directories_in_repo_paths
  @all_directories_in_repo_paths ||=
    all_files_in_repo_paths.select { |path| File.directory?(path) }
end
all_files_in_repo_paths() click to toggle source
# File lib/chef/cookbook_loader.rb, line 196
def all_files_in_repo_paths
  @all_files_in_repo_paths ||=
    repo_paths.inject([]) do |all_children, repo_path|
      all_children + Dir[File.join(Chef::Util::PathHelper.escape_glob_dir(repo_path), "*")]
    end
end
chefignore(repo_path) click to toggle source

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 186
def chefignore(repo_path)
  @chefignores ||= {}
  @chefignores[repo_path] ||= Cookbook::Chefignore.new(repo_path)
end
cookbook_version_loaders() click to toggle source

This method creates a Mash of the CookbookVersionLoaders for each cookbook.

@return [Mash<String, Cookbook::CookbookVersionLoader>]

# File lib/chef/cookbook_loader.rb, line 206
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(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