flapjack 0.7.16 → 0.7.17
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.travis.yml +1 -0
- data/CHANGELOG.md +9 -0
- data/lib/flapjack/data/entity_check.rb +13 -8
- data/lib/flapjack/data/notification_rule.rb +11 -7
- data/lib/flapjack/gateways/api.rb +28 -655
- data/lib/flapjack/gateways/api/contact_methods.rb +342 -0
- data/lib/flapjack/gateways/api/entity_methods.rb +364 -0
- data/lib/flapjack/gateways/api/entity_presenter.rb +6 -6
- data/lib/flapjack/gateways/api/rack/json_params_parser.rb +26 -0
- data/lib/flapjack/version.rb +1 -1
- data/spec/lib/flapjack/data/entity_check_spec.rb +21 -0
- data/spec/lib/flapjack/data/notification_rule_spec.rb +11 -6
- data/spec/lib/flapjack/gateways/api/contact_methods_spec.rb +709 -0
- data/spec/lib/flapjack/gateways/api/entity_check_presenter_spec.rb +1 -3
- data/spec/lib/flapjack/gateways/api/entity_methods_spec.rb +868 -0
- data/spec/lib/flapjack/gateways/api/entity_presenter_spec.rb +13 -12
- data/spec/lib/flapjack/gateways/api_spec.rb +1 -1520
- data/tmp/redis_find_spurious_unknown_states.rb +52 -0
- metadata +12 -6
- data/.rbenv-version +0 -1
- data/tmp/redis_delete_all_keys.rb +0 -11
@@ -0,0 +1,342 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'sinatra/base'
|
4
|
+
|
5
|
+
require 'flapjack/data/contact'
|
6
|
+
require 'flapjack/data/notification_rule'
|
7
|
+
|
8
|
+
module Flapjack
|
9
|
+
|
10
|
+
module Gateways
|
11
|
+
|
12
|
+
class API < Sinatra::Base
|
13
|
+
|
14
|
+
class ContactNotFound < RuntimeError
|
15
|
+
attr_reader :contact_id
|
16
|
+
def initialize(contact_id)
|
17
|
+
@contact_id = contact_id
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class NotificationRuleNotFound < RuntimeError
|
22
|
+
attr_reader :rule_id
|
23
|
+
def initialize(rule_id)
|
24
|
+
@rule_id = rule_id
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module ContactMethods
|
29
|
+
|
30
|
+
module Helpers
|
31
|
+
|
32
|
+
def find_contact(contact_id)
|
33
|
+
contact = Flapjack::Data::Contact.find_by_id(contact_id, :logger => logger, :redis => redis)
|
34
|
+
raise Flapjack::Gateways::API::ContactNotFound.new(contact_id) if contact.nil?
|
35
|
+
contact
|
36
|
+
end
|
37
|
+
|
38
|
+
def find_rule(rule_id)
|
39
|
+
rule = Flapjack::Data::NotificationRule.find_by_id(rule_id, :logger => logger, :redis => redis)
|
40
|
+
raise Flapjack::Gateways::API::NotificationRuleNotFound.new(rule_id) if rule.nil?
|
41
|
+
rule
|
42
|
+
end
|
43
|
+
|
44
|
+
def find_tags(tags)
|
45
|
+
halt err(403, "no tags") if tags.nil? || tags.empty?
|
46
|
+
tags
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.registered(app)
|
52
|
+
|
53
|
+
app.helpers Flapjack::Gateways::API::ContactMethods::Helpers
|
54
|
+
|
55
|
+
app.post '/contacts' do
|
56
|
+
pass unless 'application/json'.eql?(request.content_type)
|
57
|
+
content_type :json
|
58
|
+
|
59
|
+
errors = []
|
60
|
+
|
61
|
+
contacts_data = params[:contacts]
|
62
|
+
if contacts_data.nil? || !contacts_data.is_a?(Enumerable)
|
63
|
+
errors << "No valid contacts were submitted"
|
64
|
+
else
|
65
|
+
# stringifying as integer string params are automatically integered,
|
66
|
+
# but our redis ids are strings
|
67
|
+
contacts_data_ids = contacts_data.reject {|c| c['id'].nil? }.
|
68
|
+
map {|co| co['id'].to_s }
|
69
|
+
|
70
|
+
if contacts_data_ids.empty?
|
71
|
+
errors << "No contacts with IDs were submitted"
|
72
|
+
else
|
73
|
+
contacts = Flapjack::Data::Contact.all(:redis => redis)
|
74
|
+
contacts_h = hashify(*contacts) {|c| [c.id, c] }
|
75
|
+
contacts_ids = contacts_h.keys
|
76
|
+
|
77
|
+
# delete contacts not found in the bulk list
|
78
|
+
(contacts_ids - contacts_data_ids).each do |contact_to_delete_id|
|
79
|
+
contact_to_delete = contacts.detect {|c| c.id == contact_to_delete_id }
|
80
|
+
contact_to_delete.delete!
|
81
|
+
end
|
82
|
+
|
83
|
+
# add or update contacts found in the bulk list
|
84
|
+
contacts_data.reject {|cd| cd['id'].nil? }.each do |contact_data|
|
85
|
+
if contacts_ids.include?(contact_data['id'].to_s)
|
86
|
+
contacts_h[contact_data['id'].to_s].update(contact_data)
|
87
|
+
else
|
88
|
+
Flapjack::Data::Contact.add(contact_data, :redis => redis)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
errors.empty? ? 204 : err(403, *errors)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns all the contacts
|
97
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts
|
98
|
+
app.get '/contacts' do
|
99
|
+
content_type :json
|
100
|
+
|
101
|
+
Flapjack::Data::Contact.all(:redis => redis).to_json
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns the core information about the specified contact
|
105
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id
|
106
|
+
app.get '/contacts/:contact_id' do
|
107
|
+
content_type :json
|
108
|
+
|
109
|
+
contact = find_contact(params[:contact_id])
|
110
|
+
contact.to_json
|
111
|
+
end
|
112
|
+
|
113
|
+
# Lists this contact's notification rules
|
114
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id_notification_rules
|
115
|
+
app.get '/contacts/:contact_id/notification_rules' do
|
116
|
+
content_type :json
|
117
|
+
|
118
|
+
contact = find_contact(params[:contact_id])
|
119
|
+
contact.notification_rules.to_json
|
120
|
+
end
|
121
|
+
|
122
|
+
# Get the specified notification rule for this user
|
123
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id_notification_rules_id
|
124
|
+
app.get '/notification_rules/:id' do
|
125
|
+
content_type :json
|
126
|
+
|
127
|
+
rule = find_rule(params[:id])
|
128
|
+
rule.to_json
|
129
|
+
end
|
130
|
+
|
131
|
+
# Creates a notification rule for a contact
|
132
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-post_contacts_id_notification_rules
|
133
|
+
app.post '/notification_rules' do
|
134
|
+
content_type :json
|
135
|
+
|
136
|
+
if params[:id]
|
137
|
+
halt err(403, "post cannot be used for update, do a put instead")
|
138
|
+
end
|
139
|
+
|
140
|
+
logger.debug("post /notification_rules data: ")
|
141
|
+
logger.debug(params.inspect)
|
142
|
+
|
143
|
+
contact = find_contact(params[:contact_id])
|
144
|
+
|
145
|
+
rule_data = hashify(:entities, :entity_tags,
|
146
|
+
:warning_media, :critical_media, :time_restrictions,
|
147
|
+
:warning_blackhole, :critical_blackhole) {|k| [k, params[k]]}
|
148
|
+
|
149
|
+
rule_or_errors = contact.add_notification_rule(rule_data, :logger => logger)
|
150
|
+
|
151
|
+
unless rule_or_errors.respond_to?(:critical_media)
|
152
|
+
halt err(403, *rule_or_errors)
|
153
|
+
end
|
154
|
+
rule_or_errors.to_json
|
155
|
+
end
|
156
|
+
|
157
|
+
# Updates a notification rule
|
158
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-put_notification_rules_id
|
159
|
+
app.put('/notification_rules/:id') do
|
160
|
+
content_type :json
|
161
|
+
|
162
|
+
logger.debug("put /notification_rules/#{params[:id]} data: ")
|
163
|
+
logger.debug(params.inspect)
|
164
|
+
|
165
|
+
rule = find_rule(params[:id])
|
166
|
+
contact = find_contact(rule.contact_id)
|
167
|
+
|
168
|
+
rule_data = hashify(:entities, :entity_tags,
|
169
|
+
:warning_media, :critical_media, :time_restrictions,
|
170
|
+
:warning_blackhole, :critical_blackhole) {|k| [k, params[k]]}
|
171
|
+
|
172
|
+
errors = rule.update(rule_data, :logger => logger)
|
173
|
+
|
174
|
+
unless errors.nil? || errors.empty?
|
175
|
+
halt err(403, *errors)
|
176
|
+
end
|
177
|
+
rule.to_json
|
178
|
+
end
|
179
|
+
|
180
|
+
# Deletes a notification rule
|
181
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-put_notification_rules_id
|
182
|
+
app.delete('/notification_rules/:id') do
|
183
|
+
logger.debug("delete /notification_rules/#{params[:id]}")
|
184
|
+
rule = find_rule(params[:id])
|
185
|
+
logger.debug("rule to delete: #{rule.inspect}, contact_id: #{rule.contact_id}")
|
186
|
+
contact = find_contact(rule.contact_id)
|
187
|
+
contact.delete_notification_rule(rule)
|
188
|
+
status 204
|
189
|
+
end
|
190
|
+
|
191
|
+
# Returns the media of a contact
|
192
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id_media
|
193
|
+
app.get '/contacts/:contact_id/media' do
|
194
|
+
content_type :json
|
195
|
+
|
196
|
+
contact = find_contact(params[:contact_id])
|
197
|
+
|
198
|
+
media = contact.media
|
199
|
+
media_intervals = contact.media_intervals
|
200
|
+
media_addr_int = hashify(*media.keys) {|k|
|
201
|
+
[k, {'address' => media[k],
|
202
|
+
'interval' => media_intervals[k] }]
|
203
|
+
}
|
204
|
+
media_addr_int.to_json
|
205
|
+
end
|
206
|
+
|
207
|
+
# Returns the specified media of a contact
|
208
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id_media_media
|
209
|
+
app.get('/contacts/:contact_id/media/:id') do
|
210
|
+
content_type :json
|
211
|
+
|
212
|
+
contact = find_contact(params[:contact_id])
|
213
|
+
media = contact.media[params[:id]]
|
214
|
+
if media.nil?
|
215
|
+
halt err(403, "no #{params[:id]} for contact '#{params[:contact_id]}'")
|
216
|
+
end
|
217
|
+
interval = contact.media_intervals[params[:id]]
|
218
|
+
if interval.nil?
|
219
|
+
halt err(403, "no #{params[:id]} interval for contact '#{params[:contact_id]}'")
|
220
|
+
end
|
221
|
+
{'address' => media,
|
222
|
+
'interval' => interval}.to_json
|
223
|
+
end
|
224
|
+
|
225
|
+
# Creates or updates a media of a contact
|
226
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-put_contacts_id_media_media
|
227
|
+
app.put('/contacts/:contact_id/media/:id') do
|
228
|
+
content_type :json
|
229
|
+
|
230
|
+
contact = find_contact(params[:contact_id])
|
231
|
+
errors = []
|
232
|
+
if params[:address].nil?
|
233
|
+
errors << "no address for '#{params[:id]}' media"
|
234
|
+
end
|
235
|
+
|
236
|
+
halt err(403, *errors) unless errors.empty?
|
237
|
+
|
238
|
+
contact.set_address_for_media(params[:id], params[:address])
|
239
|
+
contact.set_interval_for_media(params[:id], params[:interval])
|
240
|
+
|
241
|
+
{'address' => contact.media[params[:id]],
|
242
|
+
'interval' => contact.media_intervals[params[:id]]}.to_json
|
243
|
+
end
|
244
|
+
|
245
|
+
# delete a media of a contact
|
246
|
+
app.delete('/contacts/:contact_id/media/:id') do
|
247
|
+
contact = find_contact(params[:contact_id])
|
248
|
+
contact.remove_media(params[:id])
|
249
|
+
status 204
|
250
|
+
end
|
251
|
+
|
252
|
+
# Returns the timezone of a contact
|
253
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id_timezone
|
254
|
+
app.get('/contacts/:contact_id/timezone') do
|
255
|
+
content_type :json
|
256
|
+
|
257
|
+
contact = find_contact(params[:contact_id])
|
258
|
+
contact.timezone.name.to_json
|
259
|
+
end
|
260
|
+
|
261
|
+
# Sets the timezone of a contact
|
262
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-put_contacts_id_timezone
|
263
|
+
app.put('/contacts/:contact_id/timezone') do
|
264
|
+
content_type :json
|
265
|
+
|
266
|
+
contact = find_contact(params[:contact_id])
|
267
|
+
contact.timezone = params[:timezone]
|
268
|
+
contact.timezone.name.to_json
|
269
|
+
end
|
270
|
+
|
271
|
+
# Removes the timezone of a contact
|
272
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-put_contacts_id_timezone
|
273
|
+
app.delete('/contacts/:contact_id/timezone') do
|
274
|
+
contact = find_contact(params[:contact_id])
|
275
|
+
contact.timezone = nil
|
276
|
+
status 204
|
277
|
+
end
|
278
|
+
|
279
|
+
app.post '/contacts/:contact_id/tags' do
|
280
|
+
content_type :json
|
281
|
+
|
282
|
+
tags = find_tags(params[:tag])
|
283
|
+
contact = find_contact(params[:contact_id])
|
284
|
+
contact.add_tags(*tags)
|
285
|
+
contact.tags.to_json
|
286
|
+
end
|
287
|
+
|
288
|
+
app.post '/contacts/:contact_id/entity_tags' do
|
289
|
+
content_type :json
|
290
|
+
contact = find_contact(params[:contact_id])
|
291
|
+
contact.entities.map {|e| e[:entity]}.each do |entity|
|
292
|
+
next unless tags = params[:entity][entity.name]
|
293
|
+
entity.add_tags(*tags)
|
294
|
+
end
|
295
|
+
contact_ent_tag = hashify(*contact.entities(:tags => true)) {|et|
|
296
|
+
[et[:entity].name, et[:tags]]
|
297
|
+
}
|
298
|
+
contact_ent_tag.to_json
|
299
|
+
end
|
300
|
+
|
301
|
+
app.delete '/contacts/:contact_id/tags' do
|
302
|
+
tags = find_tags(params[:tag])
|
303
|
+
contact = find_contact(params[:contact_id])
|
304
|
+
contact.delete_tags(*tags)
|
305
|
+
status 204
|
306
|
+
end
|
307
|
+
|
308
|
+
app.delete '/contacts/:contact_id/entity_tags' do
|
309
|
+
contact = find_contact(params[:contact_id])
|
310
|
+
contact.entities.map {|e| e[:entity]}.each do |entity|
|
311
|
+
next unless tags = params[:entity][entity.name]
|
312
|
+
entity.delete_tags(*tags)
|
313
|
+
end
|
314
|
+
status 204
|
315
|
+
end
|
316
|
+
|
317
|
+
app.get '/contacts/:contact_id/tags' do
|
318
|
+
content_type :json
|
319
|
+
|
320
|
+
contact = find_contact(params[:contact_id])
|
321
|
+
contact.tags.to_json
|
322
|
+
end
|
323
|
+
|
324
|
+
app.get '/contacts/:contact_id/entity_tags' do
|
325
|
+
content_type :json
|
326
|
+
|
327
|
+
contact = find_contact(params[:contact_id])
|
328
|
+
contact_ent_tag = hashify(*contact.entities(:tags => true)) {|et|
|
329
|
+
[et[:entity].name, et[:tags]]
|
330
|
+
}
|
331
|
+
contact_ent_tag.to_json
|
332
|
+
end
|
333
|
+
|
334
|
+
end
|
335
|
+
|
336
|
+
end
|
337
|
+
|
338
|
+
end
|
339
|
+
|
340
|
+
end
|
341
|
+
|
342
|
+
end
|
@@ -0,0 +1,364 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'sinatra/base'
|
4
|
+
|
5
|
+
require 'flapjack/data/entity'
|
6
|
+
require 'flapjack/data/entity_check'
|
7
|
+
|
8
|
+
require 'flapjack/gateways/api/entity_presenter'
|
9
|
+
require 'flapjack/gateways/api/entity_check_presenter'
|
10
|
+
|
11
|
+
module Flapjack
|
12
|
+
|
13
|
+
module Gateways
|
14
|
+
|
15
|
+
class API < Sinatra::Base
|
16
|
+
|
17
|
+
class EntityCheckNotFound < RuntimeError
|
18
|
+
attr_reader :entity, :check
|
19
|
+
def initialize(entity, check)
|
20
|
+
@entity = entity
|
21
|
+
@check = check
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class EntityNotFound < RuntimeError
|
26
|
+
attr_reader :entity
|
27
|
+
def initialize(entity)
|
28
|
+
@entity = entity
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module EntityMethods
|
33
|
+
|
34
|
+
module Helpers
|
35
|
+
|
36
|
+
def find_entity(entity_name)
|
37
|
+
entity = Flapjack::Data::Entity.find_by_name(entity_name, :redis => redis)
|
38
|
+
raise Flapjack::Gateways::API::EntityNotFound.new(entity_name) if entity.nil?
|
39
|
+
entity
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_entity_check(entity, check)
|
43
|
+
entity_check = Flapjack::Data::EntityCheck.for_entity(entity,
|
44
|
+
check, :redis => redis)
|
45
|
+
raise Flapjack::Gateways::API::EntityCheckNotFound.new(entity, check) if entity_check.nil?
|
46
|
+
entity_check
|
47
|
+
end
|
48
|
+
|
49
|
+
def find_tags(tags)
|
50
|
+
halt err(403, "no tags") if tags.nil? || tags.empty?
|
51
|
+
tags
|
52
|
+
end
|
53
|
+
|
54
|
+
def entities_and_checks(entity_name, check)
|
55
|
+
if entity_name
|
56
|
+
# backwards-compatible, single entity or entity&check from route
|
57
|
+
entities = check ? nil : [entity_name]
|
58
|
+
checks = check ? {entity_name => check} : nil
|
59
|
+
else
|
60
|
+
# new and improved bulk API queries
|
61
|
+
entities = params[:entity]
|
62
|
+
checks = params[:check]
|
63
|
+
entities = [entities] unless entities.nil? || entities.is_a?(Array)
|
64
|
+
# TODO err if checks isn't a Hash (similar rules as in flapjack-diner)
|
65
|
+
end
|
66
|
+
[entities, checks]
|
67
|
+
end
|
68
|
+
|
69
|
+
def bulk_api_check_action(entities, entity_checks, action, params = {})
|
70
|
+
unless entities.nil? || entities.empty?
|
71
|
+
entities.each do |entity_name|
|
72
|
+
entity = find_entity(entity_name)
|
73
|
+
checks = entity.check_list.sort
|
74
|
+
checks.each do |check|
|
75
|
+
action.call( find_entity_check(entity, check) )
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
unless entity_checks.nil? || entity_checks.empty?
|
81
|
+
entity_checks.each_pair do |entity_name, checks|
|
82
|
+
entity = find_entity(entity_name)
|
83
|
+
checks = [checks] unless checks.is_a?(Array)
|
84
|
+
checks.each do |check|
|
85
|
+
action.call( find_entity_check(entity, check) )
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def present_api_results(entities, entity_checks, result_type, &block)
|
92
|
+
result = []
|
93
|
+
|
94
|
+
unless entities.nil? || entities.empty?
|
95
|
+
result += entities.collect {|entity_name|
|
96
|
+
entity = find_entity(entity_name)
|
97
|
+
yield(Flapjack::Gateways::API::EntityPresenter.new(entity, :redis => redis))
|
98
|
+
}.flatten(1)
|
99
|
+
end
|
100
|
+
|
101
|
+
unless entity_checks.nil? || entity_checks.empty?
|
102
|
+
result += entity_checks.inject([]) {|memo, (entity_name, checks)|
|
103
|
+
checks = [checks] unless checks.is_a?(Array)
|
104
|
+
entity = find_entity(entity_name)
|
105
|
+
memo += checks.collect {|check|
|
106
|
+
entity_check = find_entity_check(entity, check)
|
107
|
+
{:entity => entity_name,
|
108
|
+
:check => check,
|
109
|
+
result_type.to_sym => yield(Flapjack::Gateways::API::EntityCheckPresenter.new(entity_check))
|
110
|
+
}
|
111
|
+
}
|
112
|
+
}.flatten(1)
|
113
|
+
end
|
114
|
+
|
115
|
+
result
|
116
|
+
end
|
117
|
+
|
118
|
+
# NB: casts to UTC before converting to a timestamp
|
119
|
+
def validate_and_parsetime(value)
|
120
|
+
return unless value
|
121
|
+
Time.iso8601(value).getutc.to_i
|
122
|
+
rescue ArgumentError => e
|
123
|
+
logger.error "Couldn't parse time from '#{value}'"
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
# used for backwards-compatible route matching below
|
130
|
+
ENTITY_CHECK_FRAGMENT = '(?:/([a-zA-Z0-9][a-zA-Z0-9\.\-]*[a-zA-Z0-9])(?:/(.+))?)?'
|
131
|
+
|
132
|
+
def self.registered(app)
|
133
|
+
|
134
|
+
app.helpers Flapjack::Gateways::API::EntityMethods::Helpers
|
135
|
+
|
136
|
+
app.get '/entities' do
|
137
|
+
content_type :json
|
138
|
+
ret = Flapjack::Data::Entity.all(:redis => redis).sort_by(&:name).collect {|e|
|
139
|
+
presenter = Flapjack::Gateways::API::EntityPresenter.new(e, :redis => redis)
|
140
|
+
{'id' => e.id, 'name' => e.name, 'checks' => presenter.status }
|
141
|
+
}
|
142
|
+
ret.to_json
|
143
|
+
end
|
144
|
+
|
145
|
+
app.get '/checks/:entity' do
|
146
|
+
content_type :json
|
147
|
+
entity = find_entity(params[:entity])
|
148
|
+
entity.check_list.to_json
|
149
|
+
end
|
150
|
+
|
151
|
+
app.get %r{/status#{ENTITY_CHECK_FRAGMENT}} do
|
152
|
+
content_type :json
|
153
|
+
|
154
|
+
captures = params[:captures] || []
|
155
|
+
entity_name = captures[0]
|
156
|
+
check = captures[1]
|
157
|
+
|
158
|
+
entities, checks = entities_and_checks(entity_name, check)
|
159
|
+
|
160
|
+
results = present_api_results(entities, checks, 'status') {|presenter|
|
161
|
+
presenter.status
|
162
|
+
}
|
163
|
+
|
164
|
+
if entity_name
|
165
|
+
# compatible with previous data format
|
166
|
+
results = results.collect {|status_h| status_h[:status]}
|
167
|
+
(check ? results.first : results).to_json
|
168
|
+
else
|
169
|
+
# new and improved data format which reflects the request param structure
|
170
|
+
results.to_json
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
app.get %r{/((?:outages|(?:un)?scheduled_maintenances|downtime))#{ENTITY_CHECK_FRAGMENT}} do
|
175
|
+
action = params[:captures][0].to_sym
|
176
|
+
entity_name = params[:captures][1]
|
177
|
+
check = params[:captures][2]
|
178
|
+
|
179
|
+
entities, checks = entities_and_checks(entity_name, check)
|
180
|
+
|
181
|
+
start_time = validate_and_parsetime(params[:start_time])
|
182
|
+
end_time = validate_and_parsetime(params[:end_time])
|
183
|
+
|
184
|
+
results = present_api_results(entities, checks, action) {|presenter|
|
185
|
+
presenter.send(action, start_time, end_time)
|
186
|
+
}
|
187
|
+
|
188
|
+
if check
|
189
|
+
# compatible with previous data format
|
190
|
+
results.first[action].to_json
|
191
|
+
elsif entity_name
|
192
|
+
# compatible with previous data format
|
193
|
+
rename = {:unscheduled_maintenances => :unscheduled_maintenance,
|
194
|
+
:scheduled_maintenances => :scheduled_maintenance}
|
195
|
+
drop = [:entity]
|
196
|
+
results.collect{|r|
|
197
|
+
r.inject({}) {|memo, (k, v)|
|
198
|
+
if new_k = rename[k]
|
199
|
+
memo[new_k] = v
|
200
|
+
elsif !drop.include?(k)
|
201
|
+
memo[k] = v
|
202
|
+
end
|
203
|
+
memo
|
204
|
+
}
|
205
|
+
}.to_json
|
206
|
+
else
|
207
|
+
# new and improved data format which reflects the request param structure
|
208
|
+
results.to_json
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# create a scheduled maintenance period for a check on an entity
|
213
|
+
app.post %r{/scheduled_maintenances#{ENTITY_CHECK_FRAGMENT}} do
|
214
|
+
|
215
|
+
captures = params[:captures] || []
|
216
|
+
entity_name = captures[0]
|
217
|
+
check = captures[1]
|
218
|
+
|
219
|
+
entities, checks = entities_and_checks(entity_name, check)
|
220
|
+
|
221
|
+
start_time = validate_and_parsetime(params[:start_time])
|
222
|
+
halt( err(403, "start time must be provided") ) unless start_time
|
223
|
+
|
224
|
+
act_proc = proc {|entity_check|
|
225
|
+
entity_check.create_scheduled_maintenance(:start_time => start_time,
|
226
|
+
:duration => params[:duration].to_i, :summary => params[:summary])
|
227
|
+
}
|
228
|
+
|
229
|
+
bulk_api_check_action(entities, checks, act_proc)
|
230
|
+
status 204
|
231
|
+
end
|
232
|
+
|
233
|
+
# create an acknowledgement for a service on an entity
|
234
|
+
# NB currently, this does not acknowledge a specific failure event, just
|
235
|
+
# the entity-check as a whole
|
236
|
+
app.post %r{/acknowledgements#{ENTITY_CHECK_FRAGMENT}} do
|
237
|
+
captures = params[:captures] || []
|
238
|
+
entity_name = captures[0]
|
239
|
+
check = captures[1]
|
240
|
+
|
241
|
+
entities, checks = entities_and_checks(entity_name, check)
|
242
|
+
|
243
|
+
dur = params[:duration] ? params[:duration].to_i : nil
|
244
|
+
duration = (dur.nil? || (dur <= 0)) ? (4 * 60 * 60) : dur
|
245
|
+
summary = params[:summary]
|
246
|
+
|
247
|
+
opts = {'duration' => duration}
|
248
|
+
opts['summary'] = summary if summary
|
249
|
+
|
250
|
+
act_proc = proc {|entity_check|
|
251
|
+
Flapjack::Data::Event.create_acknowledgement(
|
252
|
+
entity_check.entity_name, entity_check.check,
|
253
|
+
:summary => params[:summary],
|
254
|
+
:duration => duration,
|
255
|
+
:redis => redis)
|
256
|
+
}
|
257
|
+
|
258
|
+
bulk_api_check_action(entities, checks, act_proc)
|
259
|
+
status 204
|
260
|
+
end
|
261
|
+
|
262
|
+
app.delete %r{/((?:un)?scheduled_maintenances)} do
|
263
|
+
action = params[:captures][0]
|
264
|
+
|
265
|
+
# no backwards-compatible mode here, it's a new method
|
266
|
+
entities, checks = entities_and_checks(nil, nil)
|
267
|
+
|
268
|
+
act_proc = case action
|
269
|
+
when 'scheduled_maintenances'
|
270
|
+
start_time = validate_and_parsetime(params[:start_time])
|
271
|
+
halt( err(403, "start time must be provided") ) unless start_time
|
272
|
+
opts = {}
|
273
|
+
opts[:start_time] = start_time.to_i
|
274
|
+
proc {|entity_check| entity_check.delete_scheduled_maintenance(opts) }
|
275
|
+
when 'unscheduled_maintenances'
|
276
|
+
end_time = validate_and_parsetime(params[:end_time])
|
277
|
+
opts = {}
|
278
|
+
opts[:end_time] = end_time.to_i if end_time
|
279
|
+
proc {|entity_check| entity_check.end_unscheduled_maintenance(opts) }
|
280
|
+
end
|
281
|
+
|
282
|
+
bulk_api_check_action(entities, checks, act_proc)
|
283
|
+
status 204
|
284
|
+
end
|
285
|
+
|
286
|
+
app.post %r{/test_notifications#{ENTITY_CHECK_FRAGMENT}} do
|
287
|
+
captures = params[:captures] || []
|
288
|
+
entity_name = captures[0]
|
289
|
+
check = captures[1]
|
290
|
+
|
291
|
+
entities, checks = entities_and_checks(entity_name, check)
|
292
|
+
|
293
|
+
act_proc = proc {|entity_check|
|
294
|
+
summary = params[:summary] ||
|
295
|
+
"Testing notifications to all contacts interested in entity #{entity_check.entity.name}"
|
296
|
+
Flapjack::Data::Event.test_notifications(
|
297
|
+
entity_check.entity_name, entity_check.check,
|
298
|
+
:summary => summary,
|
299
|
+
:redis => redis)
|
300
|
+
}
|
301
|
+
|
302
|
+
bulk_api_check_action(entities, checks, act_proc)
|
303
|
+
status 204
|
304
|
+
end
|
305
|
+
|
306
|
+
app.post '/entities' do
|
307
|
+
pass unless 'application/json'.eql?(request.content_type)
|
308
|
+
|
309
|
+
errors = []
|
310
|
+
ret = nil
|
311
|
+
|
312
|
+
# FIXME should scan for invalid records before making any changes, fail early
|
313
|
+
|
314
|
+
entities = params[:entities]
|
315
|
+
unless entities
|
316
|
+
logger.debug("no entities object found in the following supplied JSON:")
|
317
|
+
logger.debug(request.body)
|
318
|
+
return err(403, "No entities object received")
|
319
|
+
end
|
320
|
+
return err(403, "The received entities object is not an Enumerable") unless entities.is_a?(Enumerable)
|
321
|
+
return err(403, "Entity with a nil id detected") unless entities.any? {|e| !e['id'].nil?}
|
322
|
+
|
323
|
+
entities.each do |entity|
|
324
|
+
unless entity['id']
|
325
|
+
errors << "Entity not imported as it has no id: #{entity.inspect}"
|
326
|
+
next
|
327
|
+
end
|
328
|
+
Flapjack::Data::Entity.add(entity, :redis => redis)
|
329
|
+
end
|
330
|
+
errors.empty? ? 204 : err(403, *errors)
|
331
|
+
end
|
332
|
+
|
333
|
+
app.post '/entities/:entity/tags' do
|
334
|
+
content_type :json
|
335
|
+
|
336
|
+
tags = find_tags(params[:tag])
|
337
|
+
entity = find_entity(params[:entity])
|
338
|
+
entity.add_tags(*tags)
|
339
|
+
entity.tags.to_json
|
340
|
+
end
|
341
|
+
|
342
|
+
app.delete '/entities/:entity/tags' do
|
343
|
+
tags = find_tags(params[:tag])
|
344
|
+
entity = find_entity(params[:entity])
|
345
|
+
entity.delete_tags(*tags)
|
346
|
+
status 204
|
347
|
+
end
|
348
|
+
|
349
|
+
app.get '/entities/:entity/tags' do
|
350
|
+
content_type :json
|
351
|
+
|
352
|
+
entity = find_entity(params[:entity])
|
353
|
+
entity.tags.to_json
|
354
|
+
end
|
355
|
+
|
356
|
+
end
|
357
|
+
|
358
|
+
end
|
359
|
+
|
360
|
+
end
|
361
|
+
|
362
|
+
end
|
363
|
+
|
364
|
+
end
|