flapjack 0.7.14 → 0.7.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/CHANGELOG.md +10 -0
  2. data/etc/flapjack_config.yaml.example +1 -0
  3. data/features/events.feature +5 -0
  4. data/features/notification_rules.feature +1 -1
  5. data/features/steps/events_steps.rb +28 -13
  6. data/features/steps/notifications_steps.rb +1 -1
  7. data/lib/flapjack/coordinator.rb +3 -1
  8. data/lib/flapjack/data/contact.rb +8 -6
  9. data/lib/flapjack/data/entity_check.rb +78 -113
  10. data/lib/flapjack/data/event.rb +54 -65
  11. data/lib/flapjack/data/notification.rb +5 -1
  12. data/lib/flapjack/executive.rb +42 -38
  13. data/lib/flapjack/filters/acknowledgement.rb +5 -5
  14. data/lib/flapjack/filters/base.rb +2 -2
  15. data/lib/flapjack/filters/delays.rb +11 -11
  16. data/lib/flapjack/filters/detect_mass_client_failures.rb +8 -8
  17. data/lib/flapjack/filters/ok.rb +6 -6
  18. data/lib/flapjack/filters/scheduled_maintenance.rb +2 -2
  19. data/lib/flapjack/filters/unscheduled_maintenance.rb +3 -2
  20. data/lib/flapjack/gateways/api.rb +374 -277
  21. data/lib/flapjack/gateways/api/entity_check_presenter.rb +52 -21
  22. data/lib/flapjack/gateways/api/entity_presenter.rb +14 -9
  23. data/lib/flapjack/gateways/email.rb +7 -0
  24. data/lib/flapjack/gateways/email/alert.html.haml +13 -1
  25. data/lib/flapjack/gateways/email/alert.text.erb +5 -4
  26. data/lib/flapjack/gateways/jabber.rb +90 -34
  27. data/lib/flapjack/gateways/pagerduty.rb +6 -2
  28. data/lib/flapjack/gateways/web.rb +13 -8
  29. data/lib/flapjack/gateways/web/views/check.haml +70 -45
  30. data/lib/flapjack/gateways/web/views/checks.haml +1 -1
  31. data/lib/flapjack/gateways/web/views/entity.haml +1 -1
  32. data/lib/flapjack/patches.rb +9 -2
  33. data/lib/flapjack/pikelet.rb +14 -10
  34. data/lib/flapjack/utility.rb +10 -4
  35. data/lib/flapjack/version.rb +1 -1
  36. data/spec/lib/flapjack/coordinator_spec.rb +19 -5
  37. data/spec/lib/flapjack/data/entity_check_spec.rb +3 -30
  38. data/spec/lib/flapjack/data/event_spec.rb +96 -1
  39. data/spec/lib/flapjack/executive_spec.rb +5 -11
  40. data/spec/lib/flapjack/gateways/api/entity_check_presenter_spec.rb +22 -3
  41. data/spec/lib/flapjack/gateways/api/entity_presenter_spec.rb +30 -15
  42. data/spec/lib/flapjack/gateways/api_spec.rb +552 -186
  43. data/spec/lib/flapjack/gateways/email_spec.rb +2 -0
  44. data/spec/lib/flapjack/gateways/jabber_spec.rb +5 -4
  45. data/spec/lib/flapjack/gateways/pagerduty_spec.rb +3 -2
  46. data/spec/lib/flapjack/gateways/web_spec.rb +17 -12
  47. data/spec/lib/flapjack/pikelet_spec.rb +5 -2
  48. metadata +4 -5
  49. data/config.ru +0 -11
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  ## Flapjack Changelog
2
2
 
3
+ # 0.7.15 - 2013-06-27
4
+ - Feature: Show acknowledgement duration on web interface, queryable via jabber gh-159 (@ali-graham)
5
+ - Feature: More info on check state in email gh-207 (@ali-graham)
6
+ - Feature: Bulk API operations gh-123 (@ali-graham)
7
+ - Bug: You can't remove an interval from a contact's media once it has one gh-153 (@ali-graham)
8
+ - Bug: Fix jabber identify boot time gh-172 (@ali-graham)
9
+ - Bug: Don't pluralise singular time periods in jabber messages gh-209 (@ali-graham)
10
+ - Bug: PUT /contacts/ID/media/MEDIA returns previous value for address gh-152 (@ali-graham)
11
+ - Bug: 'last update' shows large numbers of seconds gh-157 (@ali-graham)
12
+
3
13
  # 0.7.14 - 2013-06-19
