kennel 1.80.0 → 1.83.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Readme.md +23 -8
- data/lib/kennel/api.rb +22 -0
- data/lib/kennel/models/dashboard.rb +22 -19
- data/lib/kennel/models/monitor.rb +2 -2
- data/lib/kennel/models/record.rb +12 -10
- data/lib/kennel/models/slo.rb +2 -2
- data/lib/kennel/syncer.rb +53 -40
- data/lib/kennel/tasks.rb +28 -2
- data/lib/kennel/utils.rb +5 -0
- data/lib/kennel/version.rb +1 -1
- data/template/Readme.md +23 -8
- 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: 5c3c766383bf7f533e74b935a6faf2272e552bb7c974fa15261c70aa9d31e800
|
4
|
+
data.tar.gz: 339c1dcad92fc215d2a26b7091bbe57dc98d3f6ed5a01666e1ee683162bf20e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97abfc77652ab1afb6192c97f038dc6caa33a2f8aa505c62c6782fef1359b431d93cf293c737d9f2ed03c9bb4d22d0474ac916586aa7ed15863900dc8eb1791d
|
7
|
+
data.tar.gz: 13e247e4ab000410155b9f4835686168a2017c1ee2fd8b4e0f6619c54f0b055581b1acc855d12ac06b0ed3920782722fbffa1d303ea7aa04ed92104ab1ec6908
|
data/Readme.md
CHANGED
@@ -212,15 +212,20 @@ removing the `id` will cause kennel to create a new resource in datadog.
|
|
212
212
|
Some validations might be too strict for your usecase or just wrong, please [open an issue](https://github.com/grosser/kennel/issues) and
|
213
213
|
to unblock use the `validate: -> { false }` option.
|
214
214
|
|
215
|
-
### Linking with
|
215
|
+
### Linking resources with kennel_id
|
216
216
|
|
217
|
-
|
217
|
+
Link resources with their kennel_id in the format `project kennel_id` + `:` + `resource kennel_id`,
|
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.
|
218
220
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
221
|
+
|Resource|Type|Syntax|
|
222
|
+
|---|---|---|
|
223
|
+
|Dashboard|uptime|`monitor: {id: "foo:bar"}`|
|
224
|
+
|Dashboard|alert_graph|`alert_id: "foo:bar"`|
|
225
|
+
|Dashboard|slo|`slo_id: "foo:bar"`|
|
226
|
+
|Monitor|composite|`query: -> { "%{foo:bar} && %{foo:baz}" }`|
|
227
|
+
|Monitor|slo alert|`query: -> { "error_budget(\"%{foo:bar}\").over(\"7d\") > 123.0" }`|
|
228
|
+
|Slo|monitor|`monitor_ids: -> ["foo:bar"]`|
|
224
229
|
|
225
230
|
### Debugging changes locally
|
226
231
|
|
@@ -268,9 +273,19 @@ Run `rake kennel:alerts TAG=service:my-service` to see all un-muted alerts for a
|
|
268
273
|
|
269
274
|
### Grepping through all of datadog
|
270
275
|
|
271
|
-
|
276
|
+
```Bash
|
277
|
+
rake kennel:dump > tmp/dump
|
278
|
+
cat tmp/dump | grep foo
|
279
|
+
```
|
272
280
|
focus on a single type: `TYPE=monitors`
|
273
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
|
+
|
274
289
|
### Find all monitors with No-Data
|
275
290
|
|
276
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
|
|
@@ -140,16 +139,16 @@ module Kennel
|
|
140
139
|
when "uptime"
|
141
140
|
if ids = definition[:monitor_ids]
|
142
141
|
definition[:monitor_ids] = ids.map do |id|
|
143
|
-
tracking_id?(id) ? resolve_link(id, :monitor, id_map, **args) : id
|
142
|
+
tracking_id?(id) ? (resolve_link(id, :monitor, id_map, **args) || id) : id
|
144
143
|
end
|
145
144
|
end
|
146
145
|
when "alert_graph"
|
147
146
|
if (id = definition[:alert_id]) && tracking_id?(id)
|
148
|
-
definition[:alert_id] = resolve_link(id, :monitor, id_map, **args).to_s
|
147
|
+
definition[:alert_id] = (resolve_link(id, :monitor, id_map, **args) || id).to_s
|
149
148
|
end
|
150
149
|
when "slo"
|
151
150
|
if (id = definition[:slo_id]) && tracking_id?(id)
|
152
|
-
definition[:slo_id] = resolve_link(id, :slo, id_map, **args).to_s
|
151
|
+
definition[:slo_id] = (resolve_link(id, :slo, id_map, **args) || id).to_s
|
153
152
|
end
|
154
153
|
end
|
155
154
|
end
|
@@ -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
|
@@ -116,7 +116,7 @@ module Kennel
|
|
116
116
|
when "composite", "slo alert"
|
117
117
|
type = (as_json[:type] == "composite" ? :monitor : :slo)
|
118
118
|
as_json[:query] = as_json[:query].gsub(/%{(.*?)}/) do
|
119
|
-
resolve_link($1, type, id_map, **args)
|
119
|
+
resolve_link($1, type, id_map, **args) || $&
|
120
120
|
end
|
121
121
|
end
|
122
122
|
end
|
@@ -125,7 +125,7 @@ module Kennel
|
|
125
125
|
"monitor"
|
126
126
|
end
|
127
127
|
|
128
|
-
def url(id)
|
128
|
+
def self.url(id)
|
129
129
|
Utils.path_to_url "/monitors##{id}/edit"
|
130
130
|
end
|
131
131
|
|
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)
|
@@ -65,19 +68,18 @@ module Kennel
|
|
65
68
|
|
66
69
|
private
|
67
70
|
|
68
|
-
def resolve_link(
|
69
|
-
|
70
|
-
if
|
71
|
+
def resolve_link(tracking_id, type, id_map, force:)
|
72
|
+
id = id_map[tracking_id]
|
73
|
+
if id == :new
|
71
74
|
if force
|
72
|
-
#
|
73
|
-
invalid! "#{id} needs to already exist, try again"
|
75
|
+
invalid! "#{type} #{tracking_id} was referenced but is also created by the current run.\nIt could not be created because of a circular dependency, try creating only some of the resources"
|
74
76
|
else
|
75
|
-
|
77
|
+
nil # will be re-resolved after the linked object was created
|
76
78
|
end
|
77
|
-
elsif
|
78
|
-
|
79
|
+
elsif id
|
80
|
+
id
|
79
81
|
else
|
80
|
-
invalid! "Unable to find #{type} #{
|
82
|
+
invalid! "Unable to find #{type} #{tracking_id} (does not exist and is not being created by the current run)"
|
81
83
|
end
|
82
84
|
end
|
83
85
|
|
data/lib/kennel/models/slo.rb
CHANGED
@@ -58,7 +58,7 @@ module Kennel
|
|
58
58
|
"slo"
|
59
59
|
end
|
60
60
|
|
61
|
-
def url(id)
|
61
|
+
def self.url(id)
|
62
62
|
Utils.path_to_url "/slo?slo_id=#{id}"
|
63
63
|
end
|
64
64
|
|
@@ -69,7 +69,7 @@ module Kennel
|
|
69
69
|
def resolve_linked_tracking_ids!(id_map, **args)
|
70
70
|
return unless as_json[:monitor_ids] # ignore_default can remove it
|
71
71
|
as_json[:monitor_ids] = as_json[:monitor_ids].map do |id|
|
72
|
-
id.is_a?(String) ? resolve_link(id, :monitor, id_map, **args) : id
|
72
|
+
id.is_a?(String) ? (resolve_link(id, :monitor, id_map, **args) || id) : id
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
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
|
|
@@ -38,25 +37,16 @@ module Kennel
|
|
38
37
|
end
|
39
38
|
|
40
39
|
def update
|
41
|
-
|
42
|
-
|
43
|
-
@create.each do |_, e|
|
44
|
-
e.resolve_linked_tracking_ids!({}, force: true)
|
45
|
-
|
40
|
+
each_resolved @create do |_, e|
|
46
41
|
reply = @api.create e.class.api_resource, e.as_json
|
47
42
|
id = reply.fetch(:id)
|
48
|
-
|
49
|
-
|
50
|
-
changed.delete e
|
51
|
-
resolve_linked_tracking_ids! from: [reply], to: changed
|
52
|
-
|
53
|
-
Kennel.out.puts "Created #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.url(id)}"
|
43
|
+
populate_id_map [reply] # allow resolving ids we could previously no resolve
|
44
|
+
Kennel.out.puts "Created #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.class.url(id)}"
|
54
45
|
end
|
55
46
|
|
56
|
-
@update
|
57
|
-
e.resolve_linked_tracking_ids!({}, force: true)
|
47
|
+
each_resolved @update do |id, e|
|
58
48
|
@api.update e.class.api_resource, id, e.as_json
|
59
|
-
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)}"
|
60
50
|
end
|
61
51
|
|
62
52
|
@delete.each do |id, _, a|
|
@@ -67,6 +57,37 @@ module Kennel
|
|
67
57
|
|
68
58
|
private
|
69
59
|
|
60
|
+
# loop over items until everything is resolved or crash when we get stuck
|
61
|
+
# this solves cases like composite monitors depending on each other or monitor->monitor slo->slo monitor chains
|
62
|
+
def each_resolved(list)
|
63
|
+
list = list.dup
|
64
|
+
loop do
|
65
|
+
return if list.empty?
|
66
|
+
list.reject! do |id, e|
|
67
|
+
if resolved?(e)
|
68
|
+
yield id, e
|
69
|
+
true
|
70
|
+
else
|
71
|
+
false
|
72
|
+
end
|
73
|
+
end ||
|
74
|
+
assert_resolved(list[0][1]) # resolve something or show a circular dependency error
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# TODO: optimize by storing an instance variable if already resolved
|
79
|
+
def resolved?(e)
|
80
|
+
assert_resolved e
|
81
|
+
true
|
82
|
+
rescue ValidationError
|
83
|
+
false
|
84
|
+
end
|
85
|
+
|
86
|
+
# raises ValidationError when not resolved
|
87
|
+
def assert_resolved(e)
|
88
|
+
resolve_linked_tracking_ids! [e], force: true
|
89
|
+
end
|
90
|
+
|
70
91
|
def noop?
|
71
92
|
@create.empty? && @update.empty? && @delete.empty?
|
72
93
|
end
|
@@ -74,9 +95,15 @@ module Kennel
|
|
74
95
|
def calculate_diff
|
75
96
|
@update = []
|
76
97
|
@delete = []
|
98
|
+
@id_map = {}
|
77
99
|
|
78
100
|
actual = Progress.progress("Downloading definitions") { download_definitions }
|
79
|
-
|
101
|
+
|
102
|
+
# resolve dependencies to avoid diff
|
103
|
+
populate_id_map actual
|
104
|
+
@expected.each { |e| @id_map[e.tracking_id] ||= :new }
|
105
|
+
resolve_linked_tracking_ids! @expected
|
106
|
+
|
80
107
|
filter_by_project! actual
|
81
108
|
|
82
109
|
Progress.progress "Diffing" do
|
@@ -89,10 +116,10 @@ module Kennel
|
|
89
116
|
end
|
90
117
|
end
|
91
118
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
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 }
|
96
123
|
|
97
124
|
# pick out things to update or delete
|
98
125
|
items.each do |e, a|
|
@@ -107,27 +134,11 @@ module Kennel
|
|
107
134
|
|
108
135
|
ensure_all_ids_found
|
109
136
|
@create = @expected.map { |e| [nil, e] }
|
110
|
-
@create.sort_by! { |_, e| -DELETE_ORDER.index(e.class.api_resource) }
|
111
137
|
end
|
112
138
|
|
113
139
|
@delete.sort_by! { |_, _, a| DELETE_ORDER.index a.fetch(:api_resource) }
|
114
140
|
end
|
115
141
|
|
116
|
-
# Make diff work even though we cannot mass-fetch definitions
|
117
|
-
def fill_details(a, cache)
|
118
|
-
resource = a.fetch(:api_resource)
|
119
|
-
args = [resource, a.fetch(:id)]
|
120
|
-
full = cache.fetch(args, a[:modified] || a.fetch(:modified_at)) do
|
121
|
-
@api.show(*args)
|
122
|
-
end
|
123
|
-
a.merge!(full)
|
124
|
-
end
|
125
|
-
|
126
|
-
def details_cache(&block)
|
127
|
-
cache = FileCache.new CACHE_FILE, Kennel::VERSION
|
128
|
-
cache.open(&block)
|
129
|
-
end
|
130
|
-
|
131
142
|
def download_definitions
|
132
143
|
Utils.parallel(Models::Record.subclasses.map(&:api_resource)) do |api_resource|
|
133
144
|
results = @api.list(api_resource, with_downtimes: false) # lookup monitors without adding unnecessary downtime information
|
@@ -214,10 +225,12 @@ module Kennel
|
|
214
225
|
end
|
215
226
|
end
|
216
227
|
|
217
|
-
def
|
218
|
-
|
219
|
-
|
220
|
-
|
228
|
+
def populate_id_map(actual)
|
229
|
+
actual.each { |a| @id_map[tracking_id(a)] = a.fetch(:id) }
|
230
|
+
end
|
231
|
+
|
232
|
+
def resolve_linked_tracking_ids!(list, force: false)
|
233
|
+
list.each { |e| e.resolve_linked_tracking_ids!(@id_map, force: force) }
|
221
234
|
end
|
222
235
|
|
223
236
|
def filter_by_project!(definitions)
|
data/lib/kennel/tasks.rb
CHANGED
@@ -130,15 +130,41 @@ 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
|
+
Kennel.out.puts models[parsed.fetch("api_resource")].url(parsed.fetch("id"))
|
162
|
+
else
|
163
|
+
Kennel.out.puts resource
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
142
168
|
task :environment do
|
143
169
|
require "kennel"
|
144
170
|
gem "dotenv"
|
data/lib/kennel/utils.rb
CHANGED
@@ -23,6 +23,11 @@ module Kennel
|
|
23
23
|
.downcase
|
24
24
|
end
|
25
25
|
|
26
|
+
# for child projects, not used internally
|
27
|
+
def title_case(string)
|
28
|
+
string.split(/[\s_]/).map(&:capitalize) * " "
|
29
|
+
end
|
30
|
+
|
26
31
|
# simplified version of https://apidock.com/rails/ActiveSupport/Inflector/parameterize
|
27
32
|
def parameterize(string)
|
28
33
|
string
|
data/lib/kennel/version.rb
CHANGED
data/template/Readme.md
CHANGED
@@ -194,15 +194,20 @@ 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
|
-
|
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.
|
200
202
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
203
|
+
|Resource|Type|Syntax|
|
204
|
+
|---|---|---|
|
205
|
+
|Dashboard|uptime|`monitor: {id: "foo:bar"}`|
|
206
|
+
|Dashboard|alert_graph|`alert_id: "foo:bar"`|
|
207
|
+
|Dashboard|slo|`slo_id: "foo:bar"`|
|
208
|
+
|Monitor|composite|`query: -> { "%{foo:bar} && %{foo:baz}" }`|
|
209
|
+
|Monitor|slo alert|`query: -> { "error_budget(\"%{foo:bar}\").over(\"7d\") > 123.0" }`|
|
210
|
+
|Slo|monitor|`monitor_ids: -> ["foo:bar"]`|
|
206
211
|
|
207
212
|
### Debugging changes locally
|
208
213
|
|
@@ -250,9 +255,19 @@ Run `rake kennel:alerts TAG=service:my-service` to see all un-muted alerts for a
|
|
250
255
|
|
251
256
|
### Grepping through all of datadog
|
252
257
|
|
253
|
-
|
258
|
+
```Bash
|
259
|
+
rake kennel:dump > tmp/dump
|
260
|
+
cat tmp/dump | grep foo
|
261
|
+
```
|
254
262
|
focus on a single type: `TYPE=monitors`
|
255
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
|
+
|
256
271
|
### Find all monitors with No-Data
|
257
272
|
|
258
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.83.0
|
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-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|