kennel 1.80.0 → 1.83.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/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
|