4
14
  - Bug: Display of checks on web ui with a colon in their name is screwed gh-213 (@jessereynolds)
5
15
  - Bug: show last critical, warning, unknown notificaiton times in web ui gh-211 (@jessereynolds)
@@ -18,6 +18,7 @@ development:
18
18
  default_contact_timezone: Australia/Broken_Hill
19
19
  archive_events: true
20
20
  events_archive_maxage: 10800
21
+ new_check_scheduled_maintenance_duration: 1 month
21
22
  logger:
22
23
  level: INFO
23
24
  gateways:
@@ -390,3 +390,8 @@ Feature: events
390
390
  # When 10 seconds passes
391
391
  # And an ok event is received
392
392
  # Then a notification should not be generated
393
+
394
+ Scenario: scheduled maintenance created for initial check reference
395
+ Given the check has no state
396
+ When an ok event is received
397
+ Then scheduled maintenance should be generated
@@ -269,7 +269,7 @@ Feature: Notification rules on a per contact basis
269
269
  And 1 sms alert should be queued for +61400000001
270
270
  @time
271
271
  Scenario: Critical straight after test
272
- Given the check is check 'ping' on entity 'foo'
272
+ Given the check is check 'ping' on entity 'baz'
273
273
  And the check is in an ok state
274
274
  When a test event is received
275
275
  Then 1 email alert should be queued for malak@example.com
@@ -153,9 +153,23 @@ Given /^an entity '([\w\.\-]+)' exists$/ do |entity|
153
153
  :redis => @redis )
154
154
  end
155
155
 
156
+ Given /^the check is check '(.*)' on entity '([\w\.\-]+)'$/ do |check, entity|
157
+ @check = check
158
+ @entity = entity
159
+ end
160
+
161
+ Given /^(?:the check|check '([\w\.\-]+)' for entity '([\w\.\-]+)') has no state$/ do |check, entity|
162
+ check ||= @check
163
+ entity ||= @entity
164
+ remove_unscheduled_maintenance(entity, check)
165
+ remove_scheduled_maintenance(entity, check)
166
+ remove_notifications(entity, check)
167
+ @redis.hdel("check:#{@key}", 'state')
168
+ end
169
+
156
170
  Given /^(?:the check|check '([\w\.\-]+)' for entity '([\w\.\-]+)') is in an ok state$/ do |check, entity|
157
- check = check ? check : @check
158
- entity = entity ? entity : @entity
171
+ check ||= @check
172
+ entity ||= @entity
159
173
  remove_unscheduled_maintenance(entity, check)
160
174
  remove_scheduled_maintenance(entity, check)
161
175
  remove_notifications(entity, check)
@@ -163,8 +177,8 @@ Given /^(?:the check|check '([\w\.\-]+)' for entity '([\w\.\-]+)') is in an ok s
163
177
  end
164
178
 
165
179
  Given /^(?:the check|check '([\w\.\-]+)' for entity '([\w\.\-]+)') is in a critical state$/ do |check, entity|
166
- check = check ? check : @check
167
- entity = entity ? entity : @entity
180
+ check ||= @check
181
+ entity ||= @entity
168
182
  remove_unscheduled_maintenance(entity, check)
169
183
  remove_scheduled_maintenance(entity, check)
170
184
  remove_notifications(entity, check)
@@ -172,27 +186,22 @@ Given /^(?:the check|check '([\w\.\-]+)' for entity '([\w\.\-]+)') is in a criti
172
186
  end
173
187
 
174
188
  Given /^(?:the check|check '([\w\.\-]+)' for entity '([\w\.\-]+)') is in scheduled maintenance$/ do |check, entity|
175
- check = check ? check : @check
176
- entity = entity ? entity : @entity
189
+ check ||= @check
190
+ entity ||= @entity
177
191
  remove_unscheduled_maintenance(entity, check)
178
192
  set_scheduled_maintenance(entity, check)
179
193
  end
180
194
 
181
195
  # TODO set the state directly rather than submit & drain
182
196
  Given /^(?:the check|check '([\w\.\-]+)' for entity '([\w\.\-]+)') is in unscheduled maintenance$/ do |check, entity|
183
- check = check ? check : @check
184
- entity = entity ? entity : @entity
197
+ check ||= @check
198
+ entity ||= @entity
185
199
  remove_scheduled_maintenance(entity, check)
186
200
  set_critical_state(entity, check)
187
201
  submit_acknowledgement(entity, check)
