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
@@ -66,10 +66,16 @@ module Flapjack
66
66
  raise Flapjack::Gateways::JSONAPI::EntitiesNotFound.new(requested_entities)
67
67
  end
68
68
 
69
- linked_contact_ids = entities.empty? ? [] : Flapjack::Data::Entity.contact_ids_for(entities.map(&:id), :redis => redis)
69
+ entity_ids = entities.map(&:id)
70
+ linked_contact_ids = entities.empty? ? [] :
71
+ Flapjack::Data::Entity.contact_ids_for(entity_ids, :redis => redis)
72
+ linked_check_ids = entities.empty? ? [] :
73
+ Flapjack::Data::Entity.check_ids_for(entity_ids, :redis => redis)
70
74
 
71
75
  entities_json = entities.collect {|entity|
72
- entity.to_jsonapi(:contact_ids => linked_contact_ids[entity.id])
76
+ entity_id = entity.id
77
+ entity.to_jsonapi(:contact_ids => linked_contact_ids[entity_id],
78
+ :check_ids => linked_check_ids[entity_id] )
73
79
  }.join(",")
74
80
 
75
81
  '{"entities":[' + entities_json + ']}'
@@ -86,7 +92,7 @@ module Flapjack
86
92
 
87
93
  response.headers['Location'] = "#{request.base_url}/entities/#{entity_ids.join(',')}"
88
94
  status 201
89
- entity_ids.to_json
95
+ Flapjack.dump_json(entity_ids)
90
96
  end
91
97
 
92
98
  app.patch '/entities/:id' do
@@ -96,19 +102,31 @@ module Flapjack
96
102
  apply_json_patch('entities') do |op, property, linked, value|
97
103
  case op
98
104
  when 'replace'
99
- if ['name'].include?(property)
100
- # # Name change not supported in Flapjack v1.x, too many changes required
101
- # entity.update(property => value)
105
+ if 'name'.eql?(property)
106
+ name = entity.name
107
+ if name != value
108
+ existing = Flapjack::Data::Entity.find_by_name(value, :redis => redis)
109
+ Flapjack::Data::Entity.send(existing.nil? ? :rename : :merge,
110
+ name, value, :redis => redis)
111
+ end
102
112
  end
103
113
  when 'add'
104
- if 'contacts'.eql?(linked)
114
+ case linked
115
+ when 'contacts'
105
116
  contact = Flapjack::Data::Contact.find_by_id(value, :redis => redis)
106
117
  contact.add_entity(entity) unless contact.nil?
118
+ when 'tags'
119
+ value.respond_to?(:each) ? entity.add_tags(*value) :
120
+ entity.add_tags(value)
107
121
  end
108
122
  when 'remove'
109
- if 'contacts'.eql?(linked)
123
+ case linked
124
+ when 'contacts'
110
125
  contact = Flapjack::Data::Contact.find_by_id(value, :redis => redis)
111
126
  contact.remove_entity(entity) unless contact.nil?
127
+ when 'tags'
128
+ value.respond_to?(:each) ? entity.delete_tags(*value) :
129
+ entity.delete_tags(value)
112
130
  end
113
131
  end
114
132
  end
@@ -42,8 +42,8 @@ module Flapjack
42
42
 
43
43
  contact_id = $1
44
44
  media_type = $2
45
- halt err(422, "Could not get contact_id from media_id") if contact_id.nil?
46
- halt err(422, "Could not get media type from media_id") if media_type.nil?
45
+ halt err(422, "Could not get contact_id from media_id '#{m_id}'") if contact_id.nil?
46
+ halt err(422, "Could not get media type from media_id '#{m_id}'") if media_type.nil?
47
47
 
48
48
  contact_cache[contact_id] ||= find_contact(contact_id)
49
49
 
@@ -65,15 +65,17 @@ module Flapjack
65
65
  halt err(422, "No valid media were submitted")
66
66
  end
67
67
 
68
- unless media_data.all? {|m| m['id'].nil? }
69
- halt err(422, "Media creation cannot include IDs")
68
+ media_id_re = /^#{params[:contact_id]}_(?:#{Flapjack::Data::Contact::ALL_MEDIA.join('|')}$)/
69
+
70
+ unless media_data.all? {|m| m['id'].nil? || media_id_re === m['id'] }
71
+ halt err(422, "Media creation cannot include non-conformant IDs")
70
72
  end
71
73
 
72
74
  semaphore = obtain_semaphore(SEMAPHORE_CONTACT_MASS_UPDATE)
73
75
  contact = Flapjack::Data::Contact.find_by_id(params[:contact_id], :redis => redis)
74
76
  if contact.nil?
75
77
  semaphore.release
76
- halt err(422, "Contact id:'#{params[:contact_id]}' could not be loaded")
78
+ halt err(422, "Contact id: '#{params[:contact_id]}' could not be loaded")
77
79
  end
78
80
 
79
81
  media_data.each do |medium_data|
@@ -90,7 +92,7 @@ module Flapjack
90
92
 
91
93
  status 201
92
94
  response.headers['Location'] = "#{base_url}/media/#{media_ids.join(',')}"
93
- media_ids.to_json
95
+ Flapjack.dump_json(media_ids)
94
96
  end
95
97
 
96
98
  # get one or more media records; media ids are, for Flapjack
@@ -115,19 +117,21 @@ module Flapjack
115
117
  media_list_cache[contact.id] ||= contact.media_list
116
118
  if media_list_cache[contact.id].include?(media_type)
117
119
  medium_id = "#{contact.id}_#{media_type}"
120
+ int = contact.media_intervals[media_type]
121
+ rut = contact.media_rollup_thresholds[media_type]
118
122
  memo <<
119
123
  {:id => medium_id,
120
124
  :type => media_type,
121
125
  :address => contact.media[media_type],
122
- :interval => contact.media_intervals[media_type],
123
- :rollup_threshold => contact.media_rollup_thresholds[media_type],
126
+ :interval => int.nil? ? nil : int.to_i,
127
+ :rollup_threshold => rut.nil? ? nil : rut.to_i,
124
128
  :links => {:contacts => [contact.id]}}
125
129
  end
126
130
 
127
131
  memo
128
132
  end
129
133
 
130
- '{"media":' + media_data.to_json + '}'
134
+ '{"media":' + Flapjack.dump_json(media_data) + '}'
131
135
  end
132
136
 
133
137
  # update one or more media records; media ids are, for Flapjack
@@ -50,7 +50,7 @@ module Flapjack
50
50
  {
51
51
  'all' => Flapjack::Data::Entity.all(:redis => redis).length,
52
52
  'enabled' => Flapjack::Data::Entity.all(:enabled => true, :redis => redis).length,
53
- 'failing' => Flapjack::Data::Entity.find_all_with_failing_checks(:redis => redis).length,
53
+ 'failing' => Flapjack::Data::Entity.find_all_names_with_failing_checks(:redis => redis).length,
54
54
  }
55
55
  end
56
56
 
@@ -88,7 +88,7 @@ module Flapjack
88
88
  metrics[key] = self.send(key.to_sym)
89
89
  end
90
90
 
91
- return metrics.to_json
91
+ Flapjack.dump_json(metrics)
92
92
  end
93
93
  end
94
94
 
@@ -67,7 +67,7 @@ module Flapjack
67
67
 
68
68
  unless rule_ids.empty?
69
69
  response.headers['Location'] = "#{base_url}/notification_rules/#{rule_ids.join(',')}"
70
- rule_ids.to_json
70
+ Flapjack.dump_json(rule_ids)
71
71
  end
72
72
  end
73
73
 
@@ -46,7 +46,7 @@ module Flapjack
46
46
  contact = Flapjack::Data::Contact.find_by_id(params[:contact_id], :redis => redis)
47
47
  if contact.nil?
48
48
  semaphore.release
49
- halt err(422, "Contact id '#{params[:contact_id]}' could not be loaded")
49
+ halt err(422, "Contact id: '#{params[:contact_id]}' could not be loaded")
50
50
  end
51
51
 
52
52
  pagerduty_credential_data = fields.inject({}) do |memo, field|
@@ -61,7 +61,7 @@ module Flapjack
61
61
 
62
62
  status 201
63
63
  response.headers['Location'] = "#{base_url}/pagerduty_credentials/#{contact.id}"
64
- [contact.id].to_json
64
+ Flapjack.dump_json([contact.id])
65
65
  end
66
66
 
67
67
  app.get %r{^/pagerduty_credentials(?:/)?([^/]+)?$} do
