flapjack 0.8.10 → 0.8.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/Gemfile +1 -1
  4. data/bin/flapjack +10 -1
  5. data/bin/flapjack-nagios-receiver +1 -2
  6. data/bin/simulate-failed-check +12 -4
  7. data/etc/flapjack_config.yaml.example +2 -1
  8. data/flapjack.gemspec +1 -0
  9. data/lib/flapjack/data/contact.rb +46 -26
  10. data/lib/flapjack/data/entity.rb +28 -0
  11. data/lib/flapjack/data/entity_check.rb +52 -11
  12. data/lib/flapjack/data/event.rb +9 -3
  13. data/lib/flapjack/data/notification_rule.rb +8 -0
  14. data/lib/flapjack/gateways/api.rb +0 -1
  15. data/lib/flapjack/gateways/api/entity_check_presenter.rb +2 -1
  16. data/lib/flapjack/gateways/email.rb +1 -2
  17. data/lib/flapjack/gateways/jabber.rb +3 -3
  18. data/lib/flapjack/gateways/jsonapi.rb +186 -38
  19. data/lib/flapjack/gateways/jsonapi/check_methods.rb +120 -0
  20. data/lib/flapjack/gateways/jsonapi/{entity_check_presenter.rb → check_presenter.rb} +7 -6
  21. data/lib/flapjack/gateways/jsonapi/contact_methods.rb +61 -352
  22. data/lib/flapjack/gateways/jsonapi/entity_methods.rb +117 -248
  23. data/lib/flapjack/gateways/jsonapi/medium_methods.rb +179 -0
  24. data/lib/flapjack/gateways/jsonapi/notification_rule_methods.rb +124 -0
  25. data/lib/flapjack/gateways/jsonapi/pagerduty_credential_methods.rb +128 -0
  26. data/lib/flapjack/gateways/jsonapi/rack/json_params_parser.rb +4 -5
  27. data/lib/flapjack/gateways/jsonapi/report_methods.rb +143 -0
  28. data/lib/flapjack/gateways/web.rb +1 -0
  29. data/lib/flapjack/gateways/web/public/js/backbone.jsonapi.js +165 -101
  30. data/lib/flapjack/gateways/web/public/js/contacts.js +34 -46
  31. data/lib/flapjack/gateways/web/public/js/select2.js +232 -90
  32. data/lib/flapjack/gateways/web/public/js/select2.min.js +4 -4
  33. data/lib/flapjack/gateways/web/views/check.html.erb +11 -2
  34. data/lib/flapjack/processor.rb +6 -6
  35. data/lib/flapjack/version.rb +1 -1
  36. data/spec/lib/flapjack/data/entity_check_spec.rb +1 -1
  37. data/spec/lib/flapjack/data/event_spec.rb +10 -9
  38. data/spec/lib/flapjack/gateways/api/entity_methods_spec.rb +25 -25
  39. data/spec/lib/flapjack/gateways/api_spec.rb +23 -1
  40. data/spec/lib/flapjack/gateways/email_spec.rb +40 -2
  41. data/spec/lib/flapjack/gateways/jabber_spec.rb +1 -1
  42. data/spec/lib/flapjack/gateways/jsonapi/check_methods_spec.rb +134 -0
  43. data/spec/lib/flapjack/gateways/jsonapi/{entity_check_presenter_spec.rb → check_presenter_spec.rb} +17 -17
  44. data/spec/lib/flapjack/gateways/jsonapi/contact_methods_spec.rb +27 -232
  45. data/spec/lib/flapjack/gateways/jsonapi/entity_methods_spec.rb +217 -687
  46. data/spec/lib/flapjack/gateways/jsonapi/medium_methods_spec.rb +232 -0
  47. data/spec/lib/flapjack/gateways/jsonapi/notification_rule_methods_spec.rb +131 -0
  48. data/spec/lib/flapjack/gateways/jsonapi/pagerduty_credential_methods_spec.rb +113 -0
  49. data/spec/lib/flapjack/gateways/jsonapi/report_methods_spec.rb +546 -0
  50. data/spec/lib/flapjack/gateways/jsonapi_spec.rb +10 -1
  51. data/spec/lib/flapjack/gateways/web_spec.rb +1 -0
  52. data/spec/support/jsonapi_helper.rb +62 -0
  53. metadata +36 -8
  54. data/lib/flapjack/gateways/jsonapi/entity_presenter.rb +0 -75
  55. data/spec/lib/flapjack/gateways/jsonapi/entity_presenter_spec.rb +0 -108
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: de195d7c1f069b7e1817c654b51315c55df5ec37
4
- data.tar.gz: b6f8b5059820e001df53e1a7f3ef6fdbd49c90cb
3
+ metadata.gz: 20501028ceb91b915d143e9d9343fae832db8e1c
4
+ data.tar.gz: b2d975287acd597e04f148f589c76ed067832665
5
5
  SHA512:
