flapjack 0.6.36 → 0.6.37
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +10 -0
- data/Rakefile +2 -1
- data/lib/flapjack/api.rb +7 -4
- data/lib/flapjack/data/contact.rb +73 -35
- data/lib/flapjack/data/entity_check.rb +24 -9
- data/lib/flapjack/executive.rb +30 -40
- data/lib/flapjack/pagerduty.rb +1 -1
- data/lib/flapjack/version.rb +1 -1
- data/lib/flapjack/web.rb +59 -32
- data/lib/flapjack/web/views/_css.haml +38 -0
- data/lib/flapjack/web/views/_nav.haml +8 -0
- data/lib/flapjack/web/views/check.haml +26 -44
- data/lib/flapjack/web/views/contact.haml +50 -0
- data/lib/flapjack/web/views/contacts.haml +26 -0
- data/lib/flapjack/web/views/index.haml +7 -44
- data/lib/flapjack/web/views/self_stats.haml +7 -38
- data/spec/lib/flapjack/web_spec.rb +12 -0
- data/spec/spec_helper.rb +3 -1
- metadata +7 -3
- data/lib/flapjack/data/notification.rb +0 -13
data/.travis.yml
ADDED
data/Rakefile
CHANGED
@@ -27,7 +27,7 @@ end
|
|
27
27
|
require 'rspec/core/rake_task'
|
28
28
|
RSpec::Core::RakeTask.new(:spec)
|
29
29
|
|
30
|
-
task :default => :
|
30
|
+
task :default => :tests
|
31
31
|
|
32
32
|
namespace :verify do
|
33
33
|
task :uncommitted do
|
@@ -46,3 +46,4 @@ end
|
|
46
46
|
|
47
47
|
# FIXME: getting that intermittent gherken lexing error so removing :features from verify list
|
48
48
|
task :verify => [ 'verify:all', :spec, :features]
|
49
|
+
task :tests => [ :spec, :features]
|
data/lib/flapjack/api.rb
CHANGED
@@ -47,8 +47,13 @@ module Flapjack
|
|
47
47
|
|
48
48
|
class API < Sinatra::Base
|
49
49
|
|
50
|
-
|
51
|
-
|
50
|
+
set :show_exceptions, false
|
51
|
+
|
52
|
+
if 'test'.eql?(FLAPJACK_ENV)
|
53
|
+
# expose test errors properly
|
54
|
+
set :raise_errors, true
|
55
|
+
else
|
56
|
+
# doesn't work with Rack::Test unless we wrap tests in EM.synchrony blocks
|
52
57
|
rescue_exception = Proc.new { |env, exception|
|
53
58
|
logger.error exception.message
|
54
59
|
logger.error exception.backtrace.join("\n")
|
@@ -62,8 +67,6 @@ module Flapjack
|
|
62
67
|
|
63
68
|
extend Flapjack::Pikelet
|
64
69
|
|
65
|
-
set :show_exceptions, 'development'.eql?(FLAPJACK_ENV)
|
66
|
-
|
67
70
|
before do
|
68
71
|
# will only initialise the first time it's run
|
69
72
|
Flapjack::API.bootstrap
|
@@ -1,77 +1,115 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
# NB: use of redis.keys probably indicates we should maintain a data
|
4
|
+
# structure to avoid the need for this type of query
|
5
|
+
|
3
6
|
module Flapjack
|
4
7
|
|
5
8
|
module Data
|
6
9
|
|
7
10
|
class Contact
|
8
11
|
|
9
|
-
|
10
|
-
|
11
|
-
def self.
|
12
|
+
attr_accessor :first_name, :last_name, :email, :media, :pagerduty_credentials, :id
|
13
|
+
|
14
|
+
def self.all(options = {})
|
12
15
|
raise "Redis connection not set" unless redis = options[:redis]
|
13
|
-
logger = options[:logger]
|
14
16
|
|
15
|
-
|
16
|
-
check = entity_check.check
|
17
|
+
contact_keys = redis.keys('contact:*')
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
union
|
19
|
+
contact_keys.inject([]) {|ret, k|
|
20
|
+
k =~ /^contact:(\d+)$/
|
21
|
+
id = $1
|
22
|
+
contact = self.find_by_id(id, :redis => redis)
|
23
|
+
ret << contact if contact
|
24
|
+
ret
|
25
|
+
}
|
26
26
|
end
|
27
27
|
|
28
28
|
def self.delete_all(options = {})
|
29
29
|
raise "Redis connection not set" unless redis = options[:redis]
|
30
30
|
|
31
|
-
|
31
|
+
redis.del( redis.keys("contact:*") +
|
32
|
+
redis.keys("contact_media:*") +
|
33
|
+
redis.keys("contact_pagerduty:*") +
|
34
|
+
redis.keys('contacts_for:*') )
|
35
|
+
end
|
32
36
|
|
33
|
-
|
34
|
-
|
35
|
-
|
37
|
+
def self.find_by_id(id, options = {})
|
38
|
+
raise "Redis connection not set" unless redis = options[:redis]
|
39
|
+
raise "No id value passed" unless id
|
40
|
+
logger = options[:logger]
|
36
41
|
|
37
|
-
|
38
|
-
|
39
|
-
|
42
|
+
fn, ln, em = redis.hmget("contact:#{id}", 'first_name', 'last_name', 'email')
|
43
|
+
me = redis.hgetall("contact_media:#{id}")
|
44
|
+
|
45
|
+
# similar to code in instance method pagerduty_credentials
|
46
|
+
pc = nil
|
47
|
+
if service_key = redis.hget("contact_media:#{id}", 'pagerduty')
|
48
|
+
pc = redis.hgetall("contact_pagerduty:#{id}").merge('service_key' => service_key)
|
40
49
|
end
|
50
|
+
|
51
|
+
self.new(:first_name => fn, :last_name => ln,
|
52
|
+
:email => em, :id => id, :media => me, :pagerduty_credentials => pc, :redis => redis )
|
41
53
|
end
|
42
54
|
|
55
|
+
|
43
56
|
# NB: should probably be called in the context of a Redis multi block; not doing so
|
44
57
|
# here as calling classes may well be adding/updating multiple records in the one
|
45
58
|
# operation
|
59
|
+
# TODO maybe return the instantiated Contact record?
|
46
60
|
def self.add(contact, options = {})
|
47
61
|
raise "Redis connection not set" unless redis = options[:redis]
|
48
62
|
|
49
|
-
redis.del("contact:#{contact['id']}"
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
63
|
+
redis.del("contact:#{contact['id']}",
|
64
|
+
"contact_media:#{contact['id']}",
|
65
|
+
"contact_pagerduty:#{contact['id']}")
|
66
|
+
|
67
|
+
redis.hmset("contact:#{contact['id']}",
|
68
|
+
*['first_name', 'last_name', 'email'].collect {|f| [f, contact[f]]})
|
69
|
+
|
55
70
|
contact['media'].each_pair {|medium, address|
|
56
71
|
case medium
|
57
72
|
when 'pagerduty'
|
58
73
|
redis.hset("contact_media:#{contact['id']}", medium, address['service_key'])
|
59
|
-
redis.
|
60
|
-
|
61
|
-
redis.hset("contact_pagerduty:#{contact['id']}", 'password', address['password'])
|
74
|
+
redis.hmset("contact_pagerduty:#{contact['id']}",
|
75
|
+
*['subdomain', 'username', 'password'].collect {|f| [f, address[f]]})
|
62
76
|
else
|
63
77
|
redis.hset("contact_media:#{contact['id']}", medium, address)
|
64
78
|
end
|
65
79
|
}
|
66
80
|
end
|
67
81
|
|
68
|
-
def
|
69
|
-
|
82
|
+
def pagerduty_credentials
|
83
|
+
return unless service_key = @redis.hget("contact_media:#{self.id}", 'pagerduty')
|
84
|
+
@redis.hgetall("contact_pagerduty:#{self.id}").
|
85
|
+
merge('service_key' => service_key)
|
86
|
+
end
|
87
|
+
|
88
|
+
def entities
|
89
|
+
@redis.keys('contacts_for:*').inject([]) {|ret, k|
|
90
|
+
if @redis.sismember(k, self.id)
|
91
|
+
k =~ /^contacts_for:(.+)$/
|
92
|
+
entity_id = $1
|
93
|
+
if entity_name = @redis.hget("entity:#{entity_id}", 'name')
|
94
|
+
ret << Flapjack::Data::Entity.new(:name => entity_name,
|
95
|
+
:id => entity_id, :redis => @redis)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
ret
|
99
|
+
}
|
100
|
+
end
|
70
101
|
|
71
|
-
|
102
|
+
def name
|
103
|
+
[(self.first_name || ''), (self.last_name || '')].join(" ").strip
|
104
|
+
end
|
72
105
|
|
73
|
-
|
74
|
-
|
106
|
+
private
|
107
|
+
|
108
|
+
def initialize(options = {})
|
109
|
+
raise "Redis connection not set" unless @redis = options[:redis]
|
110
|
+
[:first_name, :last_name, :email, :media, :id].each do |field|
|
111
|
+
instance_variable_set(:"@#{field.to_s}", options[field])
|
112
|
+
end
|
75
113
|
end
|
76
114
|
|
77
115
|
end
|
@@ -6,6 +6,9 @@ require 'flapjack/patches'
|
|
6
6
|
|
7
7
|
require 'flapjack/data/entity'
|
8
8
|
|
9
|
+
# TODO might want to split the class methods out to a separate class, DAO pattern
|
10
|
+
# ( http://en.wikipedia.org/wiki/Data_access_object ).
|
11
|
+
|
9
12
|
module Flapjack
|
10
13
|
|
11
14
|
module Data
|
@@ -415,21 +418,33 @@ module Flapjack
|
|
415
418
|
# has pagerduty credentials then there'll be one hash in the array for each set of
|
416
419
|
# credentials.
|
417
420
|
def pagerduty_credentials(options)
|
418
|
-
|
419
|
-
raise "Redis connection not set" unless redis = options[:redis]
|
420
|
-
logger = options[:logger]
|
421
|
+
# raise "Redis connection not set" unless redis = options[:redis]
|
421
422
|
|
422
|
-
contacts
|
423
|
-
|
424
|
-
|
425
|
-
|
423
|
+
self.contacts.inject([]) {|ret, contact|
|
424
|
+
cred = contact.pagerduty_credentials
|
425
|
+
ret << cred if cred
|
426
|
+
ret
|
426
427
|
}
|
427
|
-
|
428
|
+
end
|
429
|
+
|
430
|
+
# takes a check, looks up contacts that are interested in this check (or in the check's entity)
|
431
|
+
# and returns an array of contact records
|
432
|
+
def contacts
|
433
|
+
entity = @entity
|
434
|
+
check = @check
|
435
|
+
|
436
|
+
if @logger
|
437
|
+
@logger.debug("contacts for #{@entity.id} (#{@entity.name}): " + @redis.smembers("contacts_for:#{@entity.id}").length.to_s)
|
438
|
+
@logger.debug("contacts for #{check}: " + @redis.smembers("contacts_for:#{check}").length.to_s)
|
439
|
+
end
|
440
|
+
|
441
|
+
union = @redis.sunion("contacts_for:#{@entity.id}", "contacts_for:#{check}")
|
442
|
+
@logger.debug("contacts for union of #{@entity.id} and #{check}: " + union.length.to_s) if @logger
|
443
|
+
union.collect {|c_id| Flapjack::Data::Contact.find_by_id(c_id, :redis => @redis) }
|
428
444
|
end
|
429
445
|
|
430
446
|
private
|
431
447
|
|
432
|
-
# Passing around the redis handle like this is a SMELL.
|
433
448
|
def initialize(entity, check, options = {})
|
434
449
|
raise "Redis connection not set" unless @redis = options[:redis]
|
435
450
|
raise "Invalid entity" unless @entity = entity
|
data/lib/flapjack/executive.rb
CHANGED
@@ -204,8 +204,7 @@ module Flapjack
|
|
204
204
|
@redis.rpush("#{event.id}:#{notification_type}_notifications", timestamp)
|
205
205
|
@logger.debug("Notification of type #{notification_type} is being generated for #{event.id}.")
|
206
206
|
|
207
|
-
send_notifications(event, notification_type,
|
208
|
-
Flapjack::Data::Contact.find_all_for_entity_check(entity_check, :redis => @redis))
|
207
|
+
send_notifications(event, notification_type, entity_check.contacts)
|
209
208
|
end
|
210
209
|
|
211
210
|
# takes an event, a notification type, and an array of contacts and creates jobs in resque
|
@@ -217,20 +216,28 @@ module Flapjack
|
|
217
216
|
'time' => event.time,
|
218
217
|
'notification_type' => notification_type }
|
219
218
|
|
220
|
-
contacts.
|
221
|
-
|
219
|
+
if contacts.empty?
|
220
|
+
@notifylog.info("#{Time.now.to_s} | #{event.id} | #{notification_type} | NO CONTACTS")
|
221
|
+
return
|
222
|
+
end
|
222
223
|
|
223
|
-
|
224
|
-
'contact_first_name' => @redis.hget("contact:#{contact_id}", 'first_name'),
|
225
|
-
'contact_last_name' => @redis.hget("contact:#{contact_id}", 'last_name'), }
|
224
|
+
contacts.each {|contact|
|
226
225
|
|
227
|
-
|
226
|
+
if contact.media.empty?
|
227
|
+
@notifylog.info("#{Time.now.to_s} | #{event.id} | #{notification_type} | #{contact.id} | NO MEDIA FOR CONTACT")
|
228
|
+
next
|
229
|
+
end
|
228
230
|
|
229
|
-
|
231
|
+
notification.merge!({'contact_id' => contact.id,
|
232
|
+
'contact_first_name' => contact.first_name,
|
233
|
+
'contact_last_name' => contact.last_name, })
|
234
|
+
|
235
|
+
contact.media.each_pair {|media_type, address|
|
236
|
+
|
237
|
+
@notifylog.info("#{Time.now.to_s} | #{event.id} | " +
|
238
|
+
"#{notification_type} | #{contact.id} | #{media_type} | #{address}")
|
230
239
|
|
231
|
-
@notifylog.info("#{Time.now.to_s} | #{event.id} | #{notification_type} | #{contact_id} | #{media} | #{address}")
|
232
240
|
# queue this notification
|
233
|
-
# FIXME: make a Contact class perhaps
|
234
241
|
notif = notification.dup
|
235
242
|
notif['media'] = media_type
|
236
243
|
notif['address'] = address
|
@@ -239,43 +246,26 @@ module Flapjack
|
|
239
246
|
notif['duration'] = dur if dur
|
240
247
|
@logger.debug("send_notifications: sending notification: #{notif.inspect}")
|
241
248
|
|
249
|
+
unless @queues[media_type.to_sym]
|
250
|
+
# TODO log error
|
251
|
+
next
|
252
|
+
end
|
253
|
+
|
254
|
+
# TODO consider changing Resque jobs to use raw blpop like the others
|
242
255
|
case media_type
|
243
256
|
when "sms"
|
244
|
-
|
245
|
-
Resque.enqueue_to(@queues[:sms], Notification::Sms, notif)
|
246
|
-
end
|
257
|
+
Resque.enqueue_to(@queues[:sms], Notification::Sms, notif)
|
247
258
|
when "email"
|
248
|
-
|
249
|
-
Resque.enqueue_to(@queues[:email], Notification::Email, notif)
|
250
|
-
end
|
259
|
+
Resque.enqueue_to(@queues[:email], Notification::Email, notif)
|
251
260
|
when "jabber"
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
@redis.rpush(@queues[:jabber], Yajl::Encoder.encode(notif))
|
256
|
-
end
|
261
|
+
# TODO move next line up into other notif value setting above?
|
262
|
+
notif['event_count'] = @event_count if @event_count
|
263
|
+
@redis.rpush(@queues[:jabber], Yajl::Encoder.encode(notif))
|
257
264
|
when "pagerduty"
|
258
|
-
|
259
|
-
@redis.rpush(@queues[:pagerduty], Yajl::Encoder.encode(notif))
|
260
|
-
end
|
265
|
+
@redis.rpush(@queues[:pagerduty], Yajl::Encoder.encode(notif))
|
261
266
|
end
|
262
267
|
}
|
263
|
-
if media.length == 0
|
264
|
-
@notifylog.info("#{Time.now.to_s} | #{event.id} | #{notification_type} | #{contact_id} | NO MEDIA FOR CONTACT")
|
265
|
-
end
|
266
268
|
}
|
267
|
-
if contacts.length == 0
|
268
|
-
@notifylog.info("#{Time.now.to_s} | #{event.id} | #{notification_type} | NO CONTACTS")
|
269
|
-
end
|
270
|
-
end
|
271
|
-
|
272
|
-
# takes a contact ID and returns a hash containing each of the media the contact wishes to be
|
273
|
-
# contacted by, and the associated address for each.
|
274
|
-
# eg:
|
275
|
-
# media_for_contact('123') -> { :sms => "+61401234567", :email => "gno@free.dom" }
|
276
|
-
#
|
277
|
-
def media_for_contact(contact)
|
278
|
-
@redis.hgetall("contact_media:#{contact}")
|
279
269
|
end
|
280
270
|
|
281
271
|
# generates a fairly unique identifier to use as a message id
|
data/lib/flapjack/pagerduty.rb
CHANGED
@@ -134,7 +134,7 @@ module Flapjack
|
|
134
134
|
# ok lets do it
|
135
135
|
unacknowledged_failing_checks.each {|check|
|
136
136
|
entity_check = Flapjack::Data::EntityCheck.for_event_id(check, { :redis => @redis_timer, :logger => @logger } )
|
137
|
-
pagerduty_credentials = entity_check.pagerduty_credentials(
|
137
|
+
pagerduty_credentials = entity_check.pagerduty_credentials(:redis => @redis_timer)
|
138
138
|
|
139
139
|
if pagerduty_credentials.length == 0
|
140
140
|
@logger.debug("Found no pagerduty creditials for #{entity_check.entity_name}:#{entity_check.check}, moving on")
|
data/lib/flapjack/version.rb
CHANGED
data/lib/flapjack/web.rb
CHANGED
@@ -9,20 +9,33 @@ require 'haml'
|
|
9
9
|
require 'rack/fiber_pool'
|
10
10
|
|
11
11
|
require 'flapjack/pikelet'
|
12
|
+
require 'flapjack/data/contact'
|
12
13
|
require 'flapjack/data/entity_check'
|
13
14
|
require 'flapjack/utility'
|
14
15
|
|
15
16
|
module Flapjack
|
16
17
|
class Web < Sinatra::Base
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
if 'test'.eql?(FLAPJACK_ENV)
|
20
|
+
# expose test errors properly
|
21
|
+
set :raise_errors, true
|
22
|
+
set :show_exceptions, false
|
23
|
+
else
|
24
|
+
rescue_exception = Proc.new do |env, e|
|
25
|
+
if settings.show_exceptions?
|
26
|
+
# ensure the sinatra error page shows properly
|
27
|
+
request = Sinatra::Request.new(env)
|
28
|
+
printer = Sinatra::ShowExceptions.new(proc{ raise e })
|
29
|
+
s, h, b = printer.call(env)
|
30
|
+
[s, h, b]
|
31
|
+
else
|
32
|
+
logger.error e.message
|
33
|
+
logger.error e.backtrace.join("\n")
|
34
|
+
[503, {}, ""]
|
35
|
+
end
|
36
|
+
end
|
25
37
|
|
38
|
+
# doesn't work with Rack::Test unless we wrap tests in EM.synchrony blocks
|
26
39
|
use Rack::FiberPool, :size => 25, :rescue_exception => rescue_exception
|
27
40
|
end
|
28
41
|
use Rack::MethodOverride
|
@@ -62,7 +75,6 @@ module Flapjack
|
|
62
75
|
end
|
63
76
|
|
64
77
|
get '/check' do
|
65
|
-
#begin
|
66
78
|
@entity = params[:entity]
|
67
79
|
@check = params[:check]
|
68
80
|
|
@@ -83,12 +95,9 @@ module Flapjack
|
|
83
95
|
@current_scheduled_maintenance = entity_check.current_maintenance(:scheduled => true)
|
84
96
|
@current_unscheduled_maintenance = entity_check.current_maintenance(:scheduled => false)
|
85
97
|
|
86
|
-
|
87
|
-
#rescue Exception => e
|
88
|
-
# puts e.message
|
89
|
-
# puts e.backtrace.join("\n")
|
90
|
-
#end
|
98
|
+
@contacts = entity_check.contacts
|
91
99
|
|
100
|
+
haml :check
|
92
101
|
end
|
93
102
|
|
94
103
|
post '/acknowledgements/:entity/:check' do
|
@@ -110,6 +119,7 @@ module Flapjack
|
|
110
119
|
end
|
111
120
|
|
112
121
|
# FIXME: there is bound to be a more idiomatic / restful way of doing this
|
122
|
+
# (probably using 'delete' or 'patch')
|
113
123
|
post '/end_unscheduled_maintenance/:entity/:check' do
|
114
124
|
@entity = params[:entity]
|
115
125
|
@check = params[:check]
|
@@ -140,29 +150,20 @@ module Flapjack
|
|
140
150
|
|
141
151
|
# modify scheduled maintenance
|
142
152
|
patch '/scheduled_maintenances/:entity/:check' do
|
153
|
+
entity_check = get_entity_check(params[:entity], params[:check])
|
154
|
+
return 404 if entity_check.nil?
|
143
155
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
entity_check = get_entity_check(params[:entity], params[:check])
|
148
|
-
return 404 if entity_check.nil?
|
149
|
-
|
150
|
-
end_time = Chronic.parse(params[:end_time]).to_i
|
151
|
-
start_time = params[:start_time].to_i
|
152
|
-
raise ArgumentError, "start time parsed to zero" unless start_time > 0
|
153
|
-
|
154
|
-
patches = {}
|
155
|
-
patches[:end_time] = end_time if end_time && (end_time > start_time)
|
156
|
+
end_time = Chronic.parse(params[:end_time]).to_i
|
157
|
+
start_time = params[:start_time].to_i
|
158
|
+
raise ArgumentError, "start time parsed to zero" unless start_time > 0
|
156
159
|
|
157
|
-
|
160
|
+
patches = {}
|
161
|
+
patches[:end_time] = end_time if end_time && (end_time > start_time)
|
158
162
|
|
159
|
-
|
160
|
-
redirect back
|
163
|
+
raise ArgumentError.new("no valid data received to patch with") if patches.empty?
|
161
164
|
|
162
|
-
|
163
|
-
|
164
|
-
puts e.backtrace.join("\n")
|
165
|
-
end
|
165
|
+
entity_check.update_scheduled_maintenance(start_time, patches)
|
166
|
+
redirect back
|
166
167
|
end
|
167
168
|
|
168
169
|
# delete a scheduled maintenance
|
@@ -174,6 +175,32 @@ module Flapjack
|
|
174
175
|
redirect back
|
175
176
|
end
|
176
177
|
|
178
|
+
get '/contacts' do
|
179
|
+
@contacts = Flapjack::Data::Contact.all(:redis => @@redis)
|
180
|
+
haml :contacts
|
181
|
+
end
|
182
|
+
|
183
|
+
get "/contacts/:contact" do
|
184
|
+
contact_id = params[:contact]
|
185
|
+
|
186
|
+
if contact_id
|
187
|
+
@contact = Flapjack::Data::Contact.find_by_id(contact_id, :redis => @@redis)
|
188
|
+
end
|
189
|
+
|
190
|
+
unless @contact
|
191
|
+
status 404
|
192
|
+
return
|
193
|
+
end
|
194
|
+
|
195
|
+
if @contact.media.has_key?('pagerduty')
|
196
|
+
@pagerduty_credentials = @contact.pagerduty_credentials
|
197
|
+
end
|
198
|
+
|
199
|
+
@entities = @contact.entities
|
200
|
+
|
201
|
+
haml :contact
|
202
|
+
end
|
203
|
+
|
177
204
|
private
|
178
205
|
|
179
206
|
def get_entity_check(entity, check)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
:css
|
2
|
+
* { margin: 0; padding: 0; }
|
3
|
+
html { font-size: 62.5%; }
|
4
|
+
body { font-size: 16px; }
|
5
|
+
div#wrapper {
|
6
|
+
margin: 24px auto;
|
7
|
+
width: 1000px;
|
8
|
+
}
|
9
|
+
h1, h2, h3, h4, h5 {
|
10
|
+
font-family: Helvetica Neue, sans-serif;
|
11
|
+
margin-bottom: 12px;
|
12
|
+
}
|
13
|
+
table {
|
14
|
+
text-align: left;
|
15
|
+
width: 100%;
|
16
|
+
}
|
17
|
+
table th {
|
18
|
+
font-family: Helvetica Neue, sans-serif;
|
19
|
+
background-color: #eee;
|
20
|
+
}
|
21
|
+
table td, table th {
|
22
|
+
padding: 4px;
|
23
|
+
}
|
24
|
+
table td.critical {
|
25
|
+
background-color: #fb9a99;
|
26
|
+
}
|
27
|
+
table td.down {
|
28
|
+
background-color: #fb9a99;
|
29
|
+
}
|
30
|
+
table td.warning {
|
31
|
+
background-color: #f9bb34;
|
32
|
+
}
|
33
|
+
table td.ok {
|
34
|
+
background-color: #B2DF8A;
|
35
|
+
}
|
36
|
+
table td.up {
|
37
|
+
background-color: #B2DF8A;
|
38
|
+
}
|
@@ -1,50 +1,15 @@
|
|
1
1
|
!!! 5
|
2
|
+
- nav_haml = Haml::Engine.new(File.read(File.dirname(__FILE__) + '/_nav.haml'))
|
3
|
+
- nav = nav_haml.render
|
4
|
+
- css_haml = Haml::Engine.new(File.read(File.dirname(__FILE__) + '/_css.haml'))
|
5
|
+
- css = css_haml.render
|
2
6
|
%html
|
3
7
|
%head
|
4
|
-
:
|
5
|
-
|
6
|
-
html { font-size: 62.5%; }
|
7
|
-
body { font-size: 16px; }
|
8
|
-
div#wrapper {
|
9
|
-
margin: 24px auto;
|
10
|
-
width: 1000px;
|
11
|
-
}
|
12
|
-
h1, h2, h3, h4, h5 {
|
13
|
-
font-family: Helvetica Neue, sans-serif;
|
14
|
-
margin-bottom: 12px;
|
15
|
-
}
|
16
|
-
table {
|
17
|
-
text-align: left;
|
18
|
-
width: 100%;
|
19
|
-
}
|
20
|
-
table th {
|
21
|
-
font-family: Helvetica Neue, sans-serif;
|
22
|
-
background-color: #eee;
|
23
|
-
}
|
24
|
-
table td, table th {
|
25
|
-
padding: 4px;
|
26
|
-
}
|
27
|
-
table td.critical {
|
28
|
-
background-color: #fb9a99;
|
29
|
-
}
|
30
|
-
table td.down {
|
31
|
-
background-color: #fb9a99;
|
32
|
-
}
|
33
|
-
table td.warning {
|
34
|
-
background-color: #f9bb34;
|
35
|
-
}
|
36
|
-
table td.ok {
|
37
|
-
background-color: #B2DF8A;
|
38
|
-
}
|
39
|
-
table td.up {
|
40
|
-
background-color: #B2DF8A;
|
41
|
-
}
|
8
|
+
%title Flapjack - Check: #{@entity}:#{@check}
|
9
|
+
= css
|
42
10
|
%body
|
43
11
|
%div#wrapper
|
44
|
-
|
45
|
-
%a(title='Dashboard' href='/') All Checks
|
46
|
-
|
|
47
|
-
%a(title='Dashboard' href='/failing') Failing Checks
|
12
|
+
= nav
|
48
13
|
%h1 #{@check} on #{@entity}
|
49
14
|
%form{:action => "/acknowledgements/#{@entity}/#{@check}", :method => "post"}
|
50
15
|
%h2
|
@@ -107,7 +72,7 @@
|
|
107
72
|
%td= last_ack_relative
|
108
73
|
%td= last_ack
|
109
74
|
%h3 Scheduled Maintenance Periods
|
110
|
-
- if @scheduled_maintenances &&
|
75
|
+
- if @scheduled_maintenances && !@scheduled_maintenances.empty?
|
111
76
|
%table
|
112
77
|
%tr
|
113
78
|
%th Start
|
@@ -165,4 +130,21 @@
|
|
165
130
|
%td
|
166
131
|
%td
|
167
132
|
%input{:type => 'submit', :value => 'Save', :class => 'button'}
|
168
|
-
|
133
|
+
%h3 Contacts
|
134
|
+
- if @contacts && !@contacts.empty?
|
135
|
+
%table
|
136
|
+
%tr
|
137
|
+
%th Name
|
138
|
+
%th Media
|
139
|
+
- @contacts.sort_by {|c| [c.last_name, c.first_name] }.each do |contact|
|
140
|
+
%tr
|
141
|
+
%td
|
142
|
+
- link = "/contacts/#{contact.id}"
|
143
|
+
%a(title='contact details' href=link) #{contact.name}
|
144
|
+
%td
|
145
|
+
- if contact.media && !contact.media.empty?
|
146
|
+
%p= contact.media.keys.collect(&:capitalize).join(", ")
|
147
|
+
- else
|
148
|
+
%p No media
|
149
|
+
- else
|
150
|
+
%p No contacts
|
@@ -0,0 +1,50 @@
|
|
1
|
+
!!! 5
|
2
|
+
- nav_haml = Haml::Engine.new(File.read(File.dirname(__FILE__) + '/_nav.haml'))
|
3
|
+
- nav = nav_haml.render
|
4
|
+
- css_haml = Haml::Engine.new(File.read(File.dirname(__FILE__) + '/_css.haml'))
|
5
|
+
- css = css_haml.render
|
6
|
+
%html
|
7
|
+
%head
|
8
|
+
%title Flapjack - Contact: #{@contact.name}
|
9
|
+
= css
|
10
|
+
%body
|
11
|
+
%div#wrapper
|
12
|
+
= nav
|
13
|
+
%h1= @contact.name
|
14
|
+
- if @entities && !@entities.empty?
|
15
|
+
%table
|
16
|
+
%tr
|
17
|
+
%th Entity name
|
18
|
+
%th Checks
|
19
|
+
%th Media
|
20
|
+
- @entities.sort_by(&:name).each do |entity|
|
21
|
+
%tr
|
22
|
+
%td
|
23
|
+
%p= entity.name
|
24
|
+
%td
|
25
|
+
- checks = entity.check_list
|
26
|
+
- if !checks.empty?
|
27
|
+
- checks.each do |check|
|
28
|
+
- link = "/check?entity=#{entity.name}&check=#{check}"
|
29
|
+
%p
|
30
|
+
%a(title='check status' href=link) #{check}
|
31
|
+
- else
|
32
|
+
%p No checks
|
33
|
+
%td
|
34
|
+
- if @contact.media && !@contact.media.empty?
|
35
|
+
%ul
|
36
|
+
- @contact.media.each_pair do |mk, mv|
|
37
|
+
- if 'pagerduty'.eql?(mk)
|
38
|
+
%li= "PagerDuty: "
|
39
|
+
%ul
|
40
|
+
- @pagerduty_credentials.each_pair do |pk, pv|
|
41
|
+
- if 'password'.eql?(pk)
|
42
|
+
%li= "#{pk}: ..."
|
43
|
+
- else
|
44
|
+
%li= "#{pk}: #{pv}"
|
45
|
+
- else
|
46
|
+
%li= "#{mk.capitalize}: #{mv}"
|
47
|
+
- else
|
48
|
+
%p No media
|
49
|
+
- else
|
50
|
+
%p No entities
|
@@ -0,0 +1,26 @@
|
|
1
|
+
!!! 5
|
2
|
+
- nav_haml = Haml::Engine.new(File.read(File.dirname(__FILE__) + '/_nav.haml'))
|
3
|
+
- nav = nav_haml.render
|
4
|
+
- css_haml = Haml::Engine.new(File.read(File.dirname(__FILE__) + '/_css.haml'))
|
5
|
+
- css = css_haml.render
|
6
|
+
%html
|
7
|
+
%head
|
8
|
+
%title Flapjack - Contacts
|
9
|
+
= css
|
10
|
+
%body
|
11
|
+
%div#wrapper
|
12
|
+
= nav
|
13
|
+
%h1 Contacts
|
14
|
+
- if @contacts && !@contacts.empty?
|
15
|
+
%table
|
16
|
+
%tr
|
17
|
+
%th Name
|
18
|
+
%th Email
|
19
|
+
- @contacts.sort_by {|c| [c.last_name, c.first_name] }.each do |contact|
|
20
|
+
%tr
|
21
|
+
%td
|
22
|
+
- link = "/contacts/#{contact.id}"
|
23
|
+
%a(title='contact details' href=link) #{contact.name}
|
24
|
+
%td= contact.email
|
25
|
+
- else
|
26
|
+
%p No contacts
|
@@ -1,52 +1,15 @@
|
|
1
1
|
!!! 5
|
2
|
+
- nav_haml = Haml::Engine.new(File.read(File.dirname(__FILE__) + '/_nav.haml'))
|
3
|
+
- nav = nav_haml.render
|
4
|
+
- css_haml = Haml::Engine.new(File.read(File.dirname(__FILE__) + '/_css.haml'))
|
5
|
+
- css = css_haml.render
|
2
6
|
%html
|
3
7
|
%head
|
4
|
-
|
5
|
-
|
6
|
-
html { font-size: 62.5%; }
|
7
|
-
body { font-size: 16px; }
|
8
|
-
div#wrapper {
|
9
|
-
margin: 24px auto;
|
10
|
-
width: 1000px;
|
11
|
-
}
|
12
|
-
h1, h2, h3, h4, h5 {
|
13
|
-
font-family: Helvetica Neue, sans-serif;
|
14
|
-
margin-bottom: 12px;
|
15
|
-
}
|
16
|
-
table {
|
17
|
-
text-align: left;
|
18
|
-
width: 100%;
|
19
|
-
}
|
20
|
-
table th {
|
21
|
-
font-family: Helvetica Neue, sans-serif;
|
22
|
-
background-color: #eee;
|
23
|
-
}
|
24
|
-
table td, table th {
|
25
|
-
padding: 4px;
|
26
|
-
}
|
27
|
-
table td.critical {
|
28
|
-
background-color: #fb9a99;
|
29
|
-
}
|
30
|
-
table td.down {
|
31
|
-
background-color: #fb9a99;
|
32
|
-
}
|
33
|
-
table td.warning {
|
34
|
-
background-color: #f9bb34;
|
35
|
-
}
|
36
|
-
table td.ok {
|
37
|
-
background-color: #B2DF8A;
|
38
|
-
}
|
39
|
-
table td.up {
|
40
|
-
background-color: #B2DF8A;
|
41
|
-
}
|
8
|
+
%title Flapjack
|
9
|
+
= css
|
42
10
|
%body
|
43
11
|
%div#wrapper
|
44
|
-
|
45
|
-
%a(title='internal stats' href='/') All Checks
|
46
|
-
|
|
47
|
-
%a(title='internal stats' href='/failing') Failing Checks
|
48
|
-
|
|
49
|
-
%a(title='internal stats' href='/self_stats') Internal Statistics
|
12
|
+
= nav
|
50
13
|
%h1 Flapjack Status
|
51
14
|
%h4
|
52
15
|
Events queued:
|
@@ -1,46 +1,15 @@
|
|
1
1
|
!!! 5
|
2
|
+
- nav_haml = Haml::Engine.new(File.read(File.dirname(__FILE__) + '/_nav.haml'))
|
3
|
+
- nav = nav_haml.render
|
4
|
+
- css_haml = Haml::Engine.new(File.read(File.dirname(__FILE__) + '/_css.haml'))
|
5
|
+
- css = css_haml.render
|
2
6
|
%html
|
3
7
|
%head
|
4
|
-
|
5
|
-
|
6
|
-
html { font-size: 62.5%; }
|
7
|
-
body { font-size: 20px; }
|
8
|
-
div#wrapper {
|
9
|
-
margin: 24px auto;
|
10
|
-
width: 800px;
|
11
|
-
}
|
12
|
-
h1, h2, h3, h4, h5 {
|
13
|
-
font-family: Helvetica Neue, sans-serif;
|
14
|
-
margin-bottom: 12px;
|
15
|
-
}
|
16
|
-
table {
|
17
|
-
text-align: left;
|
18
|
-
width: 100%;
|
19
|
-
}
|
20
|
-
table th {
|
21
|
-
font-family: Helvetica Neue, sans-serif;
|
22
|
-
background-color: #eee;
|
23
|
-
}
|
24
|
-
table td, table th {
|
25
|
-
padding: 4px;
|
26
|
-
}
|
27
|
-
table td.critical {
|
28
|
-
background-color: #fb9a99;
|
29
|
-
}
|
30
|
-
table td.down {
|
31
|
-
background-color: #fb9a99;
|
32
|
-
}
|
33
|
-
table td.warning {
|
34
|
-
background-color: #f9bb34;
|
35
|
-
}
|
36
|
-
table td.ok {
|
37
|
-
background-color: #B2DF8A;
|
38
|
-
}
|
39
|
-
table td.up {
|
40
|
-
background-color: #B2DF8A;
|
41
|
-
}
|
8
|
+
%title Flapjack - Self Stats
|
9
|
+
= css
|
42
10
|
%body
|
43
11
|
%div#wrapper
|
12
|
+
= nav
|
44
13
|
%h1 Flapjack Self Status
|
45
14
|
%h4
|
46
15
|
Events queued:
|
@@ -105,6 +105,7 @@ describe Flapjack::Web, :sinatra => true, :redis => true do
|
|
105
105
|
entity_check.should_receive(:failed?).and_return(false)
|
106
106
|
entity_check.should_receive(:current_maintenance).with(:scheduled => true).and_return(false)
|
107
107
|
entity_check.should_receive(:current_maintenance).with(:scheduled => false).and_return(false)
|
108
|
+
entity_check.should_receive(:contacts).and_return([])
|
108
109
|
|
109
110
|
Flapjack::Data::Entity.should_receive(:find_by_name).
|
110
111
|
with(entity_name, :redis => @redis).and_return(entity)
|
@@ -207,4 +208,15 @@ describe Flapjack::Web, :sinatra => true, :redis => true do
|
|
207
208
|
last_response.status.should == 302
|
208
209
|
end
|
209
210
|
|
211
|
+
it "shows a list of all known contacts" do
|
212
|
+
Flapjack::Data::Contact.should_receive(:all)
|
213
|
+
|
214
|
+
get "/contacts"
|
215
|
+
last_response.should be_ok
|
216
|
+
end
|
217
|
+
|
218
|
+
it "shows details of an individual contact found by email"
|
219
|
+
|
220
|
+
it "shows details of an individual contact found by id"
|
221
|
+
|
210
222
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -6,6 +6,8 @@ if ENV['COVERAGE']
|
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
|
+
$testing = true
|
10
|
+
|
9
11
|
FLAPJACK_ENV = ENV["FLAPJACK_ENV"] || 'test'
|
10
12
|
require 'bundler'
|
11
13
|
Bundler.require(:default, :test)
|
@@ -41,4 +43,4 @@ RSpec.configure do |config|
|
|
41
43
|
end
|
42
44
|
|
43
45
|
config.include Rack::Test::Methods, :sinatra => true
|
44
|
-
end
|
46
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flapjack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.37
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2012-
|
14
|
+
date: 2012-10-03 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: daemons
|
@@ -352,6 +352,7 @@ files:
|
|
352
352
|
- .gitignore
|
353
353
|
- .rbenv-version
|
354
354
|
- .rspec
|
355
|
+
- .travis.yml
|
355
356
|
- Gemfile
|
356
357
|
- Guardfile
|
357
358
|
- LICENCE
|
@@ -415,7 +416,6 @@ files:
|
|
415
416
|
- lib/flapjack/data/entity.rb
|
416
417
|
- lib/flapjack/data/entity_check.rb
|
417
418
|
- lib/flapjack/data/event.rb
|
418
|
-
- lib/flapjack/data/notification.rb
|
419
419
|
- lib/flapjack/executive.rb
|
420
420
|
- lib/flapjack/filters/acknowledgement.rb
|
421
421
|
- lib/flapjack/filters/base.rb
|
@@ -458,7 +458,11 @@ files:
|
|
458
458
|
- lib/flapjack/utility.rb
|
459
459
|
- lib/flapjack/version.rb
|
460
460
|
- lib/flapjack/web.rb
|
461
|
+
- lib/flapjack/web/views/_css.haml
|
462
|
+
- lib/flapjack/web/views/_nav.haml
|
461
463
|
- lib/flapjack/web/views/check.haml
|
464
|
+
- lib/flapjack/web/views/contact.haml
|
465
|
+
- lib/flapjack/web/views/contacts.haml
|
462
466
|
- lib/flapjack/web/views/index.haml
|
463
467
|
- lib/flapjack/web/views/self_stats.haml
|
464
468
|
- lib/flapjack/worker/application.rb
|