188
202
  drain_events # TODO these should only be in When clauses
189
203
  end
190
204
 
191
- Given /^the check is check '(.*)' on entity '([\w\.\-]+)'$/ do |check, entity|
192
- @check = check
193
- @entity = entity
194
- end
195
-
196
205
  When /^an ok event is received(?: for check '([\w\.\-]+)' on entity '([\w\.\-]+)')?$/ do |check, entity|
197
206
  check ||= @check
198
207
  entity ||= @entity
@@ -259,6 +268,12 @@ Then /^a notification should be generated(?: for check '([\w\.\-]+)' on entity '
259
268
  found.should be_true
260
269
  end
261
270
 
271
+ Then /^scheduled maintenance should be generated(?: for check '([\w\.\-]+)' on entity '([\w\.\-]+)')?$/ do |check, entity|
272
+ check ||= @check
273
+ entity ||= @entity
274
+ @redis.get("#{entity}:#{check}:scheduled_maintenance").should_not be_nil
275
+ end
276
+
262
277
  Then /^show me the (\w+ )*log$/ do |adjective|
263
278
  puts "the #{adjective}log:"
264
279
  puts @logger.messages.join("\n")
@@ -66,7 +66,7 @@ When /^an event notification is generated for entity '([\w\.\-]+)'$/ do |entity|
66
66
  'entity' => entity,
67
67
  'check' => 'ping')
68
68
  entity_check = Flapjack::Data::EntityCheck.for_entity_name(entity, 'ping', :redis => @redis)
69
- @app.send(:generate_notification_messages, event, entity_check)
69
+ @app.send(:generate_notification_messages, event, entity_check, Time.now.to_i)
70
70
  end
71
71
 
72
72
  Then /^an SMS notification for entity '([\w\.\-]+)' should be queued for the user$/ do |entity|
@@ -35,6 +35,8 @@ module Flapjack
35
35
  end
36
36
 
37
37
  def start(options = {})
38
+ @boot_time = Time.now
39
+
38
40
  EM.synchrony do
39
41
  add_pikelets(pikelets(@config.all))
40
42
  setup_signals if options[:signals]
@@ -131,7 +133,7 @@ module Flapjack
131
133
  start_piks = []
132
134
  pikelets_data.each_pair do |type, cfg|
133
135
  next unless pikelet = Flapjack::Pikelet.create(type,
134
- :config => cfg, :redis_config => @redis_options)
136
+ :config => cfg, :redis_config => @redis_options, :boot_time => @boot_time)
135
137
  start_piks << pikelet
136
138
  @pikelets << pikelet
137
139
  end
@@ -16,7 +16,7 @@ module Flapjack
16
16
 
17
17
  class Contact
18
18
 
19
- attr_accessor :id, :first_name, :last_name, :email, :media, :pagerduty_credentials
19
+ attr_accessor :id, :first_name, :last_name, :email, :media, :media_intervals, :pagerduty_credentials
20
20
 
21
21
  TAG_PREFIX = 'contact_tag'
22
22
 
@@ -76,6 +76,7 @@ module Flapjack
76
76
  self.first_name, self.last_name, self.email =
77
77
  @redis.hmget("contact:#{@id}", 'first_name', 'last_name', 'email')
78
78
  self.media = @redis.hgetall("contact_media:#{@id}")
79
+ self.media_intervals = @redis.hgetall("contact_media_intervals:#{self.id}")
79
80
 
80
81
  # similar to code in instance method pagerduty_credentials
81
82
  if service_key = @redis.hget("contact_media:#{@id}", 'pagerduty')
@@ -199,10 +200,6 @@ module Flapjack
199
200
  @redis.del("notification_rule:#{rule.id}")
200
201
  end
201
202
 
202
- def media_intervals
203
- @redis.hgetall("contact_media_intervals:#{self.id}")
204
- end
205
-
206
203
  # how often to notify this contact on the given media
207
204
  # return 15 mins if no value is set
208
205
  def interval_for_media(media)
@@ -211,8 +208,12 @@ module Flapjack
211
208
  end
212
209
 
213
210
  def set_interval_for_media(media, interval)
214
- raise "invalid interval" unless interval.is_a?(Integer)
211
+ if interval.nil?
212
+ @redis.hdel("contact_media_intervals:#{self.id}", media)
213
+ return
214
+ end
215
215
  @redis.hset("contact_media_intervals:#{self.id}", media, interval)
216
+ self.media_intervals = @redis.hgetall("contact_media_intervals:#{self.id}")
216
217
  end
