kennel 1.85.2 → 1.88.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: 80ab3a8fb786a4b556aa1318d3b1ab758a9b7d33e786023c02550ee004abd6e8
4
- data.tar.gz: 0e5896d464686e750a3973feff199f25030cea119c7ccb03ddc9820a359f0c74
3
+ metadata.gz: 1091fcd44daba6435e7a9321610a27b8ac2510451cf97e8183d6ab744e080288
4
+ data.tar.gz: f137076ec36e4018eaa1c1d60dd2da187f33bdb41fc3fe5c384be4787ddd9c77
5
5
  SHA512:
6
- metadata.gz: a5df094d658262ddb50a40234be1f371ebd9120ed66a0a625d68a7ec8debde55980ccc8e2c64e84afbdc16160228700c8361108b526f54ca68403e12318f8a19
7
- data.tar.gz: cb924336b3e9caac886fbc60ffc0301500a6dd59f9af53c42d1c7ac13ad5891765165b28fa7da36751da64491ca8e5070e1e1ce047830e8cb3b78363e9e2da4d
6
+ metadata.gz: 52cd265a23705e8a8afca55f819c9fed1c2ae467e8de1cba5e696cd6191619458168fd649f26f5c00da33702e4002490e13f804335dad10e2204a96d16cf12a8
7
+ data.tar.gz: d2cc461399929023573e22f55bc89699d952c9338f5e415cea9328af00d212b5e87fe2061837327ec88c4b60a2a277e1f272a511e85a9de0014142c51073f60e
@@ -3,7 +3,7 @@
3
3
  module Kennel
4
4
  class Importer
5
5
  TITLES = [:name, :title].freeze
6
- SORT_ORDER = [*TITLES, :id, :kennel_id, :type, :tags, :query, *Syncer::TRACKING_FIELDS, :template_variables].freeze
6
+ SORT_ORDER = [*TITLES, :id, :kennel_id, :type, :tags, :query, *Models::Record.subclasses.map { |k| k::TRACKING_FIELDS }, :template_variables].freeze
7
7
 
8
8
  def initialize(api)
9
9
  @api = api
@@ -31,11 +31,10 @@ module Kennel
31
31
  title.tr!(Kennel::Models::Record::LOCK, "") # avoid double lock icon
32
32
 
33
33
  # calculate or reuse kennel_id
34
- # TODO: this is copy-pasted from syncer, need to find a nice way to reuse it
35
- tracking_field = Syncer::TRACKING_FIELDS.detect { |f| data[f] }
36
34
  data[:kennel_id] =
37
- if tracking_field && data[tracking_field].sub!(/\n?-- Managed by kennel (\S+:\S+).*/, "")
38
- $1.split(":").last
35
+ if tracking_id = model.parse_tracking_id(data)
36
+ model.remove_tracking_id(data)
37
+ tracking_id.split(":").last
39
38
  else
40
39
  Kennel::Utils.parameterize(title)
41
40
  end
@@ -66,7 +65,13 @@ module Kennel
66
65
  data[:type] = "query alert" if data[:type] == "metric alert"
67
66
  when "dashboard"
68
67
  widgets = data[:widgets]&.flat_map { |widget| widget.dig(:definition, :widgets) || [widget] }
69
- widgets&.each { |widget| dry_up_query!(widget) }
68
+ widgets&.each do |widget|
69
+ convert_widget_to_compact_format!(widget)
70
+ dry_up_widget_metadata!(widget)
71
+ (widget.dig(:definition, :markers) || []).each { |m| m[:label]&.delete! " " }
72
+ end
73
+ else
74
+ # noop
70
75
  end
71
76
 
72
77
  data.delete(:tags) if data[:tags] == [] # do not create super + [] call
@@ -88,7 +93,7 @@ module Kennel
88
93
  private
89
94
 
90
95
  # reduce duplication in imports by using dry `q: :metadata` when possible
