kennel 1.139.0 → 1.141.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/lib/kennel/models/monitor.rb +34 -5
- data/lib/kennel/models/record.rb +17 -38
- data/lib/kennel/optional_validations.rb +52 -43
- data/lib/kennel/version.rb +1 -1
- data/lib/kennel.rb +1 -1
- 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: e82dacccbb0c14e8b8bedd9545b782b7405ca057a02724e89aa03f0a624fa3e4
|
4
|
+
data.tar.gz: f70302b436c823c6cbcb765edbd56da1103b83fadf398c2c904372a8f73b36a7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c34733bc887129a6bda1ebc96898ca46cec0346bb008af8020d4159d23db515de54d8db8813749ba7883c0417b0cecb243a530789a4a99d6aa146679996007aa
|
7
|
+
data.tar.gz: a7821e5e67899aa71a068bfabbd895f7c69a369a187b7245510fc969a69d4354bad352cfb953f64293140c6282fa21d38f0e8695ab60d9186a19b30faeddd019
|
@@ -30,12 +30,11 @@ module Kennel
|
|
30
30
|
}.freeze
|
31
31
|
DEFAULT_ESCALATION_MESSAGE = ["", nil].freeze
|
32
32
|
ALLOWED_PRIORITY_CLASSES = [NilClass, Integer].freeze
|
33
|
-
ALLOWED_UNLINKED = [] # rubocop:disable Style/MutableConstant placeholder for custom overrides
|
34
33
|
|
35
34
|
settings(
|
36
35
|
:query, :name, :message, :escalation_message, :critical, :type, :renotify_interval, :warning, :timeout_h, :evaluation_delay,
|
37
36
|
:ok, :no_data_timeframe, :notify_no_data, :notify_audit, :tags, :critical_recovery, :warning_recovery, :require_full_window,
|
38
|
-
:threshold_windows, :scheduling_options, :new_host_delay, :new_group_delay, :priority, :
|
37
|
+
:threshold_windows, :scheduling_options, :new_host_delay, :new_group_delay, :priority, :variables, :on_missing_data,
|
39
38
|
:notification_preset_name
|
40
39
|
)
|
41
40
|
|
@@ -265,6 +264,7 @@ module Kennel
|
|
265
264
|
end
|
266
265
|
|
267
266
|
validate_using_links(data)
|
267
|
+
validate_thresholds(data)
|
268
268
|
|
269
269
|
if type == "service check" && !data[:query].to_s.include?(".by(")
|
270
270
|
invalid! :query_must_include_by, "query must include a .by() at least .by(\"*\")"
|
@@ -320,16 +320,45 @@ module Kennel
|
|
320
320
|
case data[:type]
|
321
321
|
when "composite" # TODO: add slo to mirror resolve_linked_tracking_ids! logic
|
322
322
|
ids = data[:query].tr("-", "_").scan(/\b\d+\b/)
|
323
|
-
ids.reject! { |id| ALLOWED_UNLINKED.include?([tracking_id, id]) }
|
324
323
|
if ids.any?
|
325
324
|
invalid! :links_must_be_via_tracking_id, <<~MSG.rstrip
|
326
|
-
|
327
|
-
If
|
325
|
+
Use kennel ids in the query for linking monitors instead of #{ids}, for example `%{#{project.kennel_id}:<monitor id>}`
|
326
|
+
If the target monitors are not managed via kennel, add `ignored_errors: [:links_must_be_via_tracking_id] # linked monitors are not in kennel`
|
327
|
+
MSG
|
328
|
+
end
|
329
|
+
when "slo alert"
|
330
|
+
if (id = data[:query][/error_budget\("([a-f\d]+)"\)/, 1])
|
331
|
+
invalid! :links_must_be_via_tracking_id, <<~MSG
|
332
|
+
Use kennel ids in the query for linking alerts to slos instead of "#{id}", for example `error_budget("%{#{project.kennel_id}:slo_id_goes_here}")
|
333
|
+
If the target slo is not managed by kennel, then add `ignored_errors: [:links_must_be_via_tracking_id] # linked slo is not in kennel`
|
328
334
|
MSG
|
329
335
|
end
|
330
336
|
else # do nothing
|
331
337
|
end
|
332
338
|
end
|
339
|
+
|
340
|
+
# Prevent "Warning threshold (50.0) must be less than the alert threshold (20.0) with > comparison."
|
341
|
+
def validate_thresholds(data)
|
342
|
+
return unless (warning = data.dig(:options, :thresholds, :warning))
|
343
|
+
critical = data.dig(:options, :thresholds, :critical)
|
344
|
+
|
345
|
+
case data[:query]
|
346
|
+
when /<=?\s*\S+\s*$/
|
347
|
+
if warning <= critical
|
348
|
+
invalid!(
|
349
|
+
:alert_less_than_warning,
|
350
|
+
"Warning threshold (#{warning}) must be greater than the alert threshold (#{critical}) with < comparison"
|
351
|
+
)
|
352
|
+
end
|
353
|
+
when />=?\s*\S+\s*$/
|
354
|
+
if warning >= critical
|
355
|
+
invalid!(
|
356
|
+
:alert_less_than_warning,
|
357
|
+
"Warning threshold (#{warning}) must be less than the alert threshold (#{critical}) with > comparison"
|
358
|
+
)
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
333
362
|
end
|
334
363
|
end
|
335
364
|
end
|
data/lib/kennel/models/record.rb
CHANGED
@@ -2,14 +2,6 @@
|
|
2
2
|
module Kennel
|
3
3
|
module Models
|
4
4
|
class Record < Base
|
5
|
-
class PrepareError < StandardError
|
6
|
-
def initialize(tracking_id)
|
7
|
-
super("Error while preparing #{tracking_id}")
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
UnvalidatedRecordError = Class.new(StandardError)
|
12
|
-
|
13
5
|
include OptionalValidations
|
14
6
|
|
15
7
|
# Apart from if you just don't like the default for some reason,
|
@@ -85,12 +77,12 @@ module Kennel
|
|
85
77
|
end
|
86
78
|
end
|
87
79
|
|
88
|
-
attr_reader :project, :
|
80
|
+
attr_reader :project, :as_json
|
89
81
|
|
90
|
-
def initialize(project,
|
82
|
+
def initialize(project, ...)
|
91
83
|
raise ArgumentError, "First argument must be a project, not #{project.class}" unless project.is_a?(Project)
|
92
84
|
@project = project
|
93
|
-
super(
|
85
|
+
super(...)
|
94
86
|
end
|
95
87
|
|
96
88
|
def diff(actual)
|
@@ -141,29 +133,9 @@ module Kennel
|
|
141
133
|
end
|
142
134
|
|
143
135
|
def build
|
144
|
-
@
|
145
|
-
|
146
|
-
|
147
|
-
begin
|
148
|
-
json = build_json
|
149
|
-
(id = json.delete(:id)) && json[:id] = id
|
150
|
-
validate_json(json)
|
151
|
-
rescue StandardError
|
152
|
-
if unfiltered_validation_errors.empty?
|
153
|
-
@unfiltered_validation_errors = nil
|
154
|
-
raise PrepareError, safe_tracking_id # FIXME: this makes errors hard to debug when running tests
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
@filtered_validation_errors = filter_validation_errors
|
159
|
-
@as_json = json # Only valid if filtered_validation_errors.empty?
|
160
|
-
end
|
161
|
-
|
162
|
-
def as_json
|
163
|
-
# A courtesy to those tests that still expect as_json to perform validation and raise on error
|
164
|
-
build if @unfiltered_validation_errors.nil?
|
165
|
-
raise UnvalidatedRecordError, "#{safe_tracking_id} as_json called on invalid part" if filtered_validation_errors.any?
|
166
|
-
|
136
|
+
@as_json = build_json
|
137
|
+
(id = @as_json.delete(:id)) && @as_json[:id] = id
|
138
|
+
validate_json(@as_json)
|
167
139
|
@as_json
|
168
140
|
end
|
169
141
|
|
@@ -184,6 +156,17 @@ module Kennel
|
|
184
156
|
|
185
157
|
private
|
186
158
|
|
159
|
+
def validate_json(data)
|
160
|
+
bad = Kennel::Utils.all_keys(data).grep_v(Symbol).sort.uniq
|
161
|
+
return if bad.empty?
|
162
|
+
invalid!(
|
163
|
+
:hash_keys_must_be_symbols,
|
164
|
+
"Only use Symbols as hash keys to avoid permanent diffs when updating.\n" \
|
165
|
+
"Change these keys to be symbols (usually 'foo' => 1 --> 'foo': 1)\n" \
|
166
|
+
"#{bad.map(&:inspect).join("\n")}"
|
167
|
+
)
|
168
|
+
end
|
169
|
+
|
187
170
|
def resolve(value, type, id_map, force:)
|
188
171
|
return value unless tracking_id?(value)
|
189
172
|
resolve_link(value, type, id_map, force: force)
|
@@ -214,10 +197,6 @@ module Kennel
|
|
214
197
|
end
|
215
198
|
end
|
216
199
|
|
217
|
-
def invalid!(tag, message)
|
218
|
-
unfiltered_validation_errors << ValidationMessage.new(tag || OptionalValidations::UNIGNORABLE, message)
|
219
|
-
end
|
220
|
-
|
221
200
|
def raise_with_location(error, message)
|
222
201
|
super error, "#{message} for project #{project.kennel_id}"
|
223
202
|
end
|
@@ -4,80 +4,89 @@ module Kennel
|
|
4
4
|
ValidationMessage = Struct.new(:tag, :text)
|
5
5
|
|
6
6
|
UNIGNORABLE = :unignorable
|
7
|
+
UNUSED_IGNORES = :unused_ignores
|
7
8
|
|
8
9
|
def self.included(base)
|
9
10
|
base.settings :ignored_errors
|
10
11
|
base.defaults(ignored_errors: -> { [] })
|
12
|
+
base.attr_reader :validation_errors
|
11
13
|
end
|
12
14
|
|
13
|
-
def
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
def initialize(...)
|
16
|
+
super
|
17
|
+
@validation_errors = []
|
18
|
+
end
|
17
19
|
|
18
|
-
|
20
|
+
def invalid!(tag, message)
|
21
|
+
validation_errors << ValidationMessage.new(tag || OptionalValidations::UNIGNORABLE, message)
|
22
|
+
end
|
19
23
|
|
20
|
-
|
24
|
+
def self.valid?(parts)
|
25
|
+
parts_with_errors = parts.map { |p| [p, filter_validation_errors(p)] }
|
26
|
+
return true if parts_with_errors.all? { |_, errors| errors.empty? }
|
21
27
|
|
28
|
+
# print errors in order
|
29
|
+
example_tag = nil
|
22
30
|
Kennel.err.puts
|
23
|
-
parts_with_errors.sort_by
|
24
|
-
|
31
|
+
parts_with_errors.sort_by! { |p, _| p.safe_tracking_id }
|
32
|
+
parts_with_errors.each do |part, errors|
|
33
|
+
errors.each do |err|
|
25
34
|
Kennel.err.puts "#{part.safe_tracking_id} [#{err.tag.inspect}] #{err.text.gsub("\n", " ")}"
|
26
35
|
example_tag = err.tag unless err.tag == :unignorable
|
27
36
|
end
|
28
37
|
end
|
29
38
|
Kennel.err.puts
|
30
39
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
40
|
+
if example_tag
|
41
|
+
Kennel.err.puts <<~MESSAGE
|
42
|
+
If a particular error cannot be fixed, it can be marked as ignored via `ignored_errors`, e.g.:
|
43
|
+
Kennel::Models::Monitor.new(
|
44
|
+
...,
|
45
|
+
ignored_errors: [#{example_tag.inspect}]
|
46
|
+
)
|
37
47
|
|
38
|
-
|
48
|
+
MESSAGE
|
49
|
+
end
|
39
50
|
|
40
51
|
false
|
41
52
|
end
|
42
53
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
bad = Kennel::Utils.all_keys(data).grep_v(Symbol).sort.uniq
|
47
|
-
return if bad.empty?
|
48
|
-
invalid!(
|
49
|
-
:hash_keys_must_be_symbols,
|
50
|
-
"Only use Symbols as hash keys to avoid permanent diffs when updating.\n" \
|
51
|
-
"Change these keys to be symbols (usually 'foo' => 1 --> 'foo': 1)\n" \
|
52
|
-
"#{bad.map(&:inspect).join("\n")}"
|
53
|
-
)
|
54
|
-
end
|
54
|
+
def self.filter_validation_errors(part)
|
55
|
+
errors = part.validation_errors
|
56
|
+
ignored_tags = part.ignored_errors
|
55
57
|
|
56
|
-
|
57
|
-
|
58
|
-
if ignored_errors.empty?
|
58
|
+
if errors.empty? # 95% case, so keep it fast
|
59
|
+
if ignored_tags.empty? || ignored_tags.include?(UNUSED_IGNORES)
|
59
60
|
[]
|
60
61
|
else
|
61
|
-
|
62
|
+
# tell users to remove the whole line and not just an element
|
63
|
+
[
|
64
|
+
ValidationMessage.new(
|
65
|
+
UNUSED_IGNORES,
|
66
|
+
"`ignored_errors` is non-empty, but there are no errors to ignore. Remove `ignored_errors`"
|
67
|
+
)
|
68
|
+
]
|
62
69
|
end
|
63
70
|
else
|
64
|
-
|
65
|
-
if ENV["NO_IGNORED_ERRORS"]
|
66
|
-
|
67
|
-
unfiltered_validation_errors
|
71
|
+
reported_errors =
|
72
|
+
if ENV["NO_IGNORED_ERRORS"] # let users see what errors are suppressed
|
73
|
+
errors
|
68
74
|
else
|
69
|
-
|
70
|
-
err.tag != UNIGNORABLE && ignored_errors.include?(err.tag)
|
71
|
-
end
|
75
|
+
errors.select { |err| err.tag == UNIGNORABLE || !ignored_tags.include?(err.tag) }
|
72
76
|
end
|
73
77
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
+
# let users know when they can remove an ignore ... unless they don't care (for example for a generic monitor)
|
79
|
+
unless ignored_tags.include?(UNUSED_IGNORES)
|
80
|
+
unused_ignored_tags = ignored_tags - errors.map(&:tag)
|
81
|
+
if unused_ignored_tags.any?
|
82
|
+
reported_errors << ValidationMessage.new(
|
83
|
+
UNUSED_IGNORES,
|
84
|
+
"Unused ignores #{unused_ignored_tags.map(&:inspect).sort.uniq.join(" ")}. Remove these from `ignored_errors`"
|
85
|
+
)
|
86
|
+
end
|
78
87
|
end
|
79
88
|
|
80
|
-
|
89
|
+
reported_errors
|
81
90
|
end
|
82
91
|
end
|
83
92
|
end
|
data/lib/kennel/version.rb
CHANGED
data/lib/kennel.rb
CHANGED
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.141.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: 2023-
|
11
|
+
date: 2023-06-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: diff-lcs
|