kennel 1.98.2 → 1.101.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: 94cb1a40566de5e275526cfb3c28b8c36394ca22edab465493ace5107037f728
4
- data.tar.gz: d1adb172f9d04b610e76538eb1ce1fc68bc7a4615554819eb98bb5def0d9cc8f
3
+ metadata.gz: 11220aba046ce1fbc63b8b62e5a60f0cbd4b04862ff7394c0ad43a1776bc34a6
4
+ data.tar.gz: 257808a3036f97c8b994c35ce0c4c621bafd1a29a43da0cba6c113a9fe8297ff
5
5
  SHA512:
6
- metadata.gz: 34d0f7967089bbc6bc7e9fdd8321ce6264c40d637f1e5e3bfd8be46ea3a69aad1e8894818b4f2af814d290456248dcd7d646a141fc27011eca472638cfed73f6
7
- data.tar.gz: b48cc148859210b9af80e6ecfcfc8d3fbbbe3895bd28ae071cc3459eee353b5e0976df0e9ea0927ed196cb52be83f68e9e0d02b21503e8523101101a263fb61d
6
+ metadata.gz: 1bcd69ba301375d93c1c72f9cb1ffcf057e06d4aa3f604baada0ed4dce5cd0d4e7675227d9d4ef8f09a80198bc3e14268fcdb4cd1f3623d3a810818bbbe6d4b2
7
+ data.tar.gz: c189ecb33b4800d9d0e382f82fedbcfa8723f3ff0a27bbd2554528de60d50f774309da9d30c27374ae3365863861f81fae438d4843a34519c4d0a044a4d250ce
data/Readme.md CHANGED
@@ -206,7 +206,7 @@ removing the `id` will cause kennel to create a new resource in datadog.
206
206
  Having many projects (and their sub-resources) can quickly get out of hand.
207
207
 
208
208
  Use this class structure to keep things organized:
209
- ```ruby
209
+ ```Ruby
210
210
  # projects/project_a/base.rb
211
211
  module ProjectA
212
212
  class Base < Kennel::Models::Project
@@ -217,7 +217,7 @@ module ProjectA
217
217
  # projects/project_a/monitors/foo_alert.rb
218
218
  module ProjectA
219
219
  module Monitors
220
- class FooAlert < Kennel::Modesl::Monitor
220
+ class FooAlert < Kennel::Models::Monitor
221
221
  ...