91
- def dry_up_query!(widget)
96
+ def dry_up_widget_metadata!(widget)
92
97
  (widget.dig(:definition, :requests) || []).each do |request|
93
98
  next unless request.is_a?(Hash)
94
99
  next unless metadata = request[:metadata]
@@ -101,6 +106,19 @@ module Kennel
101
106
  end
102
107
  end
103
108
 
109
+ # new api format is very verbose, so use old dry format when possible
110
+ def convert_widget_to_compact_format!(widget)
111
+ (widget.dig(:definition, :requests) || []).each do |request|
112
+ next unless request.is_a?(Hash)
113
+ next if request[:formulas] && request[:formulas] != [{ formula: "query1" }]
114
+ next if request[:queries]&.size != 1
115
+ next if request[:queries].any? { |q| q[:data_source] != "metrics" }
116
+ request.delete(:formulas)
117
+ request[:type] = request.delete(:response_format)
118
+ request[:q] = request.delete(:queries).first.fetch(:query)
119
+ end
120
+ end
121
+
104
122
  def pretty_print(hash)
105
123
  sort_widgets hash
106
124
 
@@ -8,12 +8,61 @@ module Kennel
8
8
  READONLY_ATTRIBUTES = superclass::READONLY_ATTRIBUTES + [
9
9
  :author_handle, :author_name, :modified_at, :url, :is_read_only, :notify_list
10
10
  ]
11
+ TRACKING_FIELD = :description
11
12
  REQUEST_DEFAULTS = {
12
13
  style: { line_width: "normal", palette: "dog_classic", line_type: "solid" }
13
14
  }.freeze
14
15
  WIDGET_DEFAULTS = {
15
- "timeseries" => { show_legend: false, legend_size: "0" },
16
- "note" => { background_color: "white", font_size: "14", show_tick: false, tick_edge: "left", tick_pos: "50%", text_align: "left" }
16
+ "timeseries" => {
17
+ legend_size: "0",
18
+ markers: [],
19
+ legend_columns: [
20
+ "avg",
21
+ "min",
22
+ "max",
23
+ "value",
24
+ "sum"
25
+ ],
26
+ legend_layout: "auto",
27
+ yaxis: {
28
+ include_zero: true,
29
+ label: "",
30
+ scale: "linear",
31
+ min: "auto",
32
+ max: "auto"
33
+ },
34
+ show_legend: true,
35
+ time: {},
36
+ title_align: "left",
37
+ title_size: "16"
38
+ },
39
+ "note" => {
40
+ show_tick: false,
41
+ tick_edge: "left",
42
+ tick_pos: "50%",
43
+ text_align: "left",
44
+ has_padding: true,
45
+ background_color: "white",
46
+ font_size: "14"
47
+ },
48
+ "query_value" => {
49
+ autoscale: true,
50
+ time: {},
51
+ title_align: "left",
52
+ title_size: "16"
53
+ },
54
+ "free_text" => {
55
+ font_size: "auto"
56
+ },
57
+ "check_status" => {
58
+ title_align: "left",
59
+ title_size: "16"
60
+ },
61
+ "slo" => {
62
+ global_time_target: "0",
63
+ title_align: "left",
64
+ title_size: "16"
65
+ }
17
66
  }.freeze
18
67
  SUPPORTED_DEFINITION_OPTIONS = [:events, :markers, :precision].freeze
19
68
 
@@ -40,53 +89,43 @@ module Kennel
40
89
  def normalize(expected, actual)
41
90
  super
42
91
 
43
- ignore_default(expected, actual, DEFAULTS)
44
- ignore_default(expected, actual, reflow_type: "auto") if expected[:layout_type] == "ordered"
92
+ ignore_default expected, actual, DEFAULTS
93
+ ignore_default expected, actual, reflow_type: "auto" if expected[:layout_type] == "ordered"
45
94
 
46
95
  widgets_pairs(expected, actual).each do |pair|
