flapjack 0.4.12 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +77 -50
- data/Rakefile +78 -26
- data/TODO.md +15 -32
- data/bin/flapjack-benchmark +50 -0
- data/bin/flapjack-notifier +11 -36
- data/bin/flapjack-notifier-manager +1 -3
- data/bin/flapjack-worker +5 -19
- data/doc/PACKAGING.md +25 -0
- data/etc/flapjack/flapjack-notifier.conf.example +34 -0
- data/etc/flapjack/recipients.conf.example +14 -0
- data/features/flapjack-notifier-manager.feature +19 -0
- data/features/flapjack-worker-manager.feature +25 -0
- data/features/packaging-lintian.feature +15 -0
- data/features/persistence/couch.feature +105 -0
- data/features/persistence/sqlite3.feature +105 -0
- data/features/persistence/steps/couch_steps.rb +25 -0
- data/features/persistence/steps/generic_steps.rb +102 -0
- data/features/persistence/steps/sqlite3_steps.rb +13 -0
- data/features/steps/flapjack-notifier-manager_steps.rb +24 -0
- data/features/steps/flapjack-worker-manager_steps.rb +50 -0
- data/features/steps/packaging-lintian_steps.rb +13 -0
- data/features/support/env.rb +22 -0
- data/features/support/silent_system.rb +4 -0
- data/flapjack.gemspec +7 -11
- data/lib/flapjack/applications/notifier.rb +222 -0
- data/lib/flapjack/applications/worker.rb +99 -0
- data/lib/flapjack/checks/ping +10 -0
- data/lib/flapjack/cli/notifier.rb +80 -218
- data/lib/flapjack/cli/worker.rb +1 -86
- data/lib/flapjack/filters/any_parents_failed.rb +14 -0
- data/lib/flapjack/filters/ok.rb +13 -0
- data/lib/flapjack/inifile.rb +44 -0
- data/lib/flapjack/{notifier.rb → notifier_engine.rb} +13 -9
- data/lib/flapjack/notifiers/mailer/mailer.rb +12 -13
- data/lib/flapjack/notifiers/xmpp/xmpp.rb +2 -2
- data/lib/flapjack/patches.rb +25 -0
- data/lib/flapjack/persistence/couch.rb +5 -0
- data/lib/flapjack/persistence/couch/connection.rb +66 -0
- data/lib/flapjack/persistence/couch/couch.rb +63 -0
- data/lib/flapjack/persistence/data_mapper.rb +3 -0
- data/lib/flapjack/persistence/data_mapper/data_mapper.rb +67 -0
- data/lib/flapjack/{models → persistence/data_mapper/models}/check.rb +3 -7
- data/lib/flapjack/{models → persistence/data_mapper/models}/check_template.rb +0 -0
- data/lib/flapjack/persistence/data_mapper/models/event.rb +17 -0
- data/lib/flapjack/{models → persistence/data_mapper/models}/node.rb +0 -0
- data/lib/flapjack/{models → persistence/data_mapper/models}/related_check.rb +0 -0
- data/lib/flapjack/persistence/sqlite3.rb +3 -0
- data/lib/flapjack/persistence/sqlite3/sqlite3.rb +166 -0
- data/lib/flapjack/transports/beanstalkd.rb +33 -0
- data/lib/flapjack/transports/result.rb +58 -0
- metadata +46 -56
- data/etc/flapjack/flapjack-notifier.yaml.example +0 -8
- data/etc/flapjack/recipients.yaml.example +0 -10
- data/lib/flapjack/database.rb +0 -10
- data/lib/flapjack/result.rb +0 -47
@@ -1,246 +1,108 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require 'rubygems'
|
4
3
|
require 'ostruct'
|
5
4
|
require 'optparse'
|
6
5
|
require 'log4r'
|
7
6
|
require 'log4r/outputter/syslogoutputter'
|
8
|
-
require File.join(File.dirname(__FILE__), '..', '
|
9
|
-
require File.join(File.dirname(__FILE__), '..', 'notifier')
|
10
|
-
require File.join(File.dirname(__FILE__), '..', 'result')
|
7
|
+
require File.join(File.dirname(__FILE__), '..', 'inifile')
|
11
8
|
|
12
9
|
module Flapjack
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
10
|
+
module Notifier
|
11
|
+
class Options
|
12
|
+
def self.parse(args)
|
13
|
+
options = OpenStruct.new
|
14
|
+
opts = OptionParser.new do |opts|
|
15
|
+
opts.on('-r', '--recipients FILE', 'recipients file') do |filename|
|
16
|
+
options.recipients_filename = filename
|
17
|
+
end
|
18
|
+
opts.on('-c', '--config FILE', 'config file') do |filename|
|
19
|
+
options.config_filename = filename
|
20
|
+
end
|
21
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
22
|
+
puts opts
|
23
|
+
exit
|
24
|
+
end
|
20
25
|
end
|
21
|
-
opts.on('-p', '--port PORT', 'beanstalkd port') do |port|
|
22
|
-
options.port = port.to_i
|
23
|
-
end
|
24
|
-
opts.on('-r', '--recipients FILE', 'recipients file') do |recipients|
|
25
|
-
options.recipients = recipients.to_s
|
26
|
-
end
|
27
|
-
opts.on('-c', '--config FILE', 'config file') do |config|
|
28
|
-
options.config_filename = config.to_s
|
29
|
-
end
|
30
|
-
opts.on('-d', '--database-uri URI', 'location of the checks database') do |db|
|
31
|
-
options.database_uri = db.to_s
|
32
|
-
end
|
33
|
-
opts.on_tail("-h", "--help", "Show this message") do
|
34
|
-
puts opts
|
35
|
-
exit
|
36
|
-
end
|
37
|
-
end
|
38
26
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
48
|
-
|
49
|
-
# default the host + port
|
50
|
-
options.host ||= 'localhost'
|
51
|
-
options.port ||= 11300
|
52
|
-
|
53
|
-
@errors = []
|
54
|
-
# check that recipients file exists
|
55
|
-
if options.recipients
|
56
|
-
unless File.exists?(options.recipients.to_s)
|
57
|
-
@errors << "The specified recipients file doesn't exist!"
|
58
|
-
end
|
59
|
-
else
|
60
|
-
@errors << "You need to specify a recipients file with [-r|--recipients]."
|
61
|
-
end
|
62
|
-
|
63
|
-
# check that config file exists
|
64
|
-
if options.config_filename
|
65
|
-
unless File.exists?(options.config_filename.to_s)
|
66
|
-
@errors << "The specified config file doesn't exist!"
|
67
|
-
end
|
68
|
-
else
|
69
|
-
options.config_filename ||= "/etc/flapjack/flapjack-notifier.yaml"
|
70
|
-
unless File.exists?(options.config_filename.to_s)
|
71
|
-
@errors << "The default config file (#{options.config_filename}) doesn't exist!"
|
72
|
-
@errors << "Set one up, or specify one with [-c|--config]."
|
27
|
+
# parse the options
|
28
|
+
begin
|
29
|
+
opts.parse!(args)
|
30
|
+
rescue OptionParser::MissingArgument => e
|
31
|
+
# if an --option is missing it's argument
|
32
|
+
puts e.message.capitalize + "\n\n"
|
33
|
+
puts opts
|
34
|
+
exit 1
|
73
35
|
end
|
74
|
-
end
|
75
36
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
37
|
+
# validation of command line arguments
|
38
|
+
@errors = []
|
39
|
+
# check that recipients file exists
|
40
|
+
if options.recipients_filename
|
41
|
+
unless File.exists?(options.recipients_filename)
|
42
|
+
@errors << "The specified recipients file doesn't exist!"
|
43
|
+
end
|
44
|
+
else
|
45
|
+
@errors << "You need to specify a recipients file with --recipients."
|
81
46
|
end
|
82
|
-
puts
|
83
|
-
puts opts
|
84
|
-
exit 2
|
85
|
-
end
|
86
|
-
|
87
|
-
options
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
class NotifierCLI
|
92
|
-
attr_accessor :log, :recipients, :results_queue, :config
|
93
|
-
attr_accessor :notifier, :notifiers
|
94
|
-
attr_accessor :condition
|
95
|
-
|
96
|
-
def initialize(opts={})
|
97
|
-
@log = opts[:logger]
|
98
|
-
@log ||= Log4r::Logger.new("notifier")
|
99
|
-
end
|
100
|
-
|
101
|
-
def setup_loggers
|
102
|
-
@log.add(Log4r::StdoutOutputter.new('notifier'))
|
103
|
-
@log.add(Log4r::SyslogOutputter.new('notifier'))
|
104
|
-
end
|
105
47
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
opts[:filename] ||= File.join(Dir.pwd, "recipients.yaml")
|
112
|
-
yaml = YAML::load(File.read(opts[:filename]))
|
113
|
-
end
|
114
|
-
|
115
|
-
# FIXME: add error detection for passing in dumb things
|
116
|
-
|
117
|
-
@recipients = yaml.map do |r|
|
118
|
-
OpenStruct.new(r)
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
def setup_config(opts={})
|
123
|
-
if opts[:yaml]
|
124
|
-
yaml = opts[:yaml]
|
125
|
-
else
|
126
|
-
opts[:filename] ||= File.join(Dir.pwd, "flapjack-notifier.yaml")
|
127
|
-
yaml = YAML::load(File.read(opts[:filename]))
|
128
|
-
end
|
129
|
-
|
130
|
-
@config = OpenStruct.new(yaml)
|
131
|
-
end
|
132
|
-
|
133
|
-
def setup_database(opts={})
|
134
|
-
uri = (opts[:database_uri] || @config.database_uri)
|
135
|
-
raise ArgumentError, "database URI wasn't specified!" unless uri
|
136
|
-
DataMapper.setup(:default, uri)
|
137
|
-
validate_database
|
138
|
-
end
|
139
|
-
|
140
|
-
def validate_database
|
141
|
-
begin
|
142
|
-
DataMapper.repository(:default).adapter.execute("SELECT 'id' FROM 'checks';")
|
143
|
-
rescue Sqlite3Error
|
144
|
-
@log.warning("The specified database doesn't appear to have any structure!")
|
145
|
-
@log.warning("You need to investigate this.")
|
146
|
-
end
|
147
|
-
# FIXME: add more rescues in here!
|
148
|
-
end
|
149
|
-
|
150
|
-
def initialize_notifiers(opts={})
|
151
|
-
notifiers_directory = opts[:notifiers_directory]
|
152
|
-
notifiers_directory ||= File.expand_path(File.join(File.dirname(__FILE__), '..', 'notifiers'))
|
153
|
-
|
154
|
-
raise ArgumentError, "notifiers directory doesn't exist!" unless File.exists?(notifiers_directory)
|
155
|
-
|
156
|
-
@notifiers = []
|
157
|
-
|
158
|
-
@config.notifiers.each_pair do |notifier, config|
|
159
|
-
filename = File.join(notifiers_directory, notifier.to_s, 'init')
|
160
|
-
if File.exists?(filename + '.rb')
|
161
|
-
@log.debug("Loading the #{notifier.to_s.capitalize} notifier")
|
162
|
-
require filename
|
163
|
-
config.merge!(:logger => @log, :website_uri => @config.website_uri)
|
164
|
-
@notifiers << Flapjack::Notifiers.const_get("#{notifier.to_s.capitalize}").new(config)
|
48
|
+
# check that config file exists
|
49
|
+
if options.config_filename
|
50
|
+
unless File.exists?(options.config_filename.to_s)
|
51
|
+
@errors << "The specified config file doesn't exist!"
|
52
|
+
end
|
165
53
|
else
|
166
|
-
|
54
|
+
options.config_filename = "/etc/flapjack/flapjack-notifier.conf"
|
55
|
+
unless File.exists?(options.config_filename)
|
56
|
+
@errors << "The default config file (#{options.config_filename}) doesn't exist."
|
57
|
+
@errors << "Please set one up, or specify one with --config."
|
58
|
+
end
|
167
59
|
end
|
168
|
-
|
60
|
+
|
169
61
|
|
170
|
-
|
171
|
-
|
62
|
+
# if there are errors, print them out and exit
|
63
|
+
if @errors.size > 0
|
64
|
+
puts "Errors:"
|
65
|
+
@errors.each do |error|
|
66
|
+
puts " - #{error}"
|
67
|
+
end
|
68
|
+
puts
|
69
|
+
puts opts
|
70
|
+
exit 2
|
71
|
+
end
|
72
|
+
|
73
|
+
# config loader
|
172
74
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
# Accepts a list of recipients (:recipients) and a logger (:logger) as
|
177
|
-
# arguments. If neither of these are specified, it will default to an
|
178
|
-
# existing list of recipients and the current logger.
|
179
|
-
#
|
180
|
-
# Sets up and returns @notifier, an instance of Flapjack::Notifier
|
181
|
-
def setup_notifier(opts={})
|
182
|
-
recipients = (opts[:recipients] || @recipients)
|
183
|
-
log = (opts[:logger] || @log)
|
184
|
-
initialize_notifiers
|
185
|
-
notifiers = @notifiers # should we accept a list of notifiers?
|
186
|
-
@notifier = Flapjack::Notifier.new(:logger => log,
|
187
|
-
:notifiers => notifiers,
|
188
|
-
:recipients => recipients)
|
189
|
-
end
|
190
|
-
|
191
|
-
def process_loop
|
192
|
-
@log.info("Processing results...")
|
193
|
-
loop do
|
194
|
-
process_result
|
195
|
-
end
|
196
|
-
end
|
75
|
+
# holder for transport + persistence config
|
76
|
+
options.transport = OpenStruct.new
|
77
|
+
options.persistence = OpenStruct.new
|
197
78
|
|
198
|
-
|
199
|
-
def save_result(result)
|
200
|
-
if check = Check.get(result.id)
|
201
|
-
check.status = result.retval
|
202
|
-
check.save
|
203
|
-
else
|
204
|
-
@log.error("Got result for check #{result.id}, but it doesn't exist!")
|
205
|
-
end
|
206
|
-
end
|
79
|
+
config = Flapjack::Inifile.read(options.config_filename)
|
207
80
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
check.worst_parent_status > 0 ? true : false
|
212
|
-
else
|
213
|
-
@log.error("Got result for check #{result.id}, but it doesn't exist!")
|
214
|
-
end
|
215
|
-
end
|
81
|
+
%w(transport persistence).each do |backend|
|
82
|
+
options.send("#{backend}=", config[backend].symbolize_keys)
|
83
|
+
end
|
216
84
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
if result.warning? || result.critical?
|
224
|
-
if any_parents_warning_or_critical?(result)
|
225
|
-
@log.info("Not notifying on check '#{result.id}' as parent is failing.")
|
226
|
-
else
|
227
|
-
@log.info("Creating event for check '#{result.id}'")
|
228
|
-
# FIXME: this will be a performance hit
|
229
|
-
event = ::Event.new(:check_id => result.id)
|
230
|
-
raise unless event.save
|
85
|
+
# base config (config.blah)
|
86
|
+
config['notifier'].each_pair do |key, value|
|
87
|
+
normalised_key = key.gsub('-', '_')
|
88
|
+
values = value.split(/,*\s+/)
|
89
|
+
options.send("#{normalised_key}=", values)
|
90
|
+
end
|
231
91
|
|
232
|
-
|
233
|
-
|
92
|
+
# list of notifiers to load + their config
|
93
|
+
notifiers_to_load = options.notifiers
|
94
|
+
options.notifiers = {}
|
95
|
+
notifiers_to_load.each do |notifier|
|
96
|
+
options.notifiers[notifier] = config["#{notifier}-notifier"].symbolize_keys
|
234
97
|
end
|
235
|
-
end
|
236
98
|
|
237
|
-
|
238
|
-
|
99
|
+
# holder for recipients list
|
100
|
+
recipients = Flapjack::Inifile.read(options.recipients_filename)
|
101
|
+
options.recipients = recipients.all
|
239
102
|
|
240
|
-
|
241
|
-
|
103
|
+
options
|
104
|
+
end
|
242
105
|
end
|
243
|
-
|
244
106
|
end
|
245
|
-
|
107
|
+
|
246
108
|
end
|
data/lib/flapjack/cli/worker.rb
CHANGED
@@ -6,7 +6,6 @@ require 'ostruct'
|
|
6
6
|
require 'optparse'
|
7
7
|
require 'log4r'
|
8
8
|
require 'log4r/outputter/syslogoutputter'
|
9
|
-
require 'flapjack/result'
|
10
9
|
require 'flapjack/patches'
|
11
10
|
|
12
11
|
module Flapjack
|
@@ -48,89 +47,5 @@ module Flapjack
|
|
48
47
|
options
|
49
48
|
end
|
50
49
|
end
|
51
|
-
|
52
|
-
class Worker
|
53
|
-
|
54
|
-
attr_accessor :jobs, :results, :log
|
55
|
-
|
56
|
-
def initialize(opts={})
|
57
|
-
@jobs = Beanstalk::Pool.new(["#{opts[:host]}:#{opts[:port]}"], 'jobs')
|
58
|
-
@results = Beanstalk::Pool.new(["#{opts[:host]}:#{opts[:port]}"], 'results')
|
59
|
-
@sandbox = (opts[:check_directory] || File.expand_path(File.join(File.dirname(__FILE__), '..', 'checks')))
|
60
|
-
|
61
|
-
if opts[:logger]
|
62
|
-
@log = opts[:logger]
|
63
|
-
else
|
64
|
-
@log = Log4r::Logger.new('worker')
|
65
|
-
@log.add(Log4r::StdoutOutputter.new('worker'))
|
66
|
-
@log.add(Log4r::SyslogOutputter.new('worker'))
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def process_loop
|
71
|
-
@log.info("Booting main loop...")
|
72
|
-
loop do
|
73
|
-
process_check
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def process_check
|
78
|
-
# get next check off the beanstalk
|
79
|
-
job, check = get_check()
|
80
|
-
|
81
|
-
# do the actual check
|
82
|
-
result, retval = perform_check(check.command)
|
83
|
-
|
84
|
-
# report the results of the check
|
85
|
-
report_check(:result => result, :retval => retval, :check => check)
|
86
|
-
|
87
|
-
# create another job for the check, delete current job
|
88
|
-
cleanup_job(:job => job, :check => check)
|
89
|
-
end
|
90
|
-
|
91
|
-
def perform_check(cmd)
|
92
|
-
command = "sh -c '#{@sandbox}/#{cmd}'"
|
93
|
-
@log.debug("Executing check: \"#{command}\"")
|
94
|
-
result = `#{command}`
|
95
|
-
retval = $?.exitstatus
|
96
|
-
|
97
|
-
return result, retval
|
98
|
-
end
|
99
|
-
|
100
|
-
def report_check(opts={})
|
101
|
-
raise ArgumentError unless (opts[:result] && opts[:retval] && opts[:check])
|
102
|
-
|
103
|
-
@log.debug "Reporting results for check id #{opts[:check].id}."
|
104
|
-
@results.yput({:id => opts[:check].id,
|
105
|
-
:output => opts[:result],
|
106
|
-
:retval => opts[:retval].to_i})
|
107
|
-
end
|
108
|
-
|
109
|
-
def cleanup_job(opts={})
|
110
|
-
raise ArgumentError unless (opts[:job] && opts[:check])
|
111
|
-
|
112
|
-
# add job back onto stack
|
113
|
-
@log.debug("Putting check back onto beanstalk.")
|
114
|
-
@jobs.yput(opts[:check].to_h, 65536, opts[:check].frequency)
|
115
|
-
|
116
|
-
# FIXME: what happens when power goes out here?
|
117
|
-
|
118
|
-
# once we're done, clean up
|
119
|
-
@log.debug("Deleting job.")
|
120
|
-
opts[:job].delete
|
121
|
-
end
|
122
|
-
|
123
|
-
def get_check
|
124
|
-
@log.debug("Waiting for check...")
|
125
|
-
job = @jobs.reserve
|
126
|
-
# FIXME: maybe wrap Result as Job now that Check is reserved?
|
127
|
-
check = Result.new(YAML::load(job.body))
|
128
|
-
@log.info("Got check with id #{check.id}")
|
129
|
-
|
130
|
-
return job, check
|
131
|
-
end
|
132
|
-
|
133
|
-
end
|
134
|
-
|
135
50
|
end
|
136
|
-
|
51
|
+
|