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
|