flapjack 1.2.1 → 1.2.2

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +11 -12
  4. data/CHANGELOG.md +10 -0
  5. data/Gemfile +0 -7
  6. data/Rakefile +0 -1
  7. data/bin/flapjack +2 -0
  8. data/etc/flapjack_config.yaml.example +20 -0
  9. data/features/ack_after_sched_maint.feature +1 -1
  10. data/features/cli.feature +1 -1
  11. data/features/notification_rules.feature +1 -1
  12. data/features/notifications.feature +0 -9
  13. data/features/rollup.feature +1 -1
  14. data/features/steps/events_steps.rb +20 -8
  15. data/features/steps/notifications_steps.rb +62 -75
  16. data/features/support/env.rb +17 -8
  17. data/flapjack.gemspec +4 -4
  18. data/lib/flapjack.rb +3 -0
  19. data/lib/flapjack/cli/import.rb +1 -0
  20. data/lib/flapjack/cli/maintenance.rb +1 -0
  21. data/lib/flapjack/cli/purge.rb +2 -0
  22. data/lib/flapjack/cli/receiver.rb +1 -0
  23. data/lib/flapjack/cli/simulate.rb +1 -0
  24. data/lib/flapjack/data/alert.rb +28 -1
  25. data/lib/flapjack/data/contact.rb +1 -1
  26. data/lib/flapjack/data/entity.rb +18 -8
  27. data/lib/flapjack/data/entity_check.rb +17 -0
  28. data/lib/flapjack/data/event.rb +33 -15
  29. data/lib/flapjack/data/migration.rb +46 -23
  30. data/lib/flapjack/filters/delays.rb +13 -6
  31. data/lib/flapjack/gateways/aws_sns.rb +115 -88
  32. data/lib/flapjack/gateways/aws_sns/alert.text.erb +2 -1
  33. data/lib/flapjack/gateways/email.rb +145 -135
  34. data/lib/flapjack/gateways/email/alert.html.erb +6 -4
  35. data/lib/flapjack/gateways/email/alert.text.erb +2 -0
  36. data/lib/flapjack/gateways/jabber.rb +61 -1
  37. data/lib/flapjack/gateways/jabber/alert.text.erb +1 -1
  38. data/lib/flapjack/gateways/pagerduty/alert.text.erb +1 -1
  39. data/lib/flapjack/gateways/sms_gammu.rb +119 -0
  40. data/lib/flapjack/gateways/sms_messagenet.rb +95 -67
  41. data/lib/flapjack/gateways/sms_messagenet/alert.text.erb +2 -1
  42. data/lib/flapjack/gateways/sms_twilio.rb +102 -74
  43. data/lib/flapjack/gateways/sms_twilio/alert.text.erb +2 -1
  44. data/lib/flapjack/logger.rb +1 -1
  45. data/lib/flapjack/notifier.rb +5 -14
  46. data/lib/flapjack/patches.rb +0 -58
  47. data/lib/flapjack/pikelet.rb +8 -78
  48. data/lib/flapjack/processor.rb +3 -1
  49. data/lib/flapjack/redis_pool.rb +2 -0
  50. data/lib/flapjack/version.rb +1 -1
  51. data/spec/lib/flapjack/data/contact_spec.rb +2 -2
  52. data/spec/lib/flapjack/data/entity_spec.rb +15 -0
  53. data/spec/lib/flapjack/data/event_spec.rb +2 -2
  54. data/spec/lib/flapjack/data/migration_spec.rb +11 -0
  55. data/spec/lib/flapjack/gateways/aws_sns_spec.rb +12 -8
  56. data/spec/lib/flapjack/gateways/email_spec.rb +56 -51
  57. data/spec/lib/flapjack/gateways/sms_messagenet_spec.rb +17 -12
  58. data/spec/lib/flapjack/gateways/sms_twilio_spec.rb +17 -12
  59. data/spec/lib/flapjack/pikelet_spec.rb +9 -23
  60. data/spec/lib/flapjack/redis_pool_spec.rb +1 -0
  61. data/tasks/profile.rake +25 -109
  62. metadata +37 -39
  63. data/Gemfile-ruby1.9 +0 -30
  64. data/Gemfile-ruby1.9.lock +0 -250
  65. data/tasks/benchmarks.rake +0 -237
