kennel 1.68.0 → 1.72.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 +3 -3
- data/lib/kennel/github_reporter.rb +1 -0
- data/lib/kennel/models/dashboard.rb +11 -1
- data/lib/kennel/models/monitor.rb +6 -0
- data/lib/kennel/models/record.rb +8 -0
- data/lib/kennel/models/slo.rb +4 -0
- data/lib/kennel/syncer.rb +38 -15
- data/lib/kennel/tasks.rb +13 -9
- data/lib/kennel/utils.rb +12 -0
- data/lib/kennel/version.rb +1 -1
- data/template/Readme.md +2 -2
- 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: 1406c5a505ce76ac2af360ba94344a9c942b9c84381f5c695ff18015c62ca530
|
4
|
+
data.tar.gz: 04c66c7a56702edd27248c9c71a5c9aa87787c65714aa95112b8479bc5e3fc59
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d0b0abc841eca42e4e13a67634c22653bb56f212304baff189344339a8aeaa877eb753dbd06c5989c938b53eb5ff5c9b3764c726556aebdf5ec20c379498922
|
7
|
+
data.tar.gz: 1458d96d7d17b04beae03734106d305e55c05006de958c0eb54b2e2ab982b7646209a8d4331604b02a754fb84ccee346525a330a18161b14fc604006478067e3
|
data/Readme.md
CHANGED
@@ -23,7 +23,7 @@ Manage datadog monitors/dashboards/slos as code
|
|
23
23
|
```
|
24
24
|
- add a basic projects and teams so others can copy-paste to get started
|
25
25
|
- setup travis build for your repo
|
26
|
-
- uncomment `.travis.yml` section for automated
|
26
|
+
- uncomment `.travis.yml` section for automated PR planing and datadog updates on merge
|
27
27
|
- follow `Setup` in your repos Readme.md
|
28
28
|
<!-- NOT IN -->
|
29
29
|
|
@@ -72,7 +72,7 @@ end
|
|
72
72
|
### Updating an existing monitor
|
73
73
|
- use [datadog monitor UI](https://app.datadoghq.com/monitors/manage) to find a monitor
|
74
74
|
- get the `id` from the url
|
75
|
-
- run `
|
75
|
+
- run `URL='https://app.datadoghq.com/monitors/123' bundle exec rake kennel:import` and copy the output
|
76
76
|
- find or create a project in `projects/`
|
77
77
|
- add the monitor to `parts: [` list, for example:
|
78
78
|
```Ruby
|
@@ -118,7 +118,7 @@ end
|
|
118
118
|
### Updating an existing dashboard
|
119
119
|
- go to [datadog dashboard UI](https://app.datadoghq.com/dashboard/lists) and click on _New Dashboard_ to find a dashboard
|
120
120
|
- get the `id` from the url
|
121
|
-
- run `
|
121
|
+
- run `URL='https://app.datadoghq.com/dashboard/bet-foo-bar' bundle exec rake kennel:import` and copy the output
|
122
122
|
- find or create a project in `projects/`
|
123
123
|
- add a dashboard to `parts: [` list, for example:
|
124
124
|
```Ruby
|
@@ -16,12 +16,13 @@ module Kennel
|
|
16
16
|
TIMESERIES_DEFAULTS = { show_legend: false, legend_size: "0" }.freeze
|
17
17
|
SUPPORTED_DEFINITION_OPTIONS = [:events, :markers, :precision].freeze
|
18
18
|
|
19
|
-
settings :title, :description, :definitions, :widgets, :layout_type
|
19
|
+
settings :title, :description, :definitions, :widgets, :layout_type, :template_variable_presets
|
20
20
|
|
21
21
|
defaults(
|
22
22
|
description: -> { "" },
|
23
23
|
definitions: -> { [] },
|
24
24
|
widgets: -> { [] },
|
25
|
+
template_variable_presets: -> { [] },
|
25
26
|
id: -> { nil }
|
26
27
|
)
|
27
28
|
|
@@ -102,6 +103,7 @@ module Kennel
|
|
102
103
|
title: "#{title}#{LOCK}",
|
103
104
|
description: description,
|
104
105
|
template_variables: render_template_variables,
|
106
|
+
template_variable_presets: template_variable_presets,
|
105
107
|
widgets: all_widgets
|
106
108
|
}
|
107
109
|
|
@@ -116,6 +118,10 @@ module Kennel
|
|
116
118
|
Utils.path_to_url "/dashboard/#{id}"
|
117
119
|
end
|
118
120
|
|
121
|
+
def self.parse_url(url)
|
122
|
+
url[/\/dashboard\/([a-z\d-]+)/, 1]
|
123
|
+
end
|
124
|
+
|
119
125
|
def resolve_linked_tracking_ids(id_map)
|
120
126
|
widgets = as_json[:widgets].flat_map { |w| [w, *w.dig(:definition, :widgets) || []] }
|
121
127
|
widgets.each do |widget|
|
@@ -163,6 +169,10 @@ module Kennel
|
|
163
169
|
super
|
164
170
|
|
165
171
|
validate_template_variables data, :widgets
|
172
|
+
|
173
|
+
# Avoid diff from datadog presets sorting.
|
174
|
+
presets = data[:template_variable_presets]
|
175
|
+
invalid! "template_variable_presets must be sorted by name" if presets != presets.sort_by { |p| p[:name] }
|
166
176
|
end
|
167
177
|
|
168
178
|
def render_definitions
|
@@ -120,6 +120,12 @@ module Kennel
|
|
120
120
|
Utils.path_to_url "/monitors##{id}/edit"
|
121
121
|
end
|
122
122
|
|
123
|
+
# datadog uses both / and # as separator in it's links
|
124
|
+
def self.parse_url(url)
|
125
|
+
return unless id = url[/\/monitors[\/#](\d+)/, 1]
|
126
|
+
Integer(id)
|
127
|
+
end
|
128
|
+
|
123
129
|
def self.normalize(expected, actual)
|
124
130
|
super
|
125
131
|
options = actual.fetch(:options)
|
data/lib/kennel/models/record.rb
CHANGED
data/lib/kennel/models/slo.rb
CHANGED
@@ -62,6 +62,10 @@ module Kennel
|
|
62
62
|
Utils.path_to_url "/slo?slo_id=#{id}"
|
63
63
|
end
|
64
64
|
|
65
|
+
def self.parse_url(url)
|
66
|
+
url[/\/slo\?slo_id=([a-z\d]+)/, 1]
|
67
|
+
end
|
68
|
+
|
65
69
|
def resolve_linked_tracking_ids(id_map)
|
66
70
|
as_json[:monitor_ids] = as_json[:monitor_ids].map do |id|
|
67
71
|
id.is_a?(String) ? resolve_link(id, :monitor, id_map) : id
|
data/lib/kennel/syncer.rb
CHANGED
@@ -19,6 +19,7 @@ module Kennel
|
|
19
19
|
end
|
20
20
|
@expected.each { |e| add_tracking_id e }
|
21
21
|
calculate_diff
|
22
|
+
prevent_irreversible_partial_updates
|
22
23
|
end
|
23
24
|
|
24
25
|
def plan
|
@@ -42,7 +43,6 @@ module Kennel
|
|
42
43
|
Kennel.out.puts "Created #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.url(reply.fetch(:id))}"
|
43
44
|
end
|
44
45
|
|
45
|
-
block_irreversible_partial_updates
|
46
46
|
@update.each do |id, e|
|
47
47
|
@api.update e.class.api_resource, id, e.as_json
|
48
48
|
Kennel.out.puts "Updated #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.url(id)}"
|
@@ -151,7 +151,7 @@ module Kennel
|
|
151
151
|
return if list.empty?
|
152
152
|
list.each do |_, e, a, diff|
|
153
153
|
api_resource = (e ? e.class.api_resource : a.fetch(:api_resource))
|
154
|
-
Kennel.out.puts Utils.color(color, "#{step} #{api_resource} #{
|
154
|
+
Kennel.out.puts Utils.color(color, "#{step} #{api_resource} #{e&.tracking_id || tracking_id(a)}")
|
155
155
|
print_diff(diff) if diff # only for update
|
156
156
|
end
|
157
157
|
end
|
@@ -159,12 +159,12 @@ module Kennel
|
|
159
159
|
def print_diff(diff)
|
160
160
|
diff.each do |type, field, old, new|
|
161
161
|
if type == "+"
|
162
|
-
temp = new
|
163
|
-
new = old
|
162
|
+
temp = Utils.pretty_inspect(new)
|
163
|
+
new = Utils.pretty_inspect(old)
|
164
164
|
old = temp
|
165
165
|
else # ~ and -
|
166
|
-
old = old
|
167
|
-
new = new
|
166
|
+
old = Utils.pretty_inspect(old)
|
167
|
+
new = Utils.pretty_inspect(new)
|
168
168
|
end
|
169
169
|
|
170
170
|
if (old + new).size > 100
|
@@ -177,18 +177,30 @@ module Kennel
|
|
177
177
|
end
|
178
178
|
end
|
179
179
|
|
180
|
-
|
180
|
+
# Do not add tracking-id when working with existing ids on a branch,
|
181
|
+
# so resource do not get deleted from merges to master.
|
182
|
+
# Also make sure the diff still makes sense, by kicking out the now noop-update.
|
183
|
+
#
|
184
|
+
# Note: ideally we'd never add tracking in the first place, but at that point we do not know the diff yet
|
185
|
+
def prevent_irreversible_partial_updates
|
181
186
|
return unless @project_filter
|
182
|
-
|
183
|
-
e.id
|
184
|
-
|
187
|
+
@update.select! do |_, e, _, diff|
|
188
|
+
next true unless e.id # short circuit for performance
|
189
|
+
|
190
|
+
diff.select! do |field_diff|
|
191
|
+
(_, field, old, new) = field_diff
|
192
|
+
next true unless tracking_field?(field)
|
193
|
+
|
194
|
+
if (old_tracking = tracking_value(old))
|
195
|
+
old_tracking == tracking_value(new) || raise("do not update! (atm unreachable)")
|
196
|
+
else
|
197
|
+
field_diff[3] = remove_tracking_id(e) # make plan output match update
|
198
|
+
old != field_diff[3]
|
199
|
+
end
|
185
200
|
end
|
201
|
+
|
202
|
+
!diff.empty?
|
186
203
|
end
|
187
|
-
raise <<~TEXT
|
188
|
-
Updates with PROJECT= filter should not update tracking id in #{TRACKING_FIELDS.join("/")} of resources with a set `id:`,
|
189
|
-
since this makes them get deleted by a full update.
|
190
|
-
Remove the `id:` to test them out, which will result in a copy being created and later deleted.
|
191
|
-
TEXT
|
192
204
|
end
|
193
205
|
|
194
206
|
def resolve_linked_tracking_ids(actual)
|
@@ -212,6 +224,13 @@ module Kennel
|
|
212
224
|
json[field] = "#{json[field]}\n-- Managed by kennel #{e.tracking_id} in #{e.project.class.file_location}, do not modify manually".lstrip
|
213
225
|
end
|
214
226
|
|
227
|
+
def remove_tracking_id(e)
|
228
|
+
json = e.as_json
|
229
|
+
field = tracking_field(json)
|
230
|
+
value = json[field]
|
231
|
+
json[field] = value.dup.sub!(/\n?-- Managed by kennel .*/, "") || raise("did not find tracking id in #{value}")
|
232
|
+
end
|
233
|
+
|
215
234
|
def tracking_id(a)
|
216
235
|
tracking_value a[tracking_field(a)]
|
217
236
|
end
|
@@ -223,5 +242,9 @@ module Kennel
|
|
223
242
|
def tracking_field(a)
|
224
243
|
TRACKING_FIELDS.detect { |f| a.key?(f) }
|
225
244
|
end
|
245
|
+
|
246
|
+
def tracking_field?(field)
|
247
|
+
TRACKING_FIELDS.include?(field.to_sym)
|
248
|
+
end
|
226
249
|
end
|
227
250
|
end
|
data/lib/kennel/tasks.rb
CHANGED
@@ -68,7 +68,7 @@ namespace :kennel do
|
|
68
68
|
Kennel.update
|
69
69
|
end
|
70
70
|
|
71
|
-
desc "update if this is a push to the default branch, otherwise plan
|
71
|
+
desc "update if this is a push to the default branch, otherwise plan"
|
72
72
|
task :travis do
|
73
73
|
on_default_branch = (ENV["TRAVIS_BRANCH"] == (ENV["DEFAULT_BRANCH"] || "master"))
|
74
74
|
is_push = (ENV["TRAVIS_PULL_REQUEST"] == "false")
|
@@ -79,9 +79,7 @@ namespace :kennel do
|
|
79
79
|
"kennel:plan" # show plan in travis logs
|
80
80
|
end
|
81
81
|
|
82
|
-
|
83
|
-
Rake::Task[task_name].invoke
|
84
|
-
end
|
82
|
+
Rake::Task[task_name].invoke
|
85
83
|
end
|
86
84
|
|
87
85
|
desc "show unmuted alerts filtered by TAG, for example TAG=team:foo"
|
@@ -111,12 +109,18 @@ namespace :kennel do
|
|
111
109
|
end
|
112
110
|
end
|
113
111
|
|
114
|
-
desc "Convert existing resources to copy-
|
112
|
+
desc "Convert existing resources to copy-pasteable definitions to import existing resources (call with URL= or call with RESOURCE= and ID=)"
|
115
113
|
task import: :environment do
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
114
|
+
if (id = ENV["ID"]) && (resource = ENV["RESOURCE"])
|
115
|
+
id = Integer(id) if id =~ /^\d+$/ # dashboards can have alphanumeric ids
|
116
|
+
elsif (url = ENV["URL"])
|
117
|
+
resource, id = Kennel::Models::Record.parse_any_url(url) || Kennel::Tasks.abort("Unable to parse url")
|
118
|
+
else
|
119
|
+
possible_resources = Kennel::Models::Record.subclasses.map(&:api_resource)
|
120
|
+
Kennel::Tasks.abort("Call with URL= or call with RESOURCE=#{possible_resources.join(" or ")} and ID=")
|
121
|
+
end
|
122
|
+
|
123
|
+
Kennel.out.puts Kennel::Importer.new(Kennel.send(:api)).import(resource, id)
|
120
124
|
end
|
121
125
|
|
122
126
|
desc "Dump ALL of datadog config as raw json ... useful for grep/search TYPE=slo|monitor|dashboard"
|
data/lib/kennel/utils.rb
CHANGED
@@ -19,6 +19,7 @@ module Kennel
|
|
19
19
|
.gsub(/::/, "_") # Foo::Bar -> foo_bar
|
20
20
|
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') # FOOBar -> foo_bar
|
21
21
|
.gsub(/([a-z\d])([A-Z])/, '\1_\2') # fooBar -> foo_bar
|
22
|
+
.tr("-", "_") # foo-bar -> foo_bar
|
22
23
|
.downcase
|
23
24
|
end
|
24
25
|
|
@@ -142,6 +143,17 @@ module Kennel
|
|
142
143
|
else []
|
143
144
|
end
|
144
145
|
end
|
146
|
+
|
147
|
+
# TODO: use awesome-print or similar, but it has too many monkey-patches
|
148
|
+
# https://github.com/amazing-print/amazing_print/issues/36
|
149
|
+
def pretty_inspect(object)
|
150
|
+
string = object.inspect
|
151
|
+
string.gsub!(/:([a-z_]+)=>/, "\\1: ")
|
152
|
+
10.times do
|
153
|
+
string.gsub!(/{(\S.*?\S)}/, "{ \\1 }") || break
|
154
|
+
end
|
155
|
+
string
|
156
|
+
end
|
145
157
|
end
|
146
158
|
end
|
147
159
|
end
|
data/lib/kennel/version.rb
CHANGED
data/template/Readme.md
CHANGED
@@ -54,7 +54,7 @@ end
|
|
54
54
|
### Updating an existing monitor
|
55
55
|
- use [datadog monitor UI](https://app.datadoghq.com/monitors/manage) to find a monitor
|
56
56
|
- get the `id` from the url
|
57
|
-
- run `
|
57
|
+
- run `URL='https://app.datadoghq.com/monitors/123' bundle exec rake kennel:import` and copy the output
|
58
58
|
- find or create a project in `projects/`
|
59
59
|
- add the monitor to `parts: [` list, for example:
|
60
60
|
```Ruby
|
@@ -100,7 +100,7 @@ end
|
|
100
100
|
### Updating an existing dashboard
|
101
101
|
- go to [datadog dashboard UI](https://app.datadoghq.com/dashboard/lists) and click on _New Dashboard_ to find a dashboard
|
102
102
|
- get the `id` from the url
|
103
|
-
- run `
|
103
|
+
- run `URL='https://app.datadoghq.com/dashboard/bet-foo-bar' bundle exec rake kennel:import` and copy the output
|
104
104
|
- find or create a project in `projects/`
|
105
105
|
- add a dashboard to `parts: [` list, for example:
|
106
106
|
```Ruby
|
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.72.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: 2020-
|
11
|
+
date: 2020-06-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|