kennel 1.98.3 → 1.102.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- 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/syncer.rb +11 -3
- data/lib/kennel/tasks.rb +36 -6
- data/lib/kennel/version.rb +1 -1
- data/lib/kennel.rb +3 -1
- 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: 424978ab5e025d68c1b30adee8dcf9bbae531c34da1ce02abcc1a97165accb2f
|
4
|
+
data.tar.gz: 762d7b47d15937d0ea9b2bb7d9bb1c79d88b34828a0b455c05aa4d7498fd8f04
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2079300fb176ffe4a3c363c56dfb2fed0901b4f3cfce7ff6fd1c656f8981e16a94628eaa660ffee6a53cc86c9df2d7b604ca877c6c34c61eeec13cbe4b0484ee
|
7
|
+
data.tar.gz: 0113a4f7a4edf4813d66cb15090909e63a47945cfaa4bce22cd9fe40203f3b6dd3705c2f62652df7e09be9e4b7a7d5de1de2092074e2baa524c8392a3bb9b462
|
@@ -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/tasks.rb
CHANGED
@@ -3,6 +3,7 @@ require "English"
|
|
3
3
|
require "kennel"
|
4
4
|
require "kennel/unmuted_alerts"
|
5
5
|
require "kennel/importer"
|
6
|
+
require "json"
|
6
7
|
|
7
8
|
module Kennel
|
8
9
|
module Tasks
|
@@ -89,7 +90,7 @@ namespace :kennel do
|
|
89
90
|
Kennel::UnmutedAlerts.print(Kennel.send(:api), tag)
|
90
91
|
end
|
91
92
|
|
92
|
-
desc "show monitors with no data by TAG, for example TAG=team:foo"
|
93
|
+
desc "show monitors with no data by TAG, for example TAG=team:foo [THRESHOLD_DAYS=7] [FORMAT=json]"
|
93
94
|
task nodata: :environment do
|
94
95
|
tag = ENV["TAG"] || Kennel::Tasks.abort("Call with TAG=foo:bar")
|
95
96
|
monitors = Kennel.send(:api).list("monitor", monitor_tags: tag, group_states: "no data")
|
@@ -97,16 +98,45 @@ namespace :kennel do
|
|
97
98
|
monitors.reject! { |m| m[:tags].include? "nodata:ignore" }
|
98
99
|
if monitors.any?
|
99
100
|
Kennel.err.puts <<~TEXT
|
100
|
-
|
101
|
-
To ignore monitors with nodata, tag the monitor with "nodata:ignore"
|
101
|
+
To ignore monitors with expected nodata, tag it with "nodata:ignore"
|
102
102
|
|
103
103
|
TEXT
|
104
104
|
end
|
105
105
|
|
106
|
+
now = Time.now
|
106
107
|
monitors.each do |m|
|
107
|
-
|
108
|
-
|
109
|
-
|
108
|
+
m[:days_in_no_data] =
|
109
|
+
if m[:overall_state_modified]
|
110
|
+
since = Date.parse(m[:overall_state_modified]).to_time
|
111
|
+
((now - since) / (24 * 60 * 60)).to_i
|
112
|
+
else
|
113
|
+
999
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
if threshold = ENV["THRESHOLD_DAYS"]
|
118
|
+
monitors.select! { |m| m[:days_in_no_data] > Integer(threshold) }
|
119
|
+
end
|
120
|
+
|
121
|
+
monitors.each { |m| m[:url] = Kennel::Utils.path_to_url("/monitors/#{m[:id]}") }
|
122
|
+
|
123
|
+
if ENV["FORMAT"] == "json"
|
124
|
+
report = monitors.map do |m|
|
125
|
+
match = m[:message].to_s.match(/-- Managed by kennel (\S+:\S+) in (\S+), /) || []
|
126
|
+
m.slice(:url, :name, :tags, :days_in_no_data).merge(
|
127
|
+
kennel_tracking_id: match[1],
|
128
|
+
kennel_source: match[2]
|
129
|
+
)
|
130
|
+
end
|
131
|
+
|
132
|
+
Kennel.out.puts JSON.pretty_generate(report)
|
133
|
+
else
|
134
|
+
monitors.each do |m|
|
135
|
+
Kennel.out.puts m[:name]
|
136
|
+
Kennel.out.puts Kennel::Utils.path_to_url("/monitors/#{m[:id]}")
|
137
|
+
Kennel.out.puts "No data since #{m[:days_in_no_data]}d"
|
138
|
+
Kennel.out.puts
|
139
|
+
end
|
110
140
|
end
|
111
141
|
end
|
112
142
|
|
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
|
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.102.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-
|
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
|