@@ -72,14 +72,18 @@ module Flapjack
72
72
  end
73
73
 
74
74
  pagerduty_credentials_data = contacts.inject([]) do |memo, contact|
75
- pdc = contact.pagerduty_credentials.clone
75
+ pdc = contact.pagerduty_credentials
76
+
77
+ unless pdc.nil?
78
+ pd_data = pdc.clone
79
+ pd_data['links'] = {'contacts' => [contact.id]}
80
+ memo << pd_data
81
+ end
76
82
 
77
- pdc['links'] = {'contacts' => [contact.id]}
78
- memo << pdc
79
83
  memo
80
84
  end
81
85
 
82
- '{"pagerduty_credentials":' + pagerduty_credentials_data.to_json + '}'
86
+ '{"pagerduty_credentials":' + Flapjack.dump_json(pagerduty_credentials_data) + '}'
83
87
  end
84
88
 
85
89
  # update one or more sets of pagerduty credentials
@@ -88,21 +92,22 @@ module Flapjack
88
92
  apply_json_patch('pagerduty_credentials') do |op, property, linked, value|
89
93
  if 'replace'.eql?(op)
90
94
 
91
- pdc = contact.pagerduty_credentials.clone
95
+ pdc = contact.pagerduty_credentials
96
+ pd_data = pdc.nil? ? {} : pdc.clone
92
97
 
93
98
  case property
94
99
  when 'service_key'
95
- pdc['service_key'] = value
96
- contact.set_pagerduty_credentials(pdc)
100
+ pd_data['service_key'] = value
101
+ contact.set_pagerduty_credentials(pd_data)
97
102
  when 'subdomain'
98
- pdc['subdomain'] = value
99
- contact.set_pagerduty_credentials(pdc)
103
+ pd_data['subdomain'] = value
104
+ contact.set_pagerduty_credentials(pd_data)
100
105
  when 'username'
101
- pdc['username'] = value
102
- contact.set_pagerduty_credentials(pdc)
106
+ pd_data['username'] = value
107
+ contact.set_pagerduty_credentials(pd_data)
103
108
  when 'password'
104
- pdc['password'] = value
105
- contact.set_pagerduty_credentials(pdc)
109
+ pd_data['password'] = value
110
+ contact.set_pagerduty_credentials(pd_data)
106
111
  end
107
112
  end
108
113
  end
@@ -112,9 +117,11 @@ module Flapjack
112
117
  end
113
118
 
114
119
  app.delete '/pagerduty_credentials/:contact_id' do
115
- params[:contact_id].split(',').uniq.collect {|c_id| find_contact(c_id) }.each do |contact|
120
+ params[:contact_id].split(',').uniq.collect {|c_id|
121
+ find_contact(c_id)
122
+ }.each {|contact|
116
123
  contact.delete_pagerduty_credentials
117
- end
124
+ }
118
125
  status 204
119
126
  end
120
127
 
@@ -13,7 +13,7 @@ module Flapjack
13
13
  env['rack.request.form_input'] = env['rack.input']
14
14
  json_data = env['rack.input'].read
15
15
  env['rack.input'].rewind
16
- data = json_data.empty? ? {} : Oj.load(json_data)
16
+ data = json_data.empty? ? {} : Flapjack.load_json(json_data)
17
17
  env['rack.request.form_hash'] = data.empty? ? {} :
18
18
  (('application/json-patch+json'.eql?(t)) ? {'ops' => data} : data)
19
19
  end
@@ -27,7 +27,7 @@ module Flapjack
27
27
  end
28
28
 
29
29
  checks = if event_ids.nil?
30
- Flapjack::Data::EntityCheck.find_current(:redis => redis).collect {|check_name|
30
+ Flapjack::Data::EntityCheck.find_current_names(:redis => redis).collect {|check_name|
31
31
  find_entity_check_by_name(*check_name.split(':', 2))
32
32
  }
33
33
  elsif !event_ids.empty?
@@ -121,9 +121,9 @@ module Flapjack
121
121
  }
122
122
  end
123
123
 