47
- # conditional_formats ordering is randomly changed by datadog, compare a stable ordering
48
- pair.each do |widgets|
49
- widgets.each do |widget|
50
- if formats = widget.dig(:definition, :conditional_formats)
51
- widget[:definition][:conditional_formats] = formats.sort_by(&:hash)
52
- end
53
- end
54
- end
55
-
56
- ignore_widget_defaults pair
57
-
96
+ pair.each { |w| sort_conditional_formats w }
97
+ ignore_widget_defaults(*pair)
58
98
  ignore_request_defaults(*pair)
59
-
60
- # ids are kinda random so we always discard them
61
- pair.each { |widgets| widgets.each { |w| w.delete(:id) } }
99
+ pair.each { |widget| widget&.delete(:id) } # ids are kinda random so we always discard them
62
100
  end
63
101
  end
64
102
 
65
103
  private
66
104
 
67
- def ignore_widget_defaults(pair)
68
- pair.map(&:size).max.times do |i|
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)
105
+ # conditional_formats ordering is randomly changed by datadog, compare a stable ordering
106
+ def sort_conditional_formats(widget)
107
+ if formats = widget&.dig(:definition, :conditional_formats)
108
+ widget[:definition][:conditional_formats] = formats.sort_by(&:hash)
73
109
  end
74
110
  end
75
111
 
112
+ def ignore_widget_defaults(expected, actual)
113
+ types = [expected&.dig(:definition, :type), actual&.dig(:definition, :type)].uniq.compact
114
+ return unless types.size == 1
115
+ return unless defaults = WIDGET_DEFAULTS[types.first]
116
+ ignore_default expected&.[](:definition) || {}, actual&.[](:definition) || {}, defaults
117
+ end
118
+
76
119
  # discard styles/conditional_formats/aggregator if nothing would change when we applied (both are default or nil)
77
120
  def ignore_request_defaults(expected, actual)
78
- [expected.size, actual.size].max.times do |i|
79
- a_r = actual.dig(i, :definition, :requests) || []
80
- e_r = expected.dig(i, :definition, :requests) || []
81
- ignore_defaults e_r, a_r, REQUEST_DEFAULTS
82
- end
121
+ a_r = actual&.dig(:definition, :requests) || []
122
+ e_r = expected&.dig(:definition, :requests) || []
123
+ ignore_defaults e_r, a_r, REQUEST_DEFAULTS
83
124
  end
84
125
 
85
- def ignore_defaults(expected, actual, defaults, nesting: nil)
126
+ def ignore_defaults(expected, actual, defaults)
86
127
  [expected.size, actual.size].max.times do |i|
87
- e = expected.dig(i, *nesting) || {}
88
- a = actual.dig(i, *nesting) || {}
89
- ignore_default(e, a, defaults)
128
+ ignore_default expected[i] || {}, actual[i] || {}, defaults
90
129
  end
91
130
  end
92
131
 
@@ -99,7 +138,7 @@ module Kennel
99
138
  nested = pair.map { |d| d.dig(:widgets, i, :definition, :widgets) || [] }
100
139
  result << nested if nested.any?(&:any?)
101
140
  end
102
- result
141
+ result.flat_map { |a, e| [a.size, e.size].max.times.map { |i| [a[i], e[i]] } }
103
142
  end
104
143
  end
105
144
 
@@ -9,6 +9,7 @@ module Kennel
9
9
  READONLY_ATTRIBUTES = superclass::READONLY_ATTRIBUTES + [
10
10
  :multi, :matching_downtimes, :overall_state_modified, :overall_state, :restricted_roles
11
11
  ]
12
+ TRACKING_FIELD = :message
12
13
 
13
14
  MONITOR_DEFAULTS = {
14
15
  priority: nil
@@ -25,6 +26,7 @@ module Kennel
25
26
  groupby_simple_monitor: false
26
27
  }.freeze
27
28
  DEFAULT_ESCALATION_MESSAGE = ["", nil].freeze
