kennel 1.78.1 → 1.80.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 +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
|
+
![CI](https://github.com/grosser/kennel/workflows/CI/badge.svg)
|
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
|