lib/jamie.rb in jamie-0.1.0.beta2 vs lib/jamie.rb in jamie-0.1.0.beta3
- old
+ new
@@ -56,15 +56,24 @@
def crashes?
! crashes.empty?
end
def default_logger
- env_log = ENV['JAMIE_LOG'] && ENV['JAMIE_LOG'].downcase.to_sym
- env_log = Util.to_logger_level(env_log) unless env_log.nil?
-
Logger.new(:stdout => STDOUT, :level => env_log)
end
+
+ def default_file_logger
+ logfile = File.expand_path(File.join(".jamie", "logs", "jamie.log"))
+ Logger.new(:stdout => STDOUT, :logdev => logfile, :level => env_log)
+ end
+
+ private
+
+ def env_log
+ level = ENV['JAMIE_LOG'] && ENV['JAMIE_LOG'].downcase.to_sym
+ level = Util.to_logger_level(level) unless level.nil?
+ end
end
module Error ; end
# Base exception class from which all Jamie exceptions derive. This class
@@ -254,11 +263,10 @@
:driver => driver,
:jr => Jr.new(suite.name),
:logger => new_instance_logger(index)
}
- FileUtils.mkdir_p(log_root)
new_instance_supervised_or_not(actor_name, opts)
end
def new_instance_supervised_or_not(actor_name, opts)
if supervised
@@ -387,15 +395,17 @@
# @author Fletcher Nichol <fnichol@nichol.ca>
class Logger
include ::Logger::Severity
+ attr_reader :logdev
+
def initialize(options = {})
color = options[:color] || :bright_white
@loggers = []
- @loggers << logdev_logger(options[:logdev]) if options[:logdev]
+ @loggers << @logdev = logdev_logger(options[:logdev]) if options[:logdev]
@loggers << stdout_logger(options[:stdout], color) if options[:stdout]
@loggers << stdout_logger(STDOUT, color) if @loggers.empty?
self.progname = options[:progname] || "Jamie"
self.level = options[:level] || default_log_level
@@ -431,15 +441,16 @@
end
logger
end
def logdev_logger(filepath_or_logdev)
- LogdevLogger.new(logdev(filepath_or_logdev))
+ LogdevLogger.new(resolve_logdev(filepath_or_logdev))
end
- def logdev(filepath_or_logdev)
+ def resolve_logdev(filepath_or_logdev)
if filepath_or_logdev.is_a? String
+ FileUtils.mkdir_p(File.dirname(filepath_or_logdev))
file = File.open(File.expand_path(filepath_or_logdev), "ab")
file.sync = true
file
else
filepath_or_logdev
@@ -752,11 +763,11 @@
destroy
banner "Testing #{to_str}"
verify
destroy if destroy_mode == :passing
end
- info "Finished testing #{to_str} (#{elapsed.real} seconds)."
+ info "Finished testing #{to_str} #{Util.duration(elapsed.real)}."
Actor.current
ensure
destroy if destroy_mode == :always
end
@@ -825,11 +836,11 @@
def perform_action(verb, output_verb)
banner "#{output_verb} #{to_str}"
elapsed = action(verb) { |state| driver.public_send(verb, state) }
info("Finished #{output_verb.downcase} #{to_str}" +
- " (#{elapsed.real} seconds).")
+ " #{Util.duration(elapsed.real)}.")
yield if block_given?
Actor.current
end
def action(what, &block)
@@ -882,10 +893,15 @@
File.expand_path(File.join(
driver[:jamie_root], ".jamie", "#{name}.yml"
))
end
+ def banner(*args)
+ Jamie.logger.logdev && Jamie.logger.logdev.banner(*args)
+ super
+ end
+
# The simplest finite state machine pseudo-implementation needed to manage
# an Instance.
#
# @author Fletcher Nichol <fnichol@nichol.ca>
class FSM
@@ -1104,10 +1120,16 @@
obj.inject([]) { |a, v| a << symbolized_hash(v) ; a }
else
obj
end
end
+
+ def self.duration(total)
+ minutes = (total / 60).to_i
+ seconds = (total - (minutes * 60))
+ "(%dm%.2fs)" % [ minutes, seconds ]
+ end
end
# Mixin that wraps a command shell out invocation, providing a #run_command
# method.
#
@@ -1130,11 +1152,11 @@
subject = "[#{log_subject} command]"
info("#{subject} BEGIN (#{display_cmd(cmd)})")
sh = Mixlib::ShellOut.new(cmd, :live_stream => logger, :timeout => 60000)
sh.run_command
- info("#{subject} END (#{sh.execution_time} seconds)")
+ info("#{subject} END #{Util.duration(sh.execution_time)}")
sh.error!
rescue Mixlib::ShellOut::ShellCommandFailed => ex
raise ShellCommandFailed, ex.message
rescue Exception => error
error.extend(Jamie::Error)
@@ -1162,14 +1184,14 @@
require "jamie/driver/#{plugin}"
str_const = Util.to_camel_case(plugin)
klass = self.const_get(str_const)
klass.new(config)
+ rescue UserError
+ raise
rescue LoadError
raise ClientError, "Could not require '#{plugin}' plugin from load path"
- rescue NameError
- raise ClientError, "No class 'Jamie::Driver::#{str_const}' could be found"
rescue
raise ClientError, "Failed to create a driver for '#{plugin}' plugin"
end
# Base class for a driver. A driver is responsible for carrying out the
@@ -1191,10 +1213,13 @@
def initialize(config = {})
@config = config
self.class.defaults.each do |attr, value|
@config[attr] = value unless @config[attr]
end
+ Array(self.class.validations).each do |tuple|
+ tuple.last.call(tuple.first, config[tuple.first])
+ end
end
# Provides hash-like access to configuration keys.
#
# @param attr [Object] configuration key
@@ -1275,10 +1300,27 @@
def self.default_config(attr, value)
defaults[attr] = value
end
+ def self.validations
+ @validations
+ end
+
+ def self.required_config(attr, &block)
+ @validations = [] if @validations.nil?
+ if ! block_given?
+ klass = self
+ block = lambda do |attr, value|
+ if value.nil? || value.to_s.empty?
+ raise UserError, "#{klass}#config[:#{attr}] cannot be blank"
+ end
+ end
+ end
+ @validations << [ attr, block ]
+ end
+
def self.no_parallel_for(*methods)
Array(methods).each do |meth|
if ! ACTION_METHODS.include?(meth)
raise ClientError, "##{meth} is not a valid no_parallel_for method"
end
@@ -1355,13 +1397,17 @@
def chef_home
"/tmp/jamie-chef-solo".freeze
end
def install_omnibus(ssh_args)
+ flag = config[:require_chef_omnibus]
+ version = flag.is_a?(String) ? "-s -- -v #{flag}" : ""
+
ssh(ssh_args, <<-INSTALL.gsub(/^ {10}/, ''))
if [ ! -d "/opt/chef" ] ; then
- curl -sSL https://www.opscode.com/chef/install.sh | sudo bash
+ curl -sSL https://www.opscode.com/chef/install.sh \
+ | sudo bash #{version}
fi
INSTALL
end
def prepare_chef_home(ssh_args)
@@ -1606,9 +1652,9 @@
# Setup a collection of instance crash exceptions for error reporting
Jamie.crashes = []
Celluloid.exception_handler do |exception|
Jamie.logger.debug("An instance crashed because of #{exception.inspect}")
- Jamie.crashes << exception
+ Jamie.mutex.synchronize { Jamie.crashes << exception }
end
Jamie.mutex = Mutex.new