kennel 2.10.0 → 2.12.0

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: 9213ac86e78dd85ff280d2048bd20fe3d074c3d6218cc45021b8c11eee7c720c
4
- data.tar.gz: b2506ab43bb450a0496db0985ffa990b378ad591fa7be2a1c8db32223863680e
3
+ metadata.gz: ef24e36c2feeba08b92edd003e6d11bfefaa31bd0839765393325b29e8132cb3
4
+ data.tar.gz: 54f97c79250c564d62f01837cc85badc4cdc3357860f40e0408d33640d1a94a8
5
5
  SHA512:
6
- metadata.gz: 73331755a7074cd0eeb95c92901cb8bf477380672a553cd7ef4722ec4cb6ea6f2fd56634eef442fb4a59de961d102c7b8b0f5b39788d006c24d960573eb27500
7
- data.tar.gz: fa9ee34a57cce815f63eba43208daac2232cd04456f1200adc4181e6a381407b93962d52cbc5f9b27398caa863c8ab371758f5c216613b0218f8b9e958de6bc5
6
+ metadata.gz: '08b01acd5c750342d5134e6c2ca6d235bdb5cefc8f0b61b46e7b475ed7be6fd677772bab5a032d2a67685e02389d6741f05ef1e05a0758d3c6f6ff2fae68cb0d'
7
+ data.tar.gz: 79cc9956a1b1790c2e6bfb7f25c981542a2b21d96f8c3d1bdc5328d1e708c410372fa9405bf16a3fbaf7a27aaf1954ca71cb760ae4bfad82fa346b10635b0e8c
@@ -6,7 +6,8 @@ module Kennel
6
6
 
7
7
  OPTIONAL_SERVICE_CHECK_THRESHOLDS = [:ok, :warning].freeze
8
8
  READONLY_ATTRIBUTES = superclass::READONLY_ATTRIBUTES + [
9
- :multi, :matching_downtimes, :overall_state_modified, :overall_state, :restricted_roles, :draft_status, :assets
9
+ :multi, :matching_downtimes, :overall_state_modified, :overall_state, :restricted_roles, :draft_status, :assets,
10
+ :enable_logs_sample
10
11
  ]
11
12
  TRACKING_FIELD = :message
12
13
 
@@ -25,7 +26,7 @@ module Kennel
25
26
  group_retention_duration: nil,
26
27
  groupby_simple_monitor: false,
27
28
  variables: nil,
28
- on_missing_data: "default", # "default" is "evaluate as zero"
29
+ on_missing_data: nil,
29
30
  notification_preset_name: nil,
30
31
  notify_by: nil
31
32
  }.freeze
@@ -55,6 +56,7 @@ module Kennel
55
56
  # datadog UI sets this to false by default, but true is safer
56
57
  # except for log alerts which will always have "no error" gaps and should default to false
57
58
  notify_no_data: -> { !SKIP_NOTIFY_NO_DATA_TYPES.include?(type) },
59
+ no_data_timeframe: -> { MONITOR_OPTION_DEFAULTS.fetch(:no_data_timeframe) },
58
60
  notify_audit: -> { MONITOR_OPTION_DEFAULTS.fetch(:notify_audit) },
59
61
  new_host_delay: -> { MONITOR_OPTION_DEFAULTS.fetch(:new_host_delay) },
60
62
  new_group_delay: -> { nil },
@@ -74,6 +76,8 @@ module Kennel
74
76
  )
75
77
 
76
78
  def build_json
79
+ no_data_options = configure_no_data
80
+
77
81
  data = super.merge(
78
82
  name: "#{name}#{LOCK}",
79
83
  type: type,
@@ -83,8 +87,7 @@ module Kennel
83
87
  priority: priority,
84
88
  options: {
85
89
  timeout_h: timeout_h,
86
- notify_no_data: notify_no_data,
87
- no_data_timeframe: notify_no_data ? no_data_timeframe : nil,
90
+ **no_data_options.except(:on_missing_data),
88
91
  notify_audit: notify_audit,
89
92
  require_full_window: require_full_window,
90
93
  new_host_delay: new_host_delay,
@@ -92,33 +95,14 @@ module Kennel
92
95
  include_tags: true,
93
96
  escalation_message: Utils.presence(escalation_message.strip),
94
97
  evaluation_delay: evaluation_delay,
95
- locked: false, # deprecated: setting this to true will likely fail
96
98
  renotify_interval: renotify_interval || 0,
97
- variables: variables
99
+ variables: variables,
100
+ **configure_thresholds,
101
+ **no_data_options.slice(:on_missing_data) # moved here to avoid generated diff
98
102
  }
99
103
  )
100
104
 
101
105
  options = data[:options]
102
- if data.fetch(:type) != "composite"
103
- thresholds = (options[:thresholds] = { critical: critical })
104
-
105
- # warning, ok, critical_recovery, and warning_recovery are optional
106
- [:warning, :ok, :critical_recovery, :warning_recovery].each do |key|
107
- if (value = send(key))
108
- thresholds[key] = value
109
- end
110
- end
111
-
112
- thresholds[:critical] = critical unless
113
- case data.fetch(:type)
114
- when "service check"
115
- # avoid diff for default values of 1
116
- OPTIONAL_SERVICE_CHECK_THRESHOLDS.each { |t| thresholds[t] ||= 1 }
117
- when "query alert"
118
- # metric and query values are stored as float by datadog
119
- thresholds.each { |k, v| thresholds[k] = Float(v) }
120
- end
121
- end
122
106
 
123
107
  # set without causing lots of nulls to be stored
124
108
  if (notify_by_value = notify_by)
@@ -149,30 +133,46 @@ module Kennel
149
133
  # Add in statuses where we would re notify on. Possible values: alert, no data, warn
150
134
  if options[:renotify_interval] != 0
151
135
  statuses = ["alert"]
152
- statuses << "no data" if options[:notify_no_data]
136
+ statuses << "no data" if options[:notify_no_data] || options[:on_missing_data] == "show_and_notify_no_data"
153
137
  statuses << "warn" if options.dig(:thresholds, :warning)
154
138
  options[:renotify_statuses] = statuses
155
139
  end
156
140
 
157
- # on_missing_data cannot be used with notify_no_data or no_data_timeframe
158
- # TODO migrate everything to only use on_missing_data
159
- if data.fetch(:type) == "event-v2 alert" || on_missing_data != "default"
160
- options[:on_missing_data] = on_missing_data
161
- options[:notify_no_data] = false # cannot set nil or it's an endless update loop
162
- options.delete :no_data_timeframe
163
- end
164
-
165
141
  # only set when needed to avoid big diff
166
142
  if (notification_preset_name = notification_preset_name())
167
143
  options[:notification_preset_name] = notification_preset_name
168
144
  end
169
145
 
170
- # locked is deprecated, will fail if used
171
- options.delete :locked
172
-
173
146
  data
174
147
  end
175
148
 
149
+ # TODO: migrate everything to only use on_missing_data by only sending notify_no_data when it was set by a user
150
+ # and enforce that it is not set at the same time as on_missing_data
151
+ def configure_no_data
152
+ notify = notify_no_data
153
+ timeframe = no_data_timeframe
154
+ action = on_missing_data
155
+ action ||= "default" if type == "event-v2 alert"
156
+
157
+ # TODO: mark setting action && !notify.nil? at all as invalid
158
+ if action && timeframe
159
+ invalid! :invalid_no_data_config, "set either no_data_timeframe or on_missing_data"
160
+ end
161
+ if type == "composite" && action
162
+ invalid! :invalid_no_data_config, "cannot use on_missing_data with composite monitor"
163
+ end
164
+
165
+ # on_missing_data cannot be used with notify_no_data + no_data_timeframe
166
+ if action
167
+ { on_missing_data: action || "default" }
168
+ else
169
+ {
170
+ notify_no_data: notify,
171
+ no_data_timeframe: notify ? no_data_timeframe || default_no_data_timeframe : nil
172
+ }
173
+ end
174
+ end
175
+
176
176
  def resolve_linked_tracking_ids!(id_map, **args)
177
177
  case as_json[:type]
178
178
  when "composite", "slo alert"
@@ -194,7 +194,7 @@ module Kennel
194
194
  # deprecated this setting is no longer returned by dd for new monitors
195
195
  # datadog UI warns when setting no data timeframe to less than 2x the query window
196
196
  # limited to 24h because `no_data_timeframe must not exceed group retention` and max group retention is 24h
197
- def no_data_timeframe
197
+ def default_no_data_timeframe
198
198
  default = 60
199
199
  if type == "query alert" && (minutes = query_window_minutes)
200
200
  (minutes * 2).clamp(default, 24 * 60)
@@ -206,7 +206,8 @@ module Kennel
206
206
  # validate that monitors that alert on no data resolve in external services by using timeout_h, so it sends a
207
207
  # notification when the no data group is removed from the monitor, which datadog does automatically after 24h
208
208
  def timeout_h
209
- notify_no_data && on_missing_data != "resolve" ? 24 : MONITOR_OPTION_DEFAULTS.fetch(:timeout_h)
209
+ sending_no_data_notifications = (on_missing_data ? on_missing_data == "show_and_notify_no_data" : notify_no_data)
210
+ sending_no_data_notifications ? 24 : MONITOR_OPTION_DEFAULTS.fetch(:timeout_h)
210
211
  end
211
212
 
212
213
  def self.api_resource
@@ -235,7 +236,9 @@ module Kennel
235
236
  ignore_default(expected, actual, MONITOR_DEFAULTS)
236
237
 
237
238
  options = actual.fetch(:options)
238
- options.delete(:silenced) # we do not manage silenced, so ignore it when diffing
239
+
240
+ # we do not manage silenced: ignore it when diffing
241
+ options.delete(:silenced)
239
242
 
240
243
  # fields are not returned when set to true
241
244
  if ["service check", "event alert"].include?(actual[:type])
@@ -265,13 +268,38 @@ module Kennel
265
268
  options.delete(:escalation_message)
266
269
  expected_options.delete(:escalation_message)
267
270
  end
271
+
268
272
  # locked is deprecated: ignored when diffing
269
273
  options.delete(:locked)
270
- expected_options.delete(:locked)
271
274
  end
272
275
 
273
276
  private
274
277
 
278
+ def configure_thresholds
279
+ return {} if type == "composite"
280
+
281
+ thresholds = { critical: critical }
282
+
283
+ # set optional variables
284
+ [:warning, :ok, :critical_recovery, :warning_recovery].each do |key|
285
+ if (value = send(key))
286
+ thresholds[key] = value
287
+ end
288
+ end
289
+
290
+ # custom logic for some types
291
+ case type
292
+ when "service check"
293
+ # avoid diff for default values of 1
294
+ OPTIONAL_SERVICE_CHECK_THRESHOLDS.each { |t| thresholds[t] ||= 1 }
295
+ when "query alert"
296
+ # metric and query values are stored as float by datadog
297
+ thresholds.each { |k, v| thresholds[k] = Float(v) }
298
+ end
299
+
300
+ { thresholds: thresholds }
301
+ end
302
+
275
303
  def validate_json(data)
276
304
  super
277
305
 
@@ -287,9 +315,11 @@ module Kennel
287
315
  end
288
316
 
289
317
  # verify query includes critical value
290
- if (query_value = data.fetch(:query)[/\s*[<>]=?\s*(\d+(\.\d+)?)\s*$/, 1])
291
- if Float(query_value) != Float(data.dig(:options, :thresholds, :critical))
292
- invalid! :critical_does_not_match_query, "critical and value used in query must match"
318
+ if (critical = data.dig(:options, :thresholds, :critical))
319
+ if (query_value = data.fetch(:query)[/\s*[<>]=?\s*(\d+(\.\d+)?)\s*$/, 1])
320
+ if Float(query_value) != Float(critical)
321
+ invalid! :critical_does_not_match_query, "critical and value used in query must match"
322
+ end
293
323
  end
294
324
  end
295
325
 
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Kennel
3
- VERSION = "2.10.0"
3
+ VERSION = "2.12.0"
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: 2.10.0
4
+ version: 2.12.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: 2026-01-08 00:00:00.000000000 Z
11
+ date: 2026-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: diff-lcs