@@ -38,8 +38,6 @@ require 'flapjack/notifier'
38
38
  require 'flapjack/processor'
39
39
  require 'flapjack/patches'
40
40
 
41
- require 'resque_spec'
42
-
43
41
  class MockLogger
44
42
  attr_accessor :messages
45
43
 
@@ -122,6 +120,20 @@ redis.flushdb
122
120
  RedisDelorean.before_all(:redis => redis)
123
121
  redis.quit
124
122
 
123
+ # Not the most efficient of operations...
124
+ def redis_peek(queue, start = 0, count = nil)
125
+ size = @notifier_redis.llen(queue)
126
+ start = 0 if start < 0
127
+ count = (size - start) if count.nil? || (count > (size - start))
128
+
129
+ (0..(size - 1)).inject([]) do |memo, n|
130
+ object = @notifier_redis.rpoplpush(queue, queue)
131
+ next memo unless (n >= start || n < (start + count))
132
+ memo << Flapjack.load_json(object)
133
+ memo
134
+ end
135
+ end
136
+
125
137
  Around do |scenario, blk|
126
138
  EM.synchrony do
127
139
  blk.call
@@ -130,11 +142,13 @@ Around do |scenario, blk|
130
142
  end
131
143
 
132
144
  Before do
145
+ @redis_opts = redis_opts
133
146
  @logger = MockLogger.new
134
147
  end
135
148
 
136
149
  After do
137
150
  @logger.messages = []
151
+ @redis_opts = nil
138
152
  end
139
153
 
140
154
  Before('@processor') do
@@ -150,7 +164,7 @@ After('@processor') do
150
164
  end
151
165
 
152
166
  Before('@notifier') do
153
- @notifier = Flapjack::Notifier.new(:logger => @logger,
167
+ @notifier = Flapjack::Notifier.new(:logger => @logger,
154
168
  :redis_config => redis_opts,
155
169
  :config => {'email_queue' => 'email_notifications',
156
170
  'sms_queue' => 'sms_notifications',
@@ -165,11 +179,6 @@ After('@notifier') do
165
179
  @notifier_redis = nil
166
180
  end
167
181
 
168
- Before('@resque') do
169
- ResqueSpec.reset!
170
- @queues = {:email => 'email_queue'}
171
- end
172
-
173
182
  Before('@time') do
174
183
  RedisDelorean.before_each(:redis => @redis || @notifier_redis)
175
184
  end
data/flapjack.gemspec CHANGED
@@ -23,10 +23,9 @@ Gem::Specification.new do |gem|
23
23
  gem.add_dependency 'eventmachine', '~> 1.0.0'
24
24
  gem.add_dependency 'redis', '~> 3.0.6'
25
25
  gem.add_dependency 'hiredis'
26
+ gem.add_dependency 'em-hiredis'
26
27
  gem.add_dependency 'em-synchrony', '~> 1.0.2'
27
28
  gem.add_dependency 'em-http-request'
28
- gem.add_dependency 'em-resque'
29
- gem.add_dependency 'resque', '~> 1.23.0'
30
29
  gem.add_dependency 'sinatra'
31
30
  gem.add_dependency 'rack-fiber_pool'
32
31
  gem.add_dependency 'thin', '~> 1.6.1'
@@ -35,12 +34,13 @@ Gem::Specification.new do |gem|
35
34
  gem.add_dependency 'chronic'
36
35
  gem.add_dependency 'chronic_duration'
37
36
  gem.add_dependency 'terminal-table'
38
- gem.add_dependency 'activesupport', '~> 3.2.14'
37
+ gem.add_dependency 'activesupport'
39
38
  gem.add_dependency 'ice_cube'
40
- gem.add_dependency 'tzinfo', '~> 1.0.1'
39
+ gem.add_dependency 'tzinfo'
41
40
  gem.add_dependency 'tzinfo-data'
42
41
  gem.add_dependency 'rbtrace'
43
42
  gem.add_dependency 'rake'
44
43
  gem.add_dependency 'gli', '= 2.12.0'
45
44
  gem.add_dependency 'nokogiri', '= 1.6.2.1'
45
+ gem.add_dependency 'mysql2'
46
46
  end
data/lib/flapjack.rb CHANGED
@@ -4,6 +4,9 @@ require 'oj'
4
4
 
5
5
  module Flapjack
6
6
 
7
+ DEFAULT_INITIAL_FAILURE_DELAY = 30
8
+ DEFAULT_REPEAT_FAILURE_DELAY = 60
9
+
7
10
  def self.load_json(data)
8
11
  Oj.load(data, :mode => :strict, :symbol_keys => false)
9
12
  end
@@ -65,6 +65,7 @@ module Flapjack
65
65
  return @redis unless @redis.nil?
66
66
  @redis = Redis.new(@redis_options.merge(:driver => :hiredis))
67
67
  Flapjack::Data::Migration.migrate_entity_check_data_if_required(:redis => @redis)
68
+ Flapjack::Data::Migration.clear_orphaned_entity_ids(:redis => @redis)
68
69
  @redis
69
70
  end
70
71
 
@@ -68,6 +68,7 @@ module Flapjack
68
68
  return @redis unless @redis.nil?
69
69
  @redis = Redis.new(@redis_options.merge(:driver => :hiredis))
70
70
  Flapjack::Data::Migration.migrate_entity_check_data_if_required(:redis => @redis)
71
+ Flapjack::Data::Migration.clear_orphaned_entity_ids(:redis => @redis)
71
72
  Flapjack::Data::Migration.validate_scheduled_maintenance_periods(:redis => @redis)
72
73
  @redis
73
74
  end
@@ -40,6 +40,7 @@ module Flapjack
40
40
  purged = checks.inject([]) do |memo, check|
41
41
  pu = check.purge_history(options)
42
42
  memo << pu unless pu == 0
43
+ memo
43
44
  end
44
45
 
45
46
  if purged.empty?
@@ -55,6 +56,7 @@ module Flapjack
55
56
  return @redis unless @redis.nil?
56
57
  @redis = Redis.new(@redis_options.merge(:driver => :hiredis))
57
58
  Flapjack::Data::Migration.migrate_entity_check_data_if_required(:redis => @redis)
59
+ Flapjack::Data::Migration.clear_orphaned_entity_ids(:redis => @redis)
58
60
  @redis
59
61
  end
60
62
 
@@ -144,6 +144,7 @@ module Flapjack
144
144
  return @redis unless @redis.nil?
145
145
  @redis = Redis.new(@redis_options.merge(:driver => :hiredis))
146
146
  Flapjack::Data::Migration.migrate_entity_check_data_if_required(:redis => @redis)
147
+ Flapjack::Data::Migration.clear_orphaned_entity_ids(:redis => @redis)
147
148
  @redis
148
149
  end
149
150
 
@@ -55,6 +55,7 @@ module Flapjack
55
55
  return @redis unless @redis.nil?
56
56
  @redis = Redis.new(@redis_options)
57
57
  Flapjack::Data::Migration.migrate_entity_check_data_if_required(:redis => @redis)
58
+ Flapjack::Data::Migration.clear_orphaned_entity_ids(:redis => @redis)
58
59
  @redis
59
60
  end
60
61
 
@@ -51,7 +51,7 @@ module Flapjack
51
51
 
52
52
  include Flapjack::Utility
53
53
 
54
- def initialize(contents, opts)
54
+ def initialize(contents, opts = {})
55
55
  raise "no logger supplied" unless @logger = opts[:logger]
56
56
 
57
57
  @event_id = contents['event_id']
@@ -103,7 +103,34 @@ module Flapjack
103
103
  details['state'] && allowed_rollup_states.include?(details['state'])
104
104
  end
105
105
  end
106
+ end
107
+
108
+ def self.add(queue, alert_data, opts = {})
109
+ raise "Redis connection not set" unless redis = opts[:redis]
110
+ redis.rpush(queue, Flapjack.dump_json(alert_data))
111
+ end
112
+
113
+ def self.next(queue, opts = {})
114
+ raise "Redis connection not set" unless redis = opts[:redis]
115
+
116
+ defaults = { :block => true }
117
+ options = defaults.merge(opts)
106
118
 
119
+ if options[:block]
120
+ raw = redis.blpop(queue, 0)[1]
121
+ else
122
+ raw = redis.lpop(queue)
123
+ return unless raw
124
+ end
125
+ begin
126
+ parsed = ::Flapjack.load_json( raw )
127
+ rescue Oj::Error => e
128
+ if options[:logger]
129
+ options[:logger].warn("Error deserialising alert json: #{e}, raw json: #{raw.inspect}")
130
+ end
131
+ return nil
132
+ end
133
+ self.new( parsed, :logger => opts[:logger] )
107
134
  end
108
135
 
109
136
  def type
@@ -19,7 +19,7 @@ module Flapjack
19
19
  attr_accessor :id, :first_name, :last_name, :email, :media,
20
20
  :media_intervals, :media_rollup_thresholds, :pagerduty_credentials
21
21
 
22
- ALL_MEDIA = ['email', 'sms', 'sms_twilio', 'jabber', 'pagerduty', 'sns']
22
+ ALL_MEDIA = ['email', 'sms', 'sms_twilio', 'sms_gammu', 'jabber', 'pagerduty', 'sns']
23
23
 
24
24
  def self.all(options = {})
25
25
  raise "Redis connection not set" unless redis = options[:redis]
@@ -469,24 +469,34 @@ module Flapjack
469
469
  end
470
470
  else
471
471
  # most likely from API import
472
- existing_name = redis.hget('all_entity_names_by_id', entity_id)
472
+ this_id_original_name = redis.hget('all_entity_names_by_id', entity_id)
473
473
 
474
474
  # if there's an entity with a matching name, this will change its
475
475
  # id; if no entity exists it creates a new one
476
476
 
477
- if existing_name.nil?
477
+ this_name_original_id = redis.hget('all_entity_ids_by_name', entity_name)
478
+
479
+ if this_id_original_name.nil?
480
+ # no entity exists with a matching id
478
481
  redis.hset('all_entity_ids_by_name', entity_name, entity_id)
479
482
  redis.hset('all_entity_names_by_id', entity_id, entity_name)
480
483
 
481
- elsif existing_name != entity_name
482
- if redis.hexists('all_entity_ids_by_name', entity_name)
483
- merge(existing_name, entity_name, :redis => redis)
484
- else
485
- rename(existing_name, entity_name, :redis => redis) {|multi|
486
- multi.hdel('all_entity_ids_by_name', existing_name)
484
+ unless this_name_original_id.nil?
485
+ # an entity existed with a matching name but a different id
486
+ redis.hdel('all_entity_names_by_id', this_name_original_id)
487
+ end
488
+ elsif this_id_original_name != entity_name
489
+ # a record exists with the provided id but a different name
490
+ if this_name_original_id.nil?
491
+ # there shouldn't be any entity records left without ids (due to
492
+ # the migration code) so this code may not be needed now
493
+ rename(this_id_original_name, entity_name, :redis => redis) {|multi|
494
+ multi.hdel('all_entity_ids_by_name', this_id_original_name)
487
495
  multi.hset('all_entity_ids_by_name', entity_name, entity_id)
488
496
  multi.hset('all_entity_names_by_id', entity_id, entity_name)
489
497
  }
498
+ else
499
+ merge(this_id_original_name, entity_name, :redis => redis)
490
500
  end
491
501
  end
492
502
  end
@@ -606,6 +606,8 @@ module Flapjack
606
606
  details = options[:details]
607
607
  perfdata = options[:perfdata]
608
608
  count = options[:count]
609
+ initial_delay = options[:initial_failure_delay]
610
+ repeat_delay = options[:repeat_failure_delay]
609
611
 
610
612
  old_state = self.state
611
613
 
@@ -651,6 +653,11 @@ module Flapjack
651
653
  # hash summary and details (as they may have changed)
652
654
  multi.hset("check:#{@key}", 'summary', (summary || ''))
653
655
  multi.hset("check:#{@key}", 'details', (details || ''))
656
+
657
+ # NB: delays will revert to defaults if event sources don't continue sending
658
+ # through their custom delays in the event structure
659
+ multi.hset("check:#{@key}", 'initial_failure_delay', (initial_delay || Flapjack::DEFAULT_INITIAL_FAILURE_DELAY))
660
+ multi.hset("check:#{@key}", 'repeat_failure_delay', (repeat_delay || Flapjack::DEFAULT_REPEAT_FAILURE_DELAY))
654
661
  if perfdata
655
662
  multi.hset("check:#{@key}", 'perfdata', format_perfdata(perfdata).to_json)
656
663
  # multi.set("#{@key}:#{timestamp}:perfdata", perfdata)
@@ -777,6 +784,16 @@ module Flapjack
777
784
  data
778
785
  end
779
786
 
787
+ def initial_failure_delay
788
+ delay = @redis.hget("check:#{@key}", 'initial_failure_delay')
789
+ delay.to_i unless delay.nil?
790
+ end
791
+
792
+ def repeat_failure_delay
793
+ delay = @redis.hget("check:#{@key}", 'repeat_failure_delay')
794
+ delay.to_i unless delay.nil?
795
+ end
796
+
780
797
  # Returns a list of states for this entity check, sorted by timestamp.
781
798
  #
782
799
  # start_time and end_time should be passed as integer timestamps; these timestamps
@@ -10,10 +10,13 @@ module Flapjack
10
10
 
11
11
  attr_accessor :counter, :id_hash, :tags
12
12
 
13
- attr_reader :check, :summary, :details, :acknowledgement_id, :perfdata
13
+ attr_reader :check, :summary, :details, :acknowledgement_id, :perfdata,
14
+ :initial_failure_delay, :repeat_failure_delay
14
15
 
15
- REQUIRED_KEYS = ['type', 'state', 'entity', 'check', 'summary']
16
- OPTIONAL_KEYS = ['time', 'details', 'acknowledgement_id', 'duration', 'tags', 'perfdata']
16
+ REQUIRED_KEYS = ['type', 'state', 'entity', 'check']
17
+ OPTIONAL_KEYS = ['time', 'summary', 'details', 'acknowledgement_id',
18
+ 'duration', 'tags', 'perfdata', 'initial_failure_delay',
19
+ 'repeat_failure_delay']
17
20
 
18
21
  VALIDATIONS = {
19
22
  proc {|e| e['type'].is_a?(String) &&
@@ -32,14 +35,24 @@ module Flapjack
32
35
  proc {|e| e['check'].is_a?(String) } =>
33
36
  "check must be a string",
34
37
 
35
- proc {|e| e['summary'].is_a?(String) } =>
36
- "summary must be a string",
37
-
38
38
  proc {|e| e['time'].nil? ||
39
39
  e['time'].is_a?(Integer) ||
40
40
  (e['time'].is_a?(String) && !!(e['time'] =~ /^\d+$/)) } =>
41
41
  "time must be a positive integer, or a string castable to one",
42
42
 
43
+ proc {|e| e['initial_failure_delay'].nil? ||
44
+ e['initial_failure_delay'].is_a?(Integer) ||
45
+ (e['initial_failure_delay'].is_a?(String) && !!(e['initial_failure_delay'] =~ /^\d+$/)) } =>
46
+ "initial_failure_delay must be a positive integer, or a string castable to one",
47
+
48
+ proc {|e| e['repeat_failure_delay'].nil? ||
49
+ e['repeat_failure_delay'].is_a?(Integer) ||
50
+ (e['repeat_failure_delay'].is_a?(String) && !!(e['repeat_failure_delay'] =~ /^\d+$/)) } =>
51
+ "repeat_failure_delay must be a positive integer, or a string castable to one",
52
+
53
+ proc {|e| e['summary'].nil? || e['summary'].is_a?(String) } =>
54
+ "summary must be a string",
55
+
43
56
  proc {|e| e['details'].nil? || e['details'].is_a?(String) } =>
44
57
  "details must be a string",
45
58
 
@@ -169,14 +182,16 @@ module Flapjack
169
182
  end
170
183
 
171
184
  # creates, or modifies, an event object and adds it to the events list in redis
172
- # 'entity' => entity,
173
- # 'check' => check,
174
- # 'type' => 'service',
175
- # 'state' => state,
176
- # 'summary' => check_output,
177
- # 'details' => check_long_output,
178
- # 'perfdata' => perf_data,
179
- # 'time' => timestamp
185
+ # 'entity' => entity,
186
+ # 'check' => check,
187
+ # 'type' => 'service',
188
+ # 'state' => state,
189
+ # 'summary' => check_output,
190
+ # 'details' => check_long_output,
191
+ # 'perfdata' => perf_data,
192
+ # 'time' => timestamp,
193
+ # 'initial_failure_delay' => initial_failure_delay,
194
+ # 'repeat_failure_delay' => repeat_failure_delay
180
195
  def self.add(evt, opts = {})
181
196
  raise "Redis connection not set" unless redis = opts[:redis]
182
197
 
@@ -216,9 +231,12 @@ module Flapjack
216
231
 
217
232
  def initialize(attrs = {})
218
233
  ['type', 'state', 'entity', 'check', 'time', 'summary', 'details',
219
- 'perfdata', 'acknowledgement_id', 'duration'].each do |key|
234
+ 'perfdata', 'acknowledgement_id', 'duration', 'initial_failure_delay',
235
+ 'repeat_failure_delay'].each do |key|
220
236
  instance_variable_set("@#{key}", attrs[key])
221
237
  end
238
+ # summary is optional. set it to nil if it only contains whitespace
239
+ @summary = (@summary.is_a?(String) && ! @summary.strip.empty?) ? @summary.strip : nil
222
240
  # details is optional. set it to nil if it only contains whitespace
223
241
  @details = (@details.is_a?(String) && ! @details.strip.empty?) ? @details.strip : nil
224
242
  # perfdata is optional. set it to nil if it only contains whitespace
@@ -12,9 +12,11 @@ module Flapjack
12
12
  ENTITY_DATA_MIGRATION = 'entity_data_migration'
13
13
 
14
14
  # copied from jsonapi/contact_methods.rb, could extract both into separate file
15
- def self.obtain_semaphore(resource, options = {})
15
+ def self.obtain_semaphore(resource, description, options = {})
16
16
  raise "Redis connection not set" unless redis = options[:redis]
17
17
 
18
+ logger = options[:logger]
19
+
18
20
  semaphore = nil
19
21
  strikes = 0
20
22
  begin
@@ -27,6 +29,18 @@ module Flapjack
27
29
  end
28
30
  sempahore = nil
29
31
  end
32
+
33
+ if semaphore.nil?
34
+ unless logger.nil?
35
+ logger.fatal "Could not obtain lock for data migration (#{reason}). Ensure that " +
36
+ "no other flapjack processes are running that might be executing " +
37
+ "migrations, check logs for any exceptions, manually delete the " +
38
+ "'#{resource}' key from your Flapjack Redis " +
39
+ "database and try running Flapjack again."
40
+ end
41
+ raise "Unable to obtain semaphore #{resource}"
42
+ end
43
+
30
44
  semaphore
31
45
  end
32
46
 
@@ -37,17 +51,8 @@ module Flapjack
37
51
 
38
52
  return if redis.exists('created_ids_for_old_entities_without_ids')
39
53
 
40
- semaphore = obtain_semaphore(ENTITY_DATA_MIGRATION, :redis => redis)
41
- if semaphore.nil?
42
- unless logger.nil?
43
- logger.fatal "Could not obtain lock for data migration (entity id creation). Ensure that " +
44
- "no other flapjack processes are running that might be executing " +
45
- "migrations, check logs for any exceptions, manually delete the " +
46
- "'#{ENTITY_DATA_MIGRATION}' key from your Flapjack Redis " +
47
- "database and try running Flapjack again."
48
- end
49
- raise "Unable to obtain semaphore #{ENTITY_DATA_MIGRATION}"
50
- end
54
+ semaphore = obtain_semaphore(ENTITY_DATA_MIGRATION, 'entity id creation',
55
+ :redis => redis, :logger => logger)
51
56
 
52
57
  begin
53
58
  logger.warn "Ensuring all entities have ids ..." unless logger.nil?
@@ -72,17 +77,8 @@ module Flapjack
72
77
 
73
78
  return if redis.exists('all_checks')
74
79
 
75
- semaphore = obtain_semaphore(ENTITY_DATA_MIGRATION, :redis => redis)
76
- if semaphore.nil?
77
- unless logger.nil?
78
- logger.fatal "Could not obtain lock for entity check data migration. Ensure that " +
79
- "no other flapjack processes are running that might be executing " +
80
- "migrations, check logs for any exceptions, manually delete the " +
81
- "'#{ENTITY_DATA_MIGRATION}' key from your Flapjack Redis " +
82
- "database and try running Flapjack again."
83
- end
84
- raise "Unable to obtain semaphore #{ENTITY_DATA_MIGRATION}"
85
- end
80
+ semaphore = obtain_semaphore(ENTITY_DATA_MIGRATION, 'entity check data',
81
+ :redis => redis, :logger => logger)
86
82
 
87
83
  begin
88
84
  logger.warn "Upgrading Flapjack's entity/check Redis indexes..." unless logger.nil?
@@ -125,6 +121,33 @@ module Flapjack
125
121
  end
126
122
  end
127
123
 
124
+ def self.clear_orphaned_entity_ids(options = {})
125
+ raise "Redis connection not set" unless redis = options[:redis]
126
+
127
+ logger = options[:logger]
128
+
129
+ semaphore = obtain_semaphore(ENTITY_DATA_MIGRATION,
130
+ 'orphaned entity ids', :redis => redis, :logger => logger)
131
+
132
+ begin
133
+ logger.info "Checking for orphaned entity ids..." unless logger.nil?
134
+
135
+ valid_entity_data = redis.hgetall('all_entity_ids_by_name')
136
+
137
+ missing_ids = redis.hgetall('all_entity_names_by_id').reject {|e_id, e_name|
138
+ valid_entity_data[e_name] == e_id
139
+ }
140
+
141
+ unless missing_ids.empty?
142
+ logger.info "Clearing ids (#{missing_ids.inspect})" unless logger.nil?
143
+ redis.hdel('all_entity_names_by_id', missing_ids.keys)
144
+ end
145
+ ensure
146
+ semaphore.release
147
+ logger.info "Finished checking for orphaned entity ids." unless logger.nil?
148
+ end
149
+ end
150
+
128
151
  def self.refresh_archive_index(options = {})
129
152
  raise "Redis connection not set" unless redis = options[:redis]
130
153
  archive_keys = redis.keys('events_archive:*')