kennel 1.81.1 → 1.84.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Readme.md +12 -2
- data/lib/kennel/api.rb +22 -0
- data/lib/kennel/models/dashboard.rb +19 -16
- data/lib/kennel/models/monitor.rb +1 -11
- data/lib/kennel/models/record.rb +4 -1
- data/lib/kennel/models/slo.rb +1 -1
- data/lib/kennel/syncer.rb +6 -22
- data/lib/kennel/tasks.rb +30 -2
- data/lib/kennel/version.rb +1 -1
- data/template/Readme.md +15 -4
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69237d514f39fc9c8a1fad937bb038ce526424231fe4cc64ce3e69ac43820650
|
4
|
+
data.tar.gz: 7bf30b567652a4f6808073ff46487c7945570a6bb4c34afe60b60ad14bb30f80
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3cffb601fc240f3d6f87e41a369a772a0dce2c3de411d8eb290aa67ca89c30fbdde7d05c701e844cf12a92c398438c975175de2739129ad62d85f7c9eb667c30
|
7
|
+
data.tar.gz: 9bf61895741f6b762627f05a756355445fdd43bc488db04ae7a406a2fbc314311adf8e745ec87d4eb512cefd250d0cd3e6b777f3bb3d7cb7543e0cd6eb317ea1
|
data/Readme.md
CHANGED
@@ -216,7 +216,7 @@ to unblock use the `validate: -> { false }` option.
|
|
216
216
|
|
217
217
|
Link resources with their kennel_id in the format `project kennel_id` + `:` + `resource kennel_id`,
|
218
218
|
this should be used to create dependent resources like monitor + slos,
|
219
|
-
so they can be created in a single update and can be re-created if any of them is deleted.
|
219
|
+
so they can be created in a single update and can be re-created if any of them is deleted.
|
220
220
|
|
221
221
|
|Resource|Type|Syntax|
|
222
222
|
|---|---|---|
|
@@ -273,9 +273,19 @@ Run `rake kennel:alerts TAG=service:my-service` to see all un-muted alerts for a
|
|
273
273
|
|
274
274
|
### Grepping through all of datadog
|
275
275
|
|
276
|
-
|
276
|
+
```Bash
|
277
|
+
rake kennel:dump > tmp/dump
|
278
|
+
cat tmp/dump | grep foo
|
279
|
+
```
|
277
280
|
focus on a single type: `TYPE=monitors`
|
278
281
|
|
282
|
+
Show full resources or just their urls by pattern:
|
283
|
+
```Bash
|
284
|
+
rake kennel:dump_grep DUMP=tmp/dump PATTERN=foo URLS=true
|
285
|
+
https://foo.datadog.com/dasboard/123
|
286
|
+
https://foo.datadog.com/monitor/123
|
287
|
+
```
|
288
|
+
|
279
289
|
### Find all monitors with No-Data
|
280
290
|
|
281
291
|
`rake kennel:nodata TAG=team:foo`
|
data/lib/kennel/api.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module Kennel
|
3
|
+
# encapsulates knowledge around how the api works
|
3
4
|
class Api
|
5
|
+
CACHE_FILE = "tmp/cache/details"
|
6
|
+
|
4
7
|
def initialize(app_key, api_key)
|
5
8
|
@app_key = app_key
|
6
9
|
@api_key = api_key
|
@@ -49,8 +52,27 @@ module Kennel
|
|
49
52
|
request :delete, "/api/v1/#{api_resource}/#{id}", params: { force: "true" }, ignore_404: true
|
50
53
|
end
|
51
54
|
|
55
|
+
def fill_details!(api_resource, list)
|
56
|
+
return unless api_resource == "dashboard"
|
57
|
+
details_cache do |cache|
|
58
|
+
Utils.parallel(list) { |a| fill_detail!(api_resource, a, cache) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
52
62
|
private
|
53
63
|
|
64
|
+
# Make diff work even though we cannot mass-fetch definitions
|
65
|
+
def fill_detail!(api_resource, a, cache)
|
66
|
+
args = [api_resource, a.fetch(:id)]
|
67
|
+
full = cache.fetch(args, a.fetch(:modified_at)) { show(*args) }
|
68
|
+
a.merge!(full)
|
69
|
+
end
|
70
|
+
|
71
|
+
def details_cache(&block)
|
72
|
+
cache = FileCache.new CACHE_FILE, Kennel::VERSION
|
73
|
+
cache.open(&block)
|
74
|
+
end
|
75
|
+
|
54
76
|
def request(method, path, body: nil, params: {}, ignore_404: false)
|
55
77
|
params = params.merge(application_key: @app_key, api_key: @api_key)
|
56
78
|
query = Faraday::FlatParamsEncoder.encode(params)
|
@@ -5,7 +5,6 @@ module Kennel
|
|
5
5
|
include TemplateVariables
|
6
6
|
include OptionalValidations
|
7
7
|
|
8
|
-
API_LIST_INCOMPLETE = true
|
9
8
|
DASHBOARD_DEFAULTS = { template_variables: [] }.freeze
|
10
9
|
READONLY_ATTRIBUTES = superclass::READONLY_ATTRIBUTES + [
|
11
10
|
:author_handle, :author_name, :modified_at, :url, :is_read_only, :notify_list
|
@@ -124,7 +123,7 @@ module Kennel
|
|
124
123
|
@json
|
125
124
|
end
|
126
125
|
|
127
|
-
def url(id)
|
126
|
+
def self.url(id)
|
128
127
|
Utils.path_to_url "/dashboard/#{id}"
|
129
128
|
end
|
130
129
|
|
@@ -186,22 +185,26 @@ module Kennel
|
|
186
185
|
end
|
187
186
|
|
188
187
|
def render_definitions(definitions)
|
189
|
-
definitions.map do |title, type, display_type, queries, options = {},
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
188
|
+
definitions.map do |title, type, display_type, queries, options = {}, too_many_args = nil|
|
189
|
+
if title.is_a?(Hash) && !type
|
190
|
+
title # user gave a full widget, just use it
|
191
|
+
else
|
192
|
+
# validate inputs
|
193
|
+
if too_many_args || (!title || !type || !queries || !options.is_a?(Hash))
|
194
|
+
raise ArgumentError, "Expected exactly 5 arguments for each definition (title, type, display_type, queries, options)"
|
195
|
+
end
|
196
|
+
if (SUPPORTED_DEFINITION_OPTIONS | options.keys) != SUPPORTED_DEFINITION_OPTIONS
|
197
|
+
raise ArgumentError, "Supported options are: #{SUPPORTED_DEFINITION_OPTIONS.map(&:inspect).join(", ")}"
|
198
|
+
end
|
197
199
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
200
|
+
# build definition
|
201
|
+
requests = Array(queries).map do |q|
|
202
|
+
request = { q: q }
|
203
|
+
request[:display_type] = display_type if display_type
|
204
|
+
request
|
205
|
+
end
|
206
|
+
{ definition: { title: title, type: type, requests: requests, **options } }
|
203
207
|
end
|
204
|
-
{ definition: { title: title, type: type, requests: requests, **options } }
|
205
208
|
end
|
206
209
|
end
|
207
210
|
end
|
@@ -5,8 +5,6 @@ module Kennel
|
|
5
5
|
include OptionalValidations
|
6
6
|
|
7
7
|
RENOTIFY_INTERVALS = [0, 10, 20, 30, 40, 50, 60, 90, 120, 180, 240, 300, 360, 720, 1440].freeze # minutes
|
8
|
-
# 2d and 1w are valid timeframes for anomaly monitors
|
9
|
-
QUERY_INTERVALS = ["1m", "5m", "10m", "15m", "30m", "1h", "2h", "4h", "1d", "2d", "1w"].freeze
|
10
8
|
OPTIONAL_SERVICE_CHECK_THRESHOLDS = [:ok, :warning].freeze
|
11
9
|
READONLY_ATTRIBUTES = superclass::READONLY_ATTRIBUTES + [
|
12
10
|
:multi, :matching_downtimes, :overall_state_modified, :overall_state, :restricted_roles
|
@@ -125,7 +123,7 @@ module Kennel
|
|
125
123
|
"monitor"
|
126
124
|
end
|
127
125
|
|
128
|
-
def url(id)
|
126
|
+
def self.url(id)
|
129
127
|
Utils.path_to_url "/monitors##{id}/edit"
|
130
128
|
end
|
131
129
|
|
@@ -202,14 +200,6 @@ module Kennel
|
|
202
200
|
invalid! "renotify_interval must be one of #{RENOTIFY_INTERVALS.join(", ")}"
|
203
201
|
end
|
204
202
|
|
205
|
-
if type == "query alert"
|
206
|
-
# verify interval is valid
|
207
|
-
interval = data.fetch(:query)[/\(last_(\S+?)\)/, 1]
|
208
|
-
if interval && !QUERY_INTERVALS.include?(interval)
|
209
|
-
invalid! "query interval was #{interval}, but must be one of #{QUERY_INTERVALS.join(", ")}"
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
203
|
if ["query alert", "service check"].include?(type) # TODO: most likely more types need this
|
214
204
|
# verify is_match/is_exact_match uses available variables
|
215
205
|
message = data.fetch(:message)
|
data/lib/kennel/models/record.rb
CHANGED
@@ -6,7 +6,6 @@ module Kennel
|
|
6
6
|
READONLY_ATTRIBUTES = [
|
7
7
|
:deleted, :id, :created, :created_at, :creator, :org_id, :modified, :modified_at, :api_resource
|
8
8
|
].freeze
|
9
|
-
API_LIST_INCOMPLETE = false
|
10
9
|
|
11
10
|
settings :id, :kennel_id
|
12
11
|
|
@@ -19,6 +18,10 @@ module Kennel
|
|
19
18
|
end
|
20
19
|
end
|
21
20
|
|
21
|
+
def api_resource_map
|
22
|
+
subclasses.map { |s| [s.api_resource, s] }.to_h
|
23
|
+
end
|
24
|
+
|
22
25
|
private
|
23
26
|
|
24
27
|
def normalize(_expected, actual)
|
data/lib/kennel/models/slo.rb
CHANGED
data/lib/kennel/syncer.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module Kennel
|
3
3
|
class Syncer
|
4
|
-
CACHE_FILE = "tmp/cache/details" # keep in sync with .travis.yml caching
|
5
4
|
TRACKING_FIELDS = [:message, :description].freeze
|
6
5
|
DELETE_ORDER = ["dashboard", "slo", "monitor"].freeze # dashboards references monitors + slos, slos reference monitors
|
7
6
|
|
@@ -42,12 +41,12 @@ module Kennel
|
|
42
41
|
reply = @api.create e.class.api_resource, e.as_json
|
43
42
|
id = reply.fetch(:id)
|
44
43
|
populate_id_map [reply] # allow resolving ids we could previously no resolve
|
45
|
-
Kennel.out.puts "Created #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.url(id)}"
|
44
|
+
Kennel.out.puts "Created #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.class.url(id)}"
|
46
45
|
end
|
47
46
|
|
48
47
|
each_resolved @update do |id, e|
|
49
48
|
@api.update e.class.api_resource, id, e.as_json
|
50
|
-
Kennel.out.puts "Updated #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.url(id)}"
|
49
|
+
Kennel.out.puts "Updated #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.class.url(id)}"
|
51
50
|
end
|
52
51
|
|
53
52
|
@delete.each do |id, _, a|
|
@@ -117,10 +116,10 @@ module Kennel
|
|
117
116
|
end
|
118
117
|
end
|
119
118
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
119
|
+
# fill details of things we need to compare
|
120
|
+
detailed = Hash.new { |h, k| h[k] = [] }
|
121
|
+
items.each { |e, a| detailed[a[:api_resource]] << a if e }
|
122
|
+
detailed.each { |api_resource, actuals| @api.fill_details! api_resource, actuals }
|
124
123
|
|
125
124
|
# pick out things to update or delete
|
126
125
|
items.each do |e, a|
|
@@ -140,21 +139,6 @@ module Kennel
|
|
140
139
|
@delete.sort_by! { |_, _, a| DELETE_ORDER.index a.fetch(:api_resource) }
|
141
140
|
end
|
142
141
|
|
143
|
-
# Make diff work even though we cannot mass-fetch definitions
|
144
|
-
def fill_details(a, cache)
|
145
|
-
resource = a.fetch(:api_resource)
|
146
|
-
args = [resource, a.fetch(:id)]
|
147
|
-
full = cache.fetch(args, a[:modified] || a.fetch(:modified_at)) do
|
148
|
-
@api.show(*args)
|
149
|
-
end
|
150
|
-
a.merge!(full)
|
151
|
-
end
|
152
|
-
|
153
|
-
def details_cache(&block)
|
154
|
-
cache = FileCache.new CACHE_FILE, Kennel::VERSION
|
155
|
-
cache.open(&block)
|
156
|
-
end
|
157
|
-
|
158
142
|
def download_definitions
|
159
143
|
Utils.parallel(Models::Record.subclasses.map(&:api_resource)) do |api_resource|
|
160
144
|
results = @api.list(api_resource, with_downtimes: false) # lookup monitors without adding unnecessary downtime information
|
data/lib/kennel/tasks.rb
CHANGED
@@ -130,15 +130,43 @@ namespace :kennel do
|
|
130
130
|
if type = ENV["TYPE"]
|
131
131
|
[type]
|
132
132
|
else
|
133
|
-
Kennel::Models::Record.
|
133
|
+
Kennel::Models::Record.api_resource_map.keys
|
134
134
|
end
|
135
|
+
api = Kennel.send(:api)
|
136
|
+
list = nil
|
137
|
+
|
135
138
|
resources.each do |resource|
|
136
|
-
Kennel.
|
139
|
+
Kennel::Progress.progress("Downloading #{resource}") do
|
140
|
+
list = api.list(resource)
|
141
|
+
api.fill_details!(resource, list)
|
142
|
+
end
|
143
|
+
list.each do |r|
|
144
|
+
r[:api_resource] = resource
|
137
145
|
Kennel.out.puts JSON.pretty_generate(r)
|
138
146
|
end
|
139
147
|
end
|
140
148
|
end
|
141
149
|
|
150
|
+
desc "Find items from dump by pattern DUMP= PATTERN= [URLS=true]"
|
151
|
+
task dump_grep: :environment do
|
152
|
+
file = ENV.fetch("DUMP")
|
153
|
+
pattern = Regexp.new ENV.fetch("PATTERN")
|
154
|
+
items = File.read(file).gsub("}\n{", "}--SPLIT--{").split("--SPLIT--")
|
155
|
+
models = Kennel::Models::Record.api_resource_map
|
156
|
+
found = items.grep(pattern)
|
157
|
+
exit 1 if found.empty?
|
158
|
+
found.each do |resource|
|
159
|
+
if ENV["URLS"]
|
160
|
+
parsed = JSON.parse(resource)
|
161
|
+
url = models[parsed.fetch("api_resource")].url(parsed.fetch("id"))
|
162
|
+
title = parsed["title"] || parsed["name"]
|
163
|
+
Kennel.out.puts "#{url} # #{title}"
|
164
|
+
else
|
165
|
+
Kennel.out.puts resource
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
142
170
|
task :environment do
|
143
171
|
require "kennel"
|
144
172
|
gem "dotenv"
|
data/lib/kennel/version.rb
CHANGED
data/template/Readme.md
CHANGED
@@ -194,10 +194,11 @@ removing the `id` will cause kennel to create a new resource in datadog.
|
|
194
194
|
Some validations might be too strict for your usecase or just wrong, please [open an issue](https://github.com/grosser/kennel/issues) and
|
195
195
|
to unblock use the `validate: -> { false }` option.
|
196
196
|
|
197
|
-
### Linking with
|
197
|
+
### Linking resources with kennel_id
|
198
198
|
|
199
|
-
Link resources
|
200
|
-
this should be used to create dependent resources like monitor + slos
|
199
|
+
Link resources with their kennel_id in the format `project kennel_id` + `:` + `resource kennel_id`,
|
200
|
+
this should be used to create dependent resources like monitor + slos,
|
201
|
+
so they can be created in a single update and can be re-created if any of them is deleted.
|
201
202
|
|
202
203
|
|Resource|Type|Syntax|
|
203
204
|
|---|---|---|
|
@@ -254,9 +255,19 @@ Run `rake kennel:alerts TAG=service:my-service` to see all un-muted alerts for a
|
|
254
255
|
|
255
256
|
### Grepping through all of datadog
|
256
257
|
|
257
|
-
|
258
|
+
```Bash
|
259
|
+
rake kennel:dump > tmp/dump
|
260
|
+
cat tmp/dump | grep foo
|
261
|
+
```
|
258
262
|
focus on a single type: `TYPE=monitors`
|
259
263
|
|
264
|
+
Show full resources or just their urls by pattern:
|
265
|
+
```Bash
|
266
|
+
rake kennel:dump_grep DUMP=tmp/dump PATTERN=foo URLS=true
|
267
|
+
https://foo.datadog.com/dasboard/123
|
268
|
+
https://foo.datadog.com/monitor/123
|
269
|
+
```
|
270
|
+
|
260
271
|
### Find all monitors with No-Data
|
261
272
|
|
262
273
|
`rake kennel:nodata TAG=team:foo`
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kennel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.84.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Grosser
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-03-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|