29
+ ALLOWED_PRIORITY_CLASSES = [NilClass, Integer].freeze
28
30
 
29
31
  settings(
30
32
  :query, :name, :message, :escalation_message, :critical, :type, :renotify_interval, :warning, :timeout_h, :evaluation_delay,
@@ -217,6 +219,10 @@ module Kennel
217
219
  end
218
220
  end
219
221
  end
222
+
223
+ unless ALLOWED_PRIORITY_CLASSES.include?(priority.class)
224
+ invalid! "priority needs to be an Integer"
225
+ end
220
226
  end
221
227
  end
222
228
  end
@@ -3,8 +3,10 @@ module Kennel
3
3
  module Models
4
4
  class Record < Base
5
5
  LOCK = "\u{1F512}"
6
+ TRACKING_FIELDS = [:message, :description].freeze
6
7
  READONLY_ATTRIBUTES = [
7
- :deleted, :id, :created, :created_at, :creator, :org_id, :modified, :modified_at, :api_resource
8
+ :deleted, :id, :created, :created_at, :creator, :org_id, :modified, :modified_at,
9
+ :klass # added by syncer.rb
8
10
  ].freeze
9
11
 
10
12
  settings :id, :kennel_id
@@ -22,6 +24,18 @@ module Kennel
22
24
  subclasses.map { |s| [s.api_resource, s] }.to_h
23
25
  end
24
26
 
27
+ def parse_tracking_id(a)
28
+ a[self::TRACKING_FIELD].to_s[/-- Managed by kennel (\S+:\S+)/, 1]
29
+ end
30
+
31
+ # TODO: combine with parse into a single method or a single regex
32
+ def remove_tracking_id(a)
33
+ value = a[self::TRACKING_FIELD]
34
+ a[self::TRACKING_FIELD] =
35
+ value.dup.sub!(/\n?-- Managed by kennel .*/, "") ||
36
+ raise("did not find tracking id in #{value}")
37
+ end
38
+
25
39
  private
26
40
 
27
41
  def normalize(_expected, actual)
@@ -66,13 +80,30 @@ module Kennel
66
80
  def resolve_linked_tracking_ids!(*)
67
81
  end
68
82
 
83
+ def add_tracking_id
84
+ json = as_json
85
+ if self.class.parse_tracking_id(json)
86
+ invalid! "remove \"-- Managed by kennel\" line it from #{self.class::TRACKING_FIELD} to copy a resource"
87
+ end
88
+ json[self.class::TRACKING_FIELD] =
89
+ "#{json[self.class::TRACKING_FIELD]}\n" \
90
+ "-- Managed by kennel #{tracking_id} in #{project.class.file_location}, do not modify manually".lstrip
91
+ end
92
+
93
+ def remove_tracking_id
94
+ self.class.remove_tracking_id(as_json)
95
+ end
96
+
69
97
  private
70
98
 
71
99
  def resolve_link(tracking_id, type, id_map, force:)
72
100
  id = id_map[tracking_id]
73
101
  if id == :new
74
102
  if force
75
- invalid! "#{type} #{tracking_id} was referenced but is also created by the current run.\nIt could not be created because of a circular dependency, try creating only some of the resources"
103
+ invalid!(
104
+ "#{type} #{tracking_id} was referenced but is also created by the current run.\n" \
105
+ "It could not be created because of a circular dependency, try creating only some of the resources"
106
+ )
76
107
  else
77
108
  nil # will be re-resolved after the linked object was created
78
109
  end
@@ -3,6 +3,7 @@ module Kennel
3
3
  module Models
4
4
  class Slo < Record
5
5
  READONLY_ATTRIBUTES = superclass::READONLY_ATTRIBUTES + [:type_id, :monitor_tags]
6
+ TRACKING_FIELD = :description
6
7
  DEFAULTS = {
7
8
  description: nil,
8
9
  query: nil,
@@ -63,7 +64,7 @@ module Kennel
63
64
  end
64
65
 
65
66
  def self.parse_url(url)
66
- url[/\/slo\?.*slo_id=([a-z\d]+)/, 1]
67
+ url[/\/slo(\?.*slo_id=|\/edit\/)([a-z\d]{10,})/, 2]
67
68
  end
68
69
 
69
70
  def resolve_linked_tracking_ids!(id_map, **args)
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
- TRACKING_FIELDS = [:message, :description].freeze
5
4
  DELETE_ORDER = ["dashboard", "slo", "monitor"].freeze # dashboards references monitors + slos, slos reference monitors
6
5
 
7
6
  def initialize(api, expected, project: nil)
@@ -16,7 +15,7 @@ module Kennel
16
15
  raise "#{@project_filter} does not match any projects, try any of these:\n#{possible.join("\n")}"
17
16
  end
18
17
  end
19
- @expected.each { |e| add_tracking_id e }
18
+ @expected.each(&:add_tracking_id)
20
19
  calculate_diff
21
20
  prevent_irreversible_partial_updates
22
21
  end
@@ -39,19 +38,22 @@ module Kennel
39
38
  def update
40
39
  each_resolved @create do |_, e|
41
40
  reply = @api.create e.class.api_resource, e.as_json
41
+ reply[:klass] = e.class # store api resource class for later use
42
42
  id = reply.fetch(:id)
43
43
  populate_id_map [reply] # allow resolving ids we could previously no resolve
44
- Kennel.out.puts "Created #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.class.url(id)}"
44
+ Kennel.out.puts "Created #{e.class.api_resource} #{e.tracking_id} #{e.class.url(id)}"
45
45
  end
46
46
 
47
47
  each_resolved @update do |id, e|
48
48
  @api.update e.class.api_resource, id, e.as_json
49
- Kennel.out.puts "Updated #{e.class.api_resource} #{tracking_id(e.as_json)} #{e.class.url(id)}"
49
+ Kennel.out.puts "Updated #{e.class.api_resource} #{e.tracking_id} #{e.class.url(id)}"
50
50
  end
51
51
 
52
52
  @delete.each do |id, _, a|
53
- @api.delete a.fetch(:api_resource), id
54
- Kennel.out.puts "Deleted #{a.fetch(:api_resource)} #{tracking_id(a)} #{id}"
53
+ klass = a.fetch(:klass)
54
+ @api.delete klass.api_resource, id
55
+ tracking_id = klass.parse_tracking_id(a)
56
+ Kennel.out.puts "Deleted #{klass.api_resource} #{tracking_id} #{id}"
55
57
  end
56
58
  end
57
59
 
@@ -118,8 +120,8 @@ module Kennel
118
120
 
119
121
  # fill details of things we need to compare
120
122
  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 }
123
+ items.each { |e, a| detailed[a[:klass]] << a if e }
124
+ detailed.each { |klass, actuals| @api.fill_details! klass.api_resource, actuals }
123
125
 
124
126
  # pick out things to update or delete
125
127
  items.each do |e, a|
@@ -127,7 +129,7 @@ module Kennel
127
129
  if e
128
130
  diff = e.diff(a)
129
131
  @update << [id, e, a, diff] if diff.any?
130
- elsif tracking_id(a) # was previously managed
132
+ elsif a.fetch(:klass).parse_tracking_id(a) # was previously managed
131
133
  @delete << [id, nil, a]
132
134
  end
133
135
  end
@@ -136,14 +138,14 @@ module Kennel
136
138
  @create = @expected.map { |e| [nil, e] }
137
139
  end
138
140
 
139
- @delete.sort_by! { |_, _, a| DELETE_ORDER.index a.fetch(:api_resource) }
141
+ @delete.sort_by! { |_, _, a| DELETE_ORDER.index a.fetch(:klass).api_resource }
140
142
  end
141
143
 
142
144
  def download_definitions
143
- Utils.parallel(Models::Record.subclasses.map(&:api_resource)) do |api_resource|
144
- results = @api.list(api_resource, with_downtimes: false) # lookup monitors without adding unnecessary downtime information
145
+ Utils.parallel(Models::Record.subclasses) do |klass|
146
+ results = @api.list(klass.api_resource, with_downtimes: false) # lookup monitors without adding unnecessary downtime information
145
147
  results = results[results.keys.first] if results.is_a?(Hash) # dashboards are nested in {dashboards: []}
146
- results.each { |c| c[:api_resource] = api_resource } # store api resource for later diffing
148
+ results.each { |c| c[:klass] = klass } # store api resource for later diffing
147
149
  end.flatten(1)
148
150
  end
149
151
 
@@ -158,7 +160,7 @@ module Kennel
158
160
  def matching_expected(a)
159
161
  # index list by all the thing we look up by: tracking id and actual id
160
162
  @lookup_map ||= @expected.each_with_object({}) do |e, all|
161
- keys = [tracking_id(e.as_json)]
163
+ keys = [e.tracking_id]
162
164
  keys << "#{e.class.api_resource}:#{e.id}" if e.id
163
165
  keys.compact.each do |key|
164
166
  raise "Lookup #{key} is duplicated" if all[key]
@@ -166,14 +168,15 @@ module Kennel
166
168
  end
167
169
  end
168
170
 
169
- @lookup_map["#{a.fetch(:api_resource)}:#{a.fetch(:id)}"] || @lookup_map[tracking_id(a)]
171
+ klass = a.fetch(:klass)
172
+ @lookup_map["#{klass.api_resource}:#{a.fetch(:id)}"] || @lookup_map[klass.parse_tracking_id(a)]
170
173
  end
171
174
 
172
175
  def print_plan(step, list, color)
173
176
  return if list.empty?
174
177
  list.each do |_, e, a, diff|
175
- api_resource = (e ? e.class.api_resource : a.fetch(:api_resource))
176
- Kennel.out.puts Utils.color(color, "#{step} #{api_resource} #{e&.tracking_id || tracking_id(a)}")
178
+ klass = (e ? e.class : a.fetch(:klass))
179
+ Kennel.out.puts Utils.color(color, "#{step} #{klass.api_resource} #{e&.tracking_id || klass.parse_tracking_id(a)}")
177
180
  print_diff(diff) if diff # only for update
178
181
  end
179
182
  end
@@ -199,26 +202,23 @@ module Kennel
199
202
  end
200
203
  end
201
204
 
202
- # Do not add tracking-id when working with existing ids on a branch,
203
- # so resource do not get deleted from running an update on master (for example merge->CI).
204
- # Also make sure the diff still makes sense, by kicking out the now noop-update.
205
- #
206
- # Note: ideally we'd never add tracking in the first place, but at that point we do not know the diff yet
205
+ # - do not add tracking-id when working with existing ids on a branch,
206
+ # so resource do not get deleted when running an update on master (for example merge->CI)
207
+ # - make sure the diff is clean, by kicking out the now noop-update
208
+ # - ideally we'd never add tracking in the first place, but when adding tracking we do not know the diff yet
207
209
  def prevent_irreversible_partial_updates
208
210
  return unless @project_filter
209
211
  @update.select! do |_, e, _, diff|
210
- next true unless e.id # short circuit for performance
212
+ next true unless e.id # safe to add tracking when not having id
211
213
 
212
214
  diff.select! do |field_diff|
213
- (_, field, old, new) = field_diff
214
- next true unless tracking_field?(field)
215
+ (_, field, actual) = field_diff
216
+ # TODO: refactor this so TRACKING_FIELD stays record-private
217
+ next true if e.class::TRACKING_FIELD != field.to_sym # need to sym here because Hashdiff produces strings
218
+ next true if e.class.parse_tracking_id(field.to_sym => actual) # already has tracking id
215
219
 
216
- if (old_tracking = tracking_value(old))
217
- old_tracking == tracking_value(new) || raise("do not update! (atm unreachable)")
218
- else
219
- field_diff[3] = remove_tracking_id(e) # make plan output match update
220
- old != field_diff[3]
221
- end
220
+ field_diff[3] = e.remove_tracking_id # make `rake plan` output match what we are sending
221
+ actual != field_diff[3] # discard diff if now nothing changes
222
222
  end
223
223
 
224
224
  !diff.empty?
@@ -226,7 +226,7 @@ module Kennel
226
226
  end
227
227
 
228
228
  def populate_id_map(actual)
229
- actual.each { |a| @id_map[tracking_id(a)] = a.fetch(:id) }
229
+ actual.each { |a| @id_map[a.fetch(:klass).parse_tracking_id(a)] = a.fetch(:id) }
230
230
  end
231
231
 
232
232
  def resolve_linked_tracking_ids!(list, force: false)
@@ -236,39 +236,9 @@ module Kennel
236
236
  def filter_by_project!(definitions)
237
237
  return unless @project_filter
238
238
  definitions.select! do |a|
239
- id = tracking_id(a)
239
+ id = a.fetch(:klass).parse_tracking_id(a)
240
240
  !id || id.start_with?("#{@project_filter}:")
241
241
  end
242
242
  end
243
-
244
- def add_tracking_id(e)
245
- json = e.as_json
246
- field = tracking_field(json)
247
- raise "remove \"-- Managed by kennel\" line it from #{field} to copy a resource" if tracking_value(json[field])
248
- json[field] = "#{json[field]}\n-- Managed by kennel #{e.tracking_id} in #{e.project.class.file_location}, do not modify manually".lstrip
249
- end
250
-
251
- def remove_tracking_id(e)
252
- json = e.as_json
253
- field = tracking_field(json)
254
- value = json[field]
255
- json[field] = value.dup.sub!(/\n?-- Managed by kennel .*/, "") || raise("did not find tracking id in #{value}")
256
- end
257
-
258
- def tracking_id(a)
259
- tracking_value a[tracking_field(a)]
260
- end
261
-
262
- def tracking_value(content)
263
- content.to_s[/-- Managed by kennel (\S+:\S+)/, 1]
264
- end
265
-
266
- def tracking_field(a)
267
- TRACKING_FIELDS.detect { |f| a.key?(f) }
268
- end
269
-
270
- def tracking_field?(field)
271
- TRACKING_FIELDS.include?(field.to_sym)
272
- end
273
243
  end
274
244
  end
@@ -36,7 +36,8 @@ module Kennel
36
36
 
37
37
  def widget_queries(widget)
38
38
  requests = widget.dig(:definition, :requests) || []
39
- (requests.is_a?(Hash) ? requests.values : requests).map { |r| r[:q] } # hostmap widgets have hash requests
39
+ return requests.values.map { |r| r[:q] } if requests.is_a?(Hash) # hostmap widgets have hash requests
40
+ requests.flat_map { |r| r[:q] || r[:queries]&.map { |q| q[:query] } } # old format with q: or queries: [{query:}]
40
41
  end
41
42
  end
42
43
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Kennel
3
- VERSION = "1.85.2"
3
+ VERSION = "1.88.1"
4
4
  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.85.2
4
+ version: 1.88.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-04-17 00:00:00.000000000 Z
11
+ date: 2021-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -94,14 +94,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
94
94
  requirements:
95
95
  - - ">="
96
96
  - !ruby/object:Gem::Version
97
- version: 2.5.0
97
+ version: 2.6.0
98
98
  required_rubygems_version: !ruby/object:Gem::Requirement
99
99
  requirements:
100
100
  - - ">="
101
101
  - !ruby/object:Gem::Version
102
102
  version: '0'
103
103
  requirements: []
104
- rubygems_version: 3.1.3
104
+ rubygems_version: 3.2.16
105
105
  signing_key:
106
106
  specification_version: 4
107
107
  summary: Keep datadog monitors/dashboards/etc in version control, avoid chaotic management