flapjack 1.1.0 → 1.2.0rc1

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.ruby-version +1 -1
  4. data/.travis.yml +12 -7
  5. data/CHANGELOG.md +12 -0
  6. data/Gemfile +6 -2
  7. data/Gemfile-ruby1.9 +29 -0
  8. data/Gemfile-ruby1.9.lock +251 -0
  9. data/README.md +2 -2
  10. data/Rakefile +1 -0
  11. data/etc/flapjack_config.yaml.example +2 -2
  12. data/features/steps/events_steps.rb +2 -2
  13. data/features/steps/flapjack-netsaint-parser_steps.rb +1 -1
  14. data/features/support/env.rb +1 -6
  15. data/lib/flapjack/cli/import.rb +2 -5
  16. data/lib/flapjack/cli/purge.rb +4 -4
  17. data/lib/flapjack/cli/receiver.rb +122 -54
  18. data/lib/flapjack/cli/server.rb +0 -5
  19. data/lib/flapjack/coordinator.rb +6 -0
  20. data/lib/flapjack/data/contact.rb +10 -62
  21. data/lib/flapjack/data/entity.rb +36 -52
  22. data/lib/flapjack/data/entity_check.rb +90 -21
  23. data/lib/flapjack/data/event.rb +4 -5
  24. data/lib/flapjack/data/notification.rb +8 -10
  25. data/lib/flapjack/data/notification_rule.rb +32 -35
  26. data/lib/flapjack/data/tagged.rb +48 -0
  27. data/lib/flapjack/gateways/jabber.rb +4 -5
  28. data/lib/flapjack/gateways/jsonapi/check_methods.rb +45 -7
  29. data/lib/flapjack/gateways/jsonapi/check_presenter.rb +1 -1
  30. data/lib/flapjack/gateways/jsonapi/contact_methods.rb +8 -2
  31. data/lib/flapjack/gateways/jsonapi/entity_methods.rb +26 -8
  32. data/lib/flapjack/gateways/jsonapi/medium_methods.rb +13 -9
  33. data/lib/flapjack/gateways/jsonapi/metrics_methods.rb +2 -2
  34. data/lib/flapjack/gateways/jsonapi/notification_rule_methods.rb +1 -1
  35. data/lib/flapjack/gateways/jsonapi/pagerduty_credential_methods.rb +24 -17
  36. data/lib/flapjack/gateways/jsonapi/rack/json_params_parser.rb +1 -1
  37. data/lib/flapjack/gateways/jsonapi/report_methods.rb +4 -4
  38. data/lib/flapjack/gateways/jsonapi.rb +52 -31
  39. data/lib/flapjack/gateways/oobetet.rb +2 -3
  40. data/lib/flapjack/gateways/pagerduty.rb +9 -8
  41. data/lib/flapjack/gateways/web/public/js/backbone.jsonapi.js +19 -0
  42. data/lib/flapjack/gateways/web/public/js/flapjack.js +6 -2
  43. data/lib/flapjack/gateways/web/public/js/modules/contact.js +9 -14
  44. data/lib/flapjack/gateways/web/public/js/modules/medium.js +1 -0
  45. data/lib/flapjack/gateways/web/public/js/self_stats.js +1 -1
  46. data/lib/flapjack/gateways/web/views/edit_contacts.html.erb +3 -3
  47. data/lib/flapjack/gateways/web.rb +8 -7
  48. data/lib/flapjack/notifier.rb +2 -4
  49. data/lib/flapjack/processor.rb +2 -2
  50. data/lib/flapjack/version.rb +1 -1
  51. data/lib/flapjack.rb +10 -0
  52. data/spec/lib/flapjack/coordinator_spec.rb +18 -0
  53. data/spec/lib/flapjack/data/contact_spec.rb +4 -12
  54. data/spec/lib/flapjack/data/entity_check_spec.rb +56 -3
  55. data/spec/lib/flapjack/data/entity_spec.rb +79 -67
  56. data/spec/lib/flapjack/data/event_spec.rb +78 -78
  57. data/spec/lib/flapjack/data/notification_rule_spec.rb +4 -2
  58. data/spec/lib/flapjack/gateways/jsonapi/check_methods_spec.rb +94 -11
  59. data/spec/lib/flapjack/gateways/jsonapi/entity_methods_spec.rb +84 -0
  60. data/spec/lib/flapjack/gateways/pagerduty_spec.rb +5 -3
  61. data/spec/lib/flapjack/gateways/web_spec.rb +3 -3
  62. data/spec/service_consumers/pact_helper.rb +74 -0
  63. data/spec/service_consumers/pacts/flapjack-diner_v1.0.json +4522 -0
  64. data/spec/service_consumers/provider_states_for_flapjack-diner.rb +356 -0
  65. data/spec/spec_helper.rb +0 -8
  66. data/spec/support/jsonapi_helper.rb +1 -1
  67. data/tasks/benchmarks.rake +6 -3
  68. data/tasks/profile.rake +1 -1
  69. data/tmp/acknowledge.rb +0 -3
  70. data/tmp/create_event_ok.rb +0 -3
  71. data/tmp/create_event_unknown.rb +0 -3
  72. data/tmp/create_events_failure.rb +0 -3
  73. data/tmp/create_events_ok.rb +0 -3
  74. data/tmp/create_events_ok_fail_ack_ok.rb +0 -3
  75. data/tmp/create_events_ok_failure.rb +2 -5
  76. data/tmp/create_events_ok_failure_ack.rb +0 -3
  77. data/tmp/test_json_post.rb +4 -3
  78. data/tmp/test_notification_rules_api.rb +2 -3
  79. metadata +13 -8
  80. data/lib/flapjack/data/tag.rb +0 -61
  81. data/lib/flapjack/data/tag_set.rb +0 -16
  82. data/spec/lib/flapjack/data/tag_spec.rb +0 -36
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'oj'
4
-
5
3
  require 'flapjack/patches'
