diff --git a/acceptance/tests/file_hello_world.rb b/acceptance/tests/file_hello_world.rb index 11f66e98e..940f0fc4a 100644 --- a/acceptance/tests/file_hello_world.rb +++ b/acceptance/tests/file_hello_world.rb @@ -1,23 +1,22 @@ # Verify that a trivial manifest can be run to completion. test_name "The challenging 'Hello, World' manifest" agents.each do |agent| filename = agent.tmpfile('hello-world.txt') content = "Hello, World" manifest = "file { '#{filename}': content => '#{content}' }" step "ensure we are clean before testing..." on(agent, "rm -f #{filename}") step "run the manifest itself" apply_manifest_on(agent, manifest) do - fail_test "the expected notice of action was missing" unless - stderr.index "File[#{filename}]/ensure: defined content as" + assert_match("File[#{filename}]/ensure: defined content as", stdout, "the expected notice of action was missing") end step "verify the content of the generated files." on agent, "grep '#{content}' #{filename}" step "clean up after our test run." on(agent, "rm -f #{filename}") end diff --git a/acceptance/tests/resource/cron/should_remove_matching.rb b/acceptance/tests/resource/cron/should_remove_matching.rb index 9f6524dfa..f480867d3 100755 --- a/acceptance/tests/resource/cron/should_remove_matching.rb +++ b/acceptance/tests/resource/cron/should_remove_matching.rb @@ -1,43 +1,43 @@ test_name "puppet should remove a crontab entry based on command matching" tmpuser = "pl#{rand(999999).to_i}" tmpfile = "/tmp/cron-test-#{Time.new.to_i}" cron = '# Puppet Name: crontest\n* * * * * /bin/true\n1 1 1 1 1 /bin/true\n' create_user = "user { '#{tmpuser}': ensure => present, managehome => false }" delete_user = "user { '#{tmpuser}': ensure => absent, managehome => false }" package_cron = "case $operatingsystem { centos, redhat: {$cron = 'cronie'}\n default: {$cron ='cron'} } package {'cron': name=> $cron, ensure=>present, }" agents.each do |host| if host['platform'].include?('windows') skip_test "Test not supported on this platform" next end step "ensure the user exist via puppet" apply_manifest_on host, create_user apply_manifest_on host, package_cron step "create the existing job by hand..." run_cron_on(host,:add,tmpuser,"* * * * * /bin/true") step "Remove cron resource" on(host, puppet_resource("cron", "bogus", "user=#{tmpuser}", "command=/bin/true", "ensure=absent")) do - assert_match(/bogus\D+ensure: removed/, stderr, "Removing cron entry failed for #{tmpuser} on #{host}") + assert_match(/bogus\D+ensure: removed/, stdout, "Removing cron entry failed for #{tmpuser} on #{host}") end step "verify that crontab -l contains what you expected" run_cron_on(host,:list,tmpuser) do count = stdout.scan("/bin/true").length fail_test "found /bin/true the wrong number of times (#{count})" unless count == 0 end step "remove the crontab file for that user" run_cron_on(host,:remove,tmpuser) step "remove the user from the system" apply_manifest_on host, delete_user end diff --git a/acceptance/tests/ticket_6928_puppet_master_parse_fails.rb b/acceptance/tests/ticket_6928_puppet_master_parse_fails.rb index 3613eddad..55162a908 100644 --- a/acceptance/tests/ticket_6928_puppet_master_parse_fails.rb +++ b/acceptance/tests/ticket_6928_puppet_master_parse_fails.rb @@ -1,32 +1,32 @@ test_name "#6928: Puppet --parseonly should return deprication message" # Create good and bad formatted manifests step "Master: create valid, invalid formatted manifests" create_remote_file(master, '/tmp/good.pp', %w{notify{good:}} ) create_remote_file(master, '/tmp/bad.pp', 'notify{bad:') step "Master: use --parseonly on an invalid manifest, should return 1 and issue deprecation warning" on master, puppet_master( %w{--parseonly /tmp/bad.pp} ), :acceptable_exit_codes => [ 1 ] - assert_match(/--parseonly has been removed. Please use \'puppet parser validate \'/, stderr, "Deprecation warning not issued for --parseonly on #{master}" ) + assert_match(/--parseonly has been removed. Please use \'puppet parser validate \'/, stdout, "Deprecation warning not issued for --parseonly on #{master}" ) step "Agents: create valid, invalid formatted manifests" agents.each do |host| good = host.tmpfile('good-6928') bad = host.tmpfile('bad-6928') create_remote_file(host, good, %w{notify{good:}} ) create_remote_file(host, bad, 'notify{bad:') step "Agents: use --parseonly on an invalid manifest, should return 1 and issue deprecation warning" on(host, puppet('apply', '--parseonly', bad), :acceptable_exit_codes => [ 1 ]) do - assert_match(/--parseonly has been removed. Please use \'puppet parser validate \'/, stderr, "Deprecation warning not issued for --parseonly on #{host}" ) + assert_match(/--parseonly has been removed. Please use \'puppet parser validate \'/, stdout, "Deprecation warning not issued for --parseonly on #{host}" ) end step "Test Face for 'parser validate' with good manifest -- should pass" on(host, puppet('parser', 'validate', good), :acceptable_exit_codes => [ 0 ]) step "Test Faces for 'parser validate' with bad manifest -- should fail" on(host, puppet('parser', 'validate', bad), :acceptable_exit_codes => [ 1 ]) do - assert_match(/err: Could not parse for environment production/, stderr, "Bad manifest detection failed on #{host}" ) + assert_match(/Error: Could not parse for environment production/, stderr, "Bad manifest detection failed on #{host}" ) end end diff --git a/lib/puppet/util/log/destinations.rb b/lib/puppet/util/log/destinations.rb index bf290b446..22fcb36ca 100644 --- a/lib/puppet/util/log/destinations.rb +++ b/lib/puppet/util/log/destinations.rb @@ -1,252 +1,253 @@ Puppet::Util::Log.newdesttype :syslog do def self.suitable?(obj) Puppet.features.syslog? end def close Syslog.close end def initialize Syslog.close if Syslog.opened? name = "puppet-#{Puppet.run_mode.name}" 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" msg.to_s.split("\n").each do |line| @syslog.send(msg.level, line.gsub("%", '%%')) end else msg.to_s.split("\n").each do |line| @syslog.send(msg.level, "(%s) %s" % [msg.source.to_s.gsub("%", ""), line.gsub("%", '%%') ] ) end end end end Puppet::Util::Log.newdesttype :file do require 'fileutils' def self.match?(obj) Puppet::Util.absolute_path?(obj) end def close if defined?(@file) @file.close @file = nil end end def flush @file.flush if defined?(@file) end attr_accessor :autoflush 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)) FileUtils.mkdir_p(File.dirname(path), :mode => 0755) 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 require 'puppet/util/colors' include Puppet::Util::Colors def initialize # Flush output immediately. $stderr.sync = true $stdout.sync = true end def handle(msg) error_levels = { :warning => 'Warning', :err => 'Error', :alert => 'Alert', :emerg => 'Emergency', :crit => 'Critical' } str = msg.respond_to?(:multiline) ? msg.multiline : msg.to_s + str = msg.source == "Puppet" ? str : "#{msg.source}: #{str}" case msg.level when *error_levels.keys $stderr.puts colorize(:hred, "#{error_levels[msg.level]}: #{str}") when :info $stdout.puts "#{colorize(:green, 'Info')}: #{str}" when :debug $stdout.puts "#{colorize(:cyan, 'Debug')}: #{str}" else $stdout.puts str 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 Puppet::Util.absolute_path?(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 Puppet.log_exception(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 "Puppet::Test::LogCollector" def initialize(messages) @messages = messages end def handle(msg) @messages << msg end end Puppet::Util::Log.newdesttype :eventlog do def self.suitable?(obj) Puppet.features.eventlog? end def initialize @eventlog = Win32::EventLog.open("Application") end def to_native(level) case level when :debug,:info,:notice [Win32::EventLog::INFO, 0x01] when :warning [Win32::EventLog::WARN, 0x02] when :err,:alert,:emerg,:crit [Win32::EventLog::ERROR, 0x03] end end def handle(msg) native_type, native_id = to_native(msg.level) @eventlog.report_event( :source => "Puppet", :event_type => native_type, :event_id => native_id, :data => (msg.source and msg.source != 'Puppet' ? "#{msg.source}: " : '') + msg.to_s ) end def close if @eventlog @eventlog.close @eventlog = nil end end end diff --git a/spec/unit/util/log/destinations_spec.rb b/spec/unit/util/log/destinations_spec.rb index eb654c69e..7903803ed 100755 --- a/spec/unit/util/log/destinations_spec.rb +++ b/spec/unit/util/log/destinations_spec.rb @@ -1,138 +1,149 @@ #!/usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/log' describe Puppet::Util::Log.desttypes[:report] do before do @dest = Puppet::Util::Log.desttypes[:report] end it "should require a report at initialization" do @dest.new("foo").report.should == "foo" end it "should send new messages to the report" do report = mock 'report' dest = @dest.new(report) report.expects(:<<).with("my log") dest.handle "my log" end end describe Puppet::Util::Log.desttypes[:file] do include PuppetSpec::Files before do File.stubs(:open) # prevent actually creating the file @class = Puppet::Util::Log.desttypes[:file] end it "should default to autoflush false" do @class.new(tmpfile('log')).autoflush.should == true end describe "when matching" do shared_examples_for "file destination" do it "should match an absolute path" do @class.match?(abspath).should be_true end it "should not match a relative path" do @class.match?(relpath).should be_false end end describe "on POSIX systems", :as_platform => :posix do let (:abspath) { '/tmp/log' } let (:relpath) { 'log' } it_behaves_like "file destination" end describe "on Windows systems", :as_platform => :windows do let (:abspath) { 'C:\\temp\\log.txt' } let (:relpath) { 'log.txt' } it_behaves_like "file destination" end end end describe Puppet::Util::Log.desttypes[:syslog] do let (:klass) { Puppet::Util::Log.desttypes[:syslog] } # these tests can only be run when syslog is present, because # we can't stub the top-level Syslog module describe "when syslog is available", :if => Puppet.features.syslog? do before :each do Syslog.stubs(:opened?).returns(false) Syslog.stubs(:const_get).returns("LOG_KERN").returns(0) Syslog.stubs(:open) end it "should open syslog" do Syslog.expects(:open) klass.new end it "should close syslog" do Syslog.expects(:close) dest = klass.new dest.close end it "should send messages to syslog" do syslog = mock 'syslog' syslog.expects(:info).with("don't panic") Syslog.stubs(:open).returns(syslog) msg = Puppet::Util::Log.new(:level => :info, :message => "don't panic") dest = klass.new dest.handle(msg) end end describe "when syslog is unavailable" do it "should not be a suitable log destination" do Puppet.features.stubs(:syslog?).returns(false) klass.suitable?(:syslog).should be_false end end end describe Puppet::Util::Log.desttypes[:console] do + let (:klass) { Puppet::Util::Log.desttypes[:console] } + describe "when color is available" do before :each do subject.stubs(:console_has_color?).returns(true) end it "should support color output" do Puppet[:color] = true subject.colorize(:red, 'version').should == "\e[0;31mversion\e[0m" end it "should withhold color output when not appropriate" do Puppet[:color] = false subject.colorize(:red, 'version').should == "version" end it "should handle multiple overlapping colors in a stack-like way" do Puppet[:color] = true vstring = subject.colorize(:red, 'version') subject.colorize(:green, "(#{vstring})").should == "\e[0;32m(\e[0;31mversion\e[0;32m)\e[0m" end it "should handle resets in a stack-like way" do Puppet[:color] = true vstring = subject.colorize(:reset, 'version') subject.colorize(:green, "(#{vstring})").should == "\e[0;32m(\e[mversion\e[0;32m)\e[0m" end + + it "should include the log message's source/context in the output when available" do + Puppet[:color] = false + $stdout.expects(:puts).with("Info: a hitchhiker: don't panic") + + msg = Puppet::Util::Log.new(:level => :info, :message => "don't panic", :source => "a hitchhiker") + dest = klass.new + dest.handle(msg) + end end end