kennel 1.81.1 → 1.84.1

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: 4ecb9c89748e6eeee268d0d744622b536242af1c1d7f615bf77fc5963e1630d8
4
- data.tar.gz: e6cae4da66e0947163d1f441bce2afb19f1818dac0ed9418143b3f2a2480b220
3
+ metadata.gz: 69237d514f39fc9c8a1fad937bb038ce526424231fe4cc64ce3e69ac43820650
4
+ data.tar.gz: 7bf30b567652a4f6808073ff46487c7945570a6bb4c34afe60b60ad14bb30f80
5
5
  SHA512:
6
- metadata.gz: 7a3342551a926a55c580571524cadbb32bf7b02b77a914f32e1edf2a07b6772091373286c36871d9651a57899134364d8c9c5894a8ef1e46f72ad091ca241990
7
- data.tar.gz: 6eb25f647a3a7d47c0858ca03f0649e151f6437d711701ea24f1dd3b742b5adbfbd3f0318a531076b2236063dda194bbdec72237eaf3f1159f447ac86a95ee68
6
+ metadata.gz: 3cffb601fc240f3d6f87e41a369a772a0dce2c3de411d8eb290aa67ca89c30fbdde7d05c701e844cf12a92c398438c975175de2739129ad62d85f7c9eb667c30
7
+ data.tar.gz: 9bf61895741f6b762627f05a756355445fdd43bc488db04ae7a406a2fbc314311adf8e745ec87d4eb512cefd250d0cd3e6b777f3bb3d7cb7543e0cd6eb317ea1
data/Readme.md CHANGED
@@ -216,7 +216,7 @@ to unblock use the `validate: -> { false }` option.
216
216
 
217
217
  Link resources with their kennel_id in the format `project kennel_id` + `:` + `resource kennel_id`,
218
218
  this should be used to create dependent resources like monitor + slos,
219
- so they can be created in a single update and can be re-created if any of them is deleted.
219
+ so they can be created in a single update and can be re-created if any of them is deleted.
220
220
 
221
221
  |Resource|Type|Syntax|
222
222
  |---|---|---|
@@ -273,9 +273,19 @@ Run `rake kennel:alerts TAG=service:my-service` to see all un-muted alerts for a
273
273
 
274
274
  ### Grepping through all of datadog
275
275
 
276
- `rake kennel:dump`
276
+ ```Bash
277
+ rake kennel:dump > tmp/dump
278
+ cat tmp/dump | grep foo
279
+ ```
277
280
  focus on a single type: `TYPE=monitors`
278
281
 
282
+ Show full resources or just their urls by pattern:
283
+ ```Bash
284
+ rake kennel:dump_grep DUMP=tmp/dump PATTERN=foo URLS=true
285
+ https://foo.datadog.com/dasboard/123
286
+ https://foo.datadog.com/monitor/123
287
+ ```
288
+
279
289
  ### Find all monitors with No-Data
280
290
 
281
291
  `rake kennel:nodata TAG=team:foo`
data/lib/kennel/api.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
  module Kennel
3
+ # encapsulates knowledge around how the api works
3
4
  class Api
5
+ CACHE_FILE = "tmp/cache/details"
6
+
4
7
  def initialize(app_key, api_key)
5
8
  @app_key = app_key
6
9
  @api_key = api_key
@@ -49,8 +52,27 @@ module Kennel
49
52
  request :delete, "/api/v1/#{api_resource}/#{id}", params: { force: "true" }, ignore_404: true
50
53
  end
51
54
 
55
+ def fill_details!(api_resource, list)
56
+ return unless api_resource == "dashboard"
57
+ details_cache do |cache|
58
+ Utils.parallel(list) { |a| fill_detail!(api_resource, a, cache) }
59
+ end
60
+ end
61
+
52
62
  private
53
63
 
64
+ # Make diff work even though we cannot mass-fetch definitions
65
+ def fill_detail!(api_resource, a, cache)
66
+ args = [api_resource, a.fetch(:id)]
67
+ full = cache.fetch(args, a.fetch(:modified_at)) { show(*args) }
68
+ a.merge!(full)
69
+ end
70
+
71
+ def details_cache(&block)
72
+ cache = FileCache.new CACHE_FILE, Kennel::VERSION
73
+ cache.open(&block)
74
+ end
75
+
54
76
  def request(method, path, body: nil, params: {}, ignore_404: false)