222
222
  ```
223
223
 
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+ module Kennel
3
+ class IdMap
4
+ NEW = :new # will be created during this run
5
+
6
+ def initialize
7
+ @map = Hash.new { |h, k| h[k] = {} }
8
+ end
9
+
10
+ def get(type, tracking_id)
11
+ @map[type][tracking_id]
12
+ end
13
+
14
+ def set(type, tracking_id, id)
15
+ @map[type][tracking_id] = id
16
+ end
17
+
18
+ def set_new(type, tracking_id)
19
+ @map[type][tracking_id] = NEW
20
+ end
21
+
22
+ def new?(type, tracking_id)
23
+ @map[type][tracking_id] == NEW
24
+ end
25
+ end
26
+ end
@@ -189,16 +189,17 @@ module Kennel
189
189
  when "uptime"
190
190
  if ids = definition[:monitor_ids]
191
191
  definition[:monitor_ids] = ids.map do |id|
192
- tracking_id?(id) ? (resolve_link(id, :monitor, id_map, **args) || id) : id
192
+ resolve(id, :monitor, id_map, **args) || id
193
193
  end
194
194
  end
195
195
  when "alert_graph"
196
- if (id = definition[:alert_id]) && tracking_id?(id)
197
- definition[:alert_id] = (resolve_link(id, :monitor, id_map, **args) || id).to_s
196
+ if id = definition[:alert_id]
197
+ resolved = resolve(id, :monitor, id_map, **args) || id
198
+ definition[:alert_id] = resolved.to_s # even though it's a monitor id
198
199
  end
199
200
  when "slo"
200
- if (id = definition[:slo_id]) && tracking_id?(id)
201
- definition[:slo_id] = (resolve_link(id, :slo, id_map, **args) || id).to_s
201
+ if id = definition[:slo_id]
202
+ definition[:slo_id] = resolve(id, :slo, id_map, **args) || id
202
203
  end
203
204
  end
204
205
  end
@@ -206,10 +207,6 @@ module Kennel
206
207
 
207
208
  private
208
209
 
209
- def tracking_id?(id)
210
- id.is_a?(String) && id.include?(":")
211
- end
212
-
213
210
  # creates queries from metadata to avoid having to keep q and expression in sync
214
211
  #
215
212
  # {q: :metadata, metadata: [{expression: "sum:bar", alias_name: "foo"}, ...], }
@@ -121,7 +121,7 @@ module Kennel
121
121
  when "composite", "slo alert"
122
122
  type = (as_json[:type] == "composite" ? :monitor : :slo)
123
123
  as_json[:query] = as_json[:query].gsub(/%{(.*?)}/) do
124
- resolve_link($1, type, id_map, **args) || $&
124
+ resolve($1, type, id_map, **args) || $&
125
125
  end
126
126
  end
127
127
  end
@@ -214,27 +214,47 @@ module Kennel
214
214
  end
215
215
 
216
216
  if ["query alert", "service check"].include?(type) # TODO: most likely more types need this
217
- # verify is_match/is_exact_match uses available variables
218
- message = data.fetch(:message)
219
- used = message.scan(/{{\s*([#^]is(?:_exact)?_match)\s*([^\s}]+)/)
220
- if used.any?
221
- allowed = data.fetch(:query)[/by\s*[({]([^})]+)[})]/, 1]
222
- .to_s.gsub(/["']/, "").split(/\s*,\s*/)
223
- .map! { |w| %("#{w}.name") }
224
- used.uniq.each do |match, group|
225
- next if allowed.include?(group)
226
- invalid!(
227
- "#{match} used with #{group}, but can only be used with #{allowed.join(", ")}. " \
228
- "Group the query by #{group.sub(".name", "").tr('"', "")} or change the #{match}"
229
- )
230
- end
231
- end
217
+ validate_message_variables(data)
232
218
  end
233
219
 
234
220
  unless ALLOWED_PRIORITY_CLASSES.include?(priority.class)
235
221
  invalid! "priority needs to be an Integer"
236
222
  end
237
223
  end
224
+
225
+ # verify is_match/is_exact_match and {{foo.name}} uses available variables
226
+ def validate_message_variables(data)
227
+ message = data.fetch(:message)
228
+
229
+ used =
230
+ message.scan(/{{\s*(?:[#^]is(?:_exact)?_match)\s*"([^\s}]+)"/) + # {{#is_match "environment.name" "production"}}
231
+ message.scan(/{{\s*([^}]+\.name)\s*}}/) # Pod {{pod.name}} failed
232
+ return if used.empty?
233
+ used.flatten!(1)
234
+ used.uniq!
235
+
236
+ # TODO
237
+ # - also match without by
238
+ # - separate parsers for query and service
239
+ # - service must always allow `host`, maybe others
240
+ return unless match = data.fetch(:query).match(/(?:{([^}]*)}\s*)?by\s*[({]([^})]+)[})]/)
241
+
242
+ allowed =
243
+ match[1].to_s.split(/\s*,\s*/).map { |k| k.split(":", 2)[-2] } + # {a:b} -> a TODO: does not work for service check
244
+ match[2].to_s.gsub(/["']/, "").split(/\s*,\s*/) # by {a} -> a
245
+
246
+ allowed.compact!
247
+ allowed.uniq!
248
+ allowed.map! { |w| "#{w.tr('"', "")}.name" }
249
+
250
+ forbidden = used - allowed
251
+ return if forbidden.empty?
252
+
253
+ invalid! <<~MSG.rstrip
254
+ Used #{forbidden.join(", ")} in the message, but can only be used with #{allowed.join(", ")}.
255
+ Group or filter the query by #{forbidden.map { |f| f.sub(".name", "") }.join(", ")} to use it.
256
+ MSG
257
+ end
238
258
  end
239
259
  end
240
260
  end
@@ -100,9 +100,20 @@ module Kennel
100
100
 
101
101
  private
102
102
 
103
+ def resolve(value, type, id_map, force:)
104
+ if tracking_id?(value)
105
+ return resolve_link(value, type, id_map, force: force)
106
+ end
107
+
108
+ value
109
+ end
110
+
111
+ def tracking_id?(id)
112
+ id.is_a?(String) && id.include?(":")
113
+ end
114
+
103
115
  def resolve_link(tracking_id, type, id_map, force:)
104
- id = id_map[tracking_id]
105
- if id == :new
116
+ if id_map.new?(type.to_s, tracking_id)
106
117
  if force
107
118
  invalid!(
108
119
  "#{type} #{tracking_id} was referenced but is also created by the current run.\n" \
@@ -111,7 +122,7 @@ module Kennel
111
122
  else
112
123
  nil # will be re-resolved after the linked object was created
113
124
  end
114
- elsif id
125
+ elsif id = id_map.get(type.to_s, tracking_id)
115
126
  id
116
127
  else
117
128
  invalid! "Unable to find #{type} #{tracking_id} (does not exist and is not being created by the current run)"
@@ -70,7 +70,7 @@ module Kennel
70
70
  def resolve_linked_tracking_ids!(id_map, **args)
71
71
  return unless as_json[:monitor_ids] # ignore_default can remove it
72
72
  as_json[:monitor_ids] = as_json[:monitor_ids].map do |id|
73
- id.is_a?(String) ? (resolve_link(id, :monitor, id_map, **args) || id) : id
73
+ resolve(id, :monitor, id_map, **args) || id
74
74
  end
75
75
  end
76
76
 
@@ -26,7 +26,7 @@ module Kennel
26
26
  type: type,
27
27
  subtype: subtype,
28
28
  options: options,
29
- name: name,
29
+ name: "#{name}#{LOCK}",
30
30
  locations: locations == :all ? LOCATIONS : locations
31
31
  }
32
32
 
data/lib/kennel/syncer.rb CHANGED
@@ -122,7 +122,7 @@ module Kennel
122
122
  def calculate_diff
123
123
  @update = []
124
124
  @delete = []
125
- @id_map = {}
125
+ @id_map = IdMap.new
126
126
 
127
127
  actual = Progress.progress("Downloading definitions") { download_definitions }
128
128
 
@@ -254,11 +254,19 @@ module Kennel
254
254
  end
255
255
 
256
256
  def populate_id_map(expected, actual)
257
+ expected.each do |e|
258
+ @id_map.set_new(e.class.api_resource, e.tracking_id)
259
+ end
260
+
257
261
  actual.each do |a|
258
262
  next unless tracking_id = a.fetch(:tracking_id)
259
- @id_map[tracking_id] = a.fetch(:id)
263
+ next unless @id_map.get(a.fetch(:klass).api_resource, tracking_id)
264
+
265
+ @id_map.set(a.fetch(:klass).api_resource, tracking_id, a.fetch(:id))
266
+ if a[:klass].api_resource == "synthetics/tests"
267
+ @id_map.set(Kennel::Models::Monitor.api_resource, tracking_id, a.fetch(:monitor_id))
268
+ end
260
269
  end
261
- expected.each { |e| @id_map[e.tracking_id] ||= :new }
262
270
  end
263
271
 
264
272
  def resolve_linked_tracking_ids!(list, force: false)
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Kennel
3
- VERSION = "1.98.2"
3
+ VERSION = "1.101.0"
4
4
  end
data/lib/kennel.rb CHANGED
@@ -9,6 +9,7 @@ require "kennel/version"
9
9
  require "kennel/utils"
10
10
  require "kennel/progress"
11
11
  require "kennel/syncer"
12
+ require "kennel/id_map"
12
13
  require "kennel/api"
13
14
  require "kennel/github_reporter"
14
15
  require "kennel/subclass_tracking"
@@ -69,7 +70,8 @@ module Kennel
69
70
  path = "generated/#{part.tracking_id.tr("/", ":").sub(":", "/")}.json"
70
71
  used << File.dirname(path) # only 1 level of sub folders, so this is safe
71
72
  used << path
72
- write_file_if_necessary(path, JSON.pretty_generate(part.as_json) << "\n")
73
+ payload = part.as_json.merge(api_resource: part.class.api_resource)
74
+ write_file_if_necessary(path, JSON.pretty_generate(payload) << "\n")
73
75
  end
74
76
 
75
77
  # deleting all is slow, so only delete the extras
data/template/Readme.md CHANGED
@@ -189,7 +189,7 @@ removing the `id` will cause kennel to create a new resource in datadog.
189
189
  Having many projects (and their sub-resources) can quickly get out of hand.
190
190
 
191
191
  Use this class structure to keep things organized:
192
- ```ruby
192
+ ```Ruby
193
193
  # projects/project_a/base.rb
194
194
  module ProjectA
195
195
  class Base < Kennel::Models::Project
@@ -200,7 +200,7 @@ module ProjectA
200
200
  # projects/project_a/monitors/foo_alert.rb
201
201
  module ProjectA
202
202
  module Monitors
203
- class FooAlert < Kennel::Modesl::Monitor
203
+ class FooAlert < Kennel::Models::Monitor
204
204
  ...
205
205
  ```
206
206
 
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.98.2
4
+ version: 1.101.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: 2021-10-29 00:00:00.000000000 Z
11
+ date: 2021-11-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -77,6 +77,7 @@ files:
77
77
  - lib/kennel/api.rb
78
78
  - lib/kennel/file_cache.rb
79
79
  - lib/kennel/github_reporter.rb
80
+ - lib/kennel/id_map.rb
80
81
  - lib/kennel/importer.rb
81
82
  - lib/kennel/models/base.rb
82
83
  - lib/kennel/models/dashboard.rb