217
218
 
218
219
  def set_address_for_media(media, address)
@@ -222,6 +223,7 @@ module Flapjack
222
223
  # probably best solution is to remove the need to have the username and password
223
224
  # and subdomain as pagerduty's updated api's mean we don't them anymore I think...
224
225
  end
226
+ self.media = @redis.hgetall("contact_media:#{@id}")
225
227
  end
226
228
 
227
229
  def remove_media(media)
@@ -22,6 +22,9 @@ module Flapjack
22
22
  STATE_CRITICAL = 'critical'
23
23
  STATE_UNKNOWN = 'unknown'
24
24
 
25
+ NOTIFICATION_STATES = [:problem, :warning, :critical, :unknown,
26
+ :recovery, :acknowledgement]
27
+
25
28
  attr_accessor :entity, :check
26
29
 
27
30
  # TODO probably shouldn't always be creating on query -- work out when this should be happening
@@ -76,35 +79,9 @@ module Flapjack
76
79
  }
77
80
  end
78
81
 
79
- # TODO move to Event
80
- def create_acknowledgement(options = {})
81
- event = { 'type' => 'action',
82
- 'state' => 'acknowledgement',
83
- 'summary' => options['summary'],
84
- 'duration' => options['duration'],
85
- 'acknowledgement_id' => options['acknowledgement_id'],
86
- 'entity' => entity.name,
87
- 'check' => check
88
- }
89
- Flapjack::Data::Event.add(event, :redis => @redis)
90
- end
91
-
92
- # TODO move to Event
93
- def test_notifications(options = {})
94
- event = { 'type' => 'action',
95
- 'state' => 'test_notifications',
96
- 'summary' => options['summary'],
97
- 'details' => options['details'],
98
- 'entity' => entity.name,
99
- 'check' => check
100
- }
101
- Flapjack::Data::Event.add(event, :redis => @redis)
102
- end
103
-
104
- # FIXME: need to add summary to summary of existing unscheduled maintenance if there is
105
- # one, and extend duration / expiry time, instead of creating a separate unscheduled
106
- # outage as we are doing now...
107
82
  def create_unscheduled_maintenance(opts = {})
83
+ end_unscheduled_maintenance if in_unscheduled_maintenance?
84
+
108
85
  start_time = opts[:start_time] # unix timestamp
109
86
  duration = opts[:duration] # seconds
110
87
  summary = opts[:summary]
@@ -217,39 +194,43 @@ module Flapjack
217
194
  @redis.hget("check:#{@key}", 'state')
218
195
  end
219
196
 
220
- def update_state(state, options = {})
221
- return unless validate_state(state)
197
+ def update_state(new_state, options = {})
198
+ return unless [STATE_OK, STATE_WARNING,
199
+ STATE_CRITICAL, STATE_UNKNOWN].include?(new_state)
200
+
222
201
  timestamp = options[:timestamp] || Time.now.to_i
223
- client = options[:client]
224
202
  summary = options[:summary]
225
203
  details = options[:details]
226
204
  count = options[:count]
227
205
 
228
- # Note the current state (for speedy lookups)
229
- @redis.hset("check:#{@key}", 'state', state)
230
-
231
- # FIXME: rename to last_state_change?
232
- @redis.hset("check:#{@key}", 'last_change', timestamp)
206
+ if self.state != new_state
207
+ client = options[:client]
208
+
209
+ # Note the current state (for speedy lookups)
210
+ @redis.hset("check:#{@key}", 'state', new_state)
211
+
212
+ # FIXME: rename to last_state_change?
213
+ @redis.hset("check:#{@key}", 'last_change', timestamp)
214
+ case state
215
+ when STATE_WARNING, STATE_CRITICAL, STATE_UNKNOWN
216
+ @redis.zadd('failed_checks', timestamp, @key)
217
+ # FIXME: Iterate through a list of tags associated with an entity:check pair, and update counters
218
+ @redis.zadd("failed_checks:client:#{client}", timestamp, @key) if client
219
+ else
220
+ @redis.zrem("failed_checks", @key)
221
+ # FIXME: Iterate through a list of tags associated with an entity:check pair, and update counters
222
+ @redis.zrem("failed_checks:client:#{client}", @key) if client
223
+ end
224
+ end
233
225
 
234
- # Retain all state changes for entity:check pair
226
+ # Retain event data for entity:check pair
235
227
  @redis.rpush("#{@key}:states", timestamp)
