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<Dependency>] 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<String>] 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 CookbookSyntaxError < BerkshelfError; end
class RedirectLimitReached < BerkshelfError; 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