flapjack 0.7.35 → 0.8.0
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.
- checksums.yaml +7 -0
- data/.gitignore +1 -1
- data/Gemfile +3 -4
- data/Guardfile +1 -1
- data/README.md +38 -19
- data/Rakefile +1 -3
- data/etc/flapjack_config.yaml.example +11 -1
- data/features/steps/cli_steps.rb +3 -3
- data/features/steps/events_steps.rb +7 -6
- data/features/steps/flapjack-netsaint-parser_steps.rb +8 -8
- data/features/steps/notifications_steps.rb +10 -10
- data/features/steps/packaging-lintian_steps.rb +5 -9
- data/features/steps/time_travel_steps.rb +1 -1
- data/flapjack.gemspec +4 -3
- data/lib/flapjack/data/contact.rb +78 -6
- data/lib/flapjack/data/entity.rb +11 -2
- data/lib/flapjack/data/notification_rule.rb +67 -59
- data/lib/flapjack/data/semaphore.rb +44 -0
- data/lib/flapjack/gateways/api.rb +24 -28
- data/lib/flapjack/gateways/api/contact_methods.rb +1 -2
- data/lib/flapjack/gateways/api/entity_methods.rb +3 -3
- data/lib/flapjack/gateways/jsonapi.rb +249 -0
- data/lib/flapjack/gateways/jsonapi/contact_methods.rb +544 -0
- data/lib/flapjack/gateways/jsonapi/entity_check_presenter.rb +217 -0
- data/lib/flapjack/gateways/jsonapi/entity_methods.rb +350 -0
- data/lib/flapjack/gateways/jsonapi/entity_presenter.rb +75 -0
- data/lib/flapjack/gateways/jsonapi/rack/json_params_parser.rb +32 -0
- data/lib/flapjack/gateways/web.rb +78 -12
- data/lib/flapjack/gateways/web/public/css/bootstrap-theme.css +397 -0
- data/lib/flapjack/gateways/web/public/css/bootstrap-theme.min.css +7 -0
- data/lib/flapjack/gateways/web/public/css/bootstrap.css +7118 -0
- data/lib/flapjack/gateways/web/public/css/bootstrap.min.css +6 -8
- data/lib/flapjack/gateways/web/public/css/font-awesome.css +1338 -0
- data/lib/flapjack/gateways/web/public/css/font-awesome.min.css +4 -0
- data/lib/flapjack/gateways/web/public/css/screen.css +80 -0
- data/lib/flapjack/gateways/web/public/css/select2-bootstrap.css +87 -0
- data/lib/flapjack/gateways/web/public/css/select2.css +615 -0
- data/lib/flapjack/gateways/web/public/fonts/FontAwesome.otf +0 -0
- data/lib/flapjack/gateways/web/public/fonts/fontawesome-webfont.eot +0 -0
- data/lib/flapjack/gateways/web/public/fonts/fontawesome-webfont.svg +414 -0
- data/lib/flapjack/gateways/web/public/fonts/fontawesome-webfont.ttf +0 -0
- data/lib/flapjack/gateways/web/public/fonts/fontawesome-webfont.woff +0 -0
- data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.eot +0 -0
- data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.svg +229 -0
- data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.woff +0 -0
- data/lib/flapjack/gateways/web/public/img/flapjack-2013-notext-transparent-300-300.png +0 -0
- data/lib/flapjack/gateways/web/public/img/select2.png +0 -0
- data/lib/flapjack/gateways/web/public/img/select2x2.png +0 -0
- data/lib/flapjack/gateways/web/public/js/backbone-min.js +2 -0
- data/lib/flapjack/gateways/web/public/js/backbone.js +1581 -0
- data/lib/flapjack/gateways/web/public/js/backbone.jsonapi.js +75 -0
- data/lib/flapjack/gateways/web/public/js/bootstrap.js +2276 -0
- data/lib/flapjack/gateways/web/public/js/contacts.js +225 -0
- data/lib/flapjack/gateways/web/public/js/jquery-1.10.2.js +9789 -0
- data/lib/flapjack/gateways/web/public/js/jquery-1.10.2.min.js +6 -0
- data/lib/flapjack/gateways/web/public/js/select2.js +3255 -0
- data/lib/flapjack/gateways/web/public/js/select2.min.js +22 -0
- data/lib/flapjack/gateways/web/public/js/underscore-min.js +6 -0
- data/lib/flapjack/gateways/web/public/js/underscore.js +1276 -0
- data/lib/flapjack/gateways/web/views/check.html.erb +423 -193
- data/lib/flapjack/gateways/web/views/checks.html.erb +51 -71
- data/lib/flapjack/gateways/web/views/contact.html.erb +142 -164
- data/lib/flapjack/gateways/web/views/contacts.html.erb +20 -40
- data/lib/flapjack/gateways/web/views/edit_contacts.html.erb +83 -0
- data/lib/flapjack/gateways/web/views/entities.html.erb +18 -37
- data/lib/flapjack/gateways/web/views/entity.html.erb +46 -65
- data/lib/flapjack/gateways/web/views/index.html.erb +6 -27
- data/lib/flapjack/gateways/web/views/layout.erb +95 -0
- data/lib/flapjack/gateways/web/views/self_stats.html.erb +100 -114
- data/lib/flapjack/pikelet.rb +4 -2
- data/lib/flapjack/version.rb +1 -1
- data/spec/lib/flapjack/coordinator_spec.rb +120 -120
- data/spec/lib/flapjack/data/contact_spec.rb +66 -58
- data/spec/lib/flapjack/data/entity_check_spec.rb +179 -179
- data/spec/lib/flapjack/data/entity_spec.rb +71 -71
- data/spec/lib/flapjack/data/event_spec.rb +34 -30
- data/spec/lib/flapjack/data/message_spec.rb +6 -6
- data/spec/lib/flapjack/data/notification_rule_spec.rb +24 -24
- data/spec/lib/flapjack/data/notification_spec.rb +19 -19
- data/spec/lib/flapjack/data/semaphore_spec.rb +24 -0
- data/spec/lib/flapjack/data/tag_spec.rb +11 -10
- data/spec/lib/flapjack/gateways/api/contact_methods_spec.rb +201 -201
- data/spec/lib/flapjack/gateways/api/entity_check_presenter_spec.rb +55 -55
- data/spec/lib/flapjack/gateways/api/entity_methods_spec.rb +257 -257
- data/spec/lib/flapjack/gateways/api/entity_presenter_spec.rb +26 -26
- data/spec/lib/flapjack/gateways/api_spec.rb +1 -1
- data/spec/lib/flapjack/gateways/email_spec.rb +4 -4
- data/spec/lib/flapjack/gateways/jabber_spec.rb +77 -77
- data/spec/lib/flapjack/gateways/jsonapi/contact_methods_spec.rb +830 -0
- data/spec/lib/flapjack/gateways/jsonapi/entity_check_presenter_spec.rb +211 -0
- data/spec/lib/flapjack/gateways/jsonapi/entity_methods_spec.rb +863 -0
- data/spec/lib/flapjack/gateways/jsonapi/entity_presenter_spec.rb +108 -0
- data/spec/lib/flapjack/gateways/jsonapi_spec.rb +8 -0
- data/spec/lib/flapjack/gateways/oobetet_spec.rb +35 -35
- data/spec/lib/flapjack/gateways/pagerduty_spec.rb +40 -40
- data/spec/lib/flapjack/gateways/sms_messagenet_spec.rb +3 -3
- data/spec/lib/flapjack/gateways/web/views/check.html.erb_spec.rb +1 -1
- data/spec/lib/flapjack/gateways/web/views/contact.html.erb_spec.rb +5 -5
- data/spec/lib/flapjack/gateways/web/views/index.html.erb_spec.rb +1 -1
- data/spec/lib/flapjack/gateways/web_spec.rb +73 -74
- data/spec/lib/flapjack/logger_spec.rb +13 -13
- data/spec/lib/flapjack/pikelet_spec.rb +33 -33
- data/spec/lib/flapjack/processor_spec.rb +22 -22
- data/spec/lib/flapjack/redis_pool_spec.rb +1 -1
- data/spec/lib/flapjack/utility_spec.rb +12 -12
- data/spec/spec_helper.rb +9 -9
- data/spec/support/erb_view_helper.rb +4 -0
- metadata +107 -96
- data/lib/flapjack/gateways/web/public/css/flapjack.css +0 -49
- data/lib/flapjack/gateways/web/views/_css.html.erb +0 -42
- data/lib/flapjack/gateways/web/views/_foot.html.erb +0 -3
- data/lib/flapjack/gateways/web/views/_head.html.erb +0 -5
- data/lib/flapjack/gateways/web/views/_nav.html.erb +0 -10
data/flapjack.gemspec
CHANGED
|
@@ -4,9 +4,9 @@ require File.expand_path('../lib/flapjack/version', __FILE__)
|
|
|
4
4
|
Gem::Specification.new do |gem|
|
|
5
5
|
gem.authors = [ "Lindsay Holmwood", "Jesse Reynolds", "Ali Graham" ]
|
|
6
6
|
gem.email = "lindsay@holmwood.id.au"
|
|
7
|
-
gem.description = "Flapjack is distributed monitoring notification system that provides a scalable method for processing streams of events from Nagios and deciding who should be notified"
|
|
7
|
+
gem.description = "Flapjack is a distributed monitoring notification system that provides a scalable method for processing streams of events from Nagios and deciding who should be notified"
|
|
8
8
|
gem.summary = "Intelligent, scalable, distributed monitoring notification system."
|
|
9
|
-
gem.homepage = "http://flapjack
|
|
9
|
+
gem.homepage = "http://flapjack.io/"
|
|
10
10
|
gem.license = 'MIT'
|
|
11
11
|
|
|
12
12
|
# see http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/
|
|
@@ -24,7 +24,7 @@ Gem::Specification.new do |gem|
|
|
|
24
24
|
gem.add_dependency 'hiredis'
|
|
25
25
|
gem.add_dependency 'em-synchrony', '~> 1.0.2'
|
|
26
26
|
gem.add_dependency 'em-http-request'
|
|
27
|
-
gem.add_dependency 'redis'
|
|
27
|
+
gem.add_dependency 'redis', '~> 3.0.6'
|
|
28
28
|
gem.add_dependency 'em-resque'
|
|
29
29
|
gem.add_dependency 'resque', '~> 1.23.0'
|
|
30
30
|
gem.add_dependency 'sinatra'
|
|
@@ -38,6 +38,7 @@ Gem::Specification.new do |gem|
|
|
|
38
38
|
gem.add_dependency 'ice_cube'
|
|
39
39
|
gem.add_dependency 'tzinfo', '~> 1.0.1'
|
|
40
40
|
gem.add_dependency 'tzinfo-data'
|
|
41
|
+
gem.add_dependency 'tzinfo-data'
|
|
41
42
|
|
|
42
43
|
gem.add_development_dependency 'rake'
|
|
43
44
|
end
|
|
@@ -10,13 +10,17 @@ require 'flapjack/data/notification_rule'
|
|
|
10
10
|
require 'flapjack/data/tag'
|
|
11
11
|
require 'flapjack/data/tag_set'
|
|
12
12
|
|
|
13
|
+
require 'securerandom'
|
|
14
|
+
|
|
13
15
|
module Flapjack
|
|
14
16
|
|
|
15
17
|
module Data
|
|
16
18
|
|
|
17
19
|
class Contact
|
|
18
20
|
|
|
19
|
-
attr_accessor :id, :first_name, :last_name, :email, :media,
|
|
21
|
+
attr_accessor :id, :first_name, :last_name, :email, :media,
|
|
22
|
+
:media_intervals, :media_rollup_thresholds, :pagerduty_credentials,
|
|
23
|
+
:linked_entity_ids
|
|
20
24
|
|
|
21
25
|
TAG_PREFIX = 'contact_tag'
|
|
22
26
|
ALL_MEDIA = ['email', 'sms', 'jabber', 'pagerduty']
|
|
@@ -46,6 +50,22 @@ module Flapjack
|
|
|
46
50
|
contact
|
|
47
51
|
end
|
|
48
52
|
|
|
53
|
+
def self.find_by_ids(contact_ids, options = {})
|
|
54
|
+
raise "Redis connection not set" unless redis = options[:redis]
|
|
55
|
+
logger = options[:logger]
|
|
56
|
+
|
|
57
|
+
contact_ids.map do |id|
|
|
58
|
+
self.find_by_id(id, options)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.exists_with_id?(contact_id, options = {})
|
|
63
|
+
raise "Redis connection not set" unless redis = options[:redis]
|
|
64
|
+
raise "No id value passed" unless contact_id
|
|
65
|
+
|
|
66
|
+
redis.exists("contact:#{contact_id}")
|
|
67
|
+
end
|
|
68
|
+
|
|
49
69
|
def self.add(contact_data, options = {})
|
|
50
70
|
raise "Redis connection not set" unless redis = options[:redis]
|
|
51
71
|
contact_id = contact_data['id']
|
|
@@ -169,6 +189,36 @@ module Flapjack
|
|
|
169
189
|
}.values
|
|
170
190
|
end
|
|
171
191
|
|
|
192
|
+
def self.entities_jsonapi(contact_ids, options = {})
|
|
193
|
+
raise "Redis connection not set" unless redis = options[:redis]
|
|
194
|
+
|
|
195
|
+
entity_data = []
|
|
196
|
+
linked_entity_ids = {}
|
|
197
|
+
|
|
198
|
+
temp_set = SecureRandom.uuid
|
|
199
|
+
redis.sadd(temp_set, contact_ids)
|
|
200
|
+
|
|
201
|
+
redis.keys('contacts_for:*').each do |k|
|
|
202
|
+
contact_ids = redis.sinter(k, temp_set)
|
|
203
|
+
next if contact_ids.empty?
|
|
204
|
+
next unless k =~ /^contacts_for:([a-zA-Z0-9][a-zA-Z0-9\.\-]*[a-zA-Z0-9])(?::(\w+))?$/
|
|
205
|
+
|
|
206
|
+
entity_id = $1
|
|
207
|
+
check = $2
|
|
208
|
+
|
|
209
|
+
entity_data << {:id => entity_id, :name => redis.hget("entity:#{entity_id}", 'name')}
|
|
210
|
+
|
|
211
|
+
contact_ids.each do |contact_id|
|
|
212
|
+
linked_entity_ids[contact_id] ||= []
|
|
213
|
+
linked_entity_ids[contact_id] << entity_id
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
redis.del(temp_set)
|
|
218
|
+
|
|
219
|
+
[entity_data, linked_entity_ids]
|
|
220
|
+
end
|
|
221
|
+
|
|
172
222
|
def name
|
|
173
223
|
[(self.first_name || ''), (self.last_name || '')].join(" ").strip
|
|
174
224
|
end
|
|
@@ -408,11 +458,17 @@ module Flapjack
|
|
|
408
458
|
end
|
|
409
459
|
|
|
410
460
|
def to_json(*args)
|
|
411
|
-
{ "id"
|
|
412
|
-
"first_name"
|
|
413
|
-
"last_name"
|
|
414
|
-
"email"
|
|
415
|
-
"
|
|
461
|
+
{ "id" => self.id,
|
|
462
|
+
"first_name" => self.first_name,
|
|
463
|
+
"last_name" => self.last_name,
|
|
464
|
+
"email" => self.email,
|
|
465
|
+
"media" => self.media,
|
|
466
|
+
"media_intervals" => self.media_intervals,
|
|
467
|
+
"media_rollup_thresholds" => self.media_rollup_thresholds,
|
|
468
|
+
"timezone" => self.timezone.name,
|
|
469
|
+
"tags" => self.tags.to_a,
|
|
470
|
+
"links" => {:entities => @linked_entity_ids || []}
|
|
471
|
+
}.to_json
|
|
416
472
|
end
|
|
417
473
|
|
|
418
474
|
private
|
|
@@ -433,6 +489,12 @@ module Flapjack
|
|
|
433
489
|
redis.hmset("contact:#{contact_id}",
|
|
434
490
|
*['first_name', 'last_name', 'email'].collect {|f| [f, contact_data[f]]})
|
|
435
491
|
|
|
492
|
+
if ( ! contact_data['tags'].nil? && contact_data['tags'].is_a?(Enumerable))
|
|
493
|
+
contact_data['tags'].each do |t|
|
|
494
|
+
Flapjack::Data::Tag.create("#{TAG_PREFIX}:#{t}", [contact_id], :redis => redis)
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
|
|
436
498
|
unless contact_data['media'].nil?
|
|
437
499
|
redis.del("contact_media:#{contact_id}")
|
|
438
500
|
redis.del("contact_media_intervals:#{contact_id}")
|
|
@@ -452,6 +514,16 @@ module Flapjack
|
|
|
452
514
|
end
|
|
453
515
|
}
|
|
454
516
|
end
|
|
517
|
+
if contact_data.key?('timezone')
|
|
518
|
+
tz = contact_data['timezone']
|
|
519
|
+
if tz.nil?
|
|
520
|
+
redis.del("contact_tz:#{contact_id}")
|
|
521
|
+
else
|
|
522
|
+
# ActiveSupport::TimeZone or String
|
|
523
|
+
redis.set("contact_tz:#{contact_id}",
|
|
524
|
+
tz.respond_to?(:name) ? tz.name : tz )
|
|
525
|
+
end
|
|
526
|
+
end
|
|
455
527
|
end
|
|
456
528
|
|
|
457
529
|
end
|
data/lib/flapjack/data/entity.rb
CHANGED
|
@@ -16,9 +16,11 @@ module Flapjack
|
|
|
16
16
|
|
|
17
17
|
def self.all(options = {})
|
|
18
18
|
raise "Redis connection not set" unless redis = options[:redis]
|
|
19
|
-
redis.keys("entity_id:*")
|
|
19
|
+
keys = redis.keys("entity_id:*")
|
|
20
|
+
ids = redis.mget(keys)
|
|
21
|
+
keys.collect {|k|
|
|
20
22
|
k =~ /^entity_id:(.+)$/; entity_name = $1
|
|
21
|
-
self.new(:name => entity_name, :id =>
|
|
23
|
+
self.new(:name => entity_name, :id => ids.shift, :redis => redis)
|
|
22
24
|
}.sort_by(&:name)
|
|
23
25
|
end
|
|
24
26
|
|
|
@@ -173,6 +175,13 @@ module Flapjack
|
|
|
173
175
|
end
|
|
174
176
|
end
|
|
175
177
|
|
|
178
|
+
def as_json(*args)
|
|
179
|
+
{
|
|
180
|
+
"id" => self.id,
|
|
181
|
+
"name" => self.name,
|
|
182
|
+
}
|
|
183
|
+
end
|
|
184
|
+
|
|
176
185
|
private
|
|
177
186
|
|
|
178
187
|
# NB: initializer should not be used directly -- instead one of the finder methods
|
|
@@ -121,11 +121,11 @@ module Flapjack
|
|
|
121
121
|
refresh
|
|
122
122
|
end
|
|
123
123
|
|
|
124
|
-
def self.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
logger = options[:logger]
|
|
124
|
+
def self.prevalidate_data(rule_data, options = {})
|
|
125
|
+
errors = self.validate_data(preen(rule_data), options.merge(:id_not_required => true))
|
|
126
|
+
end
|
|
128
127
|
|
|
128
|
+
def self.preen(rule_data)
|
|
129
129
|
# make some assumptions about the incoming data
|
|
130
130
|
rule_data[:unknown_blackhole] = rule_data[:unknown_blackhole] || false
|
|
131
131
|
rule_data[:warning_blackhole] = rule_data[:warning_blackhole] || false
|
|
@@ -133,9 +133,16 @@ module Flapjack
|
|
|
133
133
|
if rule_data[:tags].is_a?(Array)
|
|
134
134
|
rule_data[:tags] = Flapjack::Data::TagSet.new(rule_data[:tags])
|
|
135
135
|
end
|
|
136
|
+
rule_data
|
|
137
|
+
end
|
|
136
138
|
|
|
137
|
-
|
|
139
|
+
def self.add_or_update(rule_data, options = {})
|
|
140
|
+
redis = options[:redis]
|
|
141
|
+
raise "a redis connection must be supplied" unless redis
|
|
142
|
+
logger = options[:logger]
|
|
138
143
|
|
|
144
|
+
rule_data = preen(rule_data)
|
|
145
|
+
errors = self.validate_data(rule_data, options)
|
|
139
146
|
return errors unless errors.nil? || errors.empty?
|
|
140
147
|
|
|
141
148
|
# whitelisting fields, rather than passing through submitted data directly
|
|
@@ -227,61 +234,62 @@ module Flapjack
|
|
|
227
234
|
end
|
|
228
235
|
|
|
229
236
|
def self.validate_data(d, options = {})
|
|
237
|
+
id_not_required = !!options[:id_not_required]
|
|
230
238
|
# hash with validation => error_message
|
|
231
|
-
validations = {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
239
|
+
validations = {}
|
|
240
|
+
validations.merge!({ proc { d.has_key?(:id) } => "id not set"}) unless id_not_required
|
|
241
|
+
validations.merge!({
|
|
242
|
+
proc { !d.has_key?(:entities) ||
|
|
243
|
+
( d[:entities].nil? ||
|
|
244
|
+
d[:entities].is_a?(Array) &&
|
|
245
|
+
d[:entities].all? {|e| e.is_a?(String)} ) } =>
|
|
246
|
+
"entities must be a list of strings",
|
|
247
|
+
|
|
248
|
+
proc { !d.has_key?(:tags) ||
|
|
249
|
+
( d[:tags].nil? ||
|
|
250
|
+
d[:tags].is_a?(Flapjack::Data::TagSet) &&
|
|
251
|
+
d[:tags].all? {|et| et.is_a?(String)} ) } =>
|
|
252
|
+
"tags must be a tag_set of strings",
|
|
253
|
+
|
|
254
|
+
proc { !d.has_key?(:time_restrictions) ||
|
|
255
|
+
( d[:time_restrictions].nil? ||
|
|
256
|
+
d[:time_restrictions].all? {|tr|
|
|
257
|
+
!!prepare_time_restriction(symbolize(tr))
|
|
258
|
+
} )
|
|
259
|
+
} =>
|
|
260
|
+
"time restrictions are invalid",
|
|
261
|
+
|
|
262
|
+
# TODO should the media types be checked against a whitelist?
|
|
263
|
+
proc { !d.has_key?(:unknown_media) ||
|
|
264
|
+
( d[:unknown_media].nil? ||
|
|
265
|
+
d[:unknown_media].is_a?(Array) &&
|
|
266
|
+
d[:unknown_media].all? {|et| et.is_a?(String)} ) } =>
|
|
267
|
+
"unknown_media must be a list of strings",
|
|
268
|
+
|
|
269
|
+
proc { !d.has_key?(:warning_media) ||
|
|
270
|
+
( d[:warning_media].nil? ||
|
|
271
|
+
d[:warning_media].is_a?(Array) &&
|
|
272
|
+
d[:warning_media].all? {|et| et.is_a?(String)} ) } =>
|
|
273
|
+
"warning_media must be a list of strings",
|
|
274
|
+
|
|
275
|
+
proc { !d.has_key?(:critical_media) ||
|
|
276
|
+
( d[:critical_media].nil? ||
|
|
277
|
+
d[:critical_media].is_a?(Array) &&
|
|
278
|
+
d[:critical_media].all? {|et| et.is_a?(String)} ) } =>
|
|
279
|
+
"critical_media must be a list of strings",
|
|
280
|
+
|
|
281
|
+
proc { !d.has_key?(:unknown_blackhole) ||
|
|
282
|
+
[TrueClass, FalseClass].include?(d[:unknown_blackhole].class) } =>
|
|
283
|
+
"unknown_blackhole must be true or false",
|
|
284
|
+
|
|
285
|
+
proc { !d.has_key?(:warning_blackhole) ||
|
|
286
|
+
[TrueClass, FalseClass].include?(d[:warning_blackhole].class) } =>
|
|
287
|
+
"warning_blackhole must be true or false",
|
|
288
|
+
|
|
289
|
+
proc { !d.has_key?(:critical_blackhole) ||
|
|
290
|
+
[TrueClass, FalseClass].include?(d[:critical_blackhole].class) } =>
|
|
291
|
+
"critical_blackhole must be true or false",
|
|
292
|
+
})
|
|
285
293
|
|
|
286
294
|
errors = validations.keys.inject([]) {|ret,vk|
|
|
287
295
|
ret << "Rule #{validations[vk]}" unless vk.call
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Flapjack
|
|
6
|
+
module Data
|
|
7
|
+
|
|
8
|
+
# http://redis.io/commands/set
|
|
9
|
+
class Semaphore
|
|
10
|
+
|
|
11
|
+
SEMAPHORE_KEYSPACE = 'semaphores:'
|
|
12
|
+
|
|
13
|
+
attr_reader :token, :expiry, :resource
|
|
14
|
+
|
|
15
|
+
class ResourceLocked < RuntimeError
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def initialize(resource, options)
|
|
19
|
+
raise "redis connection must be passed in options" unless @redis = options[:redis]
|
|
20
|
+
@resource = resource
|
|
21
|
+
@token = options[:token] || SecureRandom.uuid
|
|
22
|
+
@expiry = options[:expiry] || 30
|
|
23
|
+
|
|
24
|
+
@key = "#{SEMAPHORE_KEYSPACE}#{@resource}"
|
|
25
|
+
|
|
26
|
+
raise Flapjack::Data::Semaphore::ResourceLocked.new unless @redis.set(@key, @token, {:nx => true, :ex => @expiry})
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def release
|
|
30
|
+
unlock_script = '
|
|
31
|
+
if redis.call("get",KEYS[1]) == ARGV[1]
|
|
32
|
+
then
|
|
33
|
+
return redis.call("del",KEYS[1])
|
|
34
|
+
else
|
|
35
|
+
return 0
|
|
36
|
+
end
|
|
37
|
+
'
|
|
38
|
+
@redis.eval(unlock_script, [@key], [@token])
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
@@ -27,41 +27,37 @@ module Flapjack
|
|
|
27
27
|
|
|
28
28
|
include Flapjack::Utility
|
|
29
29
|
|
|
30
|
-
set :
|
|
30
|
+
set :dump_errors, false
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
logger
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
end
|
|
49
|
-
[status, {}, {:errors => msg}.to_json]
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
e = env['sinatra.error']
|
|
32
|
+
rescue_error = Proc.new {|status, exception, *msg|
|
|
33
|
+
if !msg || msg.empty?
|
|
34
|
+
trace = exception.backtrace.join("\n")
|
|
35
|
+
msg = "#{exception.class} - #{exception.message}"
|
|
36
|
+
msg_str = "#{msg}\n#{trace}"
|
|
37
|
+
else
|
|
38
|
+
msg_str = msg.join(", ")
|
|
39
|
+
end
|
|
40
|
+
case
|
|
41
|
+
when status < 500
|
|
42
|
+
@logger.warn "Error: #{msg_str}"
|
|
43
|
+
else
|
|
44
|
+
@logger.error "Error: #{msg_str}"
|
|
45
|
+
end
|
|
46
|
+
[status, {}, {:errors => msg}.to_json]
|
|
47
|
+
}
|
|
53
48
|
|
|
54
|
-
|
|
49
|
+
rescue_exception = Proc.new {|env, e|
|
|
50
|
+
case e
|
|
55
51
|
when Flapjack::Gateways::API::ContactNotFound
|
|
56
|
-
|
|
52
|
+
rescue_error.call(403, e, "could not find contact '#{e.contact_id}'")
|
|
57
53
|
when Flapjack::Gateways::API::NotificationRuleNotFound
|
|
58
|
-
|
|
54
|
+
rescue_error.call(403, e, "could not find notification rule '#{e.rule_id}'")
|
|
59
55
|
when Flapjack::Gateways::API::EntityNotFound
|
|
60
|
-
|
|
56
|
+
rescue_error.call(403, e, "could not find entity '#{e.entity}'")
|
|
61
57
|
when Flapjack::Gateways::API::EntityCheckNotFound
|
|
62
|
-
|
|
58
|
+
rescue_error.call(403, e, "could not find entity check '#{e.check}'")
|
|
63
59
|
else
|
|
64
|
-
|
|
60
|
+
rescue_error.call(500, e)
|
|
65
61
|
end
|
|
66
62
|
}
|
|
67
63
|
use Rack::FiberPool, :size => 25, :rescue_exception => rescue_exception
|