kennel 1.51.0 → 1.55.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf65db42c0f1dbd43a7f2a6d0910a018565db65e431cea3c39b88d796b1ccce0
4
- data.tar.gz: 1d0db8fca3e1f1b9d4085ae1e783b2b6f28a0ae3682ba9b2b743612b1b71b5dc
3
+ metadata.gz: adc230a06e4f7f969147e0c299ba61c4536d5623de432cfe3e247bece944962a
4
+ data.tar.gz: ae6d3ebe5db8fe7a5f406cda15e0a85d8a19b8d5f991a9787a63376a01572e70
5
5
  SHA512:
6
- metadata.gz: a774657cac63f4080d9767453ccf5e9c301aaaf2cae15486e26f87e54c851dc9246421cb6aa16f585a7311748dc6031ed6a65124c1cf01d19669ca1604ff78ab
7
- data.tar.gz: a922269b47847376d1287e42a06aed1fe4fdbab3c0a8f550f9983fe1d2823314c186e3f809dbb2ea607446d67fd6f168d28e113dd8f0d33428ead5979deae804
6
+ metadata.gz: 3013cb61dd5c33a7dd1772b54645d62e7cf7e70bb5202f85950ab4f2a102e723d1583725200da513a54d66020af14ca9c57b63bc50e43b1a27a4d765d478c2fb
7
+ data.tar.gz: 28f5812c2795043927ca2161712d6e16f100cf6999e21fc64c3ee8e557d663c526a30dc9eef3069d2f229a699304d5f1029eb61176c3c51f4f825f5a45ac9e56
data/Readme.md CHANGED
@@ -42,19 +42,23 @@ Keep datadog monitors/dashboards/etc in version control, avoid chaotic managemen
42
42
  - `gem install bundler && bundle install`
43
43
  - `cp .env.example .env`
44
44
  - open [Datadog API Settings](https://app.datadoghq.com/account/settings#api)
45
- - find or create your personal "Application Key" and add it to `.env` as `DATADOG_APP_KEY=` (will be on the last page if new)
46
45
  - copy any `API Key` and add it to `.env` as `DATADOG_API_KEY`
46
+ - find or create (check last page) your personal "Application Key" and add it to `.env` as `DATADOG_APP_KEY=`
47
+ - change the `DATADOG_SUBDOMAIN=app` in `.env` to your companies subdomain if you have one
47
48
  -->
48
49
 
49
50
  ### Adding a team
50
51
 
52
+ - `mention` is used for all team monitors via `super()`
53
+ - `renotify_interval` is used for all team monitors (defaults to `0` / off)
54
+ - `tags` is used for all team monitors/dashboards (defaults to `team:<team-name>`)
55
+
51
56
  ```Ruby
52
57
  # teams/my_team.rb
53
58
  module Teams
54
59
  class MyTeam < Kennel::Models::Team
55
60
  defaults(
56
- slack: -> { "my-alerts" },
57
- email: -> { "my-team@example.com" }
61
+ mention: -> { "@slack-my-team" }
58
62
  )
59
63
  end
60
64
  end
@@ -62,13 +66,14 @@ end
62
66
 
63
67
  ### Adding a new monitor
64
68
  - use [datadog monitor UI](https://app.datadoghq.com/monitors#create/metric) to create a monitor
65
- - get the `id` from the url
66
- - `RESOURCE=monitor ID=12345 bundle exec rake kennel:import`
67
69
  - see below
68
70
 
69
71
  ### Updating an existing monitor
72
+ - use [datadog monitor UI](https://app.datadoghq.com/monitors#create/metric) to find a monitor
73
+ - get the `id` from the url
74
+ - run `RESOURCE=monitor ID=12345 bundle exec rake kennel:import` and copy the output
70
75
  - find or create a project in `projects/`
71
- - add a monitor to `parts: [` list
76
+ - add the monitor to `parts: [` list, for example:
72
77
  ```Ruby
73
78
  # projects/my_project.rb
74
79
  class MyProject < Kennel::Models::Project
@@ -83,7 +88,8 @@ end
83
88
  kennel_id: -> { "load-too-high" }, # make up a unique name
84
89
  name: -> { "Foobar Load too high" }, # nice descriptive name that will show up in alerts and emails
85
90
  message: -> {
86
- # Explain what behavior to expect and how to fix the cause. Use #{super()} to add team notifications.
91
+ # Explain what behavior to expect and how to fix the cause
92
+ # Use #{super()} to add team notifications.
87
93
  <<~TEXT
88
94
  Foobar will be slow and that could cause Barfoo to go down.
89
95
  Add capacity or debug why it is suddenly slow.
@@ -98,21 +104,22 @@ end
98
104
  )
99
105
  end
100
106
  ```
101
- - `bundle exec rake plan` update to existing should be shown (not Create / Delete)
102
- - alternatively: `bundle exec rake generate` to only update the generated `json` files
107
+ - run `PROJECT=my_project bundle exec rake plan`, an Update to the existing monitor should be shown (not Create / Delete)
108
+ - alternatively: `bundle exec rake generate` to only locally update the generated `json` files
103
109
  - review changes then `git commit`
104
110
  - make a PR ... get reviewed ... merge
105
111
  - datadog is updated by travis
106
112
 
107
113
  ### Adding a new dashboard
108
114
  - go to [datadog dashboard UI](https://app.datadoghq.com/dashboard/lists) and click on _New Dashboard_ to create a dashboard
109
- - get the `id` from the url
110
- - `RESOURCE=dashboard ID=abc-def-ghi bundle exec rake kennel:import`
111
115
  - see below
112
116
 
113
117
  ### Updating an existing dashboard
118
+ - go to [datadog dashboard UI](https://app.datadoghq.com/dashboard/lists) and click on _New Dashboard_ to find a dashboard
119
+ - get the `id` from the url
120
+ - run `RESOURCE=dashboard ID=abc-def-ghi bundle exec rake kennel:import` and copy the output
114
121
  - find or create a project in `projects/`
115
- - add a dashboard to `parts: [` list
122
+ - add a dashboard to `parts: [` list, for example:
116
123
  ```Ruby
117
124
  class MyProject < Kennel::Models::Project
118
125
  defaults(
@@ -160,6 +167,7 @@ To link to existing monitors via their kennel_id
160
167
 
161
168
  - Screens `uptime` widgets can use `monitor: {id: "foo:bar"}`
162
169
  - Screens `alert_graph` widgets can use `alert_id: "foo:bar"`
170
+ - Monitors `composite` can use `query: -> { "%{foo:bar} || %{foo:baz}" }`
163
171
 
164
172
  ### Debugging changes locally
165
173
 
@@ -171,6 +179,10 @@ To link to existing monitors via their kennel_id
171
179
 
172
180
  Run `rake kennel:alerts TAG=service:my-service` to see all un-muted alerts for a given datadog monitor tag.
173
181
 
182
+ ### Validating mentions work
183
+
184
+ `rake kennel:validate_mentions` should run as part of CI
185
+
174
186
  ## Examples
175
187
 
176
188
  ### Reusable monitors/dashes/etc
@@ -195,7 +207,7 @@ Reuse it in multiple projects.
195
207
  ```Ruby
196
208
  class Database < Kennel::Models::Project
197
209
  defaults(
198
- team: -> { Kennel::Models::Team.new(slack: -> { 'foo' }, kennel_id: -> { 'foo' }) },
210
+ team: -> { Kennel::Models::Team.new(mention: -> { '@slack-foo' }, kennel_id: -> { 'foo' }) },
199
211
  parts: -> { [Monitors::LoadTooHigh.new(self, critical: -> { 13 })] }
200
212
  )
201
213
  end
@@ -53,6 +53,9 @@ module Kennel
53
53
  if query && critical
54
54
  query.sub!(/([><=]) (#{Regexp.escape(critical.to_f.to_s)}|#{Regexp.escape(critical.to_i.to_s)})$/, "\\1 \#{critical}")
55
55
  end
56
+ elsif resource == "dashboard"
57
+ widgets = data[:widgets]&.flat_map { |widget| widget.dig(:definition, :widgets) || [widget] }
58
+ widgets&.each { |widget| dry_up_query!(widget) }
56
59
  end
57
60
 
58
61
  # simplify template_variables to array of string when possible
@@ -71,6 +74,20 @@ module Kennel
71
74
 
72
75
  private
73
76
 
77
+ # reduce duplication in imports by using dry `q: :metadata` when possible
78
+ def dry_up_query!(widget)
79
+ (widget.dig(:definition, :requests) || []).each do |request|
80
+ next unless request.is_a?(Hash)
81
+ next unless metadata = request[:metadata]
82
+ next unless query = request[:q]&.dup
83
+ metadata.each do |m|
84
+ next unless exp = m[:expression]
85
+ query.sub!(exp, "")
86
+ end
87
+ request[:q] = :metadata if query.delete(", ") == ""
88
+ end
89
+ end
90
+
74
91
  def pretty_print(hash)
75
92
  sort_widgets hash
76
93
 
@@ -84,6 +101,7 @@ module Kennel
84
101
  .gsub(/(^\s*)"([a-zA-Z][a-zA-Z\d_]*)":/, "\\1\\2:") # "foo": 1 -> foo: 1
85
102
  .gsub(/: \[\n\s+\]/, ": []") # empty arrays on a single line
86
103
  .gsub(/^/, " ") # indent
104
+ .gsub('q: "metadata"', "q: :metadata") # bring symbols back
87
105
 
88
106
  "\n#{pretty}\n "
89
107
  elsif k == :message
@@ -148,6 +148,13 @@ module Kennel
148
148
 
149
149
  private
150
150
 
151
+ def resolve_link(id, id_map, force:)
152
+ id_map[id] || begin
153
+ message = "Unable to find #{id} in existing monitors (they need to be created first to link them)"
154
+ force ? invalid!(message) : Kennel.err.puts(message)
155
+ end
156
+ end
157
+
151
158
  # let users know which project/resource failed when something happens during diffing where the backtrace is hidden
152
159
  def invalid!(message)
153
160
  raise ValidationError, "#{tracking_id} #{message}"
@@ -101,11 +101,13 @@ module Kennel
101
101
  case definition[:type]
102
102
  when "uptime"
103
103
  if ids = definition[:monitor_ids]
104
- definition[:monitor_ids] = ids.map { |id| resolve_link(id, id_map) }
104
+ definition[:monitor_ids] = ids.map do |id|
105
+ tracking_id?(id) ? resolve_link(id, id_map, force: false) : id
106
+ end
105
107
  end
106
108
  when "alert_graph"
107
- if id = definition[:alert_id]
108
- definition[:alert_id] = resolve_link(id, id_map).to_s
109
+ if (id = definition[:alert_id]) && tracking_id?(id)
110
+ definition[:alert_id] = resolve_link(id, id_map, force: false).to_s
109
111
  end
110
112
  end
111
113
  end
@@ -113,6 +115,10 @@ module Kennel
113
115
 
114
116
  private
115
117
 
118
+ def tracking_id?(id)
119
+ id.is_a?(String) && !id.match?(/\A\d+\z/)
120
+ end
121
+
116
122
  # creates queries from metadata to avoid having to keep q and expression in sync
117
123
  #
118
124
  # {q: :metadata, metadata: [{expression: "sum:bar", alias_name: "foo"}, ...], }
@@ -127,16 +133,6 @@ module Kennel
127
133
  end
128
134
  end
129
135
 
130
- def resolve_link(id, id_map)
131
- return id unless tracking_id?(id)
132
- id_map[id] ||
133
- Kennel.err.puts("Unable to find #{id} in existing monitors (they need to be created first to link them)")
134
- end
135
-
136
- def tracking_id?(id)
137
- id.is_a?(String) && !id.match?(/\A\d+\z/)
138
- end
139
-
140
136
  def validate_json(data)
141
137
  super
142
138
 
@@ -15,7 +15,7 @@ module Kennel
15
15
  evaluation_delay: nil,
16
16
  new_host_delay: 300,
17
17
  timeout_h: 0,
18
- renotify_interval: 120,
18
+ renotify_interval: 0,
19
19
  no_data_timeframe: nil # this works out ok since if notify_no_data is on, it would never be nil
20
20
  }.freeze
21
21
  DEFAULT_ESCALATION_MESSAGE = ["", nil].freeze
@@ -27,9 +27,9 @@ module Kennel
27
27
  )
28
28
 
29
29
  defaults(
30
- message: -> { "\n\n@slack-#{project.slack}" },
30
+ message: -> { "\n\n#{project.mention}" },
31
31
  escalation_message: -> { DEFAULT_ESCALATION_MESSAGE.first },
32
- renotify_interval: -> { MONITOR_OPTION_DEFAULTS.fetch(:renotify_interval) },
32
+ renotify_interval: -> { project.team.renotify_interval },
33
33
  warning: -> { nil },
34
34
  ok: -> { nil },
35
35
  id: -> { nil },
@@ -108,6 +108,16 @@ module Kennel
108
108
  @as_json = data
109
109
  end
110
110
 
111
+ # resolve composite monitors ... only works when referenced monitors already exist
112
+ # since leaving names or bad ids in the query breaks the monitor update
113
+ def resolve_linked_tracking_ids(id_map)
114
+ if as_json[:type] == "composite"
115
+ as_json[:query] = as_json[:query].gsub(/%\{(.*?)\}/) do
116
+ resolve_link($1, id_map, force: true)
117
+ end
118
+ end
119
+ end
120
+
111
121
  def self.api_resource
112
122
  "monitor"
113
123
  end
@@ -2,10 +2,10 @@
2
2
  module Kennel
3
3
  module Models
4
4
  class Project < Base
5
- settings :team, :parts, :tags, :slack
5
+ settings :team, :parts, :tags, :mention
6
6
  defaults(
7
7
  tags: -> { ["service:#{kennel_id}"] + team.tags },
8
- slack: -> { team.slack }
8
+ mention: -> { team.mention }
9
9
  )
10
10
 
11
11
  def self.file_location
@@ -2,19 +2,11 @@
2
2
  module Kennel
3
3
  module Models
4
4
  class Team < Base
5
- settings :slack, :email, :tags, :kennel_id
5
+ settings :mention, :tags, :renotify_interval, :kennel_id
6
6
  defaults(
7
- tags: -> { ["team:#{kennel_id.sub(/^teams_/, "")}"] }
7
+ tags: -> { ["team:#{kennel_id.sub(/^teams_/, "")}"] },
8
+ renotify_interval: -> { 0 }
8
9
  )
9
-
10
- def initialize(*)
11
- super
12
- invalid! "remove leading # from slack" if slack.to_s.start_with?("#")
13
- end
14
-
15
- def tracking_id
16
- kennel_id
17
- end
18
10
  end
19
11
  end
20
12
  end
data/lib/kennel/tasks.rb CHANGED
@@ -12,6 +12,35 @@ namespace :kennel do
12
12
  abort "Error during diffing" unless $CHILD_STATUS.success?
13
13
  end
14
14
 
15
+ # ideally do this on every run, but it's slow (~1.5s) and brittle (might not find all + might find false-positives)
16
+ # https://help.datadoghq.com/hc/en-us/requests/254114 for automatic validation
17
+ desc "Verify that all used monitor mentions are valid"
18
+ task validate_mentions: :environment do
19
+ known = Kennel.send(:api)
20
+ .send(:request, :get, "/monitor/notifications")
21
+ .fetch(:handles)
22
+ .values
23
+ .flatten(1)
24
+ .map { |v| v.fetch(:value) }
25
+
26
+ known += ENV["KNOWN"].to_s.split(",")
27
+
28
+ bad = []
29
+ Dir["generated/**/*.json"].each do |f|
30
+ next unless message = JSON.parse(File.read(f))["message"]
31
+ used = message.scan(/\s(@[^\s{,'"]+)/).flatten(1)
32
+ .grep(/^@.*@|^@.*-/) # ignore @here etc handles ... datadog uses @foo@bar.com for emails and @foo-bar for integrations
33
+ (used - known).each { |v| bad << [f, v] }
34
+ end
35
+
36
+ if bad.any?
37
+ subdomain = ENV["DATADOG_SUBDOMAIN"]
38
+ url = (subdomain ? "https://zendesk.datadoghq.com" : "") + "/account/settings"
39
+ puts "Invalid mentions found, either ignore them by adding to `KNOWN` env var or add them via #{url}"
40
+ bad.each { |f, v| puts "Invalid mention #{v} in monitor message of #{f}" }
41
+ end
42
+ end
43
+
15
44
  desc "generate local definitions"
16
45
  task generate: :environment do
17
46
  Kennel.generate
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Kennel
3
- VERSION = "1.51.0"
3
+ VERSION = "1.55.0"
4
4
  end
data/template/Readme.md CHANGED
@@ -25,18 +25,22 @@ Keep datadog monitors/dashboards/etc in version control, avoid chaotic managemen
25
25
  - `gem install bundler && bundle install`
26
26
  - `cp .env.example .env`
27
27
  - open [Datadog API Settings](https://app.datadoghq.com/account/settings#api)
28
- - find or create your personal "Application Key" and add it to `.env` as `DATADOG_APP_KEY=` (will be on the last page if new)
29
28
  - copy any `API Key` and add it to `.env` as `DATADOG_API_KEY`
29
+ - find or create (check last page) your personal "Application Key" and add it to `.env` as `DATADOG_APP_KEY=`
30
+ - change the `DATADOG_SUBDOMAIN=app` in `.env` to your companies subdomain if you have one
30
31
 
31
32
  ### Adding a team
32
33
 
34
+ - `mention` is used for all team monitors via `super()`
35
+ - `renotify_interval` is used for all team monitors (defaults to `0` / off)
36
+ - `tags` is used for all team monitors/dashboards (defaults to `team:<team-name>`)
37
+
33
38
  ```Ruby
34
39
  # teams/my_team.rb
35
40
  module Teams
36
41
  class MyTeam < Kennel::Models::Team
37
42
  defaults(
38
- slack: -> { "my-alerts" },
39
- email: -> { "my-team@example.com" }
43
+ mention: -> { "@slack-my-team" }
40
44
  )
41
45
  end
42
46
  end
@@ -44,13 +48,14 @@ end
44
48
 
45
49
  ### Adding a new monitor
46
50
  - use [datadog monitor UI](https://app.datadoghq.com/monitors#create/metric) to create a monitor
47
- - get the `id` from the url
48
- - `RESOURCE=monitor ID=12345 bundle exec rake kennel:import`
49
51
  - see below
50
52
 
51
53
  ### Updating an existing monitor
54
+ - use [datadog monitor UI](https://app.datadoghq.com/monitors#create/metric) to find a monitor
55
+ - get the `id` from the url
56
+ - run `RESOURCE=monitor ID=12345 bundle exec rake kennel:import` and copy the output
52
57
  - find or create a project in `projects/`
53
- - add a monitor to `parts: [` list
58
+ - add the monitor to `parts: [` list, for example:
54
59
  ```Ruby
55
60
  # projects/my_project.rb
56
61
  class MyProject < Kennel::Models::Project
@@ -65,7 +70,8 @@ end
65
70
  kennel_id: -> { "load-too-high" }, # make up a unique name
66
71
  name: -> { "Foobar Load too high" }, # nice descriptive name that will show up in alerts and emails
67
72
  message: -> {
68
- # Explain what behavior to expect and how to fix the cause. Use #{super()} to add team notifications.
73
+ # Explain what behavior to expect and how to fix the cause
74
+ # Use #{super()} to add team notifications.
69
75
  <<~TEXT
70
76
  Foobar will be slow and that could cause Barfoo to go down.
71
77
  Add capacity or debug why it is suddenly slow.
@@ -80,21 +86,22 @@ end
80
86
  )
81
87
  end
82
88
  ```
83
- - `bundle exec rake plan` update to existing should be shown (not Create / Delete)
84
- - alternatively: `bundle exec rake generate` to only update the generated `json` files
89
+ - run `PROJECT=my_project bundle exec rake plan`, an Update to the existing monitor should be shown (not Create / Delete)
90
+ - alternatively: `bundle exec rake generate` to only locally update the generated `json` files
85
91
  - review changes then `git commit`
86
92
  - make a PR ... get reviewed ... merge
87
93
  - datadog is updated by travis
88
94
 
89
95
  ### Adding a new dashboard
90
96
  - go to [datadog dashboard UI](https://app.datadoghq.com/dashboard/lists) and click on _New Dashboard_ to create a dashboard
91
- - get the `id` from the url
92
- - `RESOURCE=dashboard ID=abc-def-ghi bundle exec rake kennel:import`
93
97
  - see below
94
98
 
95
99
  ### Updating an existing dashboard
100
+ - go to [datadog dashboard UI](https://app.datadoghq.com/dashboard/lists) and click on _New Dashboard_ to find a dashboard
101
+ - get the `id` from the url
102
+ - run `RESOURCE=dashboard ID=abc-def-ghi bundle exec rake kennel:import` and copy the output
96
103
  - find or create a project in `projects/`
97
- - add a dashboard to `parts: [` list
104
+ - add a dashboard to `parts: [` list, for example:
98
105
  ```Ruby
99
106
  class MyProject < Kennel::Models::Project
100
107
  defaults(
@@ -142,6 +149,7 @@ To link to existing monitors via their kennel_id
142
149
 
143
150
  - Screens `uptime` widgets can use `monitor: {id: "foo:bar"}`
144
151
  - Screens `alert_graph` widgets can use `alert_id: "foo:bar"`
152
+ - Monitors `composite` can use `query: -> { "%{foo:bar} || %{foo:baz}" }`
145
153
 
146
154
  ### Debugging changes locally
147
155
 
@@ -177,7 +185,7 @@ Reuse it in multiple projects.
177
185
  ```Ruby
178
186
  class Database < Kennel::Models::Project
179
187
  defaults(
180
- team: -> { Kennel::Models::Team.new(slack: -> { 'foo' }, kennel_id: -> { 'foo' }) },
188
+ team: -> { Kennel::Models::Team.new(mention: -> { '@slack-foo' }, kennel_id: -> { 'foo' }) },
181
189
  parts: -> { [Monitors::LoadTooHigh.new(self, critical: -> { 13 })] }
182
190
  )
183
191
  end
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.51.0
4
+ version: 1.55.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: 2019-08-23 00:00:00.000000000 Z
11
+ date: 2019-09-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday