kennel 2.21.0 → 2.22.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/lib/kennel/api.rb +75 -28
- data/lib/kennel/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b931b9c2d75d6e0d69967e3088795e72d8a5f3f558ff1dd1b6de03efde7aabf8
|
|
4
|
+
data.tar.gz: '081dde24122f351f92c7dc8f16e7877ec3dcd1f7226bd1e4fc126abd3044798d'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ffd01589e499c34d72dd477f5e3e00369582c42cacbf7eae19493e791b84ecbac42c48c5bf34e3d8c8a6245f555d29f62e3cb879cc2e24892b555766f9b5ffcc
|
|
7
|
+
data.tar.gz: 7a95114123013fd90a64993f86f217b5f4379e0d3dc6b19d876d2ac42f404b4907ef35b2396c811c4291838ca8183073523090bb2d89882dbbffa6972b40bb38
|
data/lib/kennel/api.rb
CHANGED
|
@@ -22,8 +22,9 @@ module Kennel
|
|
|
22
22
|
@client = Faraday.new(url: url)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
def show(api_resource, id, params = {})
|
|
26
|
-
response = request :get, "/api/v1/#{api_resource}/#{id}", params: params
|
|
25
|
+
def show(api_resource, id, params = {}, ignore_404: false)
|
|
26
|
+
response = request :get, "/api/v1/#{api_resource}/#{id}", params: params, ignore_404: ignore_404
|
|
27
|
+
return if response.nil? # 404 that was ignored
|
|
27
28
|
response = response.fetch(:data) if api_resource == "slo"
|
|
28
29
|
response[:id] = response.delete(:public_id) if api_resource == "synthetics/tests"
|
|
29
30
|
self.class.with_tracking(api_resource, response)
|
|
@@ -54,6 +55,8 @@ module Kennel
|
|
|
54
55
|
end
|
|
55
56
|
|
|
56
57
|
def update(api_resource, id, attributes)
|
|
58
|
+
restore_widget_ids(id, attributes) if api_resource == "dashboard"
|
|
59
|
+
|
|
57
60
|
response = request :put, "/api/v1/#{api_resource}/#{id}", body: attributes
|
|
58
61
|
response[:id] = response.delete(:public_id) if api_resource == "synthetics/tests"
|
|
59
62
|
self.class.with_tracking(api_resource, response)
|
|
@@ -74,12 +77,44 @@ module Kennel
|
|
|
74
77
|
# fill the resource with the full response from the `show` if `list` does not return it
|
|
75
78
|
def fill_details!(api_resource, list)
|
|
76
79
|
details_cache do |cache|
|
|
77
|
-
Utils.parallel(list) { |a| fill_detail!(api_resource, a, cache) }
|
|
80
|
+
results = Utils.parallel(list) { |a| fill_detail!(api_resource, a, cache) }
|
|
81
|
+
|
|
82
|
+
# drop resources that were not found (race condition between listing and showing)
|
|
83
|
+
list.select!.with_index { |_, i| results[i][1] }
|
|
84
|
+
|
|
85
|
+
results.count { |uncached, _| uncached }
|
|
78
86
|
end
|
|
79
87
|
end
|
|
80
88
|
|
|
81
89
|
private
|
|
82
90
|
|
|
91
|
+
# keep widget ids stable so stored dashboard urls that point at a widget do not break on every update
|
|
92
|
+
# widget ids are dropped when diffing since they change on every update, but that also changes them in datadog
|
|
93
|
+
# and breaks stored urls that point at a specific widget, so restore the previous ids by matching widget titles
|
|
94
|
+
def restore_widget_ids(id, attributes)
|
|
95
|
+
return unless (widgets = attributes[:widgets])
|
|
96
|
+
return unless (current = show("dashboard", id, {}, ignore_404: true))
|
|
97
|
+
|
|
98
|
+
ids = {}
|
|
99
|
+
each_widget_with_key(current[:widgets]) { |key, widget| ids[key] = widget[:id] }
|
|
100
|
+
each_widget_with_key(widgets) do |key, widget|
|
|
101
|
+
next unless (id = ids[key])
|
|
102
|
+
widget[:id] ||= id # do not set nil, that breaks updates
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# yield each widget with a key built from its (optional) group title and its own title
|
|
107
|
+
def each_widget_with_key(widgets, prefix: nil, &block)
|
|
108
|
+
widgets.each do |widget|
|
|
109
|
+
title = widget.dig(:definition, :title)
|
|
110
|
+
key = [prefix, title].compact.join("-")
|
|
111
|
+
yield key, widget
|
|
112
|
+
if (nested = widget.dig(:definition, :widgets))
|
|
113
|
+
each_widget_with_key(nested, prefix: title, &block)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
83
118
|
def with_pagination(enabled, params)
|
|
84
119
|
return yield params unless enabled
|
|
85
120
|
raise ArgumentError if params[:limit] || params[:offset]
|
|
@@ -96,15 +131,16 @@ module Kennel
|
|
|
96
131
|
end
|
|
97
132
|
|
|
98
133
|
# Make diff work even though we cannot mass-fetch definitions
|
|
134
|
+
# @return [uncached, found]
|
|
99
135
|
def fill_detail!(api_resource, a, cache)
|
|
100
|
-
uncached =
|
|
136
|
+
uncached = false
|
|
101
137
|
args = [api_resource, a.fetch(:id)]
|
|
102
138
|
full = cache.fetch(args, a.fetch(:modified_at)) do
|
|
103
|
-
uncached
|
|
104
|
-
show(*args)
|
|
139
|
+
uncached = true
|
|
140
|
+
show(*args, ignore_404: true)
|
|
105
141
|
end
|
|
106
|
-
a.merge!(full)
|
|
107
|
-
uncached
|
|
142
|
+
a.merge!(full) if full
|
|
143
|
+
[uncached, !!full]
|
|
108
144
|
end
|
|
109
145
|
|
|
110
146
|
def details_cache(&block)
|
|
@@ -117,29 +153,18 @@ module Kennel
|
|
|
117
153
|
cached = (ENV["FORCE_GET_CACHE"] && method == :get)
|
|
118
154
|
|
|
119
155
|
with_cache cached, path do
|
|
120
|
-
response =
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
request.headers["DD-API-KEY"] = @api_key
|
|
127
|
-
request.headers["DD-APPLICATION-KEY"] = @app_key
|
|
128
|
-
end
|
|
156
|
+
response = request_with_retries(path, tries) do
|
|
157
|
+
@client.send(method, path) do |request|
|
|
158
|
+
request.body = JSON.generate(body) if body
|
|
159
|
+
request.headers["Content-type"] = "application/json"
|
|
160
|
+
request.headers["DD-API-KEY"] = @api_key
|
|
161
|
+
request.headers["DD-APPLICATION-KEY"] = @app_key
|
|
129
162
|
end
|
|
130
|
-
|
|
131
|
-
if response.status == 429
|
|
132
|
-
sleep_until_rate_limit_resets(response)
|
|
133
|
-
redo
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
last_try = (i == tries - 1)
|
|
137
|
-
break if last_try || response.status < 500
|
|
138
|
-
Kennel.err.puts "Retrying on server error #{response.status} for #{path}"
|
|
139
|
-
sleep retry_backoff_time(i)
|
|
140
163
|
end
|
|
141
164
|
|
|
142
|
-
if
|
|
165
|
+
next if response.status == 404 && ignore_404
|
|
166
|
+
|
|
167
|
+
unless response.success?
|
|
143
168
|
message = "Error #{response.status} during #{method.upcase} #{path}\n"
|
|
144
169
|
message << "request:\n#{JSON.pretty_generate(body)}\nresponse:\n" if body
|
|
145
170
|
message << response.body.encode(message.encoding, invalid: :replace, undef: :replace)
|
|
@@ -154,6 +179,28 @@ module Kennel
|
|
|
154
179
|
end
|
|
155
180
|
end
|
|
156
181
|
|
|
182
|
+
# retry on rate-limits and server errors, giving up after `tries` attempts
|
|
183
|
+
def request_with_retries(path, tries, &block)
|
|
184
|
+
raise ArgumentError, "tries must be > 0" if tries < 1
|
|
185
|
+
response = nil
|
|
186
|
+
tries.times do |i|
|
|
187
|
+
response = Utils.retry Faraday::ConnectionFailed, Faraday::TimeoutError, times: 2, &block
|
|
188
|
+
|
|
189
|
+
# we do not count 429s into the "tries", tries are only for real errors
|
|
190
|
+
# so this could loop forever if datadog consistently 429s
|
|
191
|
+
if response.status == 429
|
|
192
|
+
sleep_until_rate_limit_resets(response)
|
|
193
|
+
redo
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
last_try = (i == tries - 1)
|
|
197
|
+
break if last_try || response.status < 500
|
|
198
|
+
Kennel.err.puts "Retrying on server error #{response.status} for #{path}"
|
|
199
|
+
sleep retry_backoff_time(i)
|
|
200
|
+
end
|
|
201
|
+
response
|
|
202
|
+
end
|
|
203
|
+
|
|
157
204
|
def sleep_until_rate_limit_resets(response)
|
|
158
205
|
limit = response.headers["x-ratelimit-limit"]
|
|
159
206
|
period = response.headers["x-ratelimit-period"]
|
data/lib/kennel/version.rb
CHANGED