Skip to content

Commit

Permalink
Reformat exceptions and errors with friendly messages in serial mode.
Browse files Browse the repository at this point in the history
Currently this only handles non-parallel mode.
  • Loading branch information
fnichol committed Apr 20, 2013
1 parent 38c190d commit 8f7f1b9
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 7 deletions.
3 changes: 2 additions & 1 deletion bin/kitchen
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
4 changes: 2 additions & 2 deletions lib/kitchen/driver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
75 changes: 74 additions & 1 deletion lib/kitchen/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
21 changes: 18 additions & 3 deletions lib/kitchen/instance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
#
Expand Down

0 comments on commit 8f7f1b9

Please sign in to comment.