diff --git a/lib/puppet/util/log/destinations.rb b/lib/puppet/util/log/destinations.rb
index 22b3dedb2..2e2f9a5b7 100644
--- a/lib/puppet/util/log/destinations.rb
+++ b/lib/puppet/util/log/destinations.rb
@@ -1,217 +1,229 @@
Puppet::Util::Log.newdesttype :syslog do
def close
Syslog.close
end
def initialize
Syslog.close if Syslog.opened?
name = Puppet[:name]
name = "puppet-#{name}" unless name =~ /puppet/
options = Syslog::LOG_PID | Syslog::LOG_NDELAY
# XXX This should really be configurable.
str = Puppet[:syslogfacility]
begin
facility = Syslog.const_get("LOG_#{str.upcase}")
rescue NameError
raise Puppet::Error, "Invalid syslog facility #{str}"
end
@syslog = Syslog.open(name, options, facility)
end
def handle(msg)
# XXX Syslog currently has a bug that makes it so you
# cannot log a message with a '%' in it. So, we get rid
# of them.
if msg.source == "Puppet"
@syslog.send(msg.level, msg.to_s.gsub("%", '%%'))
else
@syslog.send(msg.level, "(%s) %s" % [msg.source.to_s.gsub("%", ""),
msg.to_s.gsub("%", '%%')
]
)
end
end
end
Puppet::Util::Log.newdesttype :file do
match(/^\//)
def close
if defined?(@file)
@file.close
@file = nil
end
end
def flush
@file.flush if defined?(@file)
end
def initialize(path)
@name = path
# first make sure the directory exists
# We can't just use 'Config.use' here, because they've
# specified a "special" destination.
unless FileTest.exist?(File.dirname(path))
Puppet.recmkdir(File.dirname(path))
Puppet.info "Creating log directory #{File.dirname(path)}"
end
# create the log file, if it doesn't already exist
file = File.open(path, File::WRONLY|File::CREAT|File::APPEND)
@file = file
@autoflush = Puppet[:autoflush]
end
def handle(msg)
@file.puts("#{msg.time} #{msg.source} (#{msg.level}): #{msg}")
@file.flush if @autoflush
end
end
Puppet::Util::Log.newdesttype :console do
RED = {:console => "[0;31m", :html => "FFA0A0"}
GREEN = {:console => "[0;32m", :html => "00CD00"}
YELLOW = {:console => "[0;33m", :html => "FFFF60"}
BLUE = {:console => "[0;34m", :html => "80A0FF"}
PURPLE = {:console => "[0;35m", :html => "FFA500"}
CYAN = {:console => "[0;36m", :html => "40FFFF"}
WHITE = {:console => "[0;37m", :html => "FFFFFF"}
HRED = {:console => "[1;31m", :html => "FFA0A0"}
HGREEN = {:console => "[1;32m", :html => "00CD00"}
HYELLOW = {:console => "[1;33m", :html => "FFFF60"}
HBLUE = {:console => "[1;34m", :html => "80A0FF"}
HPURPLE = {:console => "[1;35m", :html => "FFA500"}
HCYAN = {:console => "[1;36m", :html => "40FFFF"}
HWHITE = {:console => "[1;37m", :html => "FFFFFF"}
RESET = {:console => "[0m", :html => "" }
@@colormap = {
:debug => WHITE,
:info => GREEN,
:notice => CYAN,
:warning => YELLOW,
:err => HPURPLE,
:alert => RED,
:emerg => HRED,
:crit => HRED
}
def colorize(level, str)
case Puppet[:color]
when true, :ansi, "ansi", "yes"; console_color(level, str)
when :html, "html"; html_color(level, str)
else
str
end
end
def console_color(level, str)
@@colormap[level][:console] + str + RESET[:console]
end
def html_color(level, str)
%{%s} % [@@colormap[level][:html], str]
end
def initialize
# Flush output immediately.
$stdout.sync = true
end
def handle(msg)
if msg.source == "Puppet"
puts colorize(msg.level, "#{msg.level}: #{msg}")
else
puts colorize(msg.level, "#{msg.level}: #{msg.source}: #{msg}")
end
end
end
Puppet::Util::Log.newdesttype :host do
def initialize(host)
Puppet.info "Treating #{host} as a hostname"
args = {}
if host =~ /:(\d+)/
args[:Port] = $1
args[:Server] = host.sub(/:\d+/, '')
else
args[:Server] = host
end
@name = host
@driver = Puppet::Network::Client::LogClient.new(args)
end
def handle(msg)
unless msg.is_a?(String) or msg.remote
@hostname ||= Facter["hostname"].value
unless defined?(@domain)
@domain = Facter["domain"].value
@hostname += ".#{@domain}" if @domain
end
if msg.source =~ /^\//
msg.source = @hostname + ":#{msg.source}"
elsif msg.source == "Puppet"
msg.source = @hostname + " #{msg.source}"
else
msg.source = @hostname + " #{msg.source}"
end
begin
#puts "would have sent #{msg}"
#puts "would have sent %s" %
# CGI.escape(YAML.dump(msg))
begin
tmp = CGI.escape(YAML.dump(msg))
rescue => detail
puts "Could not dump: #{detail}"
return
end
# Add the hostname to the source
@driver.addlog(tmp)
rescue => detail
puts detail.backtrace if Puppet[:trace]
Puppet.err detail
Puppet::Util::Log.close(self)
end
end
end
end
# Log to a transaction report.
Puppet::Util::Log.newdesttype :report do
attr_reader :report
match "Puppet::Transaction::Report"
def initialize(report)
@report = report
end
def handle(msg)
@report << msg
end
end
# Log to an array, just for testing.
+module Puppet::Test
+ class LogCollector
+ def initialize(logs)
+ @logs = logs
+ end
+
+ def <<(value)
+ @logs << value
+ end
+ end
+end
+
Puppet::Util::Log.newdesttype :array do
- match "Array"
+ match "Puppet::Test::LogCollector"
def initialize(messages)
@messages = messages
end
def handle(msg)
@messages << msg
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index ed4e2c2fb..0c4b076f4 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,79 +1,79 @@
unless defined?(SPEC_HELPER_IS_LOADED)
SPEC_HELPER_IS_LOADED = 1
dir = File.expand_path(File.dirname(__FILE__))
$LOAD_PATH.unshift("#{dir}/")
$LOAD_PATH.unshift("#{dir}/lib") # a spec-specific test lib dir
$LOAD_PATH.unshift("#{dir}/../lib")
# Don't want puppet getting the command line arguments for rake or autotest
ARGV.clear
require 'puppet'
require 'mocha'
gem 'rspec', '>=1.2.9'
require 'spec/autorun'
# So everyone else doesn't have to include this base constant.
module PuppetSpec
FIXTURE_DIR = File.join(dir = File.expand_path(File.dirname(__FILE__)), "fixtures") unless defined?(FIXTURE_DIR)
end
require 'lib/puppet_spec/files'
require 'monkey_patches/alias_should_to_must'
require 'monkey_patches/add_confine_and_runnable_to_rspec_dsl'
require 'monkey_patches/publicize_methods'
Spec::Runner.configure do |config|
config.mock_with :mocha
config.prepend_after :each do
Puppet.settings.clear
Puppet::Node::Environment.clear
Puppet::Util::Storage.clear
if defined?($tmpfiles)
$tmpfiles.each do |file|
file = File.expand_path(file)
if Puppet.features.posix? and file !~ /^\/tmp/ and file !~ /^\/var\/folders/
puts "Not deleting tmpfile #{file} outside of /tmp or /var/folders"
next
elsif Puppet.features.microsoft_windows?
tempdir = File.expand_path(File.join(Dir::LOCAL_APPDATA, "Temp"))
if file !~ /^#{tempdir}/
puts "Not deleting tmpfile #{file} outside of #{tempdir}"
next
end
end
if FileTest.exist?(file)
system("chmod -R 755 '#{file}'")
system("rm -rf '#{file}'")
end
end
$tmpfiles.clear
end
@logs.clear
Puppet::Util::Log.close_all
end
config.prepend_before :each do
# these globals are set by Application
$puppet_application_mode = nil
$puppet_application_name = nil
# Set the confdir and vardir to gibberish so that tests
# have to be correctly mocked.
Puppet[:confdir] = "/dev/null"
Puppet[:vardir] = "/dev/null"
# Avoid opening ports to the outside world
Puppet.settings[:bindaddress] = "127.0.0.1"
@logs = []
- Puppet::Util::Log.newdestination(@logs)
+ Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(@logs))
end
end
end
diff --git a/spec/unit/util/log_spec.rb b/spec/unit/util/log_spec.rb
index 4cec7105d..ea5d59859 100755
--- a/spec/unit/util/log_spec.rb
+++ b/spec/unit/util/log_spec.rb
@@ -1,215 +1,215 @@
#!/usr/bin/env ruby
Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") }
require 'puppet/util/log'
describe Puppet::Util::Log do
it "should write a given message to the specified destination" do
arraydest = []
- Puppet::Util::Log.newdestination(arraydest)
+ Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(arraydest))
Puppet::Util::Log.new(:level => :notice, :message => "foo")
message = arraydest.last.message
message.should == "foo"
end
describe Puppet::Util::Log::DestConsole do
before do
@console = Puppet::Util::Log::DestConsole.new
end
it "should colorize if Puppet[:color] is :ansi" do
Puppet[:color] = :ansi
@console.colorize(:alert, "abc").should == "\e[0;31mabc\e[0m"
end
it "should colorize if Puppet[:color] is 'yes'" do
Puppet[:color] = "yes"
@console.colorize(:alert, "abc").should == "\e[0;31mabc\e[0m"
end
it "should htmlize if Puppet[:color] is :html" do
Puppet[:color] = :html
@console.colorize(:alert, "abc").should == "abc"
end
it "should do nothing if Puppet[:color] is false" do
Puppet[:color] = false
@console.colorize(:alert, "abc").should == "abc"
end
it "should do nothing if Puppet[:color] is invalid" do
Puppet[:color] = "invalid option"
@console.colorize(:alert, "abc").should == "abc"
end
end
describe "instances" do
before do
Puppet::Util::Log.stubs(:newmessage)
end
[:level, :message, :time, :remote].each do |attr|
it "should have a #{attr} attribute" do
log = Puppet::Util::Log.new :level => :notice, :message => "A test message"
log.should respond_to(attr)
log.should respond_to(attr.to_s + "=")
end
end
it "should fail if created without a level" do
lambda { Puppet::Util::Log.new(:message => "A test message") }.should raise_error(ArgumentError)
end
it "should fail if created without a message" do
lambda { Puppet::Util::Log.new(:level => :notice) }.should raise_error(ArgumentError)
end
it "should make available the level passed in at initialization" do
Puppet::Util::Log.new(:level => :notice, :message => "A test message").level.should == :notice
end
it "should make available the message passed in at initialization" do
Puppet::Util::Log.new(:level => :notice, :message => "A test message").message.should == "A test message"
end
# LAK:NOTE I don't know why this behavior is here, I'm just testing what's in the code,
# at least at first.
it "should always convert messages to strings" do
Puppet::Util::Log.new(:level => :notice, :message => :foo).message.should == "foo"
end
it "should flush the log queue when the first destination is specified" do
Puppet::Util::Log.close_all
Puppet::Util::Log.expects(:flushqueue)
Puppet::Util::Log.newdestination(:console)
end
it "should convert the level to a symbol if it's passed in as a string" do
Puppet::Util::Log.new(:level => "notice", :message => :foo).level.should == :notice
end
it "should fail if the level is not a symbol or string" do
lambda { Puppet::Util::Log.new(:level => 50, :message => :foo) }.should raise_error(ArgumentError)
end
it "should fail if the provided level is not valid" do
Puppet::Util::Log.expects(:validlevel?).with(:notice).returns false
lambda { Puppet::Util::Log.new(:level => :notice, :message => :foo) }.should raise_error(ArgumentError)
end
it "should set its time to the initialization time" do
time = mock 'time'
Time.expects(:now).returns time
Puppet::Util::Log.new(:level => "notice", :message => :foo).time.should equal(time)
end
it "should make available any passed-in tags" do
log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :tags => %w{foo bar})
log.tags.should be_include("foo")
log.tags.should be_include("bar")
end
it "should use an passed-in source" do
Puppet::Util::Log.any_instance.expects(:source=).with "foo"
Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => "foo")
end
[:file, :line, :version].each do |attr|
it "should use #{attr} if provided" do
Puppet::Util::Log.any_instance.expects(attr.to_s + "=").with "foo"
Puppet::Util::Log.new(:level => "notice", :message => :foo, attr => "foo")
end
end
it "should default to 'Puppet' as its source" do
Puppet::Util::Log.new(:level => "notice", :message => :foo).source.should == "Puppet"
end
it "should register itself with Log" do
Puppet::Util::Log.expects(:newmessage)
Puppet::Util::Log.new(:level => "notice", :message => :foo)
end
it "should have a method for determining if a tag is present" do
Puppet::Util::Log.new(:level => "notice", :message => :foo).should respond_to(:tagged?)
end
it "should match a tag if any of the tags are equivalent to the passed tag as a string" do
Puppet::Util::Log.new(:level => "notice", :message => :foo, :tags => %w{one two}).should be_tagged(:one)
end
it "should tag itself with its log level" do
Puppet::Util::Log.new(:level => "notice", :message => :foo).should be_tagged(:notice)
end
it "should return its message when converted to a string" do
Puppet::Util::Log.new(:level => "notice", :message => :foo).to_s.should == "foo"
end
it "should include its time, source, level, and message when prepared for reporting" do
log = Puppet::Util::Log.new(:level => "notice", :message => :foo)
report = log.to_report
report.should be_include("notice")
report.should be_include("foo")
report.should be_include(log.source)
report.should be_include(log.time.to_s)
end
describe "when setting the source as a RAL object" do
it "should tag itself with any tags the source has" do
source = Puppet::Type.type(:file).new :path => "/foo/bar"
log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => source)
source.tags.each do |tag|
log.tags.should be_include(tag)
end
end
it "should use the source_descriptors" do
source = stub "source"
source.stubs(:source_descriptors).returns(:tags => ["tag","tag2"], :path => "path", :version => 100)
log = Puppet::Util::Log.new(:level => "notice", :message => :foo)
log.expects(:tag).with("tag")
log.expects(:tag).with("tag2")
log.expects(:version=).with(100)
log.source = source
log.source.should == "path"
end
it "should copy over any version information" do
catalog = Puppet::Resource::Catalog.new
catalog.version = 25
source = Puppet::Type.type(:file).new :path => "/foo/bar"
catalog.add_resource source
log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => source)
log.version.should == 25
end
it "should copy over any file and line information" do
source = Puppet::Type.type(:file).new :path => "/foo/bar"
source.file = "/my/file"
source.line = 50
log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => source)
log.file.should == "/my/file"
log.line.should == 50
end
end
describe "when setting the source as a non-RAL object" do
it "should not try to copy over file, version, line, or tag information" do
source = Puppet::Module.new("foo")
source.expects(:file).never
log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => source)
end
end
end
end
diff --git a/test/lib/puppettest.rb b/test/lib/puppettest.rb
index 0b3a89a72..a60092cf7 100755
--- a/test/lib/puppettest.rb
+++ b/test/lib/puppettest.rb
@@ -1,319 +1,319 @@
# Add .../test/lib
testlib = File.expand_path(File.dirname(__FILE__))
$LOAD_PATH.unshift(testlib) unless $LOAD_PATH.include?(testlib)
# Add .../lib
mainlib = File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
$LOAD_PATH.unshift(mainlib) unless $LOAD_PATH.include?(mainlib)
require 'puppet'
require 'mocha'
# Only load the test/unit class if we're not in the spec directory.
# Else we get the bogus 'no tests, no failures' message.
unless Dir.getwd =~ /spec/
require 'test/unit'
end
# Yay; hackish but it works
if ARGV.include?("-d")
ARGV.delete("-d")
$console = true
end
require File.expand_path(File.join(File.dirname(__FILE__), '../../spec/monkey_patches/publicize_methods'))
module PuppetTest
# These need to be here for when rspec tests use these
# support methods.
@@tmpfiles = []
# Munge cli arguments, so we can enable debugging if we want
# and so we can run just specific methods.
def self.munge_argv
require 'getoptlong'
result = GetoptLong.new(
[ "--debug", "-d", GetoptLong::NO_ARGUMENT ],
[ "--resolve", "-r", GetoptLong::REQUIRED_ARGUMENT ],
[ "-n", GetoptLong::REQUIRED_ARGUMENT ],
[ "--help", "-h", GetoptLong::NO_ARGUMENT ]
)
usage = "USAGE: TESTOPTS='[-n -n ...] [-d]' rake [target] [target] ..."
opts = []
dir = method = nil
result.each { |opt,arg|
case opt
when "--resolve"
dir, method = arg.split(",")
when "--debug"
$puppet_debug = true
Puppet::Util::Log.level = :debug
Puppet::Util::Log.newdestination(:console)
when "--help"
puts usage
exit
else
opts << opt << arg
end
}
suites = nil
args = ARGV.dup
# Reset the options, so the test suite can deal with them (this is
# what makes things like '-n' work).
opts.each { |o| ARGV << o }
args
end
# Find the root of the Puppet tree; this is not the test directory, but
# the parent of that dir.
def basedir(*list)
unless defined?(@@basedir)
Dir.chdir(File.dirname(__FILE__)) do
@@basedir = File.dirname(File.dirname(Dir.getwd))
end
end
if list.empty?
@@basedir
else
File.join(@@basedir, *list)
end
end
def datadir(*list)
File.join(basedir, "test", "data", *list)
end
def exampledir(*args)
@@exampledir = File.join(basedir, "examples") unless defined?(@@exampledir)
if args.empty?
return @@exampledir
else
return File.join(@@exampledir, *args)
end
end
module_function :basedir, :datadir, :exampledir
def cleanup(&block)
@@cleaners << block
end
# Rails clobbers RUBYLIB, thanks
def libsetup
curlibs = ENV["RUBYLIB"].split(":")
$LOAD_PATH.reject do |dir| dir =~ /^\/usr/ end.each do |dir|
curlibs << dir unless curlibs.include?(dir)
end
ENV["RUBYLIB"] = curlibs.join(":")
end
def logcollector
collector = []
Puppet::Util::Log.newdestination(collector)
cleanup do
Puppet::Util::Log.close(collector)
end
collector
end
def rake?
$0 =~ /test_loader/
end
# Redirect stdout and stderr
def redirect
@stderr = tempfile
@stdout = tempfile
$stderr = File.open(@stderr, "w")
$stdout = File.open(@stdout, "w")
cleanup do
$stderr = STDERR
$stdout = STDOUT
end
end
def setup
ENV["PATH"] += File::PATH_SEPARATOR + "/usr/sbin" unless ENV["PATH"].split(File::PATH_SEPARATOR).include?("/usr/sbin")
@memoryatstart = Puppet::Util.memory
if defined?(@@testcount)
@@testcount += 1
else
@@testcount = 0
end
@configpath = File.join(
tmpdir,
"configdir" + @@testcount.to_s + "/"
)
unless defined? $user and $group
$user = nonrootuser.uid.to_s
$group = nonrootgroup.gid.to_s
end
Puppet.settings.clear
Puppet[:user] = $user
Puppet[:group] = $group
Puppet[:confdir] = @configpath
Puppet[:vardir] = @configpath
Dir.mkdir(@configpath) unless File.exists?(@configpath)
@@tmpfiles << @configpath << tmpdir
@@tmppids = []
@@cleaners = []
@logs = []
# If we're running under rake, then disable debugging and such.
#if rake? or ! Puppet[:debug]
#if defined?($puppet_debug) or ! rake?
Puppet[:color] = false if textmate?
- Puppet::Util::Log.newdestination(@logs)
+ Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(@logs))
if defined? $console
Puppet.info @method_name
Puppet::Util::Log.newdestination(:console)
Puppet[:trace] = true
end
Puppet::Util::Log.level = :debug
#$VERBOSE = 1
#else
# Puppet::Util::Log.close
# Puppet::Util::Log.newdestination(@logs)
# Puppet[:httplog] = tempfile
#end
Puppet[:ignoreschedules] = true
#@start = Time.now
#Facter.stubs(:value).returns "stubbed_value"
#Facter.stubs(:to_hash).returns({})
end
def tempfile(suffix = '')
if defined?(@@tmpfilenum)
@@tmpfilenum += 1
else
@@tmpfilenum = 1
end
f = File.join(self.tmpdir, "tempfile_" + @@tmpfilenum.to_s + suffix)
@@tmpfiles ||= []
@@tmpfiles << f
f
end
def textmate?
!!ENV["TM_FILENAME"]
end
def tstdir
dir = tempfile
Dir.mkdir(dir)
dir
end
def tmpdir
unless @tmpdir
@tmpdir = case Facter["operatingsystem"].value
when "Darwin"; "/private/tmp"
when "SunOS"; "/var/tmp"
else
"/tmp"
end
@tmpdir = File.join(@tmpdir, "puppettesting#{Process.pid}")
unless File.exists?(@tmpdir)
FileUtils.mkdir_p(@tmpdir)
File.chmod(01777, @tmpdir)
end
end
@tmpdir
end
def remove_tmp_files
@@tmpfiles.each { |file|
unless file =~ /tmp/
puts "Not deleting tmpfile #{file}"
next
end
if FileTest.exists?(file)
system("chmod -R 755 #{file}")
system("rm -rf #{file}")
end
}
@@tmpfiles.clear
end
def teardown
#@stop = Time.now
#File.open("/tmp/test_times.log", ::File::WRONLY|::File::CREAT|::File::APPEND) { |f| f.puts "%0.4f %s %s" % [@stop - @start, @method_name, self.class] }
@@cleaners.each { |cleaner| cleaner.call }
remove_tmp_files
@@tmppids.each { |pid|
%x{kill -INT #{pid} 2>/dev/null}
}
@@tmppids.clear
Puppet::Util::Storage.clear
Puppet.clear
Puppet.settings.clear
Puppet::Util::Cacher.expire
@memoryatend = Puppet::Util.memory
diff = @memoryatend - @memoryatstart
Puppet.info "#{self.class}##{@method_name} memory growth (#{@memoryatstart} to #{@memoryatend}): #{diff}" if diff > 1000
# reset all of the logs
Puppet::Util::Log.close_all
@logs.clear
# Just in case there are processes waiting to die...
require 'timeout'
begin
Timeout::timeout(5) do
Process.waitall
end
rescue Timeout::Error
# just move on
end
end
def logstore
@logs = []
- Puppet::Util::Log.newdestination(@logs)
+ Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(@logs))
end
end
require 'puppettest/support'
require 'puppettest/filetesting'
require 'puppettest/fakes'
require 'puppettest/exetest'
require 'puppettest/parsertesting'
require 'puppettest/servertest'
require 'puppettest/testcase'