def self.constrain(all_cookbooks, recipe_constraints)
dep_graph = create_dependency_graph_from_cookbooks(all_cookbooks)
cookbook_constraints = recipe_constraints.map do |recipe_spec|
cookbook_name = (recipe_spec[:name][/^(.+)::/, 1] || recipe_spec[:name])
DepSelector::SolutionConstraint.new(dep_graph.package(cookbook_name),
recipe_spec[:version_constraint])
end
all_packages = all_cookbooks.inject([]) do |acc, (cookbook_name, cookbook_versions)|
acc << dep_graph.package(cookbook_name)
acc
end
soln =
begin
DepSelector::Selector.new(dep_graph).find_solution(cookbook_constraints, all_packages)
rescue DepSelector::Exceptions::InvalidSolutionConstraints => e
non_existent_cookbooks = e.non_existent_packages.map {|constraint| constraint.package.name}
cookbooks_with_no_matching_versions = e.constrained_to_no_versions.map {|constraint| constraint.package.name}
message = ""
if non_existent_cookbooks.length > 0
message += "no such " + (non_existent_cookbooks.length > 1 ? "cookbooks" : "cookbook")
message += " #{non_existent_cookbooks.join(", ")}"
end
if cookbooks_with_no_matching_versions.length > 0
if message.length > 0
message += "; "
end
message += "no versions match the constraints on " + (cookbooks_with_no_matching_versions.length > 1 ? "cookbooks" : "cookbook")
message += " #{cookbooks_with_no_matching_versions.join(", ")}"
end
message = "Run list contains invalid items: #{message}."
raise Chef::Exceptions::CookbookVersionSelection::InvalidRunListItems.new(message, non_existent_cookbooks, cookbooks_with_no_matching_versions)
rescue DepSelector::Exceptions::NoSolutionExists => e
raise Chef::Exceptions::CookbookVersionSelection::UnsatisfiableRunListItem.new(filter_dep_selector_message(e.message), e.unsatisfiable_solution_constraint, e.disabled_non_existent_packages, e.disabled_most_constrained_packages)
end
selected_cookbooks = {}
soln.each_pair do |cb_name, cb_version|
selected_cookbooks[cb_name] = all_cookbooks[cb_name].find{|cbv| cbv.version == cb_version.to_s}
end
selected_cookbooks
end