6
4
 
7
5
  require 'flapjack/data/contact'
8
6
  require 'flapjack/data/event'
9
7
  require 'flapjack/data/entity'
8
+ require 'flapjack/data/tagged'
9
+
10
10
  #FIXME: Require chronic_duration in the correct place
11
11
  require 'chronic_duration'
12
12
 
@@ -27,8 +27,37 @@ module Flapjack
27
27
  NOTIFICATION_STATES = [:problem, :warning, :critical, :unknown,
28
28
  :recovery, :acknowledgement]
29
29
 
30
+ include Tagged
31
+
30
32
  attr_accessor :entity, :check
31
33
 
34
+ def self.add(check_data, options = {})
35
+ raise "Redis connection not set" unless redis = options[:redis]
36
+
37
+ entity_id = check_data['entity_id']
38
+ raise "Entity id not provided" if entity_id.nil? || entity_id.empty?
39
+
40
+ check_name = check_data['name']
41
+ raise "Name not provided" if check_name.nil? || check_name.empty?
42
+
43
+ ent = Flapjack::Data::Entity.find_by_id(entity_id, :redis => redis)
44
+
45
+ raise "Entity not found for id '#{entity_id}'" if ent.nil?
46
+
47
+ logger = options[:logger]
48
+ timestamp = Time.now.to_i
49
+
50
+ redis.zadd("current_checks:#{ent.name}", timestamp, check_name)
51
+ redis.zadd('current_entities', timestamp, ent.name)
52
+
53
+ c = self.new(ent, check_name, :logger => logger,
54
+ :redis => redis)
55
+ if check_data['tags'] && check_data['tags'].respond_to?(:each)
56
+ c.add_tags(*check_data['tags'])
57
+ end
58
+ c
59
+ end
60
+
32
61
  def self.for_event_id(event_id, options = {})
33
62
  raise "Redis connection not set" unless redis = options[:redis]
34
63
  entity_name, check_name = event_id.split(':', 2)
@@ -36,6 +65,7 @@ module Flapjack
36
65
  logger = options[:logger]
37
66
  entity = Flapjack::Data::Entity.find_by_name(entity_name,
38
67
  :create => create_entity, :logger => logger, :redis => redis)
68
+ return if entity.nil?
39
69
  self.new(entity, check_name, :logger => logger, :redis => redis)
40
70
  end
41
71
 
@@ -65,23 +95,30 @@ module Flapjack
65
95
 
66
96
  def self.all(options = {})
67
97
  raise "Redis connection not set" unless redis = options[:redis]
68
- checks = redis.keys('check:*').map {|c| c.match(/^check:(.*)$/) ; $1}
98
+ checks = redis.keys('check:*').map {|c| c.match(/^check:(.+)$/) ; $1} |
99
+ find_current_names(:redis => redis)
69
100
  checks.map {|ec|
70
- self.for_entity_id(ec, options)
101
+ self.for_event_id(ec, options)
71
102
  }
72
103
  end
73
104
 
74
- def self.find_current_for_entity_name(entity_name, options = {})
105
+ def self.find_all_names_for_entity_name(entity_name, options = {})
106
+ raise "Redis connection not set" unless redis = options[:redis]
107
+ en = Regexp.escape(entity_name)
108
+ redis.keys('check:*').map {|c| c.match(/^check:#{en}:(.+)$/); $1}
109
+ end
110
+
111
+ def self.find_current_names_for_entity_name(entity_name, options = {})
75
112
  raise "Redis connection not set" unless redis = options[:redis]
76
113
  redis.zrange("current_checks:#{entity_name}", 0, -1)
77
114
  end
78
115
 
79
- def self.find_current(options = {})
116
+ def self.find_current_names(options = {})
80
117
  raise "Redis connection not set" unless redis = options[:redis]
81
- self.conflate_to_keys(self.find_current_by_entity(:redis => redis))
118
+ self.conflate_to_keys(self.find_current_names_by_entity(:redis => redis))
82
119
  end
83
120
 
84
- def self.find_current_by_entity(options = {})
121
+ def self.find_current_names_by_entity(options = {})
85
122
  raise "Redis connection not set" unless redis = options[:redis]
86
123
  d = {}
87
124
  redis.zrange("current_entities", 0, -1).each {|entity|
@@ -97,12 +134,12 @@ module Flapjack
97
134
  }
98
135
  end
99
136
 
100
- def self.find_current_failing(options = {})
137
+ def self.find_current_names_failing(options = {})
101
138
  raise "Redis connection not set" unless redis = options[:redis]
102
- self.conflate_to_keys(self.find_current_failing_by_entity(:redis => redis))
139
+ self.conflate_to_keys(self.find_current_names_failing_by_entity(:redis => redis))
103
140
  end
104
141
 
105
- def self.find_current_failing_by_entity(options = {})
142
+ def self.find_current_names_failing_by_entity(options = {})
106
143
  raise "Redis connection not set" unless redis = options[:redis]
107
144
  redis.zrange("failed_checks", 0, -1).inject({}) do |memo, key|
108
145
  entity, check = key.split(':', 2)
@@ -642,13 +679,21 @@ module Flapjack
642
679
  # disables a check (removes currency)
643
680
  def disable!
644
681
  @logger.debug("disabling check [#{@key}]") if @logger
645
- @redis.zrem("current_checks:#{entity.name}", check)
646
- if @redis.zcount("current_checks:#{entity.name}", '-inf', '+inf') == 0
647
- @redis.zrem("current_checks:#{entity.name}", check)
682
+ entity_name = entity.name
683
+ @redis.zrem("current_checks:#{entity_name}", check)
684
+ if @redis.zcount("current_checks:#{entity_name}", '-inf', '+inf') == 0
685
+ @redis.zrem("current_checks:#{entity_name}", check)
648
686
  @redis.zrem("current_entities", entity.name)
649
687
  end
650
688
  end
651
689
 
690
+ def enable!
691
+ timestamp = Time.now.to_i
692
+ entity_name = entity.name
693
+ redis.zadd("current_checks:#{entity_name}", timestamp, check)
694
+ redis.zadd('current_entities', timestamp, entity_name)
695
+ end
696
+
652
697
  def enabled?
653
698
  !!@redis.zscore("current_checks:#{entity.name}", check)
654
699
  end
@@ -855,13 +900,24 @@ module Flapjack
855
900
  }.compact
856
901
  end
857
902
 
858
- def tags
859
- entity, check = @key.split(":", 2)
860
- ta = Flapjack::Data::TagSet.new([])
861
- ta += entity.split('.', 2).map {|x| x.downcase}
862
- ta += check.split(' ').map {|x| x.downcase}
903
+ # override default, which would be 'entity_check_tag'
904
+ def tag_prefix
905
+ 'check_tag'
906
+ end
907
+
908
+ def tags_with_entity_and_check_name
909
+ tags_without_entity_and_check_name
910
+
911
+ # ensure that returned tags include split entity and check words
912
+ @tags += @entity.name.split('.', 2).map {|x| x.downcase} +
913
+ @check.split(' ').map {|x| x.downcase}
914
+
915
+ @tags
863
916
  end
864
917
 
918
+ alias_method :tags_without_entity_and_check_name, :tags
919
+ alias_method :tags, :tags_with_entity_and_check_name
920
+
865
921
  def ack_hash
866
922
  @ack_hash ||= @redis.hget('check_hashes_by_id', @key)
867
923
  if @ack_hash.nil?
@@ -905,14 +961,27 @@ module Flapjack
905
961
  purge_stamps.length
906
962
  end
907
963
 
964
+ def self.enabled_for(check_ids, opts = {})
965
+ raise "Redis connection not set" unless redis = opts[:redis]
966
+
967
+ check_ids.inject([]) do |memo, check_id|
968
+ entity_name, check_name = check_id.split(':', 2)
969
+ memo << check_id unless redis.zscore("current_checks:#{entity_name}", check_name).nil?
970
+ memo
971
+ end
972
+ end
973
+
908
974
  def to_jsonapi(opts = {})
909
- {
975
+ json_data = {
976
+ "id" => @key,
910
977
  "name" => @check,
911
978
  "entity_name" => @entity.name,
979
+ "enabled" => opts[:enabled].is_a?(TrueClass),
912
980
  "links" => {
913
981
  :entities => opts[:entity_ids] || [],
914
982
  }
915
- }.to_json
983
+ }
984
+ Flapjack.dump_json(json_data)
916
985
  end
917
986
 
918
987
  private
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'oj'
4
- require 'flapjack/data/tag_set'
3
+ require 'flapjack'
5
4
 
6
5
  module Flapjack
7
6
  module Data
@@ -120,7 +119,7 @@ module Flapjack
120
119
 
121
120
  def self.parse_and_validate(raw, opts = {})
122
121
  errors = []
123
- if parsed = ::Oj.load(raw)
122
+ if parsed = ::Flapjack.load_json(raw)
124
123
  if parsed.is_a?(Hash)
125
124
  errors = validation_errors_for_hash(parsed, opts)
126
125
  else
@@ -177,7 +176,7 @@ module Flapjack
177
176
  raise "Redis connection not set" unless redis = opts[:redis]
178
177
 
179
178
  evt['time'] = Time.now.to_i if evt['time'].nil?
180
- redis.lpush('events', ::Oj.dump(evt))
179
+ redis.lpush('events', ::Flapjack.dump_json(evt))
181
180
  end
182
181
 
183
182
  # Provide a count of the number of events on the queue to be processed.
@@ -220,7 +219,7 @@ module Flapjack
220
219
  # perfdata is optional. set it to nil if it only contains whitespace
221
220
  @perfdata = (@perfdata.is_a?(String) && ! @perfdata.strip.empty?) ? @perfdata.strip : nil
222
221
  if attrs['tags']
223
- @tags = Flapjack::Data::TagSet.new
222
+ @tags = Set.new
224
223
  attrs['tags'].each {|tag| @tags.add(tag)}
225
224
  end
226
225
  end
@@ -1,7 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'oj'
4
-
5
3
  require 'flapjack/data/contact'
6
4
  require 'flapjack/data/event'
7
5
  require 'flapjack/data/message'
@@ -66,7 +64,7 @@ module Flapjack
66
64
  'severity' => opts[:severity],
67
65
  'tags' => tag_data }
68
66
 
69
- redis.rpush(queue, Oj.dump(notif))
67
+ redis.rpush(queue, Flapjack.dump_json(notif))
70
68
  end
71
69
 
72
70
  def self.next(queue, opts = {})
@@ -82,7 +80,7 @@ module Flapjack
82
80
  return unless raw
83
81
  end
84
82
  begin
85
- parsed = ::Oj.load( raw )
83
+ parsed = ::Flapjack.load_json( raw )
86
84
  rescue Oj::Error => e
87
85
  if options[:logger]
88
86
  options[:logger].warn("Error deserialising notification json: #{e}, raw json: #{raw.inspect}")
@@ -133,7 +131,7 @@ module Flapjack
133
131
  media = contact.media
134
132
 
135
133
  logger.debug "Notification#messages: creating messages for contact: #{contact_id} " +
136
- "event_id: \"#{@event_id}\" state: #{@state} event_tags: #{@tags.to_json} media: #{media.inspect}"
134
+ "event_id: \"#{@event_id}\" state: #{@state} event_tags: #{Flapjack.dump_json(@tags)} media: #{media.inspect}"
137
135
  rlen = rules.length
138
136
  logger.debug "found #{rlen} rule#{(rlen == 1) ? '' : 's'} for contact #{contact_id}"
139
137
 
@@ -144,7 +142,7 @@ module Flapjack
144
142
  # for time, entity and tags
145
143
  matchers = rules.select do |rule|
