class Kennel::Models::Monitor
Constants
- ALLOWED_PRIORITY_CLASSES
- DEFAULT_ESCALATION_MESSAGE
- MONITOR_DEFAULTS
- MONITOR_OPTION_DEFAULTS
defaults that datadog uses when options are not sent, so safe to leave out if our values match their defaults
- OPTIONAL_SERVICE_CHECK_THRESHOLDS
- READONLY_ATTRIBUTES
- RENOTIFY_INTERVALS
- TRACKING_FIELD
Public Class Methods
api_resource()
click to toggle source
# File lib/kennel/models/monitor.rb, line 124 def self.api_resource "monitor" end
normalize(expected, actual)
click to toggle source
Calls superclass method
# File lib/kennel/models/monitor.rb, line 144 def self.normalize(expected, actual) super ignore_default(expected, actual, MONITOR_DEFAULTS) options = actual.fetch(:options) options.delete(:silenced) # we do not manage silenced, so ignore it when diffing # fields are not returned when set to true if ["service check", "event alert"].include?(actual[:type]) options[:include_tags] = true unless options.key?(:include_tags) options[:require_full_window] = true unless options.key?(:require_full_window) end case actual[:type] when "event alert" # setting nothing results in thresholds not getting returned from the api options[:thresholds] ||= { critical: 0 } when "service check" # fields are not returned when created with default values via UI OPTIONAL_SERVICE_CHECK_THRESHOLDS.each do |t| options[:thresholds][t] ||= 1 end end # nil / "" / 0 are not returned from the api when set via the UI options[:evaluation_delay] ||= nil expected_options = expected[:options] || {} ignore_default(expected_options, options, MONITOR_OPTION_DEFAULTS) if DEFAULT_ESCALATION_MESSAGE.include?(options[:escalation_message]) options.delete(:escalation_message) expected_options.delete(:escalation_message) end end
parse_url(url)
click to toggle source
# File lib/kennel/models/monitor.rb, line 132 def self.parse_url(url) # datadog uses / for show and # for edit as separator in it's links id = url[/\/monitors[\/#](\d+)/, 1] # slo alert url id ||= url[/\/slo\/edit\/[a-z\d]{10,}\/alerts\/(\d+)/, 1] return unless id Integer(id) end
url(id)
click to toggle source
# File lib/kennel/models/monitor.rb, line 128 def self.url(id) Utils.path_to_url "/monitors##{id}/edit" end
Public Instance Methods
as_json()
click to toggle source
# File lib/kennel/models/monitor.rb, line 57 def as_json return @as_json if @as_json data = { name: "#{name}#{LOCK}", type: type, query: query.strip, message: message.strip, tags: tags.uniq, priority: priority, options: { timeout_h: timeout_h, notify_no_data: notify_no_data, no_data_timeframe: notify_no_data ? no_data_timeframe : nil, notify_audit: notify_audit, require_full_window: require_full_window, new_host_delay: new_host_delay, include_tags: true, escalation_message: Utils.presence(escalation_message.strip), evaluation_delay: evaluation_delay, locked: false, # setting this to true prevents any edit and breaks updates when using replace workflow renotify_interval: renotify_interval || 0 } } data[:id] = id if id options = data[:options] if data.fetch(:type) != "composite" thresholds = (options[:thresholds] = { critical: critical }) # warning, ok, critical_recovery, and warning_recovery are optional [:warning, :ok, :critical_recovery, :warning_recovery].each do |key| if value = send(key) thresholds[key] = value end end thresholds[:critical] = critical unless case data.fetch(:type) when "service check" # avoid diff for default values of 1 OPTIONAL_SERVICE_CHECK_THRESHOLDS.each { |t| thresholds[t] ||= 1 } when "query alert" # metric and query values are stored as float by datadog thresholds.each { |k, v| thresholds[k] = Float(v) } end end if windows = threshold_windows options[:threshold_windows] = windows end validate_json(data) if validate @as_json = data end
resolve_linked_tracking_ids!(id_map, **args)
click to toggle source
# File lib/kennel/models/monitor.rb, line 114 def resolve_linked_tracking_ids!(id_map, **args) case as_json[:type] when "composite", "slo alert" type = (as_json[:type] == "composite" ? :monitor : :slo) as_json[:query] = as_json[:query].gsub(/%{(.*?)}/) do resolve_link($1, type, id_map, **args) || $& end end end
Private Instance Methods
require_full_window()
click to toggle source
# File lib/kennel/models/monitor.rb, line 183 def require_full_window # default 'on_average', 'at_all_times', 'in_total' aggregations to true, otherwise false # https://docs.datadoghq.com/ap/#create-a-monitor type != "query alert" || query.start_with?("avg", "min", "sum") end
validate_json(data)
click to toggle source
Calls superclass method
Kennel::OptionalValidations#validate_json
# File lib/kennel/models/monitor.rb, line 189 def validate_json(data) super type = data.fetch(:type) # do not allow deprecated type that will be coverted by datadog and then produce a diff if type == "metric alert" invalid! "type 'metric alert' is deprecated, please set to a different type (e.g. 'query alert')" end # verify query includes critical value if query_value = data.fetch(:query)[/\s*[<>]=?\s*(\d+(\.\d+)?)\s*$/, 1] if Float(query_value) != Float(data.dig(:options, :thresholds, :critical)) invalid! "critical and value used in query must match" end end # verify renotify interval is valid unless RENOTIFY_INTERVALS.include? data.dig(:options, :renotify_interval) invalid! "renotify_interval must be one of #{RENOTIFY_INTERVALS.join(", ")}" end if ["query alert", "service check"].include?(type) # TODO: most likely more types need this # verify is_match/is_exact_match uses available variables message = data.fetch(:message) used = message.scan(/{{\s*([#^]is(?:_exact)?_match)\s*([^\s}]+)/) if used.any? allowed = data.fetch(:query)[/by\s*[({]([^})]+)[})]/, 1] .to_s.gsub(/["']/, "").split(/\s*,\s*/) .map! { |w| %("#{w}.name") } used.uniq.each do |match, group| next if allowed.include?(group) invalid!( "#{match} used with #{group}, but can only be used with #{allowed.join(", ")}. " \ "Group the query by #{group.sub(".name", "").tr('"', "")} or change the #{match}" ) end end end unless ALLOWED_PRIORITY_CLASSES.include?(priority.class) invalid! "priority needs to be an Integer" end end