kennel 1.98.1 → 1.100.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: 4d0b70ee90aae5a476b1e9a324e02346856e9d76881082d46a2b219efcfe2267
4
- data.tar.gz: 86bc3f4777ec06a9b8c171e93ea95b50e9117a74283e3e6c1785d81e87889a4d
3
+ metadata.gz: 97207492f3f5afd31ab4d3b16db46f8b70a772c101c57bc26c7d135c9ece6a75
4
+ data.tar.gz: 6a2c515b9116f1da7764a0a509886fee28ec36241e982b425843f6bc64c02067
5
5
  SHA512:
6
- metadata.gz: d0c6f38a11af7b0876a5637648fe16168aa4c5f6a16bba06f51322c240b02d520bd3abf566bf04b547df7431453d500f97be06b3a03da850ca5395b6ccf04bed
7
- data.tar.gz: 45a77a1b2995f12da465a5b2ed64c6f21c93dba68b154367cf06c2a88202b648e223f0f1f80da7052e536b64f66a3cdc72a607fabfb913db3c7657f9e4000051
6
+ metadata.gz: a4304111e6d35d92ed2b1adbba277a39eb67e7b2e697a5b2cbd031731a1aa968477249f13a18377f7ea4af49bfafd8742060a3ab6c7b7161bb78fd9b63889972
7
+ data.tar.gz: 8a5588b35ef64e8a91354a5554866f5a59ca354d850ecd96c7d2d164cf79ffe8f6b8cf79ff0c3bc5d4108cba4a600211492ccd0c7033725dc3e2c0a4197e55ef
data/Readme.md CHANGED
@@ -202,11 +202,11 @@ When manually creating to import, it is best to remove the `id` and delete the m
202
202
  When an `id` is set and the original resource is deleted, kennel will fail to update,
203
203
  removing the `id` will cause kennel to create a new resource in datadog.
204
204
 
205
- ### Organizing large sub-projects / large organiations
205
+ ### Organizing many projects
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,17 @@ 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
+ @id_map.set(a.fetch(:klass).api_resource, tracking_id, a.fetch(:id))
264
+ if a[:klass].api_resource == "synthetics/tests"
265
+ @id_map.set(Kennel::Models::Monitor.api_resource, tracking_id, a.fetch(:monitor_id))
266
+ end
260
267
  end
261
- expected.each { |e| @id_map[e.tracking_id] ||= :new }
262
268
  end
263
269
 
264
270
  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.1"
3
+ VERSION = "1.100.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"
data/template/Readme.md CHANGED
@@ -185,11 +185,11 @@ When manually creating to import, it is best to remove the `id` and delete the m
185
185
  When an `id` is set and the original resource is deleted, kennel will fail to update,
186
186
  removing the `id` will cause kennel to create a new resource in datadog.
187
187
 
188
- ### Organizing large sub-projects / large organiations
188
+ ### Organizing many projects
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.1
4
+ version: 1.100.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-06 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