146
144
  logger.debug("considering rule with entities: #{rule.entities}, entities regex: #{rule.regex_entities},
147
- tags: #{rule.tags.to_json} and regex tags: #{rule.regex_tags.to_json}")
145
+ tags: #{Flapjack.dump_json(rule.tags)} and regex tags: #{Flapjack.dump_json(rule.regex_tags)}")
148
146
  rule_has_tags = rule.tags ? (rule.tags.length > 0) : false
149
147
  rule_has_regex_tags = rule.regex_tags ? (rule.regex_tags.length > 0) : false
150
148
  rule_has_entities = rule.entities ? (rule.entities.length > 0) : false
@@ -162,7 +160,7 @@ module Flapjack
162
160
 
163
161
  logger.debug "#{matchers.length} matchers remain for this contact after time, entity and tags are matched:"
164
162
  matchers.each do |matcher|
165
- logger.debug " - #{matcher.to_json}"
163
+ logger.debug " - #{matcher.to_jsonapi}"
166
164
  end
167
165
 
168
166
  # delete any general matchers if there are more specific matchers left
@@ -175,7 +173,7 @@ module Flapjack
175
173
  if num_matchers != matchers.length
176
174
  logger.debug("removal of general matchers when entity specific matchers are present: number of matchers changed from #{num_matchers} to #{matchers.length} for contact id: #{contact_id}")
177
175
  matchers.each do |matcher|
178
- logger.debug " - #{matcher.to_json}"
176
+ logger.debug " - #{matcher.to_jsonapi}"
179
177
  end
180
178
  end
181
179
  end
@@ -185,7 +183,7 @@ module Flapjack
185
183
  if blackhole_matchers.length > 0
186
184
  logger.debug "dropping this media as #{blackhole_matchers.length} blackhole matchers are present:"
187
185
  blackhole_matchers.each {|bm|
188
- logger.debug " - #{bm.to_json}"
186
+ logger.debug " - #{bm.to_jsonapi}"
189
187
  }
190
188
  next
191
189
  else
@@ -264,7 +262,7 @@ module Flapjack
264
262
  @state_duration = opts['state_duration']
265
263
  @type = opts['type']
266
264
  @severity = opts['severity']
267
- @tags = opts['tags'].is_a?(Array) ? Flapjack::Data::TagSet.new(opts['tags']) : nil
265
+ @tags = opts['tags'].is_a?(Array) ? Set.new(opts['tags']) : nil
268
266
  end
269
267
 
270
268
  # # time restrictions match?
@@ -1,10 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'oj'
4
3
  require 'active_support/time'
5
4
  require 'ice_cube'
6
5
  require 'flapjack/utility'
7
- require 'flapjack/data/tag_set'
8
6
 
9
7
  module Flapjack
10
8
  module Data
@@ -108,20 +106,19 @@ module Flapjack
108
106
  nil
109
107
  end
110
108
 
111
- def to_json(*args)
112
- self.class.hashify(:id, :contact_id, :tags, :regex_tags, :entities, :regex_entities,
113
- :time_restrictions, :unknown_media, :warning_media, :critical_media,
114
- :unknown_blackhole, :warning_blackhole, :critical_blackhole) {|k|
115
- [k, self.send(k)]
116
- }.to_json
117
- end
118
-
119
109
  def to_jsonapi(opts = {})
120
- self.class.hashify(:id, :tags, :regex_tags, :entities, :regex_entities,
110
+ json_data = self.class.hashify(:id, :tags, :regex_tags, :entities, :regex_entities,
121
111
  :time_restrictions, :unknown_media, :warning_media, :critical_media,
122
112
  :unknown_blackhole, :warning_blackhole, :critical_blackhole) {|k|
123
- [k, self.send(k)]
124
- }.merge(:links => {:contacts => [self.contact_id]}).to_json
113
+ case k
114
+ when :tags, :regex_tags
115
+ [k.to_s, self.send(k).to_a.sort]
116
+ else
117
+ [k.to_s, self.send(k)]
118
+ end
119
+ }.merge('links' => {'contacts' => [self.contact_id]})
120
+
121
+ Flapjack.dump_json(json_data)
125
122
  end
126
123
 
127
124
  # entity names match?
@@ -203,10 +200,10 @@ module Flapjack
203
200
  rule_data[:warning_blackhole] = rule_data[:warning_blackhole] || false
204
201
  rule_data[:critical_blackhole] = rule_data[:critical_blackhole] || false
205
202
  if rule_data[:tags].is_a?(Array)
206
- rule_data[:tags] = Flapjack::Data::TagSet.new(rule_data[:tags])
203
+ rule_data[:tags] = Set.new(rule_data[:tags])
207
204
  end
208
205
  if rule_data[:regex_tags].is_a?(Array)
209
- rule_data[:regex_tags] = Flapjack::Data::TagSet.new(rule_data[:regex_tags])
206
+ rule_data[:regex_tags] = Set.new(rule_data[:regex_tags])
210
207
  end
211
208
  rule_data
212
209
  end
@@ -227,14 +224,14 @@ module Flapjack
227
224
  json_rule_data = {
228
225
  :id => rule_data[:id].to_s,
229
226
  :contact_id => rule_data[:contact_id].to_s,
230
- :entities => Oj.dump(rule_data[:entities]),
231
- :regex_entities => Oj.dump(rule_data[:regex_entities]),
232
- :tags => Oj.dump(tag_data),
233
- :regex_tags => Oj.dump(regex_tag_data),
234
- :time_restrictions => Oj.dump(rule_data[:time_restrictions], :mode => :compat),
235
- :unknown_media => Oj.dump(rule_data[:unknown_media]),
236
- :warning_media => Oj.dump(rule_data[:warning_media]),
237
- :critical_media => Oj.dump(rule_data[:critical_media]),
227
+ :entities => Flapjack.dump_json(rule_data[:entities]),
228
+ :regex_entities => Flapjack.dump_json(rule_data[:regex_entities]),
229
+ :tags => Flapjack.dump_json(tag_data),
230
+ :regex_tags => Flapjack.dump_json(regex_tag_data),
231
+ :time_restrictions => Flapjack.dump_json(rule_data[:time_restrictions]),
232
+ :unknown_media => Flapjack.dump_json(rule_data[:unknown_media]),
233
+ :warning_media => Flapjack.dump_json(rule_data[:warning_media]),
234
+ :critical_media => Flapjack.dump_json(rule_data[:critical_media]),
238
235
  :unknown_blackhole => rule_data[:unknown_blackhole],
239
236
  :warning_blackhole => rule_data[:warning_blackhole],
240
237
  :critical_blackhole => rule_data[:critical_blackhole],
@@ -323,13 +320,13 @@ module Flapjack
323
320
 
324
321
  proc {|d| !d.has_key?(:tags) ||
325
322
  ( d[:tags].nil? ||
326
- d[:tags].is_a?(Flapjack::Data::TagSet) &&
323
+ d[:tags].is_a?(Set) &&
327
324
  d[:tags].all? {|et| et.is_a?(String)} ) } =>
328
325
  "tags must be a tag_set of strings",
329
326
 
330
327
  proc {|d| !d.has_key?(:regex_tags) ||
331
328
  ( d[:regex_tags].nil? ||
332
- d[:regex_tags].is_a?(Flapjack::Data::TagSet) &&
329
+ d[:regex_tags].is_a?(Set) &&
333
330
  d[:regex_tags].all? {|et| et.is_a?(String)} ) } =>
334
331
  "regex_tags must be a tag_set of strings",
335
332
 
@@ -400,16 +397,16 @@ module Flapjack
400
397
  rule_data = @redis.hgetall("notification_rule:#{@id}")
401
398
 
402
399
  @contact_id = rule_data['contact_id']
403
- tags = Oj.load(rule_data['tags'] || '')
404
- @tags = tags ? Flapjack::Data::TagSet.new(tags) : nil
405
- regex_tags = Oj.load(rule_data['regex_tags'] || '')
406
- @regex_tags = regex_tags ? Flapjack::Data::TagSet.new(regex_tags) : nil
407
- @entities = Oj.load(rule_data['entities'] || '')
408
- @regex_entities = Oj.load(rule_data['regex_entities'] || '')
409
- @time_restrictions = Oj.load(rule_data['time_restrictions'] || '')
410
- @unknown_media = Oj.load(rule_data['unknown_media'] || '')
411
- @warning_media = Oj.load(rule_data['warning_media'] || '')
412
- @critical_media = Oj.load(rule_data['critical_media'] || '')
400
+ tags = Flapjack.load_json(rule_data['tags'] || '')
401
+ @tags = tags ? Set.new(tags) : nil
402
+ regex_tags = Flapjack.load_json(rule_data['regex_tags'] || '')
403
+ @regex_tags = regex_tags ? Set.new(regex_tags) : nil
404
+ @entities = Flapjack.load_json(rule_data['entities'] || '')
405
+ @regex_entities = Flapjack.load_json(rule_data['regex_entities'] || '')
406
+ @time_restrictions = Flapjack.load_json(rule_data['time_restrictions'] || '')
407
+ @unknown_media = Flapjack.load_json(rule_data['unknown_media'] || '')
408
+ @warning_media = Flapjack.load_json(rule_data['warning_media'] || '')
409
+ @critical_media = Flapjack.load_json(rule_data['critical_media'] || '')
413
410
  @unknown_blackhole = ((rule_data['unknown_blackhole'] || 'false').downcase == 'true')
414
411
  @warning_blackhole = ((rule_data['warning_blackhole'] || 'false').downcase == 'true')
415
412
  @critical_blackhole = ((rule_data['critical_blackhole'] || 'false').downcase == 'true')
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # mixed in to some of the Flapjack data classes
4
+
5
+ module Flapjack
6
+
7
+ module Tagged
8
+
9
+ def tag_prefix
10
+ self.class.name.split('::').last.downcase + '_tag'
11
+ end
12
+
13
+ def known_key
14
+ "known_tags:#{tag_prefix}"
15
+ end
16
+
17
+ # return the set of tags for this object
18
+ def tags
19
+ return @tags unless @tags.nil?
20
+ @tags ||= Set.new( @redis.smembers(known_key).inject([]) {|memo, t|
21
+ tag_key = "#{tag_prefix}:#{t}"
22
+ memo << t if @redis.sismember(tag_key, @id.to_s)
23
+ memo
24
+ })
25
+ end
26
+
27
+ # adds tags to this object
28
+ def add_tags(*enum)
29
+ enum.each do |t|
30
+ @redis.sadd(known_key, t)
31
+ @redis.sadd("#{tag_prefix}:#{t}", @id)
32
+ tags.add(t)
33
+ end
34
+ end
35
+
36
+ # removes tags from this object
37
+ def delete_tags(*enum)
38
+ enum.each do |t|
39
+ tag_key = "#{tag_prefix}:#{t}"
40
+ @redis.srem(tag_key, @id)
41
+ tags.delete(t)
42
+ @redis.srem(known_key, t) if (@redis.scard(tag_key) == 0)
43
+ end
44
+ end
45
+
46
+ end
47
+
48
+ end
@@ -6,7 +6,6 @@ require 'socket'
6
6
 
7
7
  require 'blather/client/client'
8
8
  require 'chronic_duration'
9
- require 'oj'
10
9
 
11
10
  require 'flapjack/data/entity_check'
12
11
  require 'flapjack/redis_pool'
@@ -69,7 +68,7 @@ module Flapjack
69
68
  redis_uri = @redis_config[:path] ||
70
69
  "redis://#{@redis_config[:host] || '127.0.0.1'}:#{@redis_config[:port] || '6379'}/#{@redis_config[:db] || '0'}"
71
70
  shutdown_redis = EM::Hiredis.connect(redis_uri)
72
- shutdown_redis.rpush(@config['queue'], Oj.dump('notification_type' => 'shutdown'))
71
+ shutdown_redis.rpush(@config['queue'], Flapjack.dump_json('notification_type' => 'shutdown'))
73
72
  end
74
73
 
75
74
  def setup
@@ -398,7 +397,7 @@ module Flapjack
398
397
  when number_found == 0
399
398
  msg = "found no entities matching /#{entity_pattern}/"
400
399
  when number_found >= 1
401
- failing_list = Flapjack::Data::EntityCheck.find_current_failing_by_entity(:redis => @redis)
400
+ failing_list = Flapjack::Data::EntityCheck.find_current_names_failing_by_entity(:redis => @redis)
402
401
  entities = failing_list.select {|k,v| v.count >= 1 && entity_list.include?(k) }
403
402
  if entities.length >= 1
404
403
  entities.each_pair do |entity,check_list|
@@ -474,7 +473,7 @@ module Flapjack
474
473
  msg = "found no entities matching /#{entity_pattern}/"
475
474
  when number_found >= 1
476
475
 
477
- failing_list = Flapjack::Data::EntityCheck.find_current_failing_by_entity(:redis => @redis)
476
+ failing_list = Flapjack::Data::EntityCheck.find_current_names_failing_by_entity(:redis => @redis)
478
477
 
479
478
  my_failing_checks = Hash[failing_list.map do |k,v|
480
479
  if entity_list.include?(k)
@@ -695,7 +694,7 @@ module Flapjack
695
694
  events[queue] = @redis.blpop(queue, 0)
696
695
  event_json = events[queue][1]
697
696
  begin
698
- event = Oj.load(event_json)
697
+ event = Flapjack.load_json(event_json)
699
698
 
700
699
  @logger.debug('jabber notification event received: ' + event.inspect)
701
700
 
@@ -43,34 +43,72 @@ module Flapjack
43
43
  Flapjack::Data::EntityCheck.for_event_id(req_check, :logger => logger, :redis => redis)
44
44
  end
45
45
  else
46
- Flapjack::Data::EntityCheck.find_current(:redis => redis)
46
+ Flapjack::Data::EntityCheck.all(:logger => logger, :redis => redis)
47
47
  end
48
48
  checks.compact!
49
49
 
50
50
  if requested_checks && checks.empty?
51
- raise Flapjack::Gateways::JSONAPI::ChecksNotFound.new(requested_checks)
51
+ raise Flapjack::Gateways::JSONAPI::EntityChecksNotFound.new(requested_checks)
52
52
  end
53
53
 
54
+ check_ids = checks.collect {|c| "#{c.entity.name}:#{c.check}" }
55
+
56
+ enabled_ids = Flapjack::Data::EntityCheck.enabled_for(check_ids, :redis => redis)
57
+
54
58
  linked_entity_ids = checks.empty? ? [] : checks.inject({}) do |memo, check|
55
- memo[check.key] = check.entity.id
59
+ entity = check.entity
60
+ memo["#{entity.name}:#{check.check}"] = [entity.id]
56
61
  memo
57
62
  end
58
63
 
59
64
  checks_json = checks.collect {|check|
60
- check.to_jsonapi(:entity_ids => linked_entity_ids[check.key])
65
+ check_name = "#{check.entity.name}:#{check.check}"
66
+ check.to_jsonapi(:enabled => enabled_ids.include?(check_name),
67
+ :entity_ids => linked_entity_ids[check_name])
61
68
  }.join(",")
62
69
 
63
70
  '{"checks":[' + checks_json + ']}'
64
71
  end
65
72
 
73
+ app.post '/checks' do
74
+ checks = wrapped_params('checks')
75
+
76
+ check_names = checks.collect{|check_data|
77
+ check = Flapjack::Data::EntityCheck.add(check_data, :redis => redis)
78
+ "#{check.entity.name}:#{check.check}"
79
+ }
80
+
81
+ response.headers['Location'] = "#{request.base_url}/checks/#{check_names.join(',')}"
82
+ status 201
83
+ Flapjack.dump_json(check_names)
84
+ end
85
+
66
86
  app.patch %r{^/checks/(.+)$} do
67
87
  checks_for_check_names(params[:captures][0].split(',')).each do |check|
68
88
  apply_json_patch('checks') do |op, property, linked, value|
69
89
  case op
70
90
  when 'replace'
71
- if ['enabled'].include?(property)
72
- # explicitly checking for false being passed in
73
- check.disable! if value.is_a?(FalseClass)
91
+ case property
92
+ when 'enabled'
93
+ # explicitly checking for true/false being passed in
94
+ case value
95
+ when TrueClass
96
+ check.enable!
97
+ when FalseClass
98
+ check.disable!
99
+ end
100
+ end
101
+ when 'add'
102
+ case linked
103
+ when 'tags'
104
+ value.respond_to?(:each) ? check.add_tags(*value) :
105
+ check.add_tags(value)
106
+ end
107
+ when 'remove'
108
+ case linked
109
+ when 'tags'
110
+ value.respond_to?(:each) ? check.delete_tags(*value) :
111
+ check.delete_tags(value)
74
112
  end
75
113
  end
76
114
  end
@@ -36,7 +36,7 @@ module Flapjack
36
36
  def outage(start_time, end_time, options = {})
37
37
  # hist_states is an array of hashes, with [state, timestamp, summary] keys
38
38
  hist_states = @entity_check.historical_states(start_time, end_time)
39
- return hist_states if hist_states.empty?
39
+ return {:outages => []} if hist_states.empty?
40
40
 
41
41
  initial = @entity_check.historical_state_before(hist_states.first[:timestamp])
42
42
  hist_states.unshift(initial) if initial
@@ -45,7 +45,7 @@ module Flapjack
45
45
  missing_ids = contacts_by_id.select {|k, v| v.nil? }.keys
46
46
  unless missing_ids.empty?
47
47
  semaphore.release
48
- halt(404, "Contacts with ids #{missing_ids.join(', ')} were not found")
48
+ raise Flapjack::Gateways::JSONAPI::ContactsNotFound.new(missing_ids)
49
49
  end
50
50
 
51
51
  block.call(contacts_by_id.select {|k, v| !v.nil? }.values)
@@ -93,7 +93,7 @@ module Flapjack
93
93
 
94
94
  response.headers['Location'] = "#{base_url}/contacts/#{contact_ids.join(',')}"
95
95
  status 201
96
- contact_ids.to_json
96
+ Flapjack.dump_json(contact_ids)
97
97
  end
98
98
 
99
99
  # Returns all (/contacts) or some (/contacts/1,2,3) or one (/contacts/2) contact(s)
@@ -144,6 +144,9 @@ module Flapjack
144
144
  unless notification_rule.nil?
145
145
  contact.grab_notification_rule(notification_rule)
146
146
  end
147
+ when 'tags'
148
+ value.respond_to?(:each) ? contact.add_tags(*value) :
149
+ contact.add_tags(value)
147
150
  # when 'media' # not supported yet due to id brokenness
148
151
  end
149
152
  when 'remove'
@@ -156,6 +159,9 @@ module Flapjack
156
159
  unless notification_rule.nil?
157
160
  contact.delete_notification_rule(notification_rule)
158
161
  end
162
+ when 'tags'
163
+ value.respond_to?(:each) ? contact.delete_tags(*value) :
164
+ contact.delete_tags(value)
159
165
  # when 'media' # not supported yet due to id brokenness
160
166
  end
161
167
  end