124
- "{\"#{action}_reports\":" + report_data.to_json + "," +
125
- "\"linked\":{\"entities\":" + entity_data.to_json + "," +
126
- "\"checks\":" + check_data.to_json + "}}"
124
+ "{\"#{action}_reports\":" + Flapjack.dump_json(report_data) + "," +
125
+ "\"linked\":{\"entities\":" + Flapjack.dump_json(entity_data) + "," +
126
+ "\"checks\":" + Flapjack.dump_json(check_data) + "}}"
127
127
  end
128
128
 
129
129
  end
@@ -89,6 +89,13 @@ module Flapjack
89
89
  end
90
90
  end
91
91
 
92
+ class EntityChecksNotFound < RuntimeError
93
+ attr_reader :entity_checks
94
+ def initialize(entity_checks)
95
+ @entity_checks = entity_checks
96
+ end
97
+ end
98
+
92
99
  class ResourceLocked < RuntimeError
93
100
  attr_reader :resource
94
101
  def initialize(resource)
@@ -100,37 +107,45 @@ module Flapjack
100
107
 
101
108
  set :protection, :except => :path_traversal
102
109
 
103
- rescue_error = Proc.new {|status, exception, request_info, *msg|
104
- if !msg || msg.empty?
105
- trace = exception.backtrace.join("\n")
106
- msg = "#{exception.class} - #{exception.message}"
107
- msg_str = "#{msg}\n#{trace}"
108
- else
109
- msg_str = msg.join(", ")
110
- end
111
- case
112
- when status < 500
113
- @logger.warn "Error: #{msg_str}"
114
- else
115
- @logger.error "Error: #{msg_str}"
116
- end
110
+ @rescue_exception = Proc.new {|env, e|
117
111
 
118
- response_body = {:errors => msg}.to_json
112
+ rescue_error = Proc.new {|status, exception, request_info, *msg|
113
+ if !msg || msg.empty?
114
+ trace = exception.backtrace.join("\n")
115
+ msg = "#{exception.class} - #{exception.message}"
116
+ msg_str = "#{msg}\n#{trace}"
117
+ else
118
+ msg_str = msg.join(", ")
119
+ end
120
+ case
121
+ when status < 500
122
+ @logger.warn "Error: #{msg_str}"
123
+ else
124
+ @logger.error "Error: #{msg_str}"
125
+ end
119
126
 
120
- query_string = (request_info[:query_string].respond_to?(:length) &&
121
- request_info[:query_string].length > 0) ? "?#{request_info[:query_string]}" : ""
122
- if @logger.debug?
123
- @logger.debug("Returning #{status} for #{request_info[:request_method]} " +
124
- "#{request_info[:path_info]}#{query_string}, body: #{response_body}")
125
- elsif @logger.info?
126
- @logger.info("Returning #{status} for #{request_info[:request_method]} " +
127
- "#{request_info[:path_info]}#{query_string}")
128
- end
127
+ response_body = Flapjack.dump_json(:errors => msg)
129
128
 
130
- [status, {}, response_body]
131
- }
129
+ query_string = (request_info[:query_string].respond_to?(:length) &&
130
+ request_info[:query_string].length > 0) ? "?#{request_info[:query_string]}" : ""
131
+ if @logger.debug?
132
+ @logger.debug("Returning #{status} for #{request_info[:request_method]} " +
133
+ "#{request_info[:path_info]}#{query_string}, body: #{response_body}")
134
+ elsif @logger.info?
135
+ @logger.info("Returning #{status} for #{request_info[:request_method]} " +
136
+ "#{request_info[:path_info]}#{query_string}")
137
+ end
138
+
139
+ headers = if 'DELETE'.eql?(request_info[:request_method])
140
+ # not set by default for delete, but the error structure is JSON
141
+ {'Content-Type' => JSONAPI_MEDIA_TYPE}
142
+ else
143
+ {}
144
+ end
145
+
146
+ [status, headers, response_body]
147
+ }
132
148
 
133
- rescue_exception = Proc.new {|env, e|
134
149
  request_info = {
135
150
  :path_info => env['REQUEST_PATH'],
136
151
  :request_method => env['REQUEST_METHOD'],
@@ -147,15 +162,21 @@ module Flapjack
147
162
  rescue_error.call(404, e, request_info, "could not find notification rules '" + e.notification_rule_ids.join(', ') + "'")
148
163
  when Flapjack::Gateways::JSONAPI::EntityNotFound
149
164
  rescue_error.call(404, e, request_info, "could not find entity '#{e.entity}'")
165
+ when Flapjack::Gateways::JSONAPI::EntitiesNotFound
166
+ entity_ids = "'" + e.entity_ids.join("', '") + "'"
167
+ rescue_error.call(404, e, request_info, "could not find entities: #{entity_ids}")
150
168
  when Flapjack::Gateways::JSONAPI::EntityCheckNotFound
151
169
  rescue_error.call(404, e, request_info, "could not find entity check '#{e.check}'")
170
+ when Flapjack::Gateways::JSONAPI::EntityChecksNotFound
171
+ checks = "'" + e.entity_checks.join("', '") + "'"
172
+ rescue_error.call(404, e, request_info, "could not find entity checks: #{checks}")
152
173
  when Flapjack::Gateways::JSONAPI::ResourceLocked
153
174
  rescue_error.call(423, e, request_info, "unable to obtain lock for resource '#{e.resource}'")
154
175
  else
155
176
  rescue_error.call(500, e, request_info)
156
177
  end
157
178
  }
158
- use ::Rack::FiberPool, :size => 25, :rescue_exception => rescue_exception
179
+ use ::Rack::FiberPool, :size => 25, :rescue_exception => @rescue_exception
159
180
 
160
181
  use ::Rack::MethodOverride
161
182
  use Flapjack::Gateways::JSONAPI::Rack::JsonParamsParser
@@ -205,7 +226,7 @@ module Flapjack
205
226
  before do
206
227
  input = nil
207
228
  query_string = (request.query_string.respond_to?(:length) &&
208
- request.query_string.length > 0) ? "?#{request.query_string}" : ""
229
+ (request.query_string.length > 0)) ? "?#{request.query_string}" : ""
209
230
  if logger.debug?
210
231
  input = env['rack.input'].read
211
232
  logger.debug("#{request.request_method} #{request.path_info}#{query_string} #{input}")
@@ -256,7 +277,7 @@ module Flapjack
256
277
  def err(status, *msg)
257
278
  msg_str = msg.join(", ")
258
279
  logger.info "Error: #{msg_str}"
259
- [status, {}, {:errors => msg}.to_json]
280
+ [status, {}, Flapjack.dump_json(:errors => msg)]
260
281
  end
261
282
 
262
283
  def is_json_request?
@@ -390,7 +411,7 @@ module Flapjack
390
411
  # bare 'params' may have splat/captures for regex route, see
391
412
  # https://github.com/sinatra/sinatra/issues/453
392
413
  post '*' do
393
- halt(405) unless request.params.empty? || is_json_request? || is_jsonapi_request
414
+ halt(405) unless request.params.empty? || is_json_request? || is_jsonapi_request?
394
415
  content_type JSONAPI_MEDIA_TYPE
395
416
  cors_headers
396
417
  pass
@@ -4,7 +4,6 @@ require 'socket'
4
4
  require 'eventmachine'
5
5
  require 'em-synchrony'
6
6
  require 'blather/client/client'
7
- require 'oj'
8
7
 
9
8
  require 'flapjack/utility'
10
9
 
@@ -210,9 +209,9 @@ module Flapjack
210
209
  end
211
210
 
212
211
  def send_pagerduty_event(event)
213
- options = { :body => Oj.dump(event) }
212
+ options = { :body => Flapjack.dump_json(event) }
214
213
  http = EM::HttpRequest.new(@pagerduty_events_api_url).post(options)
215
- response = Oj.load(http.response)
214
+ response = Flapjack.load_json(http.response)
216
215
  status = http.response_header.status
217
216
  @logger.debug "send_pagerduty_event got a return code of #{status.to_s} - #{response.inspect}"
218
217
  [status, response]
@@ -4,8 +4,6 @@ require 'em-hiredis'
4
4
  require 'em-synchrony'
5
5
  require 'em-synchrony/em-http'
6
6
 
7
- require 'oj'
8
-
9
7
  require 'flapjack/data/entity_check'
10
8
  require 'flapjack/data/alert'
11
9
  require 'flapjack/redis_pool'
@@ -40,7 +38,7 @@ module Flapjack
40
38
  redis_uri = @redis_config[:path] ||
41
39
  "redis://#{@redis_config[:host] || '127.0.0.1'}:#{@redis_config[:port] || '6379'}/#{@redis_config[:db] || '0'}"
42
40
  shutdown_redis = EM::Hiredis.connect(redis_uri)
43
- shutdown_redis.rpush(@config['queue'], Oj.dump('notification_type' => 'shutdown'))
41
+ shutdown_redis.rpush(@config['queue'], Flapjack.dump_json('notification_type' => 'shutdown'))
44
42
  end
45
43
 
46
44
  def start
@@ -67,7 +65,7 @@ module Flapjack
67
65
  event_json = events[queue][1]
68
66
 
69
67
  begin
70
- event = Oj.load(event_json)
68
+ event = Flapjack.load_json(event_json)
71
69
  @logger.debug("pagerduty notification event received: " + event.inspect)
72
70
 
73
71
  if 'shutdown'.eql?(event['notification_type'])
@@ -110,10 +108,13 @@ module Flapjack
110
108
  'trigger'
111
109
  end
112
110
 
111
+ # Setting the HOSTNAME and the SERVICE makes them visible in the Pagerduty UI
113
112
  pagerduty_event = { 'service_key' => alert.address,
114
113
  'incident_key' => alert.event_id,
115
114
  'event_type' => pagerduty_type,
116
- 'description' => message }
115
+ 'description' => message,
116
+ 'details' => {'HOSTNAME' => alert.entity,
117
+ 'SERVICE' => alert.check}}
117
118
 