55
77
  params = params.merge(application_key: @app_key, api_key: @api_key)
56
78
  query = Faraday::FlatParamsEncoder.encode(params)
@@ -5,7 +5,6 @@ module Kennel
5
5
  include TemplateVariables
6
6
  include OptionalValidations
7
7
 
8
- API_LIST_INCOMPLETE = true
9
8
  DASHBOARD_DEFAULTS = { template_variables: [] }.freeze
10
9
  READONLY_ATTRIBUTES = superclass::READONLY_ATTRIBUTES + [
11
10
  :author_handle, :author_name, :modified_at, :url, :is_read_only, :notify_list
@@ -124,7 +123,7 @@ module Kennel
124
123
  @json
125
124
  end
126
125
 
127
- def url(id)
126
+ def self.url(id)
128
127
  Utils.path_to_url "/dashboard/#{id}"
129
128
  end
130
129
 
@@ -186,22 +185,26 @@ module Kennel
186
185
  end
187
186
 
188
187
  def render_definitions(definitions)
189
- definitions.map do |title, type, display_type, queries, options = {}, ignored = nil|
190
- # validate inputs
191
- if ignored || (!title || !type || !queries || !options.is_a?(Hash))
192
- raise ArgumentError, "Expected exactly 5 arguments for each definition (title, type, display_type, queries, options)"
193
- end
194
- if (SUPPORTED_DEFINITION_OPTIONS | options.keys) != SUPPORTED_DEFINITION_OPTIONS
195
- raise ArgumentError, "Supported options are: #{SUPPORTED_DEFINITION_OPTIONS.map(&:inspect).join(", ")}"
196
- end
188
+ definitions.map do |title, type, display_type, queries, options = {}, too_many_args = nil|
189
+ if title.is_a?(Hash) && !type
190
+ title # user gave a full widget, just use it
191
+ else
192
+ # validate inputs
193
+ if too_many_args || (!title || !type || !queries || !options.is_a?(Hash))
194
+ raise ArgumentError, "Expected exactly 5 arguments for each definition (title, type, display_type, queries, options)"
195
+ end
196
+ if (SUPPORTED_DEFINITION_OPTIONS | options.keys) != SUPPORTED_DEFINITION_OPTIONS
197
+ raise ArgumentError, "Supported options are: #{SUPPORTED_DEFINITION_OPTIONS.map(&:inspect).join(", ")}"
198
+ end
197
199
 
198
- # build definition
199
- requests = Array(queries).map do |q|
200
- request = { q: q }
201
- request[:display_type] = display_type if display_type
202
- request
200
+ # build definition
201
+ requests = Array(queries).map do |q|
202
+ request = { q: q }
203
+ request[:display_type] = display_type if display_type
204
+ request
205
+ end
206
+ { definition: { title: title, type: type, requests: requests, **options } }
203
207
  end
204
- { definition: { title: title, type: type, requests: requests, **options } }
205
208
  end
206
209
  end
207
210
  end
@@ -5,8 +5,6 @@ module Kennel
5
5
  include OptionalValidations
6
6
 
7
7
  RENOTIFY_INTERVALS = [0, 10, 20, 30, 40, 50, 60, 90, 120, 180, 240, 300, 360, 720, 1440].freeze # minutes
8
- # 2d and 1w are valid timeframes for anomaly monitors
9
- QUERY_INTERVALS = ["1m", "5m", "10m", "15m", "30m", "1h", "2h", "4h", "1d", "2d", "1w"].freeze
10
8
  OPTIONAL_SERVICE_CHECK_THRESHOLDS = [:ok, :warning].freeze
11
9
  READONLY_ATTRIBUTES = superclass::READONLY_ATTRIBUTES + [
12
10
  :multi, :matching_downtimes, :overall_state_modified, :overall_state, :restricted_roles
@@ -125,7 +123,7 @@ module Kennel
125
123
  "monitor"
126
124
  end
127
125
 
128
- def url(id)
126
+ def self.url(id)
129
127
  Utils.path_to_url "/monitors##{id}/edit"
130
128
  end
131
129
 
@@ -202,14 +200,6 @@ module Kennel
202
200
  invalid! "renotify_interval must be one of #{RENOTIFY_INTERVALS.join(", ")}"
203
201
  end
204
202
 
205
- if type == "query alert"
206
- # verify interval is valid
207
- interval = data.fetch(:query)[/\(last_(\S+?)\)/, 1]
208
- if interval && !QUERY_INTERVALS.include?(interval)
209
- invalid! "query interval was #{interval}, but must be one of #{QUERY_INTERVALS.join(", ")}"
210
- end
211
- end
212
-
213
203
  if ["query alert", "service check"].include?(type) # TODO: most likely more types need this
214
204
  # verify is_match/is_exact_match uses available variables
215
205
  message = data.fetch(:message)
@@ -6,7 +6,6 @@ module Kennel
6
6
  READONLY_ATTRIBUTES = [
7
7
  :deleted, :id, :created, :created_at, :creator, :org_id, :modified, :modified_at, :api_resource
8
8
  ].freeze
9
- API_LIST_INCOMPLETE = false
10
9
 
11
10
  settings :id, :kennel_id
12
11
 
@@ -19,6 +18,10 @@ module Kennel
19
18
  end
20
19
  end
21
20
 
21
+ def api_resource_map
22
+ subclasses.map { |s| [s.api_resource, s] }.to_h
23
+ end
24
+
22
25
  private
23
26
 
24
27
  def normalize(_expected, actual)
@@ -58,7 +58,7 @@ module Kennel
58
58
  "slo"
59
59
  end
60
60
 
61
- def url(id)
61
+ def self.url(id)
62
62
  Utils.path_to_url "/slo?slo_id=#{id}"
63
63
  end
64
64
 
data/lib/kennel/syncer.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module Kennel
3
3
  class Syncer
4
- CACHE_FILE = "tmp/cache/details" # keep in sync with .travis.yml caching
5
4
  TRACKING_FIELDS = [:message, :description].freeze
6
5
  DELETE_ORDER = ["dashboard", "slo", "monitor"].freeze # dashboards references monitors + slos, slos reference monitors
7
6
 
@@ -42,12 +41,12 @@ module Kennel
42
41
  reply = @api.create e.class.api_resource, e.as_json
43
42
  id = reply.fetch(:id)
44
43
  populate_id_map [reply] # allow resolving ids we could previously no resolve
45
- Kennel.out.puts "Created #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.url(id)}"
44
+ Kennel.out.puts "Created #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.class.url(id)}"
46
45
  end
47
46
 
48
47
  each_resolved @update do |id, e|
49
48
  @api.update e.class.api_resource, id, e.as_json
50
- Kennel.out.puts "Updated #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.url(id)}"
49
+ Kennel.out.puts "Updated #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.class.url(id)}"
51
50
  end
52
51
 
53
52
  @delete.each do |id, _, a|
@@ -117,10 +116,10 @@ module Kennel
117
116
  end
118
117
  end
119
118
 
120
- details_cache do |cache|
121
- # fill details of things we need to compare (only do this part in parallel for safety & balancing)
122
- Utils.parallel(items.select { |e, _| e && e.class::API_LIST_INCOMPLETE }) { |_, a| fill_details(a, cache) }
123
- end
119
+ # fill details of things we need to compare
120
+ detailed = Hash.new { |h, k| h[k] = [] }
121
+ items.each { |e, a| detailed[a[:api_resource]] << a if e }
122
+ detailed.each { |api_resource, actuals| @api.fill_details! api_resource, actuals }
124
123
 
125
124
  # pick out things to update or delete
126
125
  items.each do |e, a|
@@ -140,21 +139,6 @@ module Kennel
140
139
  @delete.sort_by! { |_, _, a| DELETE_ORDER.index a.fetch(:api_resource) }
141
140
  end
142
141
 
143
- # Make diff work even though we cannot mass-fetch definitions
144
- def fill_details(a, cache)
145
- resource = a.fetch(:api_resource)
146
- args = [resource, a.fetch(:id)]
147
- full = cache.fetch(args, a[:modified] || a.fetch(:modified_at)) do
148
- @api.show(*args)
149
- end
150
- a.merge!(full)
151
- end
152
-
153
- def details_cache(&block)
154
- cache = FileCache.new CACHE_FILE, Kennel::VERSION
155
- cache.open(&block)
156
- end
157
-
158
142
  def download_definitions
159
143
  Utils.parallel(Models::Record.subclasses.map(&:api_resource)) do |api_resource|
160
144
  results = @api.list(api_resource, with_downtimes: false) # lookup monitors without adding unnecessary downtime information
data/lib/kennel/tasks.rb CHANGED
@@ -130,15 +130,43 @@ namespace :kennel do
130
130
  if type = ENV["TYPE"]
131
131
  [type]
132
132
  else
133
- Kennel::Models::Record.subclasses.map(&:api_resource)
133
+ Kennel::Models::Record.api_resource_map.keys
134
134
  end
135
+ api = Kennel.send(:api)
136
+ list = nil
137
+
135
138
  resources.each do |resource|
136
- Kennel.send(:api).list(resource).each do |r|
139
+ Kennel::Progress.progress("Downloading #{resource}") do
140
+ list = api.list(resource)
141
+ api.fill_details!(resource, list)
142
+ end
143
+ list.each do |r|
144
+ r[:api_resource] = resource
137
145
  Kennel.out.puts JSON.pretty_generate(r)
138
146
  end
139
147
  end
140
148
  end
141
149
 
150
+ desc "Find items from dump by pattern DUMP= PATTERN= [URLS=true]"
151
+ task dump_grep: :environment do
152
+ file = ENV.fetch("DUMP")
153
+ pattern = Regexp.new ENV.fetch("PATTERN")
154
+ items = File.read(file).gsub("}\n{", "}--SPLIT--{").split("--SPLIT--")
155
+ models = Kennel::Models::Record.api_resource_map
156
+ found = items.grep(pattern)
157
+ exit 1 if found.empty?
158
+ found.each do |resource|
159
+ if ENV["URLS"]
160
+ parsed = JSON.parse(resource)
161
+ url = models[parsed.fetch("api_resource")].url(parsed.fetch("id"))
162
+ title = parsed["title"] || parsed["name"]
163
+ Kennel.out.puts "#{url} # #{title}"
164
+ else
165
+ Kennel.out.puts resource
166
+ end
167
+ end
168
+ end
169
+
142
170
  task :environment do
143
171
  require "kennel"
144
172
  gem "dotenv"
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Kennel
3
- VERSION = "1.81.1"
3
+ VERSION = "1.84.1"
4
4
  end
data/template/Readme.md CHANGED
@@ -194,10 +194,11 @@ removing the `id` will cause kennel to create a new resource in datadog.
194
194
  Some validations might be too strict for your usecase or just wrong, please [open an issue](https://github.com/grosser/kennel/issues) and
195
195
  to unblock use the `validate: -> { false }` option.
196
196
 
197
- ### Linking with kennel_ids
197
+ ### Linking resources with kennel_id
198
198
 
199
- Link resources via their kennel_id `projects kennel_id` + `:` + `monitors kennel id`,
200
- this should be used to create dependent resources like monitor + slos.
199
+ Link resources with their kennel_id in the format `project kennel_id` + `:` + `resource kennel_id`,
200
+ this should be used to create dependent resources like monitor + slos,
201
+ so they can be created in a single update and can be re-created if any of them is deleted.
201
202
 
202
203
  |Resource|Type|Syntax|
203
204
  |---|---|---|
@@ -254,9 +255,19 @@ Run `rake kennel:alerts TAG=service:my-service` to see all un-muted alerts for a
254
255
 
255
256
  ### Grepping through all of datadog
256
257
 
257
- `rake kennel:dump`
258
+ ```Bash
259
+ rake kennel:dump > tmp/dump
260
+ cat tmp/dump | grep foo
261
+ ```
258
262
  focus on a single type: `TYPE=monitors`
259
263
 
264
+ Show full resources or just their urls by pattern:
265
+ ```Bash
266
+ rake kennel:dump_grep DUMP=tmp/dump PATTERN=foo URLS=true
267
+ https://foo.datadog.com/dasboard/123
268
+ https://foo.datadog.com/monitor/123
269
+ ```
270
+
260
271
  ### Find all monitors with No-Data
261
272
 
262
273
  `rake kennel:nodata TAG=team:foo`
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.81.1
4
+ version: 1.84.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: 2021-01-21 00:00:00.000000000 Z
11
+ date: 2021-03-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday