flapjack 0.6.36 → 0.6.37
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/.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
|