summaryrefslogtreecommitdiff
path: root/spec/integration/agent/logging_spec.rb
blob: 1c4394a6e32e85c25e696bdcd9c0d5c57801e258 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#! /usr/bin/env ruby
require 'spec_helper'

require 'puppet'
require 'puppet/daemon'
require 'puppet/application/agent'

# The command line flags affecting #20900 and #20919:
#
# --onetime
# --daemonize
# --no-daemonize
# --logdest
# --verbose
# --debug
# (no flags)     (-)
#
# d and nd are mutally exclusive
#
# Combinations without logdest, verbose or debug:
#
# --onetime --daemonize
# --onetime --no-daemonize
# --onetime
# --daemonize
# --no-daemonize
# -
#
# 6 cases X [--logdest=console, --logdest=syslog, --logdest=/some/file, <nothing added>]
# = 24 cases to test
#
# X [--verbose, --debug, <nothing added>]
# = 72 cases to test
#
# Expectations of behavior are defined in the expected_loggers, expected_level methods,
# so adapting to a change in logging behavior should hopefully be mostly a matter of
# adjusting the logic in those methods to define new behavior.
#
# Note that this test does not have anything to say about what happens to logging after
# daemonizing.
describe 'agent logging' do
  ONETIME  = '--onetime'
  DAEMONIZE  = '--daemonize'
  NO_DAEMONIZE  = '--no-daemonize'
  LOGDEST_FILE = '--logdest=/dev/null/foo'
  LOGDEST_SYSLOG = '--logdest=syslog'
  LOGDEST_CONSOLE = '--logdest=console'
  VERBOSE  = '--verbose'
  DEBUG  = '--debug'

  DEFAULT_LOG_LEVEL = :notice
  INFO_LEVEL        = :info
  DEBUG_LEVEL       = :debug
  CONSOLE           = :console
  SYSLOG            = :syslog
  EVENTLOG          = :eventlog
  FILE              = :file

  ONETIME_DAEMONIZE_ARGS = [
    [ONETIME],
    [ONETIME, DAEMONIZE],
    [ONETIME, NO_DAEMONIZE],
    [DAEMONIZE],
    [NO_DAEMONIZE],
    [],
  ]
  LOG_DEST_ARGS = [LOGDEST_FILE, LOGDEST_SYSLOG, LOGDEST_CONSOLE, nil]
  LOG_LEVEL_ARGS = [VERBOSE, DEBUG, nil]

  shared_examples "an agent" do |argv, expected|
    before(:each) do
      # Don't actually run the agent, bypassing cert checks, forking and the puppet run itself
      Puppet::Application::Agent.any_instance.stubs(:run_command)
    end

    def double_of_bin_puppet_agent_call(argv)
      argv.unshift('agent')
      command_line = Puppet::Util::CommandLine.new('puppet', argv)
      command_line.execute
    end

    if Puppet.features.microsoft_windows? && argv.include?(DAEMONIZE)

      it "should exit on a platform which cannot daemonize if the --daemonize flag is set" do
        expect { double_of_bin_puppet_agent_call(argv) }.to raise_error(SystemExit)
      end

    else

      it "when evoked with #{argv}, logs to #{expected[:loggers].inspect} at level #{expected[:level]}" do
        if Facter.value(:kernelmajversion).to_f < 6.0
          pending("requires win32-eventlog gem upgrade to 0.6.2 on Windows 2003")
        end

        # This logger is created by the Puppet::Settings object which creates and
        # applies a catalog to ensure that configuration files and users are in
        # place.
        #
        # It's not something we are specifically testing here since it occurs
        # regardless of user flags.
        Puppet::Util::Log.expects(:newdestination).with(instance_of(Puppet::Transaction::Report)).at_least_once
        expected[:loggers].each do |logclass|
          Puppet::Util::Log.expects(:newdestination).with(logclass).at_least_once
        end
        double_of_bin_puppet_agent_call(argv)

        Puppet::Util::Log.level.should == expected[:level]
      end

    end
  end

  def self.no_log_dest_set_in(argv)
    ([LOGDEST_SYSLOG, LOGDEST_CONSOLE, LOGDEST_FILE] & argv).empty?
  end

  def self.verbose_or_debug_set_in_argv(argv)
    !([VERBOSE, DEBUG] & argv).empty?
  end

  def self.log_dest_is_set_to(argv, log_dest)
    argv.include?(log_dest)
  end

  # @param argv Array of commandline flags
  # @return Set<Symbol> of expected loggers
  def self.expected_loggers(argv)
    loggers = Set.new
    loggers << CONSOLE if verbose_or_debug_set_in_argv(argv)
    loggers << 'console' if log_dest_is_set_to(argv, LOGDEST_CONSOLE)
    loggers << '/dev/null/foo' if log_dest_is_set_to(argv, LOGDEST_FILE)
    if Puppet.features.microsoft_windows?
      # an explicit call to --logdest syslog on windows is swallowed silently with no
      # logger created (see #suitable() of the syslog Puppet::Util::Log::Destination subclass)
      # however Puppet::Util::Log.newdestination('syslog') does get called...so we have
      # to set an expectation
      loggers << 'syslog' if log_dest_is_set_to(argv, LOGDEST_SYSLOG)

      loggers << EVENTLOG if no_log_dest_set_in(argv)
    else
      # posix
      loggers << 'syslog' if log_dest_is_set_to(argv, LOGDEST_SYSLOG)
      loggers << SYSLOG if no_log_dest_set_in(argv)
    end
    return loggers
  end

  # @param argv Array of commandline flags
  # @return Symbol of the expected log level
  def self.expected_level(argv)
    case
      when argv.include?(VERBOSE) then INFO_LEVEL
      when argv.include?(DEBUG) then DEBUG_LEVEL
      else DEFAULT_LOG_LEVEL
    end
  end

  # @param argv Array of commandline flags
  # @return Hash of expected loggers and the expected log level
  def self.with_expectations_based_on(argv)
    {
      :loggers => expected_loggers(argv),
      :level => expected_level(argv),
    }
  end

# For running a single spec (by line number): rspec -l150 spec/integration/agent/logging_spec.rb
#  debug_argv = []
#  it_should_behave_like( "an agent", [debug_argv], with_expectations_based_on([debug_argv]))

  ONETIME_DAEMONIZE_ARGS.each do |onetime_daemonize_args|
    LOG_LEVEL_ARGS.each do |log_level_args|
      LOG_DEST_ARGS.each do |log_dest_args|
        argv = (onetime_daemonize_args + [log_level_args, log_dest_args]).flatten.compact

        describe "for #{argv}" do
          it_should_behave_like( "an agent", argv, with_expectations_based_on(argv))
        end
      end
    end
  end
end