Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Reformat exceptions and errors with friendly messages in serial mode.

Currently this only handles non-parallel mode.
  • Loading branch information...
commit 8f7f1b974cf1f59a781eb3d899fcee034942da0e 1 parent 38c190d
@fnichol fnichol authored
View
3  bin/kitchen
@@ -8,5 +8,6 @@ Signal.trap("INT") { exit 1 }
$:.unshift File.join(File.dirname(__FILE__), %w{.. lib})
require 'rubygems'
require 'kitchen/cli'
+require 'kitchen/errors'
-Kitchen::CLI.start
+Kitchen.with_friendly_errors { Kitchen::CLI.start }
View
4 lib/kitchen/driver.rb
@@ -41,8 +41,8 @@ def self.for_plugin(plugin, config)
rescue LoadError, NameError
raise ClientError,
"Could not load the '#{plugin}' driver from the load path." +
- " Please ensure that your driver is installed as a gem or included" +
- " in your Gemfile if using Bundler."
+ " Please ensure that your driver is installed as a gem or included" +
+ " in your Gemfile if using Bundler."
end
end
end
View
75 lib/kitchen/errors.rb
@@ -18,7 +18,28 @@
module Kitchen
- module Error ; end
+ module Error
+
+ def self.formatted_trace(exception)
+ arr = formatted_exception(exception).dup
+ last = arr.pop
+ if exception.respond_to?(:original) && exception.original
+ arr += formatted_exception(exception.original, "Nested Exception")
+ last = arr.pop
+ end
+ arr += ["Backtrace".center(22, "-"), exception.backtrace, last].flatten
+ arr
+ end
+
+ def self.formatted_exception(exception, title = "Exception")
+ [
+ title.center(22, "-"),
+ "Class: #{exception.class}",
+ "Message: #{exception.message}",
+ "".center(22, "-"),
+ ]
+ end
+ end
# Base exception class from which all Kitchen exceptions derive. This class
# nests an exception when this class is re-raised from a rescue block.
@@ -49,4 +70,56 @@ class TransientFailure < StandardError ; end
# Exception class for any exceptions raised when performing an instance
# action.
class ActionFailed < TransientFailure ; end
+
+ # Exception class capturing what caused an instance to die.
+ class InstanceFailure < TransientFailure ; end
+
+ def self.with_friendly_errors
+ yield
+ rescue Kitchen::InstanceFailure => e
+ Kitchen.mutex.synchronize do
+ handle_instance_failure(e)
+ end
+ exit 10
+ rescue Kitchen::Error => e
+ Kitchen.mutex.synchronize do
+ handle_error(e)
+ end
+ exit 20
+ end
+
+ private
+
+ def self.file_log(level, lines)
+ Array(lines).each do |line|
+ if Kitchen.logger.debug?
+ Kitchen.logger.debug(line)
+ else
+ Kitchen.logger.logdev && Kitchen.logger.logdev.public_send(level, line)
+ end
+ end
+ end
+
+ def self.stderr_log(lines)
+ Array(lines).each do |line|
+ $stderr.puts(Color.colorize(">>>>>> #{line}", :red))
+ end
+ end
+
+ def self.debug_log(lines)
+ Array(lines).each { |line| Kitchen.logger.debug(line) }
+ end
+
+ def self.handle_instance_failure(e)
+ stderr_log(e.message.split("\n"))
+ stderr_log(Error.formatted_exception(e.original))
+ file_log(:error, e.message.split("\n").first)
+ debug_log(Error.formatted_trace(e))
+ end
+
+ def self.handle_error(e)
+ stderr_log(Error.formatted_exception(e))
+ stderr_log("Please see .kitchen/logs/kitchen.log for more details\n")
+ file_log(:error, Error.formatted_trace(e))
+ end
end
View
21 lib/kitchen/instance.rb
@@ -260,10 +260,14 @@ def action(what, &block)
end
state[:last_action] = what.to_s
elapsed
- rescue ActionFailed
- raise
+ rescue ActionFailed => e
+ log_failure(what, e)
+ raise InstanceFailure, failure_message(what) +
+ "\nPlease see .kitchen/logs/#{self.name}.log for more details", caller
rescue Exception => e
- raise ActionFailed, "Failed to complete ##{what} action: [#{e.message}]"
+ log_failure(what, e)
+ raise ActionFailed,
+ "Failed to complete ##{what} action: [#{e.message}]", caller
ensure
state_file.write(state)
end
@@ -289,6 +293,17 @@ def banner(*args)
super
end
+ def log_failure(what, e)
+ return if logger.logdev.nil?
+
+ logger.logdev.error(failure_message(what))
+ Error.formatted_trace(e).each { |line| logger.logdev.error(line) }
+ end
+
+ def failure_message(what)
+ "#{what.capitalize} failed on instance #{self.to_str}"
+ end
+
# The simplest finite state machine pseudo-implementation needed to manage
# an Instance.
#
Please sign in to comment.
Something went wrong with that request. Please try again.