236
- @redis.set("#{@key}:#{timestamp}:state", state)
228
+ @redis.set("#{@key}:#{timestamp}:state", new_state)
237
229
  @redis.set("#{@key}:#{timestamp}:summary", summary) if summary
238
230
  @redis.set("#{@key}:#{timestamp}:details", details) if details
239
231
  @redis.set("#{@key}:#{timestamp}:count", count) if count
240
232
 
241
233
  @redis.zadd("#{@key}:sorted_state_timestamps", timestamp, timestamp)
242
-
243
- case state
244
- when STATE_WARNING, STATE_CRITICAL, STATE_UNKNOWN
245
- @redis.zadd('failed_checks', timestamp, @key)
246
- # FIXME: Iterate through a list of tags associated with an entity:check pair, and update counters
247
- @redis.zadd("failed_checks:client:#{client}", timestamp, @key) if client
248
- else
249
- @redis.zrem("failed_checks", @key)
250
- # FIXME: Iterate through a list of tags associated with an entity:check pair, and update counters
251
- @redis.zrem("failed_checks:client:#{client}", @key) if client
252
- end
253
234
  end
254
235
 
255
236
  def last_update
@@ -268,61 +249,31 @@ module Flapjack
268
249
  lc.to_i
269
250
  end
270
251
 
271
- def last_problem_notification
272
- lpn = @redis.get("#{@key}:last_problem_notification")
273
- return unless (lpn && lpn =~ /^\d+$/)
274
- lpn.to_i
275
- end
276
-
277
- def last_warning_notification
278
- lwn = @redis.get("#{@key}:last_warning_notification")
279
- return unless (lwn && lwn =~ /^\d+$/)
280
- lwn.to_i
281
- end
282
-
283
- def last_critical_notification
284
- lcn = @redis.get("#{@key}:last_critical_notification")
285
- return unless (lcn && lcn =~ /^\d+$/)
286
- lcn.to_i
287
- end
288
-
289
- def last_unknown_notification
290
- lcn = @redis.get("#{@key}:last_unknown_notification")
291
- return unless (lcn && lcn =~ /^\d+$/)
292
- lcn.to_i
293
- end
294
-
295
- def last_recovery_notification
296
- lrn = @redis.get("#{@key}:last_recovery_notification")
297
- return unless (lrn && lrn =~ /^\d+$/)
298
- lrn.to_i
299
- end
300
-
301
- def last_acknowledgement_notification
302
- lan = @redis.get("#{@key}:last_acknowledgement_notification")
303
- return unless (lan && lan =~ /^\d+$/)
304
- lan.to_i
252
+ def last_notification_for_state(state)
253
+ return unless NOTIFICATION_STATES.include?(state)
254
+ ln = @redis.get("#{@key}:last_#{state.to_s}_notification")
255
+ return {:timestamp => nil, :summary => nil} unless (ln && ln =~ /^\d+$/)
256
+ { :timestamp => ln.to_i,
257
+ :summary => @redis.get("#{@key}:#{ln.to_i}:summary") }
305
258
  end
306
259
 
307
260
  def last_notifications_of_each_type
308
- ln = {:warning => last_warning_notification,
309
- :critical => last_critical_notification,
310
- :unknown => last_unknown_notification,
311
- :recovery => last_recovery_notification,
312
- :acknowledgement => last_acknowledgement_notification }
313
- ln
261
+ NOTIFICATION_STATES.inject({}) do |memo, state|
262
+ memo[state] = last_notification_for_state(state) unless (state == :problem)
263
+ memo
264
+ end
314
265
  end
315
266
 
316
267
  def max_notified_severity_of_current_failure
317
- last_recovery = last_recovery_notification || 0
268
+ last_recovery = last_notification_for_state(:recovery)[:timestamp] || 0
318
269
 
319
- last_critical = last_critical_notification
270
+ last_critical = last_notification_for_state(:critical)[:timestamp]
320
271
  return STATE_CRITICAL if last_critical && (last_critical > last_recovery)
321
272
 
322
- last_warning = last_warning_notification
273
+ last_warning = last_notification_for_state(:warning)[:timestamp]
323
274
  return STATE_WARNING if last_warning && (last_warning > last_recovery)
324
275
 
325
- last_unknown = last_unknown_notification
276
+ last_unknown = last_notification_for_state(:unknown)[:timestamp]
326
277
  return STATE_UNKNOWN if last_unknown && (last_unknown > last_recovery)
327
278
 
328
279
  nil
