# # Copyright:: Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # module ChefCLI class CachedCookbookNotFound < StandardError end class LocalCookbookNotFound < StandardError end class MalformedCookbook < StandardError end class DependencyConflict < StandardError end class CookbookNotInWorkingSet < DependencyConflict end class InvalidCookbookLockData < StandardError end class CachedCookbookModified < StandardError end class CookbookDoesNotContainRequiredRecipe < StandardError end class InvalidPolicyfileAttribute < StandardError end class OmnibusInstallNotFound < RuntimeError require_relative "dist" def initialize super("Can not find omnibus installation directory for #{ChefCLI::Dist::PRODUCT}.") end end class UnsupportedFeature < StandardError end class PolicyfileError < StandardError end class InvalidPolicyfileSourceURI < StandardError def initialize(url, reason = nil) @url = url @reason = reason msg = "'#{@url}' is not a valid Policyfile Source URI" msg << " #{@reason}." unless @reason.nil? super(msg) end end class MissingCookbookLockData < StandardError end class PolicyfileMissingCookbookMetadata < StandardError def initialize(cookbook_root) super("Policyfile specified to use cookbook metadata, but neither #{cookbook_root}/metadata.rb or #{cookbook_root}/metadata.json was found.") end end class PolicyfileBadCookbookMetadata < StandardError def initialize(cookbook_root, e) super("Cookbook metadata for cookbook at #{cookbook_root} could not be parsed:\n Original Exception: #{e}") end end class InvalidLockfile < StandardError end class InvalidPolicyfileFilename < StandardError end class InvalidUndoRecord < StandardError end class CantUndo < StandardError end class UndoRecordNotFound < StandardError end class MultipleErrors < StandardError end class BUG < RuntimeError end class IncludePolicyCookbookSourceConflict < StandardError end class CookbookSourceConflict < StandardError attr_reader :conflicting_cookbooks attr_reader :cookbook_sources def initialize(conflicting_cookbooks, cookbook_sources) @conflicting_cookbooks = conflicting_cookbooks @cookbook_sources = cookbook_sources super(compute_message) end private def compute_message conflicting_cookbook_sets = cookbook_sources.combination(2).map do |source_a, source_b| overlapping_cookbooks = conflicting_cookbooks.select do |cookbook_name| source_a.universe_graph.key?(cookbook_name) && source_b.universe_graph.key?(cookbook_name) end "Source #{source_a.desc} and #{source_b.desc} contain conflicting cookbooks:\n" + overlapping_cookbooks.sort.map { |c| "- #{c}" }.join("\n") + "\n\n" + resolution_message(overlapping_cookbooks) end conflicting_cookbook_sets.join("\n") end def resolution_message(overlapping_cookbooks) example_source = cookbook_sources.first source_key, location = example_source.default_source_args <<~EXAMPLE You can set a preferred source to resolve this issue with code like: default_source :#{source_key}, "#{location}" do |s| s.preferred_for "#{overlapping_cookbooks.join('", "')}" end EXAMPLE end end class PolicyfileLockDownloadError < StandardError end class LocalPolicyfileLockNotFound < StandardError end class InvalidPolicyfileLocation < StandardError end end