flapjack-diner 1.0.0 → 1.2.0
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.rubocop.yml +21 -0
- data/.rubocop_todo.yml +135 -0
- data/Gemfile +7 -0
- data/README.md +86 -20
- data/flapjack-diner.gemspec +11 -15
- data/lib/flapjack-diner.rb +23 -548
- data/lib/flapjack-diner/argument_validator.rb +31 -22
- data/lib/flapjack-diner/resources/checks.rb +58 -0
- data/lib/flapjack-diner/resources/contacts.rb +70 -0
- data/lib/flapjack-diner/resources/entities.rb +68 -0
- data/lib/flapjack-diner/resources/maintenance_periods.rb +82 -0
- data/lib/flapjack-diner/resources/media.rb +61 -0
- data/lib/flapjack-diner/resources/notification_rules.rb +66 -0
- data/lib/flapjack-diner/resources/notifications.rb +27 -0
- data/lib/flapjack-diner/resources/pagerduty_credentials.rb +60 -0
- data/lib/flapjack-diner/resources/reports.rb +33 -0
- data/lib/flapjack-diner/tools.rb +277 -0
- data/lib/flapjack-diner/version.rb +1 -1
- data/spec/argument_validator_spec.rb +15 -15
- data/spec/flapjack-diner_spec.rb +58 -1275
- data/spec/pacts/flapjack-diner-flapjack.json +4522 -0
- data/spec/resources/checks_spec.rb +171 -0
- data/spec/resources/contacts_spec.rb +297 -0
- data/spec/resources/entities_spec.rb +181 -0
- data/spec/resources/maintenance_periods_spec.rb +603 -0
- data/spec/resources/media_spec.rb +277 -0
- data/spec/resources/notification_rules_spec.rb +341 -0
- data/spec/resources/notifications_spec.rb +210 -0
- data/spec/resources/pagerduty_credentials_spec.rb +243 -0
- data/spec/resources/reports_spec.rb +255 -0
- data/spec/spec_helper.rb +14 -2
- metadata +35 -72
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'json'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
require 'flapjack-diner/version'
|
6
|
+
require 'flapjack-diner/argument_validator'
|
7
|
+
|
8
|
+
module Flapjack
|
9
|
+
module Diner
|
10
|
+
module Resources
|
11
|
+
module NotificationRules
|
12
|
+
def create_contact_notification_rules(*args)
|
13
|
+
ids, data = unwrap_ids(*args), unwrap_create_data(*args)
|
14
|
+
raise "'create_contact_notification_rules' requires at least one " \
|
15
|
+
'contact id parameter' if ids.nil? || ids.empty?
|
16
|
+
validate_params(data) do
|
17
|
+
validate :query => STRING_PARAMS, :as => :array_of_strings
|
18
|
+
validate :query => BOOLEAN_PARAMS, :as => :boolean
|
19
|
+
end
|
20
|
+
perform_post("/contacts/#{escaped_ids(ids)}/notification_rules",
|
21
|
+
nil, :notification_rules => data)
|
22
|
+
end
|
23
|
+
|
24
|
+
def notification_rules(*ids)
|
25
|
+
perform_get('notification_rules', '/notification_rules', ids)
|
26
|
+
end
|
27
|
+
|
28
|
+
def update_notification_rules(*args)
|
29
|
+
ids, params = unwrap_ids(*args), unwrap_params(*args)
|
30
|
+
raise "'update_notification_rules' requires at least one " \
|
31
|
+
'notification rule id parameter' if ids.nil? || ids.empty?
|
32
|
+
validate_params(params) do
|
33
|
+
validate :query => STRING_PARAMS, :as => :array_of_strings
|
34
|
+
validate :query => BOOLEAN_PARAMS, :as => :boolean
|
35
|
+
end
|
36
|
+
perform_patch("/notification_rules/#{escaped_ids(ids)}", nil,
|
37
|
+
update_notification_rules_ops(params))
|
38
|
+
end
|
39
|
+
|
40
|
+
def delete_notification_rules(*ids)
|
41
|
+
raise "'delete_notification_rules' requires at least one " \
|
42
|
+
'notification rule id parameter' if ids.nil? || ids.empty?
|
43
|
+
perform_delete('/notification_rules', ids)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
STRING_PARAMS = [:entities, :regex_entities, :tags, :regex_tags,
|
49
|
+
:unknown_media, :warning_media, :critical_media]
|
50
|
+
BOOLEAN_PARAMS = [:unknown_blackhole, :warning_blackhole,
|
51
|
+
:critical_blackhole]
|
52
|
+
OTHER_PARAMS = [:time_restrictions]
|
53
|
+
|
54
|
+
def update_notification_rules_ops(params)
|
55
|
+
ops = params.each_with_object([]) do |(k, v), memo|
|
56
|
+
next unless (STRING_PARAMS + BOOLEAN_PARAMS + OTHER_PARAMS).include?(k)
|
57
|
+
memo << patch_replace('notification_rules', k, v)
|
58
|
+
end
|
59
|
+
raise "'update_notification_rules' did not find any valid update " \
|
60
|
+
'fields' if ops.empty?
|
61
|
+
ops
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'json'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
require 'flapjack-diner/version'
|
6
|
+
require 'flapjack-diner/argument_validator'
|
7
|
+
|
8
|
+
module Flapjack
|
9
|
+
module Diner
|
10
|
+
module Resources
|
11
|
+
module Notifications
|
12
|
+
%w(entities checks).each do |data_type|
|
13
|
+
define_method("create_test_notifications_#{data_type}") do |*args|
|
14
|
+
ids, data = unwrap_ids(*args), unwrap_create_data(*args)
|
15
|
+
raise "'create_test_notifications_#{data_type}' requires at " \
|
16
|
+
"least one #{data_type} id parameter" if ids.nil? || ids.empty?
|
17
|
+
validate_params(data) do
|
18
|
+
validate :query => :summary, :as => :string
|
19
|
+
end
|
20
|
+
perform_post("/test_notifications/#{data_type}", ids,
|
21
|
+
:test_notifications => data)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'json'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
require 'flapjack-diner/version'
|
6
|
+
require 'flapjack-diner/argument_validator'
|
7
|
+
|
8
|
+
module Flapjack
|
9
|
+
module Diner
|
10
|
+
module Resources
|
11
|
+
module PagerdutyCredentials
|
12
|
+
def create_contact_pagerduty_credentials(*args)
|
13
|
+
ids, data = unwrap_ids(*args), unwrap_create_data(*args)
|
14
|
+
raise "'create_contact_pagerduty_credentials' requires at least " \
|
15
|
+
'one contact id parameter' if ids.nil? || ids.empty?
|
16
|
+
validate_params(data) do
|
17
|
+
validate :query => [:service_key], :as => [:required, :string]
|
18
|
+
end
|
19
|
+
perform_post("/contacts/#{escaped_ids(ids)}/pagerduty_credentials",
|
20
|
+
nil, :pagerduty_credentials => data)
|
21
|
+
end
|
22
|
+
|
23
|
+
def pagerduty_credentials(*ids)
|
24
|
+
perform_get('pagerduty_credentials', '/pagerduty_credentials', ids)
|
25
|
+
end
|
26
|
+
|
27
|
+
def update_pagerduty_credentials(*args)
|
28
|
+
ids, params = unwrap_ids(*args), unwrap_params(*args)
|
29
|
+
raise "'update_pagerduty_credentials' requires at least one " \
|
30
|
+
' pagerduty_credentials id parameter' if ids.nil? || ids.empty?
|
31
|
+
validate_params(params) do
|
32
|
+
validate :query => [:service_key, :subdomain,
|
33
|
+
:username, :password], :as => :string
|
34
|
+
end
|
35
|
+
perform_patch("/pagerduty_credentials/#{escaped_ids(ids)}",
|
36
|
+
nil, update_pagerduty_credentials_ops(params))
|
37
|
+
end
|
38
|
+
|
39
|
+
def delete_pagerduty_credentials(*ids)
|
40
|
+
raise "'delete_pagerduty_credentials' requires at least one " \
|
41
|
+
'pagerduty_credentials id parameter' if ids.nil? || ids.empty?
|
42
|
+
perform_delete('/pagerduty_credentials', ids)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def update_pagerduty_credentials_ops(params)
|
48
|
+
ops = params.each_with_object([]) do |(k, v), memo|
|
49
|
+
next unless [:service_key, :subdomain,
|
50
|
+
:username, :password].include?(k)
|
51
|
+
memo << patch_replace('pagerduty_credentials', k, v)
|
52
|
+
end
|
53
|
+
raise "'update_pagerduty_credentials' did not find any valid " \
|
54
|
+
'update fields' if ops.empty?
|
55
|
+
ops
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'json'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
require 'flapjack-diner/version'
|
6
|
+
require 'flapjack-diner/argument_validator'
|
7
|
+
|
8
|
+
module Flapjack
|
9
|
+
module Diner
|
10
|
+
module Resources
|
11
|
+
module Reports
|
12
|
+
%w(entities checks).each do |data_type|
|
13
|
+
define_method("status_report_#{data_type}") do |*ids|
|
14
|
+
perform_get('status_reports', "/status_report/#{data_type}", ids)
|
15
|
+
end
|
16
|
+
|
17
|
+
%w(scheduled_maintenance unscheduled_maintenance
|
18
|
+
downtime outage).each do |report_type|
|
19
|
+
define_method("#{report_type}_report_#{data_type}") do |*args|
|
20
|
+
ids, params = unwrap_ids(*args), unwrap_params(*args)
|
21
|
+
validate_params(params) do
|
22
|
+
validate :query => [:start_time, :end_time], :as => :time
|
23
|
+
end
|
24
|
+
perform_get("#{report_type}_reports",
|
25
|
+
"/#{report_type}_report/#{data_type}",
|
26
|
+
ids, params)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,277 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Flapjack
|
4
|
+
module Diner
|
5
|
+
module Tools
|
6
|
+
SUCCESS_STATUS_CODES = [200, 201, 204]
|
7
|
+
|
8
|
+
attr_accessor :last_error
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def log_request(method_type, req_uri, data = nil)
|
13
|
+
return if logger.nil? || req_uri.nil?
|
14
|
+
log_msg = "#{method_type} #{req_uri}"
|
15
|
+
unless %w(GET DELETE).include?(method_type) || data.nil?
|
16
|
+
log_msg << "\n Body: #{data.inspect}"
|
17
|
+
end
|
18
|
+
logger.info log_msg
|
19
|
+
end
|
20
|
+
|
21
|
+
def perform_get(name, path, ids = [], data = nil)
|
22
|
+
@last_error = nil
|
23
|
+
req_uri = build_uri(path, ids, data)
|
24
|
+
log_request('GET', req_uri, data)
|
25
|
+
handled = handle_response(get(req_uri.request_uri))
|
26
|
+
|
27
|
+
result = (!handled.nil? && handled.is_a?(Hash)) ? handled[name] :
|
28
|
+
handled
|
29
|
+
|
30
|
+
return_keys_as_strings.is_a?(TrueClass) ? result : symbolize(result)
|
31
|
+
end
|
32
|
+
|
33
|
+
def perform_post(path, ids = [], data = nil)
|
34
|
+
@last_error = nil
|
35
|
+
req_uri = build_uri(path, ids)
|
36
|
+
log_request('POST', req_uri, data)
|
37
|
+
opts = if data.nil?
|
38
|
+
{}
|
39
|
+
else
|
40
|
+
{:body => prepare_nested_query(data).to_json,
|
41
|
+
:headers => {'Content-Type' => 'application/vnd.api+json'}}
|
42
|
+
end
|
43
|
+
handle_response(post(req_uri.request_uri, opts))
|
44
|
+
end
|
45
|
+
|
46
|
+
def perform_patch(path, ids = [], data = nil)
|
47
|
+
@last_error = nil
|
48
|
+
req_uri = build_uri(path, ids)
|
49
|
+
log_request('PATCH', req_uri, data)
|
50
|
+
opts = if data.nil?
|
51
|
+
{}
|
52
|
+
else
|
53
|
+
{:body => prepare_nested_query(data).to_json,
|
54
|
+
:headers => {'Content-Type' => 'application/json-patch+json'}}
|
55
|
+
end
|
56
|
+
handle_response(patch(req_uri.request_uri, opts))
|
57
|
+
end
|
58
|
+
|
59
|
+
def perform_delete(path, ids = [], data = nil)
|
60
|
+
@last_error = nil
|
61
|
+
req_uri = build_uri(path, ids, data)
|
62
|
+
log_request('DELETE', req_uri, data)
|
63
|
+
handle_response(delete(req_uri.request_uri))
|
64
|
+
end
|
65
|
+
|
66
|
+
def log_response(response)
|
67
|
+
return if logger.nil? || !response.respond_to?(:code)
|
68
|
+
response_message = " Response Code: #{response.code}"
|
69
|
+
unless response.message.nil? || (response.message.eql?(''))
|
70
|
+
response_message << " #{response.message}"
|
71
|
+
end
|
72
|
+
logger.info response_message
|
73
|
+
return if response.body.nil?
|
74
|
+
logger.info " Response Body: #{response.body[0..300]}"
|
75
|
+
end
|
76
|
+
|
77
|
+
def handle_response(response)
|
78
|
+
log_response(response)
|
79
|
+
return true if 204.eql?(response.code)
|
80
|
+
parsed = if response.respond_to?(:parsed_response)
|
81
|
+
response.parsed_response
|
82
|
+
else
|
83
|
+
nil
|
84
|
+
end
|
85
|
+
return parsed if [200, 201].include?(response.code)
|
86
|
+
@last_error = handle_error(response.code, parsed)
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
|
90
|
+
def handle_error(code, parsed)
|
91
|
+
case parsed
|
92
|
+
when Hash
|
93
|
+
err = {'status_code' => code}.merge(parsed)
|
94
|
+
return_keys_as_strings.is_a?(TrueClass) ? err : symbolize(err)
|
95
|
+
else
|
96
|
+
parsed
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def validate_params(query = {}, &validation)
|
101
|
+
return unless block_given?
|
102
|
+
case query
|
103
|
+
when Array
|
104
|
+
query.each do |q|
|
105
|
+
ArgumentValidator.new(q).instance_eval(&validation)
|
106
|
+
end
|
107
|
+
else
|
108
|
+
ArgumentValidator.new(query).instance_eval(&validation)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# copied from Rack::Utils -- builds the query string for GETs
|
113
|
+
def build_nested_query(value, prefix = nil)
|
114
|
+
case value
|
115
|
+
when Array
|
116
|
+
build_array_query(value, prefix)
|
117
|
+
when Hash
|
118
|
+
build_hash_query(value, prefix)
|
119
|
+
else
|
120
|
+
build_data_query(value, prefix)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def build_array_query(value, prefix)
|
125
|
+
value.map {|v| build_nested_query(v, "#{prefix}[]") }.join('&')
|
126
|
+
end
|
127
|
+
|
128
|
+
def build_hash_query(value, prefix)
|
129
|
+
value.map do |k, v|
|
130
|
+
data = prefix ? "#{prefix}[#{escape(k)}]" : escape(k)
|
131
|
+
build_nested_query(v, data)
|
132
|
+
end.join('&')
|
133
|
+
end
|
134
|
+
|
135
|
+
def build_data_query(value, prefix)
|
136
|
+
if value.respond_to?(:iso8601)
|
137
|
+
raise(ArgumentError, 'Value must be a Hash') if prefix.nil?
|
138
|
+
"#{prefix}=#{escape(value.iso8601)}"
|
139
|
+
elsif value.is_a?(String) || value.is_a?(Integer)
|
140
|
+
raise(ArgumentError, 'Value must be a Hash') if prefix.nil?
|
141
|
+
"#{prefix}=#{escape(value.to_s)}"
|
142
|
+
else
|
143
|
+
prefix
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def escaped_ids(ids = [])
|
148
|
+
ids.map {|id| URI.escape(id.to_s) }.join(',')
|
149
|
+
end
|
150
|
+
|
151
|
+
def escape(s)
|
152
|
+
URI.encode_www_form_component(s)
|
153
|
+
end
|
154
|
+
|
155
|
+
def unwrap_ids(*args)
|
156
|
+
args.select {|a| a.is_a?(String) || a.is_a?(Integer) }
|
157
|
+
end
|
158
|
+
|
159
|
+
def unwrap_params(*args)
|
160
|
+
args.each_with_object({}) do |e, a|
|
161
|
+
a.update(symbolize(e)) if e.is_a?(Hash)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def unwrap_data(*args)
|
166
|
+
args.select {|a| a.is_a?(Array) && a.all? {|av| av.is_a?(Hash) } }
|
167
|
+
.reduce([], &:'+')
|
168
|
+
end
|
169
|
+
|
170
|
+
def unwrap_create_data(*args)
|
171
|
+
data_h = args.select {|a| a.is_a?(Hash) }
|
172
|
+
data_a = unwrap_data(*args)
|
173
|
+
|
174
|
+
raise 'Create data may be passed as a Hash or an Array of Hashes, ' \
|
175
|
+
'not both' unless data_h.empty? || data_a.empty?
|
176
|
+
|
177
|
+
data = data_h
|
178
|
+
data = data_a if data.empty?
|
179
|
+
data = nil if data.empty?
|
180
|
+
data
|
181
|
+
end
|
182
|
+
|
183
|
+
def patch_replace(type, k, v)
|
184
|
+
{:op => 'replace',
|
185
|
+
:path => "/#{type}/0/#{k}",
|
186
|
+
:value => v}
|
187
|
+
end
|
188
|
+
|
189
|
+
def patch_add(type, linked, v)
|
190
|
+
{:op => 'add',
|
191
|
+
:path => "/#{type}/0/links/#{linked}/-",
|
192
|
+
:value => v}
|
193
|
+
end
|
194
|
+
|
195
|
+
def patch_remove(type, linked, v)
|
196
|
+
{:op => 'remove',
|
197
|
+
:path => "/#{type}/0/links/#{linked}/#{v}"}
|
198
|
+
end
|
199
|
+
|
200
|
+
# used for the JSON data hashes in POST, PUT, DELETE
|
201
|
+
def prepare_nested_query(value)
|
202
|
+
case value
|
203
|
+
when Array
|
204
|
+
prepare_array_query(value)
|
205
|
+
when Hash
|
206
|
+
prepare_hash_query(value)
|
207
|
+
else
|
208
|
+
prepare_data_query(value)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def prepare_array_query(value)
|
213
|
+
value.map {|v| prepare_nested_query(v) }
|
214
|
+
end
|
215
|
+
|
216
|
+
def prepare_hash_query(value)
|
217
|
+
value.each_with_object({}) do |(k, v), a|
|
218
|
+
a[k] = prepare_nested_query(v)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def prepare_data_query(value)
|
223
|
+
if value.respond_to?(:iso8601)
|
224
|
+
value.iso8601
|
225
|
+
else
|
226
|
+
case value
|
227
|
+
when Integer, TrueClass, FalseClass, NilClass
|
228
|
+
value
|
229
|
+
else
|
230
|
+
value.to_s
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def normalise_port(port_str, protocol)
|
236
|
+
if port_str.nil? || port_str.to_i < 1 || port_str.to_i > 65_535
|
237
|
+
'https'.eql?(protocol) ? 443 : 80
|
238
|
+
else
|
239
|
+
port_str.to_i
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def protocol_host_port
|
244
|
+
%r{^(?:(?<protocol>https?)://)
|
245
|
+
(?<host>[a-zA-Z0-9][a-zA-Z0-9\.\-]*[a-zA-Z0-9])
|
246
|
+
(?::(?<port>\d+))?
|
247
|
+
}ix =~ base_uri
|
248
|
+
|
249
|
+
protocol = protocol.nil? ? 'http' : protocol.downcase
|
250
|
+
[protocol, host, normalise_port(port, protocol)]
|
251
|
+
end
|
252
|
+
|
253
|
+
def build_uri(path, ids = [], params = [])
|
254
|
+
pr, ho, po = protocol_host_port
|
255
|
+
path += '/' + escaped_ids(ids) unless ids.nil? || ids.empty?
|
256
|
+
query = if params.nil? || params.empty?
|
257
|
+
nil
|
258
|
+
else
|
259
|
+
build_nested_query(params)
|
260
|
+
end
|
261
|
+
URI::HTTP.build(:protocol => pr, :host => ho, :port => po,
|
262
|
+
:path => path, :query => query)
|
263
|
+
end
|
264
|
+
|
265
|
+
def symbolize(obj)
|
266
|
+
case obj
|
267
|
+
when Hash
|
268
|
+
obj.each_with_object({}) {|(k, v), a| a[k.to_sym] = symbolize(v) }
|
269
|
+
when Array
|
270
|
+
obj.each_with_object([]) {|e, a| a << symbolize(e) }
|
271
|
+
else
|
272
|
+
obj
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|