@@ -331,15 +282,16 @@ module Flapjack
331
282
  # unpredictable results if there are multiple notifications of different
332
283
  # types sent at the same time
333
284
  def last_notification
334
- nils = { :type => nil, :timestamp => nil }
285
+ nils = { :type => nil, :timestamp => nil, :summary => nil }
286
+
335
287
  lne = last_notifications_of_each_type
336
- ln = lne.delete_if {|type, timestamp| timestamp.nil? || timestamp.to_i == 0 }
337
- if ln.find {|type, timestamp| type == :warning or type == :critical}
338
- ln = ln.delete_if {|type, timestamp| type == :problem }
288
+ ln = lne.delete_if {|type, notif| notif[:timestamp].nil? || notif[:timestamp].to_i <= 0 }
289
+ if ln.find {|type, notif| type == :warning or type == :critical}
290
+ ln = ln.delete_if {|type, notif| type == :problem }
339
291
  end
340
- return nils unless ln.length > 0
341
- lns = ln.sort_by { |type, timestamp| timestamp }.last
342
- { :type => lns[0], :timestamp => lns[1] }
292
+ return nils if ln.empty?
293
+ lns = ln.sort_by { |type, notif| notif[:timestamp] }.last
294
+ { :type => lns[0], :timestamp => lns[1][:timestamp], :summary => lns[1][:summary] }
343
295
  end
344
296
 
345
297
  def event_count_at(timestamp)
@@ -373,20 +325,37 @@ module Flapjack
373
325
  # from midnight to 11:59:59 PM. Pass nil for either end to leave that
374
326
  # side unbounded.
375
327
  def historical_states(start_time, end_time, opts = {})
376
- start_time ||= '-inf'
377
- end_time ||= '+inf'
328
+ start_time = '-inf' if start_time.to_i <= 0
329
+ end_time = '+inf' if end_time.to_i <= 0
330
+
331
+ args = ["#{@key}:sorted_state_timestamps"]
332
+
378
333
  order = opts[:order]
379
- query = (order && 'desc'.eql?(order.downcase)) ? :zrevrangebyscore : :zrangebyscore
380
- state_ts = @redis.send(query, "#{@key}:sorted_state_timestamps", start_time, end_time)
334
+ if (order && 'desc'.eql?(order.downcase))
335
+ query = :zrevrangebyscore
336
+ args += [end_time.to_s, start_time.to_s]
337
+ else
338
+ query = :zrangebyscore
339
+ args += [start_time.to_s, end_time.to_s]
340
+ end
341
+
342
+ if opts[:limit] && (opts[:limit].to_i > 0)
343
+ args << {:limit => [0, opts[:limit]]}
344
+ end
345
+
346
+ state_ts = @redis.send(query, *args)
381
347
 
382
348
  state_data = nil
383
349
 
384
350
  @redis.multi do |r|
385
351
  state_data = state_ts.collect {|ts|
386
- {:timestamp => ts.to_i,
387
- :state => r.get("#{@key}:#{ts}:state"),
388
- :summary => r.get("#{@key}:#{ts}:summary"),
389
- :details => r.get("#{@key}:#{ts}:details")}
352
+ {:timestamp => ts.to_i,
353
+ :state => r.get("#{@key}:#{ts}:state"),
354
+ :summary => r.get("#{@key}:#{ts}:summary"),
355
+ :details => r.get("#{@key}:#{ts}:details"),
356
+ # :count => r.get("#{@key}:#{ts}:count"),
357
+ # :check_latency => r.get("#{@key}:#{ts}:check_latency")
358
+ }
390
359
  }
391
360
  end
392
361
 
@@ -405,7 +374,7 @@ module Flapjack
405
374
  # if any.
406
375
  def historical_state_before(timestamp)
407
376
  pos = @redis.zrank("#{@key}:sorted_state_timestamps", timestamp)
408
- return if pos < 1
377
+ return if pos.nil? || pos < 1
409
378
  ts = @redis.zrange("#{@key}:sorted_state_timestamps", pos - 1, pos)
410
379
  return if ts.nil? || ts.empty?
411
380
  {:timestamp => ts.first.to_i,
@@ -475,10 +444,6 @@ module Flapjack
475
444
  @key = "#{entity.name}:#{check}"
476
445
  end
477
446
 
478
- def validate_state(state)
479
- [STATE_OK, STATE_WARNING, STATE_CRITICAL, STATE_UNKNOWN].include?(state)
480
- end
481
-
482
447
  end
483
448
 
484
449
  end