6
- metadata.gz: e0131f314a1c13869035a9ec07cbca94bd8a79b56af800b7183291e50fade89bccd023223ccf2b7037d0c8bab9fc20ca25aec348e0e0ca8baf371071f530ad47
7
- data.tar.gz: 9710ad57f6ced3f0b4f276690dce38e5accd4361613ef3944811893d7faa46c5871e92e07da2cd30428d7a85cfc2b3d7887067c9b1f068565fc577410c7538c6
6
+ metadata.gz: 051d4c2f9f58b9b57a45105a10c7cd64353c2a3c485b53a57a8bcc54f7546ed36058a50dfcb52a663413ffc2f8d5c955651a5cb554a48ea276b073eb93345409
7
+ data.tar.gz: ea18f80815f9172ca9a0f3f2021934b9f38d5da6cec030c0f27ad493dec8178ef40120f3004a898adfe50f6a1df22b0ac89c45a8a6492110694f02d2c5986e8f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  ## Flapjack Changelog
2
2
 
3
+ # 0.8.11 - 2014-05-01
4
+ - Feature: simulate-failed-check - give -t a default of 45 seconds (@jessereynolds)
5
+ - Feature: allow email messages to have custom from address #468 (@mattdelves)
6
+ - Feature: jabber - The reply message should only include the filtered list of entity checks #472 (@someword)
7
+ - Feature: Add perfdata handling #471 (@mattdelves)
8
+ - Feature: jsonapi featureset reaches critical mass #474 (@ali-graham)
9
+ - Feature: Add rbtrace command line option for profiling #479 (@auxesis)
10
+ - Bug: ack'ing via jabber fails #480 (@jessereynolds)
11
+
3
12
  # 0.8.10 - 2014-04-28
4
13
  - Feature: Add regex entities to notification rules #463 (@jswoods)
5
14
  - Bug: oobetet gateway exception sending pagerduty event #464 (@jessereynolds)
data/Gemfile CHANGED
@@ -11,7 +11,7 @@ group :test do
11
11
  gem 'cucumber'
12
12
  gem 'delorean'
13
13
  gem 'rack-test'
14
- gem 'async_rack_test'
14
+ gem 'async_rack_test', '>= 0.0.5'
15
15
  gem 'resque_spec'
16
16
  gem 'webmock'
17
17
  gem 'guard'
data/bin/flapjack CHANGED
@@ -53,10 +53,18 @@ optparse = OptionParser.new do |opts|
53
53
  options.config = c
54
54
  end
55
55
 
56
+ opts.on("-n", "--environment [ENV]", String, "Environment to boot") do |e|
57
+ options.environment = e
58
+ end
59
+
56
60
  opts.on("-d", "--[no-]daemonize", "Daemonize?") do |d|
57
61
  options.daemonize = d
58
62
  end
59
63
 
64
+ opts.on('-r', '--rbtrace', 'Enable rbtrace profiling') do
65
+ require 'rbtrace'
66
+ end
67
+
60
68
  opts.on("-p", "--pidfile [PATH]", String, "PATH to the pidfile to write to") do |pid|
61
69
  options.pidfile = pid
62
70
  end
@@ -88,7 +96,7 @@ elsif !["start", "stop", "restart", "reload", "status"].include?(ARGV[0])
88
96
  exit 1
89
97
  end
90
98
 
91
- FLAPJACK_ENV = ENV['FLAPJACK_ENV'] || 'production'
99
+ FLAPJACK_ENV = options.environment || ENV['FLAPJACK_ENV'] || 'production'
92
100
 
93
101
  config = Flapjack::Configuration.new
94
102
  config.load(options.config)
@@ -158,6 +166,7 @@ when "start"
158
166
  puts "Flapjack is already running."
159
167
  else
160
168
  print "Flapjack starting..."
169
+ print "\n" unless daemonize
161
170
  return_value = nil
162
171
  runner.execute(:daemonize => daemonize) {
163
172
  return_value = flapjack_coord.call
@@ -59,6 +59,7 @@ def process_input(opts)
59
59
  'state' => state,
60
60
  'summary' => check_output,
61
61
  'details' => details,
62
+ 'perfdata' => check_perfdata,
62
63
  'time' => timestamp,
63
64
  }
64
65
  Flapjack::Data::Event.add(event, :redis => redis)
@@ -243,5 +244,3 @@ else
243
244
  exit 1
244
245
 
245
246
  end
246
-
247
-
@@ -26,7 +26,7 @@ end
26
26
 
27
27
  def fail(opts)
28
28
  redis = Redis.new(opts[:redis_options])
29
- stop_after = opts[:minutes].to_i * 60
29
+ stop_after = (opts[:minutes] * 60).to_i
30
30
  recover = opts[:recover]
31
31
  state = opts[:state] || 'critical'
32
32
  event = {
@@ -82,8 +82,12 @@ optparse = OptionParser.new do |opts|
82
82
  options.config = c
83
83
  end
84
84
 
85
- opts.on("-t", "--time MINUTES", String, "MINUTES to generate failure events for") do |t|
86
- options.minutes = t
85
+ opts.on("-n", "--environment [ENV]", String, "Environment to boot") do |e|
86
+ options.environment = e
87
+ end
88
+
89
+ opts.on("-t", "--time MINUTES", String, "MINUTES to generate failure events for (0.75)") do |t|
90
+ options.minutes = t.to_f
87
91
  end
88
92
 
89
93
  opts.on("-i", "--interval SECONDS", String, "SECONDS between events, can be decimal eg 0.1 (10)") do |i|
@@ -109,7 +113,11 @@ unless options.interval.to_f > 0
109
113
  options.interval = 10.0
110
114
  end
111
115
 
112
- FLAPJACK_ENV = ENV['FLAPJACK_ENV'] || 'production'
116
+ unless options.minutes
117
+ options.minutes = 0.75
118
+ end
119
+
120
+ FLAPJACK_ENV = options.environment || ENV['FLAPJACK_ENV'] || 'production'
113
121
 
114
122
  config = Flapjack::Configuration.new
115
123
  config.load(options.config)
@@ -49,8 +49,9 @@ production:
49
49
  level: INFO
50
50
  syslog_errors: yes
51
51
  smtp_config:
52
- # port 1025 is the default port for http://mailcatcher.me
52
+ #from: "flapjack@noreply.example"
53
53
  host: 127.0.0.1
54
+ # 1025 is the default port for http://mailcatcher.me
54
55
  port: 1025
55
56
  starttls: false
56
57
  #auth:
data/flapjack.gemspec CHANGED
@@ -38,6 +38,7 @@ Gem::Specification.new do |gem|
38
38
  gem.add_dependency 'ice_cube'
39
39
  gem.add_dependency 'tzinfo', '~> 1.0.1'
40
40
  gem.add_dependency 'tzinfo-data'
41
+ gem.add_dependency 'rbtrace'
41
42
 
42
43
  gem.add_development_dependency 'rake'
43
44
  end
@@ -19,8 +19,7 @@ module Flapjack
19
19
  class Contact
20
20
 
21
21
  attr_accessor :id, :first_name, :last_name, :email, :media,
22
- :media_intervals, :media_rollup_thresholds, :pagerduty_credentials,
23
- :linked_entity_ids, :linked_media_ids
22
+ :media_intervals, :media_rollup_thresholds, :pagerduty_credentials
24
23
 
25
24
  TAG_PREFIX = 'contact_tag'
26
25
  ALL_MEDIA = ['email', 'sms', 'jabber', 'pagerduty']
@@ -155,6 +154,11 @@ module Flapjack
155
154
  *['subdomain', 'username', 'password'].collect {|f| [f, details[f]]})
156
155
  end
157
156
 
157
+ def delete_pagerduty_credentials
158
+ @redis.hdel("contact_media:#{self.id}", 'pagerduty')
159
+ @redis.del("contact_pagerduty:#{self.id}")
160
+ end
161
+
158
162
  # returns false if this contact was already in the set for the entity
159
163
  def add_entity(entity)
160
164
  key = "contacts_for:#{entity.id}"
@@ -201,11 +205,10 @@ module Flapjack
201
205
  }.values
202
206
  end
203
207
 
204
- def self.entities_jsonapi(contact_ids, options = {})
208
+ def self.entity_ids_for(contact_ids, options = {})
205
209
  raise "Redis connection not set" unless redis = options[:redis]
206
210
 
207
- entity_data = []
208
- linked_entity_ids = {}
211
+ entity_ids = {}
209
212
 
210
213
  temp_set = SecureRandom.uuid
211
214
  redis.sadd(temp_set, contact_ids)
@@ -216,28 +219,30 @@ module Flapjack
216
219
  next unless k =~ /^contacts_for:([a-zA-Z0-9][a-zA-Z0-9\.\-]*[a-zA-Z0-9])(?::(\w+))?$/
217
220
 
218
221
  entity_id = $1
219
- check = $2
220
-
221
- entity_data << {:id => entity_id, :name => redis.hget("entity:#{entity_id}", 'name')}
222
+ # check = $2
222
223
 
223
224
  contact_ids.each do |contact_id|
224
- linked_entity_ids[contact_id] ||= []
225
- linked_entity_ids[contact_id] << entity_id
225
+ entity_ids[contact_id] ||= []
226
+ entity_ids[contact_id] << entity_id
226
227
  end
227
228
  end
228
229
 
229
230
  redis.del(temp_set)
230
231
 
231
- [entity_data, linked_entity_ids]
232
+ entity_ids
232
233
  end
233
234
 
234
235
  def name
235
236
  [(self.first_name || ''), (self.last_name || '')].join(" ").strip
236
237
  end
237
238
 
239
+ def notification_rule_ids
240
+ @redis.smembers("contact_notification_rules:#{self.id}")
241
+ end
242
+
238
243
  # return an array of the notification rules of this contact
239
244
  def notification_rules(opts = {})
240
- rules = @redis.smembers("contact_notification_rules:#{self.id}").inject([]) do |ret, rule_id|
245
+ rules = self.notification_rule_ids.inject([]) do |ret, rule_id|
241
246
  unless (rule_id.nil? || rule_id == '')
242
247
  ret << Flapjack::Data::NotificationRule.find_by_id(rule_id, :redis => @redis)
243
248
  end
@@ -268,6 +273,14 @@ module Flapjack
268
273
  :redis => @redis, :logger => opts[:logger])
269
274
  end
270
275
 
276
+ # move an existing notification rule from another contact to this one
277
+ def grab_notification_rule(rule)
278
+ @redis.srem("contact_notification_rules:#{rule.contact.id}", rule.id)
279
+ rule.contact_id = self.id
280
+ rule.update({})
281
+ @redis.sadd("contact_notification_rules:#{self.id}", rule.id)
282
+ end
283
+
271
284
  def delete_notification_rule(rule)
272
285
  @redis.srem("contact_notification_rules:#{self.id}", rule.id)
273
286
  @redis.del("notification_rule:#{rule.id}")
@@ -436,7 +449,11 @@ module Flapjack
436
449
  # return a list of media enabled for this contact
437
450
  # eg [ 'email', 'sms' ]
438
451
  def media_list
439
- @redis.hkeys("contact_media:#{self.id}")
452
+ @redis.hkeys("contact_media:#{self.id}") - ['pagerduty']
453
+ end
454
+
455
+ def media_ids
456
+ self.media_list.collect {|medium| "#{self.id}_#{medium}" }
440
457
  end
441
458
 
442
459
  # return the timezone of the contact, or the system default if none is set
@@ -484,16 +501,17 @@ module Flapjack
484
501
  }.to_json
485
502
  end
486
503
 
487
- def to_jsonapi(*args)
488
- { "id" => self.id,
489
- "first_name" => self.first_name,
490
- "last_name" => self.last_name,
491
- "email" => self.email,
492
- "timezone" => self.timezone.name,
493
- "tags" => self.tags.to_a,
494
- "links" => {
495
- :entities => @linked_entity_ids || [],
496
- :media => @linked_media_ids || []
504
+ def to_jsonapi(opts = {})
505
+ { "id" => self.id,
506
+ "first_name" => self.first_name,
507
+ "last_name" => self.last_name,
508
+ "email" => self.email,
509
+ "timezone" => self.timezone.name,
510
+ "tags" => self.tags.to_a,
511
+ "links" => {
512
+ :entities => opts[:entity_ids] || [],
513
+ :media => self.media_ids || [],
514
+ :notification_rules => self.notification_rule_ids || [],
497
515
  }
498
516
  }.to_json
499
517
  end
@@ -512,9 +530,11 @@ module Flapjack
512
530
  def self.add_or_update(contact_id, contact_data, options = {})
513
531
  raise "Redis connection not set" unless redis = options[:redis]
514
532
 
515
- # TODO check that the rest of this is safe for the update case
516
- redis.hmset("contact:#{contact_id}",
517
- *['first_name', 'last_name', 'email'].collect {|f| [f, contact_data[f]]})
533
+ attrs = (['first_name', 'last_name', 'email'] & contact_data.keys).collect do |key|
534
+ [key, contact_data[key]]
535
+ end.flatten(1)
536
+
537
+ redis.hmset("contact:#{contact_id}", *attrs) unless attrs.empty?
518
538
 
519
539
  if ( ! contact_data['tags'].nil? && contact_data['tags'].is_a?(Enumerable))
520
540
  contact_data['tags'].each do |t|
@@ -78,6 +78,15 @@ module Flapjack
78
78
  self.new(:name => entity_name, :id => entity_id, :redis => redis)
79
79
  end
80
80
 
81
+ def self.find_by_ids(entity_ids, options = {})
82
+ raise "Redis connection not set" unless redis = options[:redis]
83
+ logger = options[:logger]
84
+
85
+ entity_ids.map do |id|
86
+ self.find_by_id(id, options)
87
+ end
88
+ end
89
+
81
90
  # NB: if we're worried about user input, https://github.com/mudge/re2
82
91
  # has bindings for a non-backtracking RE engine that runs in linear
83
92
  # time
@@ -145,6 +154,15 @@ module Flapjack
145
154
  }.compact
146
155
  end
147
156
 
157
+ def self.contact_ids_for(entity_ids, options = {})
158
+ raise "Redis connection not set" unless redis = options[:redis]
159
+
160
+ entity_ids.inject({}) do |memo, entity_id|
161
+ memo[entity_id] = redis.smembers("contacts_for:#{entity_id}")
162
+ memo
163
+ end
164
+ end
165
+
148
166
  def check_list
149
167
  @redis.zrange("current_checks:#{@name}", 0, -1)
150
168
  end
@@ -186,6 +204,16 @@ module Flapjack
186
204
  }
187
205
  end
188
206
 
207
+ def to_jsonapi(opts = {})
208
+ {
209
+ "id" => self.id,
210
+ "name" => self.name,
211
+ "links" => {
212
+ :contacts => opts[:contact_ids] || [],
213
+ }
214
+ }.to_json
215
+ end
216
+
189
217
  private
190
218
 
191
219
  # NB: initializer should not be used directly -- instead one of the finder methods
@@ -27,31 +27,38 @@ module Flapjack
27
27
 
28
28
  attr_accessor :entity, :check
29
29
 
30
- # TODO probably shouldn't always be creating on query -- work out when this should be happening
31
30
  def self.for_event_id(event_id, options = {})
32
31
  raise "Redis connection not set" unless redis = options[:redis]
32
+ entity_name, check_name = event_id.split(':', 2)
33
+ create_entity = options[:create_entity]
33
34
  logger = options[:logger]
34
- entity_name, check = event_id.split(':', 2)
35
- self.new(Flapjack::Data::Entity.find_by_name(entity_name, :redis => redis, :create => true, :logger => true), check,
36
- :redis => redis, :logger => logger)
35
+ entity = Flapjack::Data::Entity.find_by_name(entity_name,
36
+ :create => create_entity, :logger => logger, :redis => redis)
37
+ self.new(entity, check_name, :logger => logger, :redis => redis)
37
38
  end
38
39
 
39
- # TODO probably shouldn't always be creating on query -- work out when this should be happening
40
- def self.for_entity_name(entity_name, check, options = {})
40
+ def self.for_entity_name(entity_name, check_name, options = {})
41
41
  raise "Redis connection not set" unless redis = options[:redis]
42
- self.new(Flapjack::Data::Entity.find_by_name(entity_name, :redis => redis, :create => true), check,
43
- :redis => redis)
42
+ create_entity = options[:create_entity]
43
+ logger = options[:logger]
44
+ entity = Flapjack::Data::Entity.find_by_name(entity_name,
45
+ :create => create_entity, :logger => logger, :redis => redis)
46
+ self.new(entity, check_name, :logger => logger, :redis => redis)
44
47
  end
45
48
 
46
49
  def self.for_entity_id(entity_id, check, options = {})
47
50
  raise "Redis connection not set" unless redis = options[:redis]
48
- self.new(Flapjack::Data::Entity.find_by_id(entity_id, :redis => redis), check,
49
- :redis => redis)
51
+ create_entity = options[:create_entity]
52
+ logger = options[:logger]
53
+ entity = Flapjack::Data::Entity.find_by_id(entity_id,
54
+ :create => create_entity, :logger => logger, :redis => redis)
55
+ self.new(entity, check, :redis => redis)
50
56
  end
51
57
 
52
58
  def self.for_entity(entity, check, options = {})
53
59
  raise "Redis connection not set" unless redis = options[:redis]
54
- self.new(entity, check, :redis => redis)
60
+ logger = options[:logger]
61
+ self.new(entity, check, :logger => logger, :redis => redis)
55
62
  end
56
63
 
57
64
  def self.find_all_for_entity_name(entity_name, options = {})
@@ -360,6 +367,7 @@ module Flapjack
360
367
  timestamp = options[:timestamp] || Time.now.to_i
361
368
  summary = options[:summary]
362
369
  details = options[:details]
370
+ perfdata = options[:perfdata]
363
371
  count = options[:count]
364
372
 
365
373
  old_state = self.state
@@ -400,6 +408,10 @@ module Flapjack
400
408
  # hash summary and details (as they may have changed)
401
409
  @redis.hset("check:#{@key}", 'summary', (summary || ''))
402
410
  @redis.hset("check:#{@key}", 'details', (details || ''))
411
+ if perfdata
412
+ @redis.hset("check:#{@key}", 'perfdata', format_perfdata(perfdata).to_json)
413
+ # @redis.set("#{@key}:#{timestamp}:perfdata", perfdata)
414
+ end
403
415
 
404
416
  @redis.exec
405
417
  end
@@ -503,6 +515,18 @@ module Flapjack
503
515
  @redis.hget("check:#{@key}", 'details')
504
516
  end
505
517
 
518
+ def perfdata
519
+ data = @redis.hget("check:#{@key}", 'perfdata')
520
+ begin
521
+ data = JSON.parse(data) if data
522
+ rescue
523
+ data = "Unable to parse string: #{data}"
524
+ end
525
+
526
+ data = [data] if data.is_a?(Hash)
527
+ data
528
+ end
529
+
506
530
  # Returns a list of states for this entity check, sorted by timestamp.
507
531
  #
508
532
  # start_time and end_time should be passed as integer timestamps; these timestamps
@@ -647,6 +671,23 @@ module Flapjack
647
671
  @logger = options[:logger]
648
672
  end
649
673
 
674
+ def format_perfdata(perfdata)
675
+ # example perfdata: time=0.486630s;;;0.000000 size=909B;;;0
676
+ items = perfdata.split(' ')
677
+ # Do some fancy regex
678
+ data = []
679
+ items.each do |item|
680
+ components = item.split '='
681
+ key = components[0].to_s
682
+ value = ""
683
+ if components[1]
684
+ value = components[1].split(';')[0].to_s
685
+ end
686
+ data << {"key" => key, "value" => value}
687
+ end
688
+ data
689
+ end
690
+
650
691
  end
651
692
 
652
693
  end