flapjack 0.7.35 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|