118
119
  send_pagerduty_event(pagerduty_event)
119
120
  alert.record_send_success!
@@ -163,9 +164,9 @@ module Flapjack
163
164
  end
164
165
 
165
166
  def send_pagerduty_event(event)
166
- options = { :body => Oj.dump(event) }
167
+ options = { :body => Flapjack.dump_json(event) }
167
168
  http = EM::HttpRequest.new(PAGERDUTY_EVENTS_API_URL).post(options)
168
- response = Oj.load(http.response)
169
+ response = Flapjack.load_json(http.response)
169
170
  status = http.response_header.status
170
171
  @logger.debug "send_pagerduty_event got a return code of #{status.to_s} - #{response.inspect}"
171
172
  unless status == 200
@@ -260,7 +261,7 @@ module Flapjack
260
261
 
261
262
  http = EM::HttpRequest.new(url).get(options)
262
263
  begin
263
- response = Oj.load(http.response)
264
+ response = Flapjack.load_json(http.response)
264
265
  rescue Oj::Error
265
266
  @logger.error("failed to parse json from a post to #{url} ... response headers and body follows...")
266
267
  return nil
@@ -46,6 +46,25 @@ toolbox.batchRequest = function(klass, ids, amount, success) {
46
46
 
47
47
  Backbone.JSONAPIModel = Backbone.Model.extend({
48
48
 
49
+ initialize: function() {
50
+ var _persisted;
51
+
52
+ this.isPersisted = function () {
53
+ if ( !_.isUndefined(_persisted) && !_.isNull(_persisted) ) {
54
+ return(_persisted);
55
+ } else {
56
+ return(!_.isNull(this.id));
57
+ }
58
+ };
59
+ this.setPersisted = function(pers) {
60
+ _persisted = pers;
61
+ };
62
+ },
63
+
64
+ isNew: function() {
65
+ return(!this.isPersisted());
66
+ },
67
+
49
68
  // the following two methods, and batchRequest, should be folded into
50
69
  // Collection.fetch (and Model.fetch?)
51
70
  resolveLink: function(name, klass, superset) {
@@ -44,7 +44,11 @@ $(document).ready(function() {
44
44
  // skip if modal showing
45
45
  if ( $('#contactModal').hasClass('in') ) { return; }
46
46
 
47
- var contactDetailsView = new (Contact.Views.ContactDetails)({model: new (Contact.Model)()});
47
+ var contact = new (Contact.Model)();
48
+ contact.setPersisted(false);
49
+ contact.set('id', toolbox.generateUUID());
50
+
51
+ var contactDetailsView = new (Contact.Views.ContactDetails)({model: contact});
48
52
  contactDetailsView.render();
49
53
 
50
54
  $('div.modal-dialog').append(contactDetailsView.$el);
@@ -75,4 +79,4 @@ $(document).ready(function() {
75
79
  }
76
80
  });
77
81
 
78
- });
82
+ });
@@ -17,6 +17,7 @@
17
17
  links: {},
18
18
  },
19
19
  initialize: function(){
20
+ Backbone.JSONAPIModel.prototype.initialize.apply(this, arguments);
20
21
  this.on('change', this.setDirty, this);
21
22
  },
22
23
  toJSON: function() {
@@ -65,17 +66,7 @@
65
66
 
66
67
  this.collection.each(function(contact) {
67
68
  var item = new (Contact.Views.ListItem)({ model: contact });
68
- var itemEl = item.render().$el;
69
-
70
- itemEl.on('mouseenter', function(e) {
71
- e.stopPropagation();
72
- $(this).find('td.actions button').css('visibility', 'visible');
73
- }).on('mouseleave', function(e) {
74
- e.stopPropagation();
75
- $(this).find('td.actions button').css('visibility', 'hidden');
76
- });
77
-
78
- jqel.append(itemEl);
69
+ jqel.append(item.render().$el);
79
70
  });
80
71
 
81
72
  return this;
@@ -122,7 +113,7 @@
122
113
 
123
114
  var context = this;
124
115
 
125
- var deferreds = this.model.resolveLinks({media: Medium.List});
116
+ var deferreds = this.model.resolveLinks({media: Medium.List});
126
117
 
127
118
  $.when.apply($, deferreds).done(
128
119
  function() {
@@ -214,6 +205,7 @@
214
205
 
215
206
  if ( this.model.isNew() ) {
216
207
  var save_success = function(model, response, options) {
208
+ model.setPersisted(true);
217
209
  flapjack.contactList.add(model);
218
210
  $('#contactModal').modal('hide');
219
211
  };
@@ -297,8 +289,10 @@
297
289
  type: type,
298
290
  address: '',
299
291
  interval: 15,
300
- rollup_threshold: 3,
292
+ rollup_threshold: 3
301
293
  });
294
+ medium.setPersisted(false);
295
+ medium.set('id', options.contact.get('id') + '_' + type);
302
296
  context.collection.add(medium);
303
297
  }
304
298
  medium.contact = options.contact;
@@ -365,7 +359,8 @@
365
359
  var attrs = _.pick(model.attributes, changedAttrKeys);
366
360
  if ( model.isNew() ) {
367
361
  model.save(attrs);
368
- model.set('id', model.contact.get('id') + '_' + model.get('type'));
362
+ model.setPersisted(true);
363
+ model.contact.addLinked('contacts', 'media', model);
369
364
  } else {
370
365
  model.patch('media', attrs);
371
366
  }
@@ -6,6 +6,7 @@
6
6
  Medium.Model = Backbone.JSONAPIModel.extend({
7
7
  name: 'media',
8
8
  initialize: function(){
9
+ Backbone.JSONAPIModel.prototype.initialize.apply(this, arguments);
9
10
  this.on('change', this.setDirty, this);
10
11
  },
11
12
  defaults: {
@@ -66,7 +66,7 @@ $(document).ready(function() {
66
66
  var d = new Date().getTime();
67
67
  var value = {x: d, y: json.event_queue_length}
68
68
  data[0].values.push(value);
69
- console.log(data[0].values.length);
69
+ // console.log(data[0].values.length);
70
70
 
71
71
  // Remove old data, to keep the graph performant
72
72
  if (data[0].values.length > 100) {
@@ -20,13 +20,13 @@
20
20
  <td><@- first_name @></td>
21
21
  <td><@- last_name @></td>
22
22
  <td class="actions">
23
- <button type="button" class="btn btn-default contact-media" style="visibility: hidden" aria-hidden="true">Media</button>
23
+ <button type="button" class="btn btn-default contact-media" aria-hidden="true">Media</button>
24
24
  </td>
25
25
  <td class="actions">
26
- <button type="button" class="btn btn-default contact-entities" style="visibility: hidden" aria-hidden="true">Entities</button>
26
+ <button type="button" class="btn btn-default contact-entities" aria-hidden="true">Entities</button>
27
27
  </td>
28
28
  <td class="actions">
29
- <button type="button" class="btn btn-danger delete-contact" style="visibility: hidden" aria-hidden="true">&minus;</button>
29
+ <button type="button" class="btn btn-danger delete-contact">Delete</button>
30
30
  </td>
31
31
  </script>
32
32