flapjack 0.7.35 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -1
  3. data/Gemfile +3 -4
  4. data/Guardfile +1 -1
  5. data/README.md +38 -19
  6. data/Rakefile +1 -3
  7. data/etc/flapjack_config.yaml.example +11 -1
  8. data/features/steps/cli_steps.rb +3 -3
  9. data/features/steps/events_steps.rb +7 -6
  10. data/features/steps/flapjack-netsaint-parser_steps.rb +8 -8
  11. data/features/steps/notifications_steps.rb +10 -10
  12. data/features/steps/packaging-lintian_steps.rb +5 -9
  13. data/features/steps/time_travel_steps.rb +1 -1
  14. data/flapjack.gemspec +4 -3
  15. data/lib/flapjack/data/contact.rb +78 -6
  16. data/lib/flapjack/data/entity.rb +11 -2
  17. data/lib/flapjack/data/notification_rule.rb +67 -59
  18. data/lib/flapjack/data/semaphore.rb +44 -0
  19. data/lib/flapjack/gateways/api.rb +24 -28
  20. data/lib/flapjack/gateways/api/contact_methods.rb +1 -2
  21. data/lib/flapjack/gateways/api/entity_methods.rb +3 -3
  22. data/lib/flapjack/gateways/jsonapi.rb +249 -0
  23. data/lib/flapjack/gateways/jsonapi/contact_methods.rb +544 -0
  24. data/lib/flapjack/gateways/jsonapi/entity_check_presenter.rb +217 -0
  25. data/lib/flapjack/gateways/jsonapi/entity_methods.rb +350 -0
  26. data/lib/flapjack/gateways/jsonapi/entity_presenter.rb +75 -0
  27. data/lib/flapjack/gateways/jsonapi/rack/json_params_parser.rb +32 -0
  28. data/lib/flapjack/gateways/web.rb +78 -12
  29. data/lib/flapjack/gateways/web/public/css/bootstrap-theme.css +397 -0
  30. data/lib/flapjack/gateways/web/public/css/bootstrap-theme.min.css +7 -0
  31. data/lib/flapjack/gateways/web/public/css/bootstrap.css +7118 -0
  32. data/lib/flapjack/gateways/web/public/css/bootstrap.min.css +6 -8
  33. data/lib/flapjack/gateways/web/public/css/font-awesome.css +1338 -0
  34. data/lib/flapjack/gateways/web/public/css/font-awesome.min.css +4 -0
  35. data/lib/flapjack/gateways/web/public/css/screen.css +80 -0
  36. data/lib/flapjack/gateways/web/public/css/select2-bootstrap.css +87 -0
  37. data/lib/flapjack/gateways/web/public/css/select2.css +615 -0
  38. data/lib/flapjack/gateways/web/public/fonts/FontAwesome.otf +0 -0
  39. data/lib/flapjack/gateways/web/public/fonts/fontawesome-webfont.eot +0 -0
  40. data/lib/flapjack/gateways/web/public/fonts/fontawesome-webfont.svg +414 -0
  41. data/lib/flapjack/gateways/web/public/fonts/fontawesome-webfont.ttf +0 -0
  42. data/lib/flapjack/gateways/web/public/fonts/fontawesome-webfont.woff +0 -0
  43. data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.eot +0 -0
  44. data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.svg +229 -0
  45. data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.ttf +0 -0
  46. data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.woff +0 -0
  47. data/lib/flapjack/gateways/web/public/img/flapjack-2013-notext-transparent-300-300.png +0 -0
  48. data/lib/flapjack/gateways/web/public/img/select2.png +0 -0
  49. data/lib/flapjack/gateways/web/public/img/select2x2.png +0 -0
  50. data/lib/flapjack/gateways/web/public/js/backbone-min.js +2 -0
  51. data/lib/flapjack/gateways/web/public/js/backbone.js +1581 -0
  52. data/lib/flapjack/gateways/web/public/js/backbone.jsonapi.js +75 -0
  53. data/lib/flapjack/gateways/web/public/js/bootstrap.js +2276 -0
  54. data/lib/flapjack/gateways/web/public/js/contacts.js +225 -0
  55. data/lib/flapjack/gateways/web/public/js/jquery-1.10.2.js +9789 -0
  56. data/lib/flapjack/gateways/web/public/js/jquery-1.10.2.min.js +6 -0
  57. data/lib/flapjack/gateways/web/public/js/select2.js +3255 -0
  58. data/lib/flapjack/gateways/web/public/js/select2.min.js +22 -0
  59. data/lib/flapjack/gateways/web/public/js/underscore-min.js +6 -0
  60. data/lib/flapjack/gateways/web/public/js/underscore.js +1276 -0
  61. data/lib/flapjack/gateways/web/views/check.html.erb +423 -193
  62. data/lib/flapjack/gateways/web/views/checks.html.erb +51 -71
  63. data/lib/flapjack/gateways/web/views/contact.html.erb +142 -164
  64. data/lib/flapjack/gateways/web/views/contacts.html.erb +20 -40
  65. data/lib/flapjack/gateways/web/views/edit_contacts.html.erb +83 -0
  66. data/lib/flapjack/gateways/web/views/entities.html.erb +18 -37
  67. data/lib/flapjack/gateways/web/views/entity.html.erb +46 -65
  68. data/lib/flapjack/gateways/web/views/index.html.erb +6 -27
  69. data/lib/flapjack/gateways/web/views/layout.erb +95 -0
  70. data/lib/flapjack/gateways/web/views/self_stats.html.erb +100 -114
  71. data/lib/flapjack/pikelet.rb +4 -2
  72. data/lib/flapjack/version.rb +1 -1
  73. data/spec/lib/flapjack/coordinator_spec.rb +120 -120
  74. data/spec/lib/flapjack/data/contact_spec.rb +66 -58
  75. data/spec/lib/flapjack/data/entity_check_spec.rb +179 -179
  76. data/spec/lib/flapjack/data/entity_spec.rb +71 -71
  77. data/spec/lib/flapjack/data/event_spec.rb +34 -30
  78. data/spec/lib/flapjack/data/message_spec.rb +6 -6
  79. data/spec/lib/flapjack/data/notification_rule_spec.rb +24 -24
  80. data/spec/lib/flapjack/data/notification_spec.rb +19 -19
  81. data/spec/lib/flapjack/data/semaphore_spec.rb +24 -0
  82. data/spec/lib/flapjack/data/tag_spec.rb +11 -10
  83. data/spec/lib/flapjack/gateways/api/contact_methods_spec.rb +201 -201
  84. data/spec/lib/flapjack/gateways/api/entity_check_presenter_spec.rb +55 -55
  85. data/spec/lib/flapjack/gateways/api/entity_methods_spec.rb +257 -257
  86. data/spec/lib/flapjack/gateways/api/entity_presenter_spec.rb +26 -26
  87. data/spec/lib/flapjack/gateways/api_spec.rb +1 -1
  88. data/spec/lib/flapjack/gateways/email_spec.rb +4 -4
  89. data/spec/lib/flapjack/gateways/jabber_spec.rb +77 -77
  90. data/spec/lib/flapjack/gateways/jsonapi/contact_methods_spec.rb +830 -0
  91. data/spec/lib/flapjack/gateways/jsonapi/entity_check_presenter_spec.rb +211 -0
  92. data/spec/lib/flapjack/gateways/jsonapi/entity_methods_spec.rb +863 -0
  93. data/spec/lib/flapjack/gateways/jsonapi/entity_presenter_spec.rb +108 -0
  94. data/spec/lib/flapjack/gateways/jsonapi_spec.rb +8 -0
  95. data/spec/lib/flapjack/gateways/oobetet_spec.rb +35 -35
  96. data/spec/lib/flapjack/gateways/pagerduty_spec.rb +40 -40
  97. data/spec/lib/flapjack/gateways/sms_messagenet_spec.rb +3 -3
  98. data/spec/lib/flapjack/gateways/web/views/check.html.erb_spec.rb +1 -1
  99. data/spec/lib/flapjack/gateways/web/views/contact.html.erb_spec.rb +5 -5
  100. data/spec/lib/flapjack/gateways/web/views/index.html.erb_spec.rb +1 -1
  101. data/spec/lib/flapjack/gateways/web_spec.rb +73 -74
  102. data/spec/lib/flapjack/logger_spec.rb +13 -13
  103. data/spec/lib/flapjack/pikelet_spec.rb +33 -33
  104. data/spec/lib/flapjack/processor_spec.rb +22 -22
  105. data/spec/lib/flapjack/redis_pool_spec.rb +1 -1
  106. data/spec/lib/flapjack/utility_spec.rb +12 -12
  107. data/spec/spec_helper.rb +9 -9
  108. data/spec/support/erb_view_helper.rb +4 -0
  109. metadata +107 -96
  110. data/lib/flapjack/gateways/web/public/css/flapjack.css +0 -49
  111. data/lib/flapjack/gateways/web/views/_css.html.erb +0 -42
  112. data/lib/flapjack/gateways/web/views/_foot.html.erb +0 -3
  113. data/lib/flapjack/gateways/web/views/_head.html.erb +0 -5
  114. 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-project.com/"
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, :media_intervals, :media_rollup_thresholds, :pagerduty_credentials
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" => self.id,
412
- "first_name" => self.first_name,
413
- "last_name" => self.last_name,
414
- "email" => self.email,
415
- "tags" => self.tags.to_a }.to_json
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
@@ -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:*").collect {|k|
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 => redis.get("entity_id:#{entity_name}"), :redis => redis)
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.add_or_update(rule_data, options = {})
125
- redis = options[:redis]
126
- raise "a redis connection must be supplied" unless redis
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
- errors = self.validate_data(rule_data, options)
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 = {proc { d.has_key?(:id) } =>
232
- "id not set",
233
-
234
- proc { !d.has_key?(:entities) ||
235
- ( d[:entities].nil? ||
236
- d[:entities].is_a?(Array) &&
237
- d[:entities].all? {|e| e.is_a?(String)} ) } =>
238
- "entities must be a list of strings",
239
-
240
- proc { !d.has_key?(:tags) ||
241
- ( d[:tags].nil? ||
242
- d[:tags].is_a?(Flapjack::Data::TagSet) &&
243
- d[:tags].all? {|et| et.is_a?(String)} ) } =>
244
- "tags must be a tag_set of strings",
245
-
246
- proc { !d.has_key?(:time_restrictions) ||
247
- ( d[:time_restrictions].nil? ||
248
- d[:time_restrictions].all? {|tr|
249
- !!prepare_time_restriction(symbolize(tr))
250
- } )
251
- } =>
252
- "time restrictions are invalid",
253
-
254
- # TODO should the media types be checked against a whitelist?
255
- proc { !d.has_key?(:unknown_media) ||
256
- ( d[:unknown_media].nil? ||
257
- d[:unknown_media].is_a?(Array) &&
258
- d[:unknown_media].all? {|et| et.is_a?(String)} ) } =>
259
- "unknown_media must be a list of strings",
260
-
261
- proc { !d.has_key?(:warning_media) ||
262
- ( d[:warning_media].nil? ||
263
- d[:warning_media].is_a?(Array) &&
264
- d[:warning_media].all? {|et| et.is_a?(String)} ) } =>
265
- "warning_media must be a list of strings",
266
-
267
- proc { !d.has_key?(:critical_media) ||
268
- ( d[:critical_media].nil? ||
269
- d[:critical_media].is_a?(Array) &&
270
- d[:critical_media].all? {|et| et.is_a?(String)} ) } =>
271
- "critical_media must be a list of strings",
272
-
273
- proc { !d.has_key?(:unknown_blackhole) ||
274
- [TrueClass, FalseClass].include?(d[:unknown_blackhole].class) } =>
275
- "unknown_blackhole must be true or false",
276
-
277
- proc { !d.has_key?(:warning_blackhole) ||
278
- [TrueClass, FalseClass].include?(d[:warning_blackhole].class) } =>
279
- "warning_blackhole must be true or false",
280
-
281
- proc { !d.has_key?(:critical_blackhole) ||
282
- [TrueClass, FalseClass].include?(d[:critical_blackhole].class) } =>
283
- "critical_blackhole must be true or false",
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 :show_exceptions, false
30
+ set :dump_errors, false
31
31
 
32
- rescue_exception = Proc.new { |env, exception|
33
-
34
- error = proc {|status, exception, *msg|
35
- if !msg || msg.empty?
36
- trace = exception.backtrace.join("\n")
37
- msg = "#{exception.class} - #{exception.message}"
38
- msg_str = "#{msg}\n#{trace}"
39
- else
40
- msg_str = msg.join(", ")
41
- end
42
- logger = Flapjack::Gateways::API.instance_variable_get('@logger')
43
- case
44
- when status < 500
45
- logger.warn "Error: #{msg_str}"
46
- else
47
- logger.error "Error: #{msg_str}"
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
- case exception
49
+ rescue_exception = Proc.new {|env, e|
50
+ case e
55
51
  when Flapjack::Gateways::API::ContactNotFound
56
- error.call(403, e, "could not find contact '#{e.contact_id}'")
52
+ rescue_error.call(403, e, "could not find contact '#{e.contact_id}'")
57
53
  when Flapjack::Gateways::API::NotificationRuleNotFound
58
- error.call(403, e, "could not find notification rule '#{e.rule_id}'")
54
+ rescue_error.call(403, e, "could not find notification rule '#{e.rule_id}'")
59
55
  when Flapjack::Gateways::API::EntityNotFound
60
- error.call(403, e, "could not find entity '#{e.entity}'")
56
+ rescue_error.call(403, e, "could not find entity '#{e.entity}'")
61
57
  when Flapjack::Gateways::API::EntityCheckNotFound
62
- error.call(403, e, "could not find entity check '#{e.check}'")
58
+ rescue_error.call(403, e, "could not find entity check '#{e.check}'")
63
59
  else
64
- error.call(500, exception)
60
+ rescue_error.call(500, e)
65
61
  end
66
62
  }
67
63
  use Rack::FiberPool, :size => 25, :rescue_exception => rescue_exception