summaryrefslogtreecommitdiff
path: root/knife/lib/exceptions.rb
diff options
context:
space:
mode:
Diffstat (limited to 'knife/lib/exceptions.rb')
-rw-r--r--knife/lib/exceptions.rb565
1 files changed, 565 insertions, 0 deletions
diff --git a/knife/lib/exceptions.rb b/knife/lib/exceptions.rb
new file mode 100644
index 0000000000..ffdbdcbaba
--- /dev/null
+++ b/knife/lib/exceptions.rb
@@ -0,0 +1,565 @@
+#
+# Author:: Adam Jacob (<adam@chef.io>)
+# Author:: Seth Falcon (<seth@chef.io>)
+# Author:: Kyle Goodwin (<kgoodwin@primerevenue.com>)
+# Copyright:: Copyright (c) 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.
+
+require "chef-config/exceptions"
+require "chef-utils/dist" unless defined?(ChefUtils::Dist)
+require_relative "constants"
+
+class Chef
+ # == Chef::Exceptions
+ # Chef's custom exceptions are all contained within the Chef::Exceptions
+ # namespace.
+ class Exceptions
+
+ ConfigurationError = ChefConfig::ConfigurationError
+
+ # Backcompat with Chef::ShellOut code:
+ require "mixlib/shellout/exceptions"
+
+ def self.const_missing(const_name)
+ if const_name == :ShellCommandFailed
+ Chef::Log.warn("Chef::Exceptions::ShellCommandFailed is deprecated, use Mixlib::ShellOut::ShellCommandFailed")
+ called_from = caller[0..3].inject("Called from:\n") { |msg, trace_line| msg << " #{trace_line}\n" }
+ Chef::Log.warn(called_from)
+ Mixlib::ShellOut::ShellCommandFailed
+ else
+ super
+ end
+ end
+
+ class Application < RuntimeError; end
+ class SigInt < RuntimeError; end
+ class SigTerm < RuntimeError; end
+ class Cron < RuntimeError; end
+ class WindowsEnv < RuntimeError; end
+ class Exec < RuntimeError; end
+ class Execute < RuntimeError; end
+ class ErlCall < RuntimeError; end
+ class FileNotFound < RuntimeError; end
+ class Package < RuntimeError; end
+ class Service < RuntimeError; end
+ class Script < RuntimeError; end
+ class Route < RuntimeError; end
+ class SearchIndex < RuntimeError; end
+ class Override < RuntimeError; end
+ class UnsupportedAction < RuntimeError; end
+ class MissingLibrary < RuntimeError; end
+
+ class CannotDetermineNodeName < RuntimeError
+ def initialize
+ super "Unable to determine node name: configure node_name or configure the system's hostname and fqdn"
+ end
+ end
+
+ class User < RuntimeError; end
+ class Group < RuntimeError; end
+ class Link < RuntimeError; end
+ class Mount < RuntimeError; end
+ class Reboot < Exception; end # rubocop:disable Lint/InheritException
+ class RebootPending < Exception; end # rubocop:disable Lint/InheritException
+ class RebootFailed < Mixlib::ShellOut::ShellCommandFailed; end
+ class ClientUpgraded < Exception; end # rubocop:disable Lint/InheritException
+ class PrivateKeyMissing < RuntimeError; end
+ class CannotWritePrivateKey < RuntimeError; end
+ class RoleNotFound < RuntimeError; end
+ class DuplicateRole < RuntimeError; end
+ class ValidationFailed < ArgumentError; end
+ class CannotValidateStaticallyError < ArgumentError; end
+ class InvalidPrivateKey < ArgumentError; end
+ class MissingKeyAttribute < ArgumentError; end
+ class KeyCommandInputError < ArgumentError; end
+
+ class BootstrapCommandInputError < ArgumentError
+ def initialize
+ super "You cannot pass both --json-attributes and --json-attribute-file. Please pass one or none."
+ end
+ end
+
+ class InvalidKeyArgument < ArgumentError; end
+ class InvalidKeyAttribute < ArgumentError; end
+ class InvalidUserAttribute < ArgumentError; end
+ class InvalidClientAttribute < ArgumentError; end
+ class RedirectLimitExceeded < RuntimeError; end
+ class AmbiguousRunlistSpecification < ArgumentError; end
+ class CookbookFrozen < ArgumentError; end
+ class CookbookNotFound < RuntimeError; end
+ class OnlyApiVersion0SupportedForAction < RuntimeError; end
+ # Cookbook loader used to raise an argument error when cookbook not found.
+ # for back compat, need to raise an error that inherits from ArgumentError
+ class CookbookNotFoundInRepo < ArgumentError; end
+ class CookbookMergingError < RuntimeError; end
+ class RecipeNotFound < ArgumentError; end
+ # AttributeNotFound really means the attribute file could not be found
+ class AttributeNotFound < RuntimeError; end
+ # NoSuchAttribute is raised on access by node.read!("foo", "bar") when node["foo"]["bar"] does not exist.
+ class NoSuchAttribute < RuntimeError; end
+ # AttributeTypeMismatch is raised by node.write!("foo", "bar", "baz") when e.g. node["foo"] = "bar" (overwriting String with Hash)
+ class AttributeTypeMismatch < RuntimeError; end
+ class MissingCookbookDependency < StandardError; end # CHEF-5120
+ class InvalidCommandOption < RuntimeError; end
+ class CommandTimeout < RuntimeError; end
+ class RequestedUIDUnavailable < RuntimeError; end
+ class InvalidHomeDirectory < ArgumentError; end
+ class DsclCommandFailed < RuntimeError; end
+ class PlistUtilCommandFailed < RuntimeError; end
+ class UserIDNotFound < ArgumentError; end
+ class GroupIDNotFound < ArgumentError; end
+ class ConflictingMembersInGroup < ArgumentError; end
+ class InvalidResourceReference < RuntimeError; end
+ class ResourceNotFound < RuntimeError; end
+ class ProviderNotFound < RuntimeError; end
+ NoProviderAvailable = ProviderNotFound
+ class VerificationNotFound < RuntimeError; end
+ class InvalidEventType < ArgumentError; end
+ class MultipleIdentityError < RuntimeError; end
+ # Used in Resource::ActionClass#load_current_resource to denote that
+ # the resource doesn't actually exist (for example, the file does not exist)
+ class CurrentValueDoesNotExist < RuntimeError; end
+
+ # Can't find a Resource of this type that is valid on this platform.
+ class NoSuchResourceType < NameError
+ def initialize(short_name, node)
+ super "Cannot find a resource for #{short_name} on #{node[:platform]} version #{node[:platform_version]} with target_mode? #{Chef::Config.target_mode?}"
+ end
+ end
+
+ class InvalidPolicybuilderCall < ArgumentError; end
+
+ class InvalidResourceSpecification < ArgumentError; end
+ class SolrConnectionError < RuntimeError; end
+ class IllegalChecksumRevert < RuntimeError; end
+ class CookbookVersionNameMismatch < ArgumentError; end
+ class MissingParentDirectory < RuntimeError; end
+ class UnresolvableGitReference < RuntimeError; end
+ class InvalidRemoteGitReference < RuntimeError; end
+ class InvalidEnvironmentRunListSpecification < ArgumentError; end
+ class InvalidDataBagItemID < ArgumentError; end
+ class InvalidDataBagName < ArgumentError; end
+ class EnclosingDirectoryDoesNotExist < ArgumentError; end
+ # Errors originating from calls to the Win32 API
+ class Win32APIError < RuntimeError; end
+
+ # Thrown when Win32 API layer binds to non-existent Win32 function. Occurs
+ # when older versions of Windows don't support newer Win32 API functions.
+ class Win32APIFunctionNotImplemented < NotImplementedError; end # rubocop:disable Lint/InheritException
+ # Attempting to run windows code on a not-windows node
+ class Win32NotWindows < RuntimeError; end
+ class WindowsNotAdmin < RuntimeError; end
+ # Attempting to access a 64-bit only resource on a 32-bit Windows system
+ class Win32ArchitectureIncorrect < RuntimeError; end
+ class ObsoleteDependencySyntax < ArgumentError; end
+ class InvalidDataBagPath < ArgumentError; end
+ class DuplicateDataBagItem < RuntimeError; end
+
+ class PowershellCmdletException < RuntimeError; end
+ class LCMParser < RuntimeError; end
+
+ class CannotDetermineHomebrewOwner < Package; end
+ class CannotDetermineWindowsInstallerType < Package; end
+ class NoWindowsPackageSource < Package; end
+
+ # for example, if both recipes/default.yml, recipes/default.yaml are present
+ class AmbiguousYAMLFile < RuntimeError; end
+
+ # Can not create staging file during file deployment
+ class FileContentStagingError < RuntimeError
+ def initialize(errors)
+ super "Staging tempfile can not be created during file deployment.\n Errors: #{errors.join('\n')}!"
+ end
+ end
+
+ # A different version of a cookbook was added to a
+ # VersionedRecipeList than the one already there.
+ class CookbookVersionConflict < ArgumentError; end
+
+ # does not follow X.Y.Z format. ArgumentError?
+ class InvalidPlatformVersion < ArgumentError; end
+ class InvalidCookbookVersion < ArgumentError; end
+
+ # version constraint should be a string or array, or it doesn't
+ # match OP VERSION. ArgumentError?
+ class InvalidVersionConstraint < ArgumentError; end
+
+ # Version constraints are not allowed in chef-solo
+ class IllegalVersionConstraint < NotImplementedError; end # rubocop:disable Lint/InheritException
+
+ class MetadataNotValid < StandardError; end
+
+ class MetadataNotFound < StandardError
+ attr_reader :install_path
+ attr_reader :cookbook_name
+
+ def initialize(install_path, cookbook_name)
+ @install_path = install_path
+ @cookbook_name = cookbook_name
+ super "No metadata.rb or metadata.json found for cookbook #{@cookbook_name} in #{@install_path}"
+ end
+ end
+
+ # File operation attempted but no permissions to perform it
+ class InsufficientPermissions < RuntimeError; end
+
+ # Ifconfig failed
+ class Ifconfig < RuntimeError; end
+
+ # Invalid "source" parameter to a remote_file resource
+ class InvalidRemoteFileURI < ArgumentError; end
+
+ # Node::Attribute computes the merged version of of attributes
+ # and makes it read-only. Attempting to modify a read-only
+ # attribute will cause this error.
+ class ImmutableAttributeModification < NoMethodError
+ def initialize
+ super "Node attributes are read-only when you do not specify which precedence level to set. " +
+ %q{To set an attribute use code like `node.default["key"] = "value"'}
+ end
+ end
+
+ # Merged node attributes are invalidated when the component
+ # attributes are updated. Attempting to read from a stale copy
+ # of merged attributes will trigger this error.
+ class StaleAttributeRead < StandardError; end
+
+ # Registry Helper throws the following errors
+ class Win32RegArchitectureIncorrect < Win32ArchitectureIncorrect; end
+ class Win32RegHiveMissing < ArgumentError; end
+ class Win32RegKeyMissing < RuntimeError; end
+ class Win32RegValueMissing < RuntimeError; end
+ class Win32RegDataMissing < RuntimeError; end
+ class Win32RegValueExists < RuntimeError; end
+ class Win32RegNoRecursive < ArgumentError; end
+ class Win32RegTypeDoesNotExist < ArgumentError; end
+ class Win32RegBadType < ArgumentError; end
+ class Win32RegBadValueSize < ArgumentError; end
+ class Win32RegTypesMismatch < ArgumentError; end
+
+ # incorrect input for registry_key create action throws following error
+ class RegKeyValuesTypeMissing < ArgumentError; end
+ class RegKeyValuesDataMissing < ArgumentError; end
+
+ class InvalidEnvironmentPath < ArgumentError; end
+ class EnvironmentNotFound < RuntimeError; end
+
+ # File-like resource found a non-file (socket, pipe, directory, etc) at its destination
+ class FileTypeMismatch < RuntimeError; end
+
+ # File (or descendent) resource configured to manage symlink source, but
+ # the symlink that is there either loops or points to a nonexistent file
+ class InvalidSymlink < RuntimeError; end
+
+ class ChildConvergeError < RuntimeError; end
+
+ class DeprecatedFeatureError < RuntimeError
+ def initialize(message)
+ super("#{message} (raising error due to treat_deprecation_warnings_as_errors being set)")
+ end
+ end
+
+ class MissingRole < RuntimeError
+ attr_reader :expansion
+
+ def initialize(message_or_expansion = NOT_PASSED)
+ @expansion = nil
+ case message_or_expansion
+ when NOT_PASSED
+ super()
+ when String
+ super
+ when RunList::RunListExpansion
+ @expansion = message_or_expansion
+ missing_roles = @expansion.errors.join(", ")
+ super("The expanded run list includes nonexistent roles: #{missing_roles}")
+ end
+ end
+
+ end
+
+ class Secret
+ class RetrievalError < RuntimeError; end
+ class ConfigurationInvalid < RuntimeError; end
+ class FetchFailed < RuntimeError; end
+ class MissingSecretName < RuntimeError; end
+ class InvalidSecretName < RuntimeError; end
+
+ class InvalidFetcherService < RuntimeError
+ def initialize(given, fetcher_service_names)
+ super("#{given} is not a supported secrets service. Supported services are: :#{fetcher_service_names.join(" :")}")
+ end
+ end
+
+ class MissingFetcher < RuntimeError
+ def initialize(fetcher_service_names)
+ super("No secret service provided. Supported services are: :#{fetcher_service_names.join(" :")}")
+ end
+ end
+
+ class Azure
+ class IdentityNotFound < RuntimeError
+ def initialize
+ super("The managed identity could not be found. This could mean one of the following things:\n\n" \
+ " 1. The VM has no system or user assigned identities.\n" \
+ " 2. The managed identity object_id or client_id that was specified is not assigned to the VM.\n")
+ end
+ end
+ end
+ end
+
+ # Exception class for collecting multiple failures. Used when running
+ # delayed notifications so that chef can process each delayed
+ # notification even if chef client or other notifications fail.
+ class MultipleFailures < StandardError
+ def initialize(*args)
+ super
+ @all_failures = []
+ end
+
+ def message
+ base = "Multiple failures occurred:\n"
+ @all_failures.inject(base) do |message, (location, error)|
+ message << "* #{error.class} occurred in #{location}: #{error.message}\n"
+ end
+ end
+
+ def client_run_failure(exception)
+ set_backtrace(exception.backtrace)
+ @all_failures << [ "#{ChefUtils::Dist::Infra::PRODUCT} run", exception ]
+ end
+
+ def notification_failure(exception)
+ @all_failures << [ "delayed notification", exception ]
+ end
+
+ def raise!
+ unless empty?
+ raise for_raise
+ end
+ end
+
+ def empty?
+ @all_failures.empty?
+ end
+
+ def for_raise
+ if @all_failures.size == 1
+ @all_failures[0][1]
+ else
+ self
+ end
+ end
+ end
+
+ class CookbookVersionSelection
+
+ # Compound exception: In run_list expansion and resolution,
+ # run_list items referred to cookbooks that don't exist and/or
+ # have no versions available.
+ class InvalidRunListItems < StandardError
+ attr_reader :non_existent_cookbooks
+ attr_reader :cookbooks_with_no_matching_versions
+
+ def initialize(message, non_existent_cookbooks, cookbooks_with_no_matching_versions)
+ super(message)
+
+ @non_existent_cookbooks = non_existent_cookbooks
+ @cookbooks_with_no_matching_versions = cookbooks_with_no_matching_versions
+ end
+
+ def to_json(*a)
+ result = {
+ "message" => message,
+ "non_existent_cookbooks" => non_existent_cookbooks,
+ "cookbooks_with_no_versions" => cookbooks_with_no_matching_versions,
+ }
+ Chef::JSONCompat.to_json(result, *a)
+ end
+ end
+
+ # In run_list expansion and resolution, a constraint was
+ # unsatisfiable.
+ #
+ # This exception may not be the complete error report. If you
+ # resolve the misconfiguration represented by this exception and
+ # re-solve, you may get another exception
+ class UnsatisfiableRunListItem < StandardError
+ attr_reader :run_list_item
+ attr_reader :non_existent_cookbooks, :most_constrained_cookbooks
+
+ # most_constrained_cookbooks: if I were to remove constraints
+ # regarding these cookbooks, I would get a solution or move on
+ # to the next error (deeper in the graph). An item in this list
+ # may be unsatisfiable, but when resolved may also reveal
+ # further unsatisfiable constraints; this condition would not be
+ # reported.
+ def initialize(message, run_list_item, non_existent_cookbooks, most_constrained_cookbooks)
+ super(message)
+
+ @run_list_item = run_list_item
+ @non_existent_cookbooks = non_existent_cookbooks
+ @most_constrained_cookbooks = most_constrained_cookbooks
+ end
+
+ def to_json(*a)
+ result = {
+ "message" => message,
+ "unsatisfiable_run_list_item" => run_list_item,
+ "non_existent_cookbooks" => non_existent_cookbooks,
+ "most_constrained_cookbooks" => most_constrained_cookbooks,
+ }
+ Chef::JSONCompat.to_json(result, *a)
+ end
+ end
+
+ end # CookbookVersionSelection
+
+ # When the server sends a redirect, RFC 2616 states a user-agent should
+ # not follow it with a method other than GET or HEAD, unless a specific
+ # action is taken by the user. A redirect received as response to a
+ # non-GET and non-HEAD request will thus raise an InvalidRedirect.
+ class InvalidRedirect < StandardError; end
+
+ # Raised when the content length of a download does not match the content
+ # length declared in the http response.
+ class ContentLengthMismatch < RuntimeError
+ def initialize(response_length, content_length)
+ super <<~EOF
+ Response body length #{response_length} does not match HTTP Content-Length header #{content_length}.
+ This error is most often caused by network issues (proxies, etc) outside of #{ChefUtils::Dist::Infra::CLIENT}.
+ EOF
+ end
+ end
+
+ class UnsupportedPlatform < RuntimeError
+ def initialize(platform)
+ super "This functionality is not supported on platform #{platform}."
+ end
+ end
+
+ # Raised when Chef::Config[:run_lock_timeout] is set and some other client run fails
+ # to release the run lock before Chef::Config[:run_lock_timeout] seconds pass.
+ class RunLockTimeout < RuntimeError
+ def initialize(duration, blocking_pid)
+ super "Unable to acquire lock. Waited #{duration} seconds for #{blocking_pid} to release."
+ end
+ end
+
+ class ChecksumMismatch < RuntimeError
+ def initialize(res_cksum, cont_cksum)
+ super "Checksum on resource (#{res_cksum}...) does not match checksum on content (#{cont_cksum}...)"
+ end
+ end
+
+ class BadProxyURI < RuntimeError; end
+
+ # Raised by Chef::JSONCompat
+ class JSON
+ class EncodeError < RuntimeError; end
+ class ParseError < RuntimeError; end
+ end
+
+ class InvalidSearchQuery < ArgumentError; end
+
+ # Raised by Chef::ProviderResolver
+ class AmbiguousProviderResolution < RuntimeError
+ def initialize(resource, classes)
+ super "Found more than one provider for #{resource.resource_name} resource: #{classes}"
+ end
+ end
+
+ # If a converge fails, we want to wrap the output from those errors into 1 error so we can
+ # see both issues in the output. It is possible that nil will be provided. You must call `fill_backtrace`
+ # to correctly populate the backtrace with the wrapped backtraces.
+ class RunFailedWrappingError < RuntimeError
+ attr_reader :wrapped_errors
+
+ def initialize(*errors)
+ errors = errors.compact
+ output = "Found #{errors.size} errors, they are stored in the backtrace"
+ @wrapped_errors = errors
+ super output
+ end
+
+ def fill_backtrace
+ backtrace = []
+ wrapped_errors.each_with_index do |e, i|
+ backtrace << "#{i + 1}) #{e.class} - #{e.message}"
+ backtrace += e.backtrace if e.backtrace
+ backtrace << "" unless i == wrapped_errors.length - 1
+ end
+ set_backtrace(backtrace)
+ end
+ end
+
+ class PIDFileLockfileMatch < RuntimeError
+ def initialize
+ super "PID file and lockfile are not permitted to match. Specify a different location with --pid or --lockfile"
+ end
+ end
+
+ class CookbookChefVersionMismatch < RuntimeError
+ def initialize(chef_version, cookbook_name, cookbook_version, *constraints)
+ constraint_str = constraints.map { |c| c.requirement.as_list.to_s }.join(", ")
+ super "Cookbook '#{cookbook_name}' version '#{cookbook_version}' depends on #{ChefUtils::Dist::Infra::PRODUCT} version #{constraint_str}, but the running #{ChefUtils::Dist::Infra::PRODUCT} version is #{chef_version}"
+ end
+ end
+
+ class CookbookOhaiVersionMismatch < RuntimeError
+ def initialize(ohai_version, cookbook_name, cookbook_version, *constraints)
+ constraint_str = constraints.map { |c| c.requirement.as_list.to_s }.join(", ")
+ super "Cookbook '#{cookbook_name}' version '#{cookbook_version}' depends on ohai version #{constraint_str}, but the running ohai version is #{ohai_version}"
+ end
+ end
+
+ class MultipleDscResourcesFound < RuntimeError
+ attr_reader :resources_found
+
+ def initialize(resources_found)
+ @resources_found = resources_found
+ matches_info = @resources_found.each do |r|
+ if r["Module"].nil?
+ "Resource #{r["Name"]} was found in #{r["Module"]["Name"]}"
+ else
+ "Resource #{r["Name"]} is a binary resource"
+ end
+ end
+ super "Found multiple resources matching #{matches_info[0]["Module"]["Name"]}:\n#{(matches_info.map { |f| f["Module"]["Version"] }).uniq.join("\n")}"
+ end
+ end
+
+ # exception specific to invalid usage of 'dsc_resource' resource
+ class DSCModuleNameMissing < ArgumentError; end
+
+ class GemRequirementConflict < RuntimeError
+ def initialize(gem_name, option, value1, value2)
+ super "Conflicting requirements for gem '#{gem_name}': Both #{value1.inspect} and #{value2.inspect} given for option #{option.inspect}"
+ end
+ end
+
+ class UnifiedModeImmediateSubscriptionEarlierResource < RuntimeError
+ def initialize(notification)
+ super "immediate subscription from #{notification.resource} resource cannot be setup to #{notification.notifying_resource} resource, which has already fired while in unified mode"
+ end
+ end
+
+ class UnifiedModeBeforeSubscriptionEarlierResource < RuntimeError
+ def initialize(notification)
+ super "before subscription from #{notification.resource} resource cannot be setup to #{notification.notifying_resource} resource, which has already fired while in unified mode"
+ end
+ end
+ end
+end