debian-mirror-gitlab/app/models/alert_management/alert.rb

197 lines
6.7 KiB
Ruby
Raw Normal View History

2020-05-24 23:13:21 +05:30
# frozen_string_literal: true
2020-06-23 00:09:42 +05:30
require_dependency 'alert_management'
2020-05-24 23:13:21 +05:30
module AlertManagement
class Alert < ApplicationRecord
2020-06-23 00:09:42 +05:30
include IidRoutes
2020-05-24 23:13:21 +05:30
include AtomicInternalId
include ShaAttribute
include Sortable
2020-06-23 00:09:42 +05:30
include Noteable
2021-10-27 15:23:28 +05:30
include Mentionable
2020-05-24 23:13:21 +05:30
include Gitlab::SQL::Pattern
2020-07-28 23:09:34 +05:30
include Presentable
2020-11-24 15:15:51 +05:30
include Gitlab::Utils::StrongMemoize
include Referable
2021-10-27 15:23:28 +05:30
include ::IncidentManagement::Escalatable
2020-07-28 23:09:34 +05:30
2020-05-24 23:13:21 +05:30
belongs_to :project
belongs_to :issue, optional: true
2020-07-28 23:09:34 +05:30
belongs_to :prometheus_alert, optional: true
belongs_to :environment, optional: true
2020-05-24 23:13:21 +05:30
2020-06-23 00:09:42 +05:30
has_many :alert_assignees, inverse_of: :alert
has_many :assignees, through: :alert_assignees
has_many :notes, as: :noteable, inverse_of: :noteable, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
has_many :ordered_notes, -> { fresh }, as: :noteable, class_name: 'Note'
has_many :user_mentions, class_name: 'AlertManagement::AlertUserMention', foreign_key: :alert_management_alert_id
2022-06-21 17:19:12 +05:30
has_many :metric_images, class_name: '::AlertManagement::MetricImage'
2020-06-23 00:09:42 +05:30
2021-01-29 00:20:46 +05:30
has_internal_id :iid, scope: :project
2020-05-24 23:13:21 +05:30
sha_attribute :fingerprint
2021-10-27 15:23:28 +05:30
# Allow :ended_at to be managed by Escalatable
alias_attribute :resolved_at, :ended_at
2021-01-03 14:25:43 +05:30
TITLE_MAX_LENGTH = 200
DESCRIPTION_MAX_LENGTH = 1_000
SERVICE_MAX_LENGTH = 100
TOOL_MAX_LENGTH = 100
2020-05-24 23:13:21 +05:30
HOSTS_MAX_LENGTH = 255
2021-01-03 14:25:43 +05:30
validates :title, length: { maximum: TITLE_MAX_LENGTH }, presence: true
validates :description, length: { maximum: DESCRIPTION_MAX_LENGTH }
validates :service, length: { maximum: SERVICE_MAX_LENGTH }
validates :monitoring_tool, length: { maximum: TOOL_MAX_LENGTH }
2020-05-24 23:13:21 +05:30
validates :project, presence: true
validates :events, presence: true
validates :severity, presence: true
validates :started_at, presence: true
2020-07-28 23:09:34 +05:30
validates :fingerprint, allow_blank: true, uniqueness: {
scope: :project,
conditions: -> { not_resolved },
2023-03-04 22:38:38 +05:30
message: ->(object, data) { _('Cannot have multiple unresolved alerts') }
2020-07-28 23:09:34 +05:30
}, unless: :resolved?
2021-01-03 14:25:43 +05:30
validate :hosts_format
2020-05-24 23:13:21 +05:30
enum severity: {
critical: 0,
high: 1,
medium: 2,
low: 3,
info: 4,
unknown: 5
}
2021-02-22 17:27:13 +05:30
enum domain: {
operations: 0,
threat_monitoring: 1
}
2020-05-24 23:13:21 +05:30
delegate :iid, to: :issue, prefix: true, allow_nil: true
2020-11-24 15:15:51 +05:30
delegate :details_url, to: :present
2020-05-24 23:13:21 +05:30
2023-03-04 22:38:38 +05:30
scope :for_iid, ->(iid) { where(iid: iid) }
scope :for_fingerprint, ->(project, fingerprint) { where(project: project, fingerprint: fingerprint) }
scope :for_environment, ->(environment) { where(environment: environment) }
scope :for_assignee_username, ->(assignee_username) { joins(:assignees).merge(User.by_username(assignee_username)) }
scope :search, ->(query) { fuzzy_search(query, [:title, :description, :monitoring_tool, :service]) }
2021-01-03 14:25:43 +05:30
scope :not_resolved, -> { without_status(:resolved) }
2020-07-28 23:09:34 +05:30
scope :with_prometheus_alert, -> { includes(:prometheus_alert) }
2021-02-22 17:27:13 +05:30
scope :with_operations_alerts, -> { where(domain: :operations) }
2020-05-24 23:13:21 +05:30
2023-03-04 22:38:38 +05:30
scope :order_start_time, ->(sort_order) { order(started_at: sort_order) }
scope :order_end_time, ->(sort_order) { order(ended_at: sort_order) }
scope :order_event_count, ->(sort_order) { order(events: sort_order) }
2020-07-28 23:09:34 +05:30
# Ascending sort order sorts severity from less critical to more critical.
# Descending sort order sorts severity from more critical to less critical.
# https://gitlab.com/gitlab-org/gitlab/-/issues/221242#what-is-the-expected-correct-behavior
2023-03-04 22:38:38 +05:30
scope :order_severity, ->(sort_order) { order(severity: sort_order == :asc ? :desc : :asc) }
2020-10-24 23:57:45 +05:30
scope :order_severity_with_open_prometheus_alert, -> { open.with_prometheus_alert.order(severity: :asc, started_at: :desc) }
2020-07-28 23:09:34 +05:30
scope :counts_by_project_id, -> { group(:project_id).count }
alias_method :state, :status_name
2020-05-24 23:13:21 +05:30
2021-01-03 14:25:43 +05:30
def self.counts_by_status
group(:status).count.transform_keys { |k| status_name(k) }
end
2020-05-24 23:13:21 +05:30
def self.sort_by_attribute(method)
case method.to_s
2020-06-23 00:09:42 +05:30
when 'started_at_asc' then order_start_time(:asc)
when 'started_at_desc' then order_start_time(:desc)
when 'ended_at_asc' then order_end_time(:asc)
when 'ended_at_desc' then order_end_time(:desc)
when 'event_count_asc' then order_event_count(:asc)
when 'event_count_desc' then order_event_count(:desc)
2020-05-24 23:13:21 +05:30
when 'severity_asc' then order_severity(:asc)
when 'severity_desc' then order_severity(:desc)
when 'status_asc' then order_status(:asc)
when 'status_desc' then order_status(:desc)
else
order_by(method)
end
end
2022-07-16 23:28:13 +05:30
def self.find_unresolved_alert(project, fingerprint)
for_fingerprint(project, fingerprint).not_resolved.take
end
2020-07-28 23:09:34 +05:30
def self.last_prometheus_alert_by_project_id
ids = select(arel_table[:id].maximum).group(:project_id)
with_prometheus_alert.where(id: ids)
end
2020-11-24 15:15:51 +05:30
def self.reference_prefix
'^alert#'
end
2020-05-24 23:13:21 +05:30
2020-11-24 15:15:51 +05:30
def self.reference_pattern
@reference_pattern ||= %r{
(#{Project.reference_pattern})?
#{Regexp.escape(reference_prefix)}(?<alert>\d+)
}x
end
def self.link_reference_pattern
2021-09-30 23:02:18 +05:30
@link_reference_pattern ||= super("alert_management", %r{(?<alert>\d+)/details(\#)?})
2020-11-24 15:15:51 +05:30
end
def self.reference_valid?(reference)
reference.to_i > 0 && reference.to_i <= Gitlab::Database::MAX_INT_VALUE
2020-05-24 23:13:21 +05:30
end
def prometheus?
2021-01-03 14:25:43 +05:30
monitoring_tool == Gitlab::AlertManagement::Payload::MONITORING_TOOLS[:prometheus]
2020-05-24 23:13:21 +05:30
end
2020-06-23 00:09:42 +05:30
def register_new_event!
increment!(:events)
end
2020-11-24 15:15:51 +05:30
def to_reference(from = nil, full: false)
reference = "#{self.class.reference_prefix}#{iid}"
"#{project.to_reference_base(from, full: full)}#{reference}"
2020-06-23 00:09:42 +05:30
end
2021-09-30 23:02:18 +05:30
def execute_integrations
return unless project.has_active_integrations?(:alert_hooks)
2020-06-23 00:09:42 +05:30
2021-09-30 23:02:18 +05:30
project.execute_integrations(hook_data, :alert_hooks)
2020-06-23 00:09:42 +05:30
end
2020-11-24 15:15:51 +05:30
# Representation of the alert's payload. Avoid accessing
# #payload attribute directly.
def parsed_payload
strong_memoize(:parsed_payload) do
Gitlab::AlertManagement::Payload.parse(project, payload, monitoring_tool: monitoring_tool)
end
2020-07-28 23:09:34 +05:30
end
2021-08-04 16:29:09 +05:30
def to_ability_name
'alert_management_alert'
end
2020-05-24 23:13:21 +05:30
private
2020-06-23 00:09:42 +05:30
def hook_data
Gitlab::DataBuilder::Alert.build(self)
end
2021-01-03 14:25:43 +05:30
def hosts_format
2020-05-24 23:13:21 +05:30
return unless hosts
errors.add(:hosts, "hosts array is over #{HOSTS_MAX_LENGTH} chars") if hosts.join.length > HOSTS_MAX_LENGTH
2021-01-03 14:25:43 +05:30
errors.add(:hosts, "hosts array cannot be nested") if hosts.flatten != hosts
2020-05-24 23:13:21 +05:30
end
end
end
2021-02-22 17:27:13 +05:30
2021-06-08 01:23:25 +05:30
AlertManagement::Alert.prepend_mod_with('AlertManagement::Alert')