flapjack 0.7.14 → 0.7.15

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.
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