kennel 1.78.1 → 1.80.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 +4 -3
- data/lib/kennel.rb +36 -8
- data/lib/kennel/models/dashboard.rb +3 -3
- data/lib/kennel/models/monitor.rb +20 -6
- data/lib/kennel/template_variables.rb +13 -9
- data/lib/kennel/version.rb +1 -1
- data/template/Readme.md +3 -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: 0cc9d71dff0d4c4bf8a29d9c58c1b62efbb739f3ebdc0d63fb7bd1a3e1cfec7c
|
4
|
+
data.tar.gz: 312fbda2e1577d071ba7cad7a6c5571b6b3913517cc3e5e9b92f7858637f96dc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d07c6d405178e7114c1ebaf67ce3f92a09a411208d20353bf7d07f453093e322b98f2e8e2f501cf902a601fafa1b352b5fa19bff9d54f1d042e87fc7ef81277
|
7
|
+
data.tar.gz: 9d681c94f1d61bbc60a098ea0b6fe12e3efc5b6e3c771ec49743db8e6edaae1c83a64ae322a9f5759fc677465b199842f0401c8a38973d1bbaa14157f53f94da
|
data/Readme.md
CHANGED
@@ -83,8 +83,8 @@ end
|
|
83
83
|
- `gem install bundler && bundle install`
|
84
84
|
- `cp .env.example .env`
|
85
85
|
- open [Datadog API Settings](https://app.datadoghq.com/account/settings#api)
|
86
|
-
-
|
87
|
-
-
|
86
|
+
- create a `API Key` or get an existing one from an admin, then add it to `.env` as `DATADOG_API_KEY`
|
87
|
+
- open [Datadog API Settings](https://app.datadoghq.com/access/application-keys) and create a new key, then add it to `.env` as `DATADOG_APP_KEY=`
|
88
88
|
- change the `DATADOG_SUBDOMAIN=app` in `.env` to your companies subdomain if you have one
|
89
89
|
- verify it works by running `rake plan`, it might show some diff, but should not crash
|
90
90
|
-->
|
@@ -219,6 +219,7 @@ To link to existing monitors via their kennel_id `projects kennel_id` + `:` + `m
|
|
219
219
|
- Screens `uptime` widgets can use `monitor: {id: "foo:bar"}`
|
220
220
|
- Screens `alert_graph` widgets can use `alert_id: "foo:bar"`
|
221
221
|
- Monitors `composite` can use `query: -> { "%{foo:bar} || %{foo:baz}" }`
|
222
|
+
- Monitors `slo alert` can use `query: -> { "error_budget(\"%{foo:bar}\").over(\"7d\") > 123.0" }`
|
222
223
|
- Slos can use `monitor_ids: -> ["foo:bar"]`
|
223
224
|
|
224
225
|
### Debugging changes locally
|
@@ -296,5 +297,5 @@ Author
|
|
296
297
|
[Michael Grosser](http://grosser.it)<br/>
|
297
298
|
michael@grosser.it<br/>
|
298
299
|
License: MIT<br/>
|
299
|
-
|
300
|
+

|
300
301
|
<!-- NOT IN -->
|
data/lib/kennel.rb
CHANGED
@@ -39,12 +39,7 @@ module Kennel
|
|
39
39
|
attr_accessor :out, :err
|
40
40
|
|
41
41
|
def generate
|
42
|
-
|
43
|
-
generated.each do |part|
|
44
|
-
path = "generated/#{part.tracking_id.sub(":", "/")}.json"
|
45
|
-
FileUtils.mkdir_p(File.dirname(path))
|
46
|
-
File.write(path, JSON.pretty_generate(part.as_json) << "\n")
|
47
|
-
end
|
42
|
+
store generated
|
48
43
|
end
|
49
44
|
|
50
45
|
def plan
|
@@ -58,6 +53,35 @@ module Kennel
|
|
58
53
|
|
59
54
|
private
|
60
55
|
|
56
|
+
def store(parts)
|
57
|
+
Progress.progress "Storing" do
|
58
|
+
old = Dir["generated/**/*"]
|
59
|
+
used = []
|
60
|
+
|
61
|
+
Utils.parallel(parts, max: 2) do |part|
|
62
|
+
path = "generated/#{part.tracking_id.tr("/", ":").sub(":", "/")}.json"
|
63
|
+
used << File.dirname(path) # only 1 level of sub folders, so this is safe
|
64
|
+
used << path
|
65
|
+
write_file_if_necessary(path, JSON.pretty_generate(part.as_json) << "\n")
|
66
|
+
end
|
67
|
+
|
68
|
+
# deleting all is slow, so only delete the extras
|
69
|
+
(old - used).each { |p| FileUtils.rm_rf(p) }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def write_file_if_necessary(path, content)
|
74
|
+
# 99% case
|
75
|
+
begin
|
76
|
+
return if File.read(path) == content
|
77
|
+
rescue Errno::ENOENT
|
78
|
+
FileUtils.mkdir_p(File.dirname(path))
|
79
|
+
end
|
80
|
+
|
81
|
+
# slow 1% case
|
82
|
+
File.write(path, content)
|
83
|
+
end
|
84
|
+
|
61
85
|
def syncer
|
62
86
|
@syncer ||= Syncer.new(api, generated, project: ENV["PROJECT"])
|
63
87
|
end
|
@@ -73,8 +97,12 @@ module Kennel
|
|
73
97
|
parts = Models::Project.recursive_subclasses.flat_map do |project_class|
|
74
98
|
project_class.new.validated_parts
|
75
99
|
end
|
76
|
-
parts.
|
77
|
-
|
100
|
+
parts.group_by(&:tracking_id).each do |tracking_id, same|
|
101
|
+
next if same.size == 1
|
102
|
+
raise <<~ERROR
|
103
|
+
#{tracking_id} is defined #{same.size} times
|
104
|
+
use a different `kennel_id` when defining multiple projects/monitors/dashboards to avoid this conflict
|
105
|
+
ERROR
|
78
106
|
end
|
79
107
|
parts
|
80
108
|
end
|
@@ -105,7 +105,7 @@ module Kennel
|
|
105
105
|
|
106
106
|
def as_json
|
107
107
|
return @json if @json
|
108
|
-
all_widgets = render_definitions + widgets
|
108
|
+
all_widgets = render_definitions(definitions) + widgets
|
109
109
|
expand_q all_widgets
|
110
110
|
|
111
111
|
@json = {
|
@@ -178,14 +178,14 @@ module Kennel
|
|
178
178
|
def validate_json(data)
|
179
179
|
super
|
180
180
|
|
181
|
-
validate_template_variables data
|
181
|
+
validate_template_variables data
|
182
182
|
|
183
183
|
# Avoid diff from datadog presets sorting.
|
184
184
|
presets = data[:template_variable_presets]
|
185
185
|
invalid! "template_variable_presets must be sorted by name" if presets && presets != presets.sort_by { |p| p[:name] }
|
186
186
|
end
|
187
187
|
|
188
|
-
def render_definitions
|
188
|
+
def render_definitions(definitions)
|
189
189
|
definitions.map do |title, type, display_type, queries, options = {}, ignored = nil|
|
190
190
|
# validate inputs
|
191
191
|
if ignored || (!title || !type || !queries || !options.is_a?(Hash))
|
@@ -12,6 +12,10 @@ module Kennel
|
|
12
12
|
:multi, :matching_downtimes, :overall_state_modified, :overall_state, :restricted_roles
|
13
13
|
]
|
14
14
|
|
15
|
+
MONITOR_DEFAULTS = {
|
16
|
+
priority: nil
|
17
|
+
}.freeze
|
18
|
+
|
15
19
|
# defaults that datadog uses when options are not sent, so safe to leave out if our values match their defaults
|
16
20
|
MONITOR_OPTION_DEFAULTS = {
|
17
21
|
evaluation_delay: nil,
|
@@ -27,7 +31,7 @@ module Kennel
|
|
27
31
|
settings(
|
28
32
|
:query, :name, :message, :escalation_message, :critical, :type, :renotify_interval, :warning, :timeout_h, :evaluation_delay,
|
29
33
|
:ok, :no_data_timeframe, :notify_no_data, :notify_audit, :tags, :critical_recovery, :warning_recovery, :require_full_window,
|
30
|
-
:threshold_windows, :new_host_delay
|
34
|
+
:threshold_windows, :new_host_delay, :priority
|
31
35
|
)
|
32
36
|
|
33
37
|
defaults(
|
@@ -46,7 +50,8 @@ module Kennel
|
|
46
50
|
evaluation_delay: -> { MONITOR_OPTION_DEFAULTS.fetch(:evaluation_delay) },
|
47
51
|
critical_recovery: -> { nil },
|
48
52
|
warning_recovery: -> { nil },
|
49
|
-
threshold_windows: -> { nil }
|
53
|
+
threshold_windows: -> { nil },
|
54
|
+
priority: -> { MONITOR_DEFAULTS.fetch(:priority) }
|
50
55
|
)
|
51
56
|
|
52
57
|
def as_json
|
@@ -57,6 +62,7 @@ module Kennel
|
|
57
62
|
query: query.strip,
|
58
63
|
message: message.strip,
|
59
64
|
tags: tags.uniq,
|
65
|
+
priority: priority,
|
60
66
|
options: {
|
61
67
|
timeout_h: timeout_h,
|
62
68
|
notify_no_data: notify_no_data,
|
@@ -106,9 +112,11 @@ module Kennel
|
|
106
112
|
end
|
107
113
|
|
108
114
|
def resolve_linked_tracking_ids!(id_map, **args)
|
109
|
-
|
110
|
-
|
111
|
-
|
115
|
+
case as_json[:type]
|
116
|
+
when "composite", "slo alert"
|
117
|
+
type = (as_json[:type] == "composite" ? :monitor : :slo)
|
118
|
+
as_json[:query] = as_json[:query].gsub(/%{(.*?)}/) do
|
119
|
+
resolve_link($1, type, id_map, **args)
|
112
120
|
end
|
113
121
|
end
|
114
122
|
end
|
@@ -129,6 +137,9 @@ module Kennel
|
|
129
137
|
|
130
138
|
def self.normalize(expected, actual)
|
131
139
|
super
|
140
|
+
|
141
|
+
ignore_default(expected, actual, MONITOR_DEFAULTS)
|
142
|
+
|
132
143
|
options = actual.fetch(:options)
|
133
144
|
options.delete(:silenced) # we do not manage silenced, so ignore it when diffing
|
134
145
|
|
@@ -209,7 +220,10 @@ module Kennel
|
|
209
220
|
.map! { |w| %("#{w}.name") }
|
210
221
|
used.uniq.each do |match, group|
|
211
222
|
next if allowed.include?(group)
|
212
|
-
invalid!
|
223
|
+
invalid!(
|
224
|
+
"#{match} used with #{group}, but can only be used with #{allowed.join(", ")}. " \
|
225
|
+
"Group the query by #{group.sub(".name", "").tr('"', "")} or change the #{match}"
|
226
|
+
)
|
213
227
|
end
|
214
228
|
end
|
215
229
|
end
|
@@ -16,18 +16,22 @@ module Kennel
|
|
16
16
|
|
17
17
|
# check for queries that do not use the variables and would be misleading
|
18
18
|
# TODO: do the same check for apm_query and their group_by
|
19
|
-
def validate_template_variables(data
|
19
|
+
def validate_template_variables(data)
|
20
20
|
variables = (data[:template_variables] || []).map { |v| "$#{v.fetch(:name)}" }
|
21
|
-
|
21
|
+
return if variables.empty?
|
22
|
+
|
23
|
+
queries = data[:widgets].flat_map do |widget|
|
22
24
|
([widget] + (widget.dig(:definition, :widgets) || [])).flat_map { |w| widget_queries(w) }
|
23
25
|
end.compact
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
|
27
|
+
matches = variables.map { |v| Regexp.new "#{Regexp.escape(v)}\\b" }
|
28
|
+
queries.reject! { |q| matches.all? { |m| q.match? m } }
|
29
|
+
return if queries.empty?
|
30
|
+
|
31
|
+
invalid!(
|
32
|
+
"queries #{queries.join(", ")} must use the template variables #{variables.join(", ")}\n" \
|
33
|
+
"If that is not possible, add `validate: -> { false } # query foo in bar does not have baz tag`"
|
34
|
+
)
|
31
35
|
end
|
32
36
|
|
33
37
|
def widget_queries(widget)
|
data/lib/kennel/version.rb
CHANGED
data/template/Readme.md
CHANGED
@@ -66,8 +66,8 @@ end
|
|
66
66
|
- `gem install bundler && bundle install`
|
67
67
|
- `cp .env.example .env`
|
68
68
|
- open [Datadog API Settings](https://app.datadoghq.com/account/settings#api)
|
69
|
-
-
|
70
|
-
-
|
69
|
+
- create a `API Key` or get an existing one from an admin, then add it to `.env` as `DATADOG_API_KEY`
|
70
|
+
- open [Datadog API Settings](https://app.datadoghq.com/access/application-keys) and create a new key, then add it to `.env` as `DATADOG_APP_KEY=`
|
71
71
|
- change the `DATADOG_SUBDOMAIN=app` in `.env` to your companies subdomain if you have one
|
72
72
|
- verify it works by running `rake plan`, it might show some diff, but should not crash
|
73
73
|
|
@@ -201,6 +201,7 @@ To link to existing monitors via their kennel_id `projects kennel_id` + `:` + `m
|
|
201
201
|
- Screens `uptime` widgets can use `monitor: {id: "foo:bar"}`
|
202
202
|
- Screens `alert_graph` widgets can use `alert_id: "foo:bar"`
|
203
203
|
- Monitors `composite` can use `query: -> { "%{foo:bar} || %{foo:baz}" }`
|
204
|
+
- Monitors `slo alert` can use `query: -> { "error_budget(\"%{foo:bar}\").over(\"7d\") > 123.0" }`
|
204
205
|
- Slos can use `monitor_ids: -> ["foo:bar"]`
|
205
206
|
|
206
207
|
### Debugging changes locally
|
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.80.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:
|
11
|
+
date: 2021-01-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|