kennel 1.98.2 → 1.101.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 +2 -2
- data/lib/kennel/id_map.rb +26 -0
- data/lib/kennel/models/dashboard.rb +6 -9
- data/lib/kennel/models/monitor.rb +36 -16
- data/lib/kennel/models/record.rb +14 -3
- data/lib/kennel/models/slo.rb +1 -1
- data/lib/kennel/models/synthetic_test.rb +1 -1
- data/lib/kennel/syncer.rb +11 -3
- data/lib/kennel/version.rb +1 -1
- data/lib/kennel.rb +3 -1
- data/template/Readme.md +2 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 11220aba046ce1fbc63b8b62e5a60f0cbd4b04862ff7394c0ad43a1776bc34a6
|
4
|
+
data.tar.gz: 257808a3036f97c8b994c35ce0c4c621bafd1a29a43da0cba6c113a9fe8297ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1bcd69ba301375d93c1c72f9cb1ffcf057e06d4aa3f604baada0ed4dce5cd0d4e7675227d9d4ef8f09a80198bc3e14268fcdb4cd1f3623d3a810818bbbe6d4b2
|
7
|
+
data.tar.gz: c189ecb33b4800d9d0e382f82fedbcfa8723f3ff0a27bbd2554528de60d50f774309da9d30c27374ae3365863861f81fae438d4843a34519c4d0a044a4d250ce
|
data/Readme.md
CHANGED
@@ -206,7 +206,7 @@ removing the `id` will cause kennel to create a new resource in datadog.
|
|
206
206
|
Having many projects (and their sub-resources) can quickly get out of hand.
|
207
207
|
|
208
208
|
Use this class structure to keep things organized:
|
209
|
-
```
|
209
|
+
```Ruby
|
210
210
|
# projects/project_a/base.rb
|
211
211
|
module ProjectA
|
212
212
|
class Base < Kennel::Models::Project
|
@@ -217,7 +217,7 @@ module ProjectA
|
|
217
217
|
# projects/project_a/monitors/foo_alert.rb
|
218
218
|
module ProjectA
|
219
219
|
module Monitors
|
220
|
-
class FooAlert < Kennel::
|
220
|
+
class FooAlert < Kennel::Models::Monitor
|
221
221
|
...
|
222
222
|
```
|
223
223
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Kennel
|
3
|
+
class IdMap
|
4
|
+
NEW = :new # will be created during this run
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@map = Hash.new { |h, k| h[k] = {} }
|
8
|
+
end
|
9
|
+
|
10
|
+
def get(type, tracking_id)
|
11
|
+
@map[type][tracking_id]
|
12
|
+
end
|
13
|
+
|
14
|
+
def set(type, tracking_id, id)
|
15
|
+
@map[type][tracking_id] = id
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_new(type, tracking_id)
|
19
|
+
@map[type][tracking_id] = NEW
|
20
|
+
end
|
21
|
+
|
22
|
+
def new?(type, tracking_id)
|
23
|
+
@map[type][tracking_id] == NEW
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -189,16 +189,17 @@ module Kennel
|
|
189
189
|
when "uptime"
|
190
190
|
if ids = definition[:monitor_ids]
|
191
191
|
definition[:monitor_ids] = ids.map do |id|
|
192
|
-
|
192
|
+
resolve(id, :monitor, id_map, **args) || id
|
193
193
|
end
|
194
194
|
end
|
195
195
|
when "alert_graph"
|
196
|
-
if
|
197
|
-
|
196
|
+
if id = definition[:alert_id]
|
197
|
+
resolved = resolve(id, :monitor, id_map, **args) || id
|
198
|
+
definition[:alert_id] = resolved.to_s # even though it's a monitor id
|
198
199
|
end
|
199
200
|
when "slo"
|
200
|
-
if
|
201
|
-
definition[:slo_id] = (
|
201
|
+
if id = definition[:slo_id]
|
202
|
+
definition[:slo_id] = resolve(id, :slo, id_map, **args) || id
|
202
203
|
end
|
203
204
|
end
|
204
205
|
end
|
@@ -206,10 +207,6 @@ module Kennel
|
|
206
207
|
|
207
208
|
private
|
208
209
|
|
209
|
-
def tracking_id?(id)
|
210
|
-
id.is_a?(String) && id.include?(":")
|
211
|
-
end
|
212
|
-
|
213
210
|
# creates queries from metadata to avoid having to keep q and expression in sync
|
214
211
|
#
|
215
212
|
# {q: :metadata, metadata: [{expression: "sum:bar", alias_name: "foo"}, ...], }
|
@@ -121,7 +121,7 @@ module Kennel
|
|
121
121
|
when "composite", "slo alert"
|
122
122
|
type = (as_json[:type] == "composite" ? :monitor : :slo)
|
123
123
|
as_json[:query] = as_json[:query].gsub(/%{(.*?)}/) do
|
124
|
-
|
124
|
+
resolve($1, type, id_map, **args) || $&
|
125
125
|
end
|
126
126
|
end
|
127
127
|
end
|
@@ -214,27 +214,47 @@ module Kennel
|
|
214
214
|
end
|
215
215
|
|
216
216
|
if ["query alert", "service check"].include?(type) # TODO: most likely more types need this
|
217
|
-
|
218
|
-
message = data.fetch(:message)
|
219
|
-
used = message.scan(/{{\s*([#^]is(?:_exact)?_match)\s*([^\s}]+)/)
|
220
|
-
if used.any?
|
221
|
-
allowed = data.fetch(:query)[/by\s*[({]([^})]+)[})]/, 1]
|
222
|
-
.to_s.gsub(/["']/, "").split(/\s*,\s*/)
|
223
|
-
.map! { |w| %("#{w}.name") }
|
224
|
-
used.uniq.each do |match, group|
|
225
|
-
next if allowed.include?(group)
|
226
|
-
invalid!(
|
227
|
-
"#{match} used with #{group}, but can only be used with #{allowed.join(", ")}. " \
|
228
|
-
"Group the query by #{group.sub(".name", "").tr('"', "")} or change the #{match}"
|
229
|
-
)
|
230
|
-
end
|
231
|
-
end
|
217
|
+
validate_message_variables(data)
|
232
218
|
end
|
233
219
|
|
234
220
|
unless ALLOWED_PRIORITY_CLASSES.include?(priority.class)
|
235
221
|
invalid! "priority needs to be an Integer"
|
236
222
|
end
|
237
223
|
end
|
224
|
+
|
225
|
+
# verify is_match/is_exact_match and {{foo.name}} uses available variables
|
226
|
+
def validate_message_variables(data)
|
227
|
+
message = data.fetch(:message)
|
228
|
+
|
229
|
+
used =
|
230
|
+
message.scan(/{{\s*(?:[#^]is(?:_exact)?_match)\s*"([^\s}]+)"/) + # {{#is_match "environment.name" "production"}}
|
231
|
+
message.scan(/{{\s*([^}]+\.name)\s*}}/) # Pod {{pod.name}} failed
|
232
|
+
return if used.empty?
|
233
|
+
used.flatten!(1)
|
234
|
+
used.uniq!
|
235
|
+
|
236
|
+
# TODO
|
237
|
+
# - also match without by
|
238
|
+
# - separate parsers for query and service
|
239
|
+
# - service must always allow `host`, maybe others
|
240
|
+
return unless match = data.fetch(:query).match(/(?:{([^}]*)}\s*)?by\s*[({]([^})]+)[})]/)
|
241
|
+
|
242
|
+
allowed =
|
243
|
+
match[1].to_s.split(/\s*,\s*/).map { |k| k.split(":", 2)[-2] } + # {a:b} -> a TODO: does not work for service check
|
244
|
+
match[2].to_s.gsub(/["']/, "").split(/\s*,\s*/) # by {a} -> a
|
245
|
+
|
246
|
+
allowed.compact!
|
247
|
+
allowed.uniq!
|
248
|
+
allowed.map! { |w| "#{w.tr('"', "")}.name" }
|
249
|
+
|
250
|
+
forbidden = used - allowed
|
251
|
+
return if forbidden.empty?
|
252
|
+
|
253
|
+
invalid! <<~MSG.rstrip
|
254
|
+
Used #{forbidden.join(", ")} in the message, but can only be used with #{allowed.join(", ")}.
|
255
|
+
Group or filter the query by #{forbidden.map { |f| f.sub(".name", "") }.join(", ")} to use it.
|
256
|
+
MSG
|
257
|
+
end
|
238
258
|
end
|
239
259
|
end
|
240
260
|
end
|
data/lib/kennel/models/record.rb
CHANGED
@@ -100,9 +100,20 @@ module Kennel
|
|
100
100
|
|
101
101
|
private
|
102
102
|
|
103
|
+
def resolve(value, type, id_map, force:)
|
104
|
+
if tracking_id?(value)
|
105
|
+
return resolve_link(value, type, id_map, force: force)
|
106
|
+
end
|
107
|
+
|
108
|
+
value
|
109
|
+
end
|
110
|
+
|
111
|
+
def tracking_id?(id)
|
112
|
+
id.is_a?(String) && id.include?(":")
|
113
|
+
end
|
114
|
+
|
103
115
|
def resolve_link(tracking_id, type, id_map, force:)
|
104
|
-
|
105
|
-
if id == :new
|
116
|
+
if id_map.new?(type.to_s, tracking_id)
|
106
117
|
if force
|
107
118
|
invalid!(
|
108
119
|
"#{type} #{tracking_id} was referenced but is also created by the current run.\n" \
|
@@ -111,7 +122,7 @@ module Kennel
|
|
111
122
|
else
|
112
123
|
nil # will be re-resolved after the linked object was created
|
113
124
|
end
|
114
|
-
elsif id
|
125
|
+
elsif id = id_map.get(type.to_s, tracking_id)
|
115
126
|
id
|
116
127
|
else
|
117
128
|
invalid! "Unable to find #{type} #{tracking_id} (does not exist and is not being created by the current run)"
|
data/lib/kennel/models/slo.rb
CHANGED
@@ -70,7 +70,7 @@ module Kennel
|
|
70
70
|
def resolve_linked_tracking_ids!(id_map, **args)
|
71
71
|
return unless as_json[:monitor_ids] # ignore_default can remove it
|
72
72
|
as_json[:monitor_ids] = as_json[:monitor_ids].map do |id|
|
73
|
-
|
73
|
+
resolve(id, :monitor, id_map, **args) || id
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
data/lib/kennel/syncer.rb
CHANGED
@@ -122,7 +122,7 @@ module Kennel
|
|
122
122
|
def calculate_diff
|
123
123
|
@update = []
|
124
124
|
@delete = []
|
125
|
-
@id_map =
|
125
|
+
@id_map = IdMap.new
|
126
126
|
|
127
127
|
actual = Progress.progress("Downloading definitions") { download_definitions }
|
128
128
|
|
@@ -254,11 +254,19 @@ module Kennel
|
|
254
254
|
end
|
255
255
|
|
256
256
|
def populate_id_map(expected, actual)
|
257
|
+
expected.each do |e|
|
258
|
+
@id_map.set_new(e.class.api_resource, e.tracking_id)
|
259
|
+
end
|
260
|
+
|
257
261
|
actual.each do |a|
|
258
262
|
next unless tracking_id = a.fetch(:tracking_id)
|
259
|
-
@id_map
|
263
|
+
next unless @id_map.get(a.fetch(:klass).api_resource, tracking_id)
|
264
|
+
|
265
|
+
@id_map.set(a.fetch(:klass).api_resource, tracking_id, a.fetch(:id))
|
266
|
+
if a[:klass].api_resource == "synthetics/tests"
|
267
|
+
@id_map.set(Kennel::Models::Monitor.api_resource, tracking_id, a.fetch(:monitor_id))
|
268
|
+
end
|
260
269
|
end
|
261
|
-
expected.each { |e| @id_map[e.tracking_id] ||= :new }
|
262
270
|
end
|
263
271
|
|
264
272
|
def resolve_linked_tracking_ids!(list, force: false)
|
data/lib/kennel/version.rb
CHANGED
data/lib/kennel.rb
CHANGED
@@ -9,6 +9,7 @@ require "kennel/version"
|
|
9
9
|
require "kennel/utils"
|
10
10
|
require "kennel/progress"
|
11
11
|
require "kennel/syncer"
|
12
|
+
require "kennel/id_map"
|
12
13
|
require "kennel/api"
|
13
14
|
require "kennel/github_reporter"
|
14
15
|
require "kennel/subclass_tracking"
|
@@ -69,7 +70,8 @@ module Kennel
|
|
69
70
|
path = "generated/#{part.tracking_id.tr("/", ":").sub(":", "/")}.json"
|
70
71
|
used << File.dirname(path) # only 1 level of sub folders, so this is safe
|
71
72
|
used << path
|
72
|
-
|
73
|
+
payload = part.as_json.merge(api_resource: part.class.api_resource)
|
74
|
+
write_file_if_necessary(path, JSON.pretty_generate(payload) << "\n")
|
73
75
|
end
|
74
76
|
|
75
77
|
# deleting all is slow, so only delete the extras
|
data/template/Readme.md
CHANGED
@@ -189,7 +189,7 @@ removing the `id` will cause kennel to create a new resource in datadog.
|
|
189
189
|
Having many projects (and their sub-resources) can quickly get out of hand.
|
190
190
|
|
191
191
|
Use this class structure to keep things organized:
|
192
|
-
```
|
192
|
+
```Ruby
|
193
193
|
# projects/project_a/base.rb
|
194
194
|
module ProjectA
|
195
195
|
class Base < Kennel::Models::Project
|
@@ -200,7 +200,7 @@ module ProjectA
|
|
200
200
|
# projects/project_a/monitors/foo_alert.rb
|
201
201
|
module ProjectA
|
202
202
|
module Monitors
|
203
|
-
class FooAlert < Kennel::
|
203
|
+
class FooAlert < Kennel::Models::Monitor
|
204
204
|
...
|
205
205
|
```
|
206
206
|
|
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.101.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-11-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -77,6 +77,7 @@ files:
|
|
77
77
|
- lib/kennel/api.rb
|
78
78
|
- lib/kennel/file_cache.rb
|
79
79
|
- lib/kennel/github_reporter.rb
|
80
|
+
- lib/kennel/id_map.rb
|
80
81
|
- lib/kennel/importer.rb
|
81
82
|
- lib/kennel/models/base.rb
|
82
83
|
- lib/kennel/models/dashboard.rb
|