kennel 1.72.2 → 1.75.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1aa2b3776b0249a4013afd8ef6b078f4a783b9df6461193a83ed25b4b4721ce7
4
- data.tar.gz: 4b60552e08b14ce5e124dde66329d74983abf5306b9aa4e4e0ae66414fc42a16
3
+ metadata.gz: 23af2ecfb9019df555f7fe3a3954f4d992208927744093ff1f8886afd571e457
4
+ data.tar.gz: 6e2c902c47020e46b5fc0e647d8d63bbd7227657ce78303c673470be840ecbf9
5
5
  SHA512:
6
- metadata.gz: 04bdf0ba96f2c3911215d9f611b397a763c1890f7404bff62f050605b017944bb7e820ec2ba13697b7e29a7e007ddeb70324dfba30be6b896bf05f4f4ed1218c
7
- data.tar.gz: '08cb0890b0235e1af195a465e9026f8465c5fed46ab1828e78c27f69002720c8a7c0c3ec58c0b6821da12d5ef57013c58a1cf7798435f30067daa84cf6f4c8f8'
6
+ metadata.gz: c2dfa7e9f54c83d3369b48b91896cb7dc7cfe75bb94d672d7de07bc7a6b8ea3f63ebe38788dddf5de90ead3b73f977c42f87497d61e7b23def6ae385d0a42213
7
+ data.tar.gz: 9ebb18f6d9c54be18a783d8511e3128b9c59b5898f9f14a1be77c871dcdab837681bf04050ec97a1a2f675c84fda624469edaed0af9f5e0da11b60517ba93cea
data/Readme.md CHANGED
@@ -1,17 +1,58 @@
1
- # Kennel
2
-
3
1
  ![](template/github/cage.jpg?raw=true)
4
2
 
5
- Manage datadog monitors/dashboards/slos as code
3
+ Manage Datadog Monitors / Dashboards / Slos as code
6
4
 
7
- - Documented, reusable, and searchable
8
- - Changes are PR reviewed and auditable
5
+ - DRY, searchable, audited, documented
6
+ - Changes are PR reviewed and applied on merge
9
7
  - Updating shows diff before applying
10
- - Automated import of existing monitors/dashboards/slos
8
+ - Automated import of existing resources
9
+ - Resources are grouped into projects that belong to teams and inherit tags
10
+ - No copy-pasting of ids to create new resources
11
+ - Automated cleanup when removing code
12
+ - [Helpers](#helpers) for automating common tasks
13
+
14
+ ### Applying changes
11
15
 
12
16
  ![](template/github/screen.png?raw=true)
17
+
18
+ ### Example code
19
+
20
+ ```Ruby
21
+ # teams/foo.rb
22
+ module Teams
23
+ class Foo < Kennel::Models::Team
24
+ defaults(mention: -> { "@slack-my-team" })
25
+ end
26
+ end
27
+
28
+ # projects/bar.rb
29
+ class Bar < Kennel::Models::Project
30
+ defaults(
31
+ team: -> { Teams::Foo.new }, # use mention and tags from the team
32
+ parts: -> {
33
+ [
34
+ Kennel::Models::Monitor.new(
35
+ self, # the current project
36
+ type: -> { "query alert" },
37
+ kennel_id: -> { "load-too-high" }, # pick a unique name
38
+ name: -> { "Foobar Load too high" }, # nice descriptive name that will show up in alerts and emails
39
+ message: -> {
40
+ <<~TEXT
41
+ This is bad!
42
+ #{super()} # inserts mention from team
43
+ TEXT
44
+ },
45
+ query: -> { "avg(last_5m):avg:system.load.5{hostgroup:api} by {pod} > #{critical}" },
46
+ critical: -> { 20 }
47
+ )
48
+ ]
49
+ }
50
+ )
51
+ end
52
+ ```
53
+
13
54
  <!-- NOT IN template/Readme.md -->
14
- ## Install
55
+ ## Installation
15
56
 
16
57
  - create a new private `kennel` repo for your organization (do not fork this repo)
17
58
  - use the template folder as starting point:
@@ -22,8 +63,8 @@ Manage datadog monitors/dashboards/slos as code
22
63
  cd kennel && git add . && git commit -m 'initial'
23
64
  ```
24
65
  - add a basic projects and teams so others can copy-paste to get started
25
- - setup travis build for your repo
26
- - uncomment `.travis.yml` section for automated PR planing and datadog updates on merge
66
+ - setup CI build for your repo (travis and Github Actions supported)
67
+ - uncomment `.travis.yml` section for datadog updates on merge (TODO: example setup for Github Actions)
27
68
  - follow `Setup` in your repos Readme.md
28
69
  <!-- NOT IN -->
29
70
 
@@ -109,7 +150,7 @@ end
109
150
  - alternatively: `bundle exec rake generate` to only locally update the generated `json` files
110
151
  - review changes then `git commit`
111
152
  - make a PR ... get reviewed ... merge
112
- - datadog is updated by travis
153
+ - datadog is updated by CI
113
154
 
114
155
  ### Adding a new dashboard
115
156
  - go to [datadog dashboard UI](https://app.datadoghq.com/dashboard/lists) and click on _New Dashboard_ to create a dashboard
@@ -176,25 +217,7 @@ To link to existing monitors via their kennel_id
176
217
  - figure out project name by converting the class name to snake-case
177
218
  - run `PROJECT=foo bundle exec rake kennel:update_datadog` to test changes for a single project
178
219
 
179
- ### Listing un-muted alerts
180
-
181
- Run `rake kennel:alerts TAG=service:my-service` to see all un-muted alerts for a given datadog monitor tag.
182
-
183
- ### Validating mentions work
184
-
185
- `rake kennel:validate_mentions` should run as part of CI
186
-
187
- ### Grepping through all of datadog
188
-
189
- `TYPE=monitor rake kennel:dump`
190
-
191
- ### Find all monitors with No-Data
192
-
193
- `rake kennel:nodata TAG=team:foo`
194
-
195
- ## Examples
196
-
197
- ### Reusable monitors/dashes/etc
220
+ ### Reuse
198
221
 
199
222
  Add to `parts/<folder>`.
200
223
 
@@ -221,8 +244,30 @@ class Database < Kennel::Models::Project
221
244
  )
222
245
  end
223
246
  ```
247
+
248
+ ## Helpers
249
+
250
+ ### Listing un-muted alerts
251
+
252
+ Run `rake kennel:alerts TAG=service:my-service` to see all un-muted alerts for a given datadog monitor tag.
253
+
254
+ ### Validating mentions work
255
+
256
+ `rake kennel:validate_mentions` should run as part of CI
257
+
258
+ ### Grepping through all of datadog
259
+
260
+ `TYPE=monitor rake kennel:dump`
261
+
262
+ ### Find all monitors with No-Data
263
+
264
+ `rake kennel:nodata TAG=team:foo`
265
+
224
266
  <!-- NOT IN template/Readme.md -->
225
267
 
268
+
269
+ ## Development
270
+
226
271
  ### Integration testing
227
272
 
228
273
  ```Bash
@@ -29,7 +29,6 @@ require "kennel/models/project"
29
29
  require "kennel/models/team"
30
30
 
31
31
  module Kennel
32
- MISSING_ID = 1
33
32
  class ValidationError < RuntimeError
34
33
  end
35
34
 
@@ -40,7 +40,8 @@ module Kennel
40
40
  Kennel::Utils.parameterize(title)
41
41
  end
42
42
 
43
- if resource == "monitor"
43
+ case resource
44
+ when "monitor"
44
45
  # flatten monitor options so they are all on the base
45
46
  data.merge!(data.delete(:options))
46
47
  data.merge!(data.delete(:thresholds) || {})
@@ -55,7 +56,7 @@ module Kennel
55
56
  end
56
57
 
57
58
  data[:type] = "query alert" if data[:type] == "metric alert"
58
- elsif resource == "dashboard"
59
+ when "dashboard"
59
60
  widgets = data[:widgets]&.flat_map { |widget| widget.dig(:definition, :widgets) || [widget] }
60
61
  widgets&.each { |widget| dry_up_query!(widget) }
61
62
  end
@@ -13,7 +13,10 @@ module Kennel
13
13
  REQUEST_DEFAULTS = {
14
14
  style: { line_width: "normal", palette: "dog_classic", line_type: "solid" }
15
15
  }.freeze
16
- TIMESERIES_DEFAULTS = { show_legend: false, legend_size: "0" }.freeze
16
+ WIDGET_DEFAULTS = {
17
+ "timeseries" => { show_legend: false, legend_size: "0" },
18
+ "note" => { background_color: "white", font_size: "14", show_tick: false, tick_edge: "left", tick_pos: "50%", text_align: "left" }
19
+ }.freeze
17
20
  SUPPORTED_DEFINITION_OPTIONS = [:events, :markers, :precision].freeze
18
21
 
19
22
  DEFAULTS = {
@@ -26,7 +29,7 @@ module Kennel
26
29
  description: -> { "" },
27
30
  definitions: -> { [] },
28
31
  widgets: -> { [] },
29
- template_variable_presets: -> { nil },
32
+ template_variable_presets: -> { DEFAULTS.fetch(:template_variable_presets) },
30
33
  id: -> { nil }
31
34
  )
32
35
 
@@ -50,7 +53,7 @@ module Kennel
50
53
  end
51
54
  end
52
55
 
53
- ignore_definition_defaults_for_type pair, "timeseries", TIMESERIES_DEFAULTS
56
+ ignore_widget_defaults pair
54
57
 
55
58
  ignore_request_defaults(*pair)
56
59
 
@@ -61,11 +64,12 @@ module Kennel
61
64
 
62
65
  private
63
66
 
64
- def ignore_definition_defaults_for_type(pair, type, defaults)
67
+ def ignore_widget_defaults(pair)
65
68
  pair.map(&:size).max.times do |i|
66
- if pair.all? { |w| w.dig(i, :definition, :type) == type }
67
- ignore_defaults(pair[0], pair[1], defaults, nesting: :definition)
68
- end
69
+ types = pair.map { |w| w.dig(i, :definition, :type) }.uniq
70
+ next unless types.size == 1
71
+ next unless defaults = WIDGET_DEFAULTS[types.first]
72
+ ignore_defaults(pair[0], pair[1], defaults, nesting: :definition)
69
73
  end
70
74
  end
71
75
 
@@ -128,7 +132,7 @@ module Kennel
128
132
  url[/\/dashboard\/([a-z\d-]+)/, 1]
129
133
  end
130
134
 
131
- def resolve_linked_tracking_ids(id_map)
135
+ def resolve_linked_tracking_ids!(id_map, **args)
132
136
  widgets = as_json[:widgets].flat_map { |w| [w, *w.dig(:definition, :widgets) || []] }
133
137
  widgets.each do |widget|
134
138
  next unless definition = widget[:definition]
@@ -136,16 +140,16 @@ module Kennel
136
140
  when "uptime"
137
141
  if ids = definition[:monitor_ids]
138
142
  definition[:monitor_ids] = ids.map do |id|
139
- tracking_id?(id) ? resolve_link(id, :monitor, id_map) : id
143
+ tracking_id?(id) ? resolve_link(id, :monitor, id_map, **args) : id
140
144
  end
141
145
  end
142
146
  when "alert_graph"
143
147
  if (id = definition[:alert_id]) && tracking_id?(id)
144
- definition[:alert_id] = resolve_link(id, :monitor, id_map).to_s
148
+ definition[:alert_id] = resolve_link(id, :monitor, id_map, **args).to_s
145
149
  end
146
150
  when "slo"
147
151
  if (id = definition[:slo_id]) && tracking_id?(id)
148
- definition[:slo_id] = resolve_link(id, :slo, id_map).to_s
152
+ definition[:slo_id] = resolve_link(id, :slo, id_map, **args).to_s
149
153
  end
150
154
  end
151
155
  end
@@ -18,7 +18,8 @@ module Kennel
18
18
  new_host_delay: 300,
19
19
  timeout_h: 0,
20
20
  renotify_interval: 0,
21
- no_data_timeframe: nil # this works out ok since if notify_no_data is on, it would never be nil
21
+ no_data_timeframe: nil, # this works out ok since if notify_no_data is on, it would never be nil
22
+ groupby_simple_monitor: false
22
23
  }.freeze
23
24
  DEFAULT_ESCALATION_MESSAGE = ["", nil].freeze
24
25
 
@@ -103,11 +104,10 @@ module Kennel
103
104
  @as_json = data
104
105
  end
105
106
 
106
- def resolve_linked_tracking_ids(id_map)
107
+ def resolve_linked_tracking_ids!(id_map, **args)
107
108
  if as_json[:type] == "composite"
108
109
  as_json[:query] = as_json[:query].gsub(/%\{(.*?)\}/) do
109
- # need force here since it validates the id exists
110
- resolve_link($1, :monitor, id_map, force: true)
110
+ resolve_link($1, :monitor, id_map, **args)
111
111
  end
112
112
  end
113
113
  end
@@ -120,7 +120,7 @@ module Kennel
120
120
  Utils.path_to_url "/monitors##{id}/edit"
121
121
  end
122
122
 
123
- # datadog uses both / and # as separator in it's links
123
+ # datadog uses / for show and # for edit as separator in it's links
124
124
  def self.parse_url(url)
125
125
  return unless id = url[/\/monitors[\/#](\d+)/, 1]
126
126
  Integer(id)
@@ -202,7 +202,7 @@ module Kennel
202
202
  # verify is_match uses available variables
203
203
  message = data.fetch(:message)
204
204
  used = message.scan(/{{\s*#is_match\s*"([a-zA-Z\d_.-]+).name"/).flatten.uniq
205
- allowed = data.fetch(:query)[/by\s*[\({]([^\}\)]+)[}\)]/, 1].to_s.gsub(/["']/, "").split(/\s*,\s*/)
205
+ allowed = data.fetch(:query)[/by\s*[({]([^})]+)[})]/, 1].to_s.gsub(/["']/, "").split(/\s*,\s*/)
206
206
  unsupported = used - allowed
207
207
  if unsupported.any?
208
208
  invalid! "is_match used with #{unsupported}, but metric is only grouped by #{allowed}"
@@ -60,23 +60,22 @@ module Kennel
60
60
  "#{project.kennel_id}:#{kennel_id}"
61
61
  end
62
62
 
63
- def resolve_linked_tracking_ids(*)
63
+ def resolve_linked_tracking_ids!(*)
64
64
  end
65
65
 
66
66
  private
67
67
 
68
- def resolve_link(id, type, id_map, force: false)
69
- found = id_map[id]
70
- return found if found && found != :new
71
- api_resource = self.class.api_resource
72
-
73
- if found == :new
68
+ def resolve_link(id, type, id_map, force:)
69
+ value = id_map[id]
70
+ if value == :new
74
71
  if force
75
- invalid! "#{api_resource.capitalize} #{id} will be created in the current run and can only be used after that"
72
+ # TODO: remove the need for this by sorting monitors by missing resolutions
73
+ invalid! "#{id} needs to already exist, try again"
76
74
  else
77
- Kennel.err.puts "#{api_resource.capitalize} #{id} will be created in the current run, the next run will link it properly"
78
- Kennel::MISSING_ID
75
+ id # will be re-resolved by syncer after the linked object was created
79
76
  end
77
+ elsif value
78
+ value
80
79
  else
81
80
  invalid! "Unable to find #{type} #{id} (does not exist and is not being created by the current run)"
82
81
  end
@@ -66,9 +66,9 @@ module Kennel
66
66
  url[/\/slo\?slo_id=([a-z\d]+)/, 1]
67
67
  end
68
68
 
69
- def resolve_linked_tracking_ids(id_map)
69
+ def resolve_linked_tracking_ids!(id_map, **args)
70
70
  as_json[:monitor_ids] = as_json[:monitor_ids].map do |id|
71
- id.is_a?(String) ? resolve_link(id, :monitor, id_map) : id
71
+ id.is_a?(String) ? resolve_link(id, :monitor, id_map, **args) : id
72
72
  end
73
73
  end
74
74
 
@@ -38,12 +38,23 @@ module Kennel
38
38
  end
39
39
 
40
40
  def update
41
+ changed = (@create + @update).map { |_, e| e } unless @create.empty?
42
+
41
43
  @create.each do |_, e|
44
+ e.resolve_linked_tracking_ids!({}, force: true)
45
+
42
46
  reply = @api.create e.class.api_resource, e.as_json
43
- Kennel.out.puts "Created #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.url(reply.fetch(:id))}"
47
+ id = reply.fetch(:id)
48
+
49
+ # resolve ids we could previously no resolve
50
+ changed.delete e
51
+ resolve_linked_tracking_ids! from: [reply], to: changed
52
+
53
+ Kennel.out.puts "Created #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.url(id)}"
44
54
  end
45
55
 
46
56
  @update.each do |id, e|
57
+ e.resolve_linked_tracking_ids!({}, force: true)
47
58
  @api.update e.class.api_resource, id, e.as_json
48
59
  Kennel.out.puts "Updated #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.url(id)}"
49
60
  end
@@ -65,12 +76,10 @@ module Kennel
65
76
  @delete = []
66
77
 
67
78
  actual = Progress.progress("Downloading definitions") { download_definitions }
68
-
69
- resolve_linked_tracking_ids actual
79
+ resolve_linked_tracking_ids! from: actual, to: @expected
80
+ filter_by_project! actual
70
81
 
71
82
  Progress.progress "Diffing" do
72
- filter_by_project! actual
73
-
74
83
  items = actual.map do |a|
75
84
  e = matching_expected(a)
76
85
  if e && @expected.delete(e)
@@ -98,6 +107,7 @@ module Kennel
98
107
 
99
108
  ensure_all_ids_found
100
109
  @create = @expected.map { |e| [nil, e] }
110
+ @create.sort_by! { |_, e| -DELETE_ORDER.index(e.class.api_resource) }
101
111
  end
102
112
 
103
113
  @delete.sort_by! { |_, _, a| DELETE_ORDER.index a.fetch(:api_resource) }
@@ -178,7 +188,7 @@ module Kennel
178
188
  end
179
189
 
180
190
  # Do not add tracking-id when working with existing ids on a branch,
181
- # so resource do not get deleted from merges to master.
191
+ # so resource do not get deleted fr:om merges to master.
182
192
  # Also make sure the diff still makes sense, by kicking out the now noop-update.
183
193
  #
184
194
  # Note: ideally we'd never add tracking in the first place, but at that point we do not know the diff yet
@@ -203,10 +213,10 @@ module Kennel
203
213
  end
204
214
  end
205
215
 
206
- def resolve_linked_tracking_ids(actual)
207
- map = actual.each_with_object({}) { |a, lookup| lookup[tracking_id(a)] = a.fetch(:id) }
208
- @expected.each { |e| map[e.tracking_id] ||= :new }
209
- @expected.each { |e| e.resolve_linked_tracking_ids(map) }
216
+ def resolve_linked_tracking_ids!(from:, to:)
217
+ map = from.each_with_object({}) { |a, lookup| lookup[tracking_id(a)] = a.fetch(:id) }
218
+ to.each { |e| map[e.tracking_id] ||= :new }
219
+ to.each { |e| e.resolve_linked_tracking_ids!(map, force: false) }
210
220
  end
211
221
 
212
222
  def filter_by_project!(definitions)
@@ -68,15 +68,16 @@ namespace :kennel do
68
68
  Kennel.update
69
69
  end
70
70
 
71
- desc "update if this is a push to the default branch, otherwise plan"
72
- task :travis do
73
- on_default_branch = (ENV["TRAVIS_BRANCH"] == (ENV["DEFAULT_BRANCH"] || "master"))
74
- is_push = (ENV["TRAVIS_PULL_REQUEST"] == "false")
71
+ desc "update on push to the default branch, otherwise show plan"
72
+ task :ci do
73
+ branch = (ENV["TRAVIS_BRANCH"] || ENV["GITHUB_REF"]).to_s.sub(/^refs\/heads\//, "")
74
+ on_default_branch = (branch == (ENV["DEFAULT_BRANCH"] || "master"))
75
+ is_push = (ENV["TRAVIS_PULL_REQUEST"] == "false" || ENV["GITHUB_EVENT_NAME"] == "push")
75
76
  task_name =
76
77
  if on_default_branch && is_push
77
78
  "kennel:update_datadog"
78
79
  else
79
- "kennel:plan" # show plan in travis logs
80
+ "kennel:plan" # show plan in CI logs
80
81
  end
81
82
 
82
83
  Rake::Task[task_name].invoke
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Kennel
3
- VERSION = "1.72.2"
3
+ VERSION = "1.75.1"
4
4
  end
@@ -1,16 +1,57 @@
1
- # Kennel
2
-
3
1
  ![](github/cage.jpg?raw=true)
4
2
 
5
- Manage datadog monitors/dashboards/slos as code
3
+ Manage Datadog Monitors / Dashboards / Slos as code
6
4
 
7
- - Documented, reusable, and searchable
8
- - Changes are PR reviewed and auditable
5
+ - DRY, searchable, audited, documented
6
+ - Changes are PR reviewed and applied on merge
9
7
  - Updating shows diff before applying
10
- - Automated import of existing monitors/dashboards/slos
8
+ - Automated import of existing resources
9
+ - Resources are grouped into projects that belong to teams and inherit tags
10
+ - No copy-pasting of ids to create new resources
11
+ - Automated cleanup when removing code
12
+ - [Helpers](#helpers) for automating common tasks
13
+
14
+ ### Applying changes
11
15
 
12
16
  ![](github/screen.png?raw=true)
13
17
 
18
+ ### Example code
19
+
20
+ ```Ruby
21
+ # teams/foo.rb
22
+ module Teams
23
+ class Foo < Kennel::Models::Team
24
+ defaults(mention: -> { "@slack-my-team" })
25
+ end
26
+ end
27
+
28
+ # projects/bar.rb
29
+ class Bar < Kennel::Models::Project
30
+ defaults(
31
+ team: -> { Teams::Foo.new }, # use mention and tags from the team
32
+ parts: -> {
33
+ [
34
+ Kennel::Models::Monitor.new(
35
+ self, # the current project
36
+ type: -> { "query alert" },
37
+ kennel_id: -> { "load-too-high" }, # pick a unique name
38
+ name: -> { "Foobar Load too high" }, # nice descriptive name that will show up in alerts and emails
39
+ message: -> {
40
+ <<~TEXT
41
+ This is bad!
42
+ #{super()} # inserts mention from team
43
+ TEXT
44
+ },
45
+ query: -> { "avg(last_5m):avg:system.load.5{hostgroup:api} by {pod} > #{critical}" },
46
+ critical: -> { 20 }
47
+ )
48
+ ]
49
+ }
50
+ )
51
+ end
52
+ ```
53
+
54
+
14
55
  ## Structure
15
56
 
16
57
  - `projects/` monitors/dashboards/etc scoped by project
@@ -91,7 +132,7 @@ end
91
132
  - alternatively: `bundle exec rake generate` to only locally update the generated `json` files
92
133
  - review changes then `git commit`
93
134
  - make a PR ... get reviewed ... merge
94
- - datadog is updated by travis
135
+ - datadog is updated by CI
95
136
 
96
137
  ### Adding a new dashboard
97
138
  - go to [datadog dashboard UI](https://app.datadoghq.com/dashboard/lists) and click on _New Dashboard_ to create a dashboard
@@ -158,25 +199,7 @@ To link to existing monitors via their kennel_id
158
199
  - figure out project name by converting the class name to snake-case
159
200
  - run `PROJECT=foo bundle exec rake kennel:update_datadog` to test changes for a single project
160
201
 
161
- ### Listing un-muted alerts
162
-
163
- Run `rake kennel:alerts TAG=service:my-service` to see all un-muted alerts for a given datadog monitor tag.
164
-
165
- ### Validating mentions work
166
-
167
- `rake kennel:validate_mentions` should run as part of CI
168
-
169
- ### Grepping through all of datadog
170
-
171
- `TYPE=monitor rake kennel:dump`
172
-
173
- ### Find all monitors with No-Data
174
-
175
- `rake kennel:nodata TAG=team:foo`
176
-
177
- ## Examples
178
-
179
- ### Reusable monitors/dashes/etc
202
+ ### Reuse
180
203
 
181
204
  Add to `parts/<folder>`.
182
205
 
@@ -203,3 +226,22 @@ class Database < Kennel::Models::Project
203
226
  )
204
227
  end
205
228
  ```
229
+
230
+ ## Helpers
231
+
232
+ ### Listing un-muted alerts
233
+
234
+ Run `rake kennel:alerts TAG=service:my-service` to see all un-muted alerts for a given datadog monitor tag.
235
+
236
+ ### Validating mentions work
237
+
238
+ `rake kennel:validate_mentions` should run as part of CI
239
+
240
+ ### Grepping through all of datadog
241
+
242
+ `TYPE=monitor rake kennel:dump`
243
+
244
+ ### Find all monitors with No-Data
245
+
246
+ `rake kennel:nodata TAG=team:foo`
247
+
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.72.2
4
+ version: 1.75.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Grosser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-25 00:00:00.000000000 Z
11
+ date: 2020-07-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday