module Berkshelf class BerkshelfError < StandardError class << self # @param [Integer] code def set_status_code(code) define_method(:status_code) { code } define_singleton_method(:status_code) { code } end end alias_method :message, :to_s end class DeprecatedError < BerkshelfError; set_status_code(10); end class InternalError < BerkshelfError; set_status_code(99); end class ArgumentError < InternalError; end class AbstractFunction < InternalError; end class ConfigurationError < InternalError; end class BerksfileNotFound < BerkshelfError set_status_code(100) # @param [#to_s] filepath # the path where a Berksfile was not found def initialize(filepath) @filepath = File.dirname(File.expand_path(filepath)) rescue filepath end def to_s "No Berksfile or Berksfile.lock found at '#{@filepath}'!" end alias_method :message, :to_s end class CookbookNotFound < BerkshelfError set_status_code(103) def initialize(name, version, location) @name = name @version = version @location = location end def to_s if @version "Cookbook '#{@name}' (#{@version}) not found #{@location}!" else "Cookbook '#{@name}' not found #{@location}!" end end alias_method :message, :to_s end class DuplicateDependencyDefined < BerkshelfError set_status_code(105) def initialize(name) @name = name end def to_s out = "Your Berksfile contains multiple entries named " out << "'#{@name}'. Please remove duplicate dependencies, or put them in " out << "different groups." out end alias_method :message, :to_s end class NoSolutionError < BerkshelfError set_status_code(106) attr_reader :demands # @param [Array] demands def initialize(demands, original_exception) @demands = demands @original_exception = original_exception end def to_s @original_exception.to_s + "Unable to find a solution for demands: #{demands.join(', ')}" end alias_method :message, :to_s end class CookbookSyntaxError < BerkshelfError; set_status_code(107); end class ConstraintNotSatisfied < BerkshelfError; set_status_code(111); end class BerksfileReadError < BerkshelfError set_status_code(113) # @param [#set_status_code] original_error def initialize(original_error) @original_error = original_error @error_message = original_error.to_s @error_backtrace = original_error.backtrace end def status_code @original_error.respond_to?(:status_code) ? @original_error.status_code : 113 end alias_method :original_backtrace, :backtrace def backtrace Array(@error_backtrace) + Array(original_backtrace) end def to_s [ "An error occurred while reading the Berksfile:", "", " #{@error_message}", ].join("\n") end alias_method :message, :to_s end class MismatchedCookbookName < BerkshelfError set_status_code(114) # @param [Dependency] dependency # the dependency with the expected name # @param [CachedCookbook] cached_cookbook # the cached_cookbook with the mismatched name def initialize(dependency, cached_cookbook) @dependency = dependency @cached_cookbook = cached_cookbook end def to_s out = "In your Berksfile, you have:\n" out << "\n" out << " cookbook '#{@dependency.name}'\n" out << "\n" out << "But that cookbook is actually named '#{@cached_cookbook.cookbook_name}'\n" out << "\n" out << "This can cause potentially unwanted side-effects in the future.\n" out << "\n" out << "NOTE: If you do not explicitly set the 'name' attribute in the " out << "metadata, the name of the directory will be used instead. This " out << "is often a cause of confusion for dependency solving." out end alias_method :message, :to_s end class InvalidConfiguration < BerkshelfError set_status_code(115) def initialize(errors) @errors = errors end def to_s out = "Invalid configuration:\n" @errors.each do |key, errors| errors.each do |error| out << " #{key} #{error}\n" end end out.strip end alias_method :message, :to_s end class InsufficientPrivledges < BerkshelfError set_status_code(119) def initialize(path) @path = path end def to_s "You do not have permission to write to '#{@path}'! Please chown the " \ "path to the current user, chmod the permissions to include the " \ "user, or choose a different path." end alias_method :message, :to_s end class DependencyNotFound < BerkshelfError set_status_code(120) # @param [String, Array] names # the list of cookbook names that were not defined def initialize(names) @names = Array(names) end def to_s if @names.size == 1 "Dependency '#{@names.first}' was not found. Please make sure it is " \ "in your Berksfile, and then run `berks install` to download and " \ "install the missing dependencies." else out = "The following dependencies were not found:\n" @names.each do |name| out << " * #{name}\n" end out << "\n" out << "Please make sure they are in your Berksfile, and then run " out << "`berks install` to download and install the missing " out << "dependencies." out end end alias_method :message, :to_s end class CommunitySiteError < BerkshelfError set_status_code(123) def initialize(uri, message) @api_uri = uri @message = message end def to_s "An unexpected error occurred retrieving #{@message} from the cookbook " \ "site at '#{@api_uri}'." end alias_method :message, :to_s end class CookbookValidationFailure < BerkshelfError set_status_code(124) # @param [Location] location # the location (or any subclass) raising this validation error # @param [CachedCookbook] cached_cookbook # the cached_cookbook that does not satisfy the constraint def initialize(dependency, cached_cookbook) @dependency = dependency @cached_cookbook = cached_cookbook end def to_s "The cookbook downloaded for #{@dependency} did not satisfy the constraint." end alias_method :message, :to_s end class UploadFailure < BerkshelfError; end class FrozenCookbook < UploadFailure set_status_code(126) # @param [CachedCookbook] cookbook def initialize(cookbook) @cookbook = cookbook end def to_s "The cookbook #{@cookbook.cookbook_name} (#{@cookbook.version}) " \ "already exists and is frozen on the Chef Server. Use the --force " \ "option to override." end alias_method :message, :to_s end class OutdatedDependency < BerkshelfError set_status_code(128) # @param [Dependency] locked_dependency # the locked dependency # @param [Dependency] dependency # the dependency that is outdated def initialize(locked, dependency) @locked = locked @dependency = dependency end def to_s "Berkshelf could not find compatible versions for cookbook '#{@dependency.name}':\n" + " In Berksfile:\n" + " #{@dependency.name} (#{@dependency.version_constraint})\n\n" + " In Berksfile.lock:\n" + " #{@locked.name} (#{@locked.version})\n\n" + "Try running `berks update #{@dependency.name}`, which will try to find '#{@dependency.name}' matching " + "'#{@dependency.version_constraint}'." end alias_method :message, :to_s end class EnvironmentNotFound < BerkshelfError set_status_code(129) def initialize(environment_name) @environment_name = environment_name end def to_s "The environment '#{@environment_name}' does not exist" end alias_method :message, :to_s end class ChefConnectionError < BerkshelfError set_status_code(130) def to_s "There was an error connecting to the Chef Server" end end class UnknownCompressionType < BerkshelfError set_status_code(131) def initialize(target, destination) @target = target @destination = destination end def to_s "The file at '#{@target}' is not a known compression type, and cannot be decompressed into '#{@destination}'" end alias_method :message, :to_s end # Raised when a cookbook or its recipes contain a space or invalid # character in the path. # # @param [CachedCookbook] cookbook # the cookbook that failed validation # @param [Array<#to_s>] files # the list of files that were not valid class InvalidCookbookFiles < BerkshelfError set_status_code(132) def initialize(cookbook, files) @cookbook = cookbook @files = files end def to_s [ "The cookbook '#{@cookbook.cookbook_name}' has invalid filenames:", "", " " + @files.map(&:to_s).join("\n "), "", "Please note, spaces are not a valid character in filenames", ].join("\n") end alias_method :message, :to_s end class LicenseNotFound < BerkshelfError set_status_code(134) attr_reader :license def initialize(license) @license = license end def to_s "Unknown license: '#{license}'\n" + "Available licenses: #{CookbookGenerator::LICENSES.join(', ')}" end alias_method :message, :to_s end # Raised when a cookbook or its recipes contain a space or invalid # character in the path. class ConfigNotFound < BerkshelfError set_status_code(135) # @param [String] type # the type of config that was not found (Berkshelf, Chef, etc) # @param [#to_s] path # the path to the specified Chef config that did not exist def initialize(type, path) @type = type.to_s @path = path end def to_s "No #{@type.capitalize} config file found at: '#{@path}'!" end alias_method :message, :to_s end class LockfileParserError < BerkshelfError set_status_code(136) # @param [String] lockfile # the path to the Lockfile # @param [~Exception] original # the original exception class def initialize(original) @original = original end def to_s "Error reading the Berkshelf lockfile:\n\n" \ " #{@original.class}: #{@original.message}" end alias_method :message, :to_s end class InvalidSourceURI < BerkshelfError set_status_code(137) def initialize(url, reason = nil) @url = url @reason = reason end def to_s msg = "'#{@url}' is not a valid Berkshelf source URI." msg << " #{@reason}." unless @reason.nil? msg end alias_method :message, :to_s end class DuplicateDemand < BerkshelfError; set_status_code(138); end class LockfileNotFound < BerkshelfError set_status_code(140) def to_s "Lockfile not found! Run `berks install` to create the lockfile." end end class NotACookbook < BerkshelfError set_status_code(141) # @param [String] path # the path to the thing that is not a cookbook def initialize(path) @path = File.expand_path(path) rescue path end def to_s "The resource at '#{@path}' does not appear to be a valid cookbook. " \ "Does it have a metadata.rb?" end alias_method :message, :to_s end class PackageError < BerkshelfError; set_status_code(143); end class LockfileOutOfSync < BerkshelfError set_status_code(144) def to_s "The lockfile is out of sync! Run `berks install` to sync the lockfile." end end class DependencyNotInstalled < BerkshelfError set_status_code(145) def initialize(dependency) @name = dependency.name @version = dependency.locked_version end def to_s "The cookbook '#{@name} (#{@version})' is not installed. Please run " \ "`berks install` to download and install the missing dependency." end alias_method :message, :to_s end class NoAPISourcesDefined < BerkshelfError set_status_code(146) def to_s "Your Berksfile does not define any API sources! You must define " \ "at least one source in order to download cookbooks. To add the " \ "default Berkshelf API server, add the following code to the top of " \ "your Berksfile:" \ "\n\n" \ " source 'https://supermarket.chef.io'" end end class GraphvizNotInstalled < BerkshelfError set_status_code(147) def to_s "Graphviz is not installed! In order to generate a dependency graph, " \ "you must install Graphviz. Please visit the Graphviz homepage at " \ "http://www.graphviz.org/ or consult your package manager for more " \ "information on how to install Graphviz." end end class GraphvizCommandFailed < BerkshelfError set_status_code(148) def initialize(command, output) @command = command @output = output end def to_s "The Graphviz command `#{@command}` failed to execute properly. Here " \ "is the standard error from the command:\n\n#{@output}" end alias_method :message, :to_s end class MissingLockfileCookbookVersion < CookbookNotFound set_status_code(149) def to_s super + " " \ "This can happen if the remote cookbook has been deleted or if the sources inside the Berksfile have changed. " \ "Please run `berks update #{@name}` to resolve to a valid version." end end # Raised when envFile provided to apply -f is not found class EnvironmentFileNotFound < EnvironmentNotFound set_status_code(150) def intialize(environment_file) @environment_file = environment_file end def to_s "Could not find environment file #{@environment_file}" end end # Git errors # ------------------------------ class GitError < BerkshelfError; set_status_code(400); end class GitNotInstalled < GitError def initialize super "You need to install Git before you can download " \ "cookbooks from git repositories. For more information, please " \ "see the Git docs: http://git-scm.org. If you have git installed, " \ "please make sure it is in your $PATH and accessible by the user " \ "running this command." end end class GitCommandError < GitError def initialize(command, path, stderr = nil) out = "Git error: command `git #{command}` failed. If this error " out << "persists, try removing the cache directory at '#{path}'." if stderr out << "Output from the command:\n\n" out << stderr end super(out) end end end