Commit f29726c5 authored by Vitali Tatarintev's avatar Vitali Tatarintev

Merge branch 'sy-condense-alert-email-presenter' into 'master'

Remove Alerting::Alert and AlertManagement::AlertParams

See merge request gitlab-org/gitlab!41220
parents 55e69f81 b315c266
......@@ -56,16 +56,14 @@ module Emails
subject: @message.subject)
end
def prometheus_alert_fired_email(project_id, user_id, alert_payload)
def prometheus_alert_fired_email(project_id, user_id, alert_attributes)
@project = ::Project.find(project_id)
user = ::User.find(user_id)
@alert = ::Gitlab::Alerting::Alert
.new(project: @project, payload: alert_payload)
.present
return unless @alert.valid?
@alert = AlertManagement::Alert.new(alert_attributes.with_indifferent_access).present
return unless @alert.parsed_payload.has_required_attributes?
subject_text = "Alert: #{@alert.full_title}"
subject_text = "Alert: #{@alert.email_title}"
mail(to: user.notification_email_for(@project.group), subject: subject(subject_text))
end
end
......
......@@ -191,7 +191,7 @@ module AlertManagement
end
def prometheus?
monitoring_tool == Gitlab::AlertManagement::AlertParams::MONITORING_TOOLS[:prometheus]
monitoring_tool == Gitlab::AlertManagement::Payload::MONITORING_TOOLS[:prometheus]
end
def register_new_event!
......
......@@ -8,6 +8,7 @@ module AlertManagement
MARKDOWN_LINE_BREAK = " \n"
HORIZONTAL_LINE = "\n\n---\n\n"
INCIDENT_LABEL_NAME = ::IncidentManagement::CreateIncidentLabelService::LABEL_PROPERTIES[:title]
delegate :metrics_dashboard_url, :runbook, to: :parsed_payload
......@@ -38,6 +39,30 @@ module AlertManagement
Gitlab::Utils::InlineHash.merge_keys(payload)
end
def show_incident_issues_link?
project.incident_management_setting&.create_issue?
end
def show_performance_dashboard_link?
prometheus_alert.present?
end
def incident_issues_link
project_issues_url(project, label_name: INCIDENT_LABEL_NAME)
end
def performance_dashboard_link
if environment
metrics_project_environment_url(project, environment)
else
metrics_project_environments_url(project)
end
end
def email_title
[environment&.name, query_title].compact.join(': ')
end
private
attr_reader :alert, :project
......@@ -80,5 +105,11 @@ module AlertManagement
def host_links
hosts.join(' ')
end
def query_title
return title unless prometheus_alert
"#{prometheus_alert.title} #{prometheus_alert.computed_operator} #{prometheus_alert.threshold} for 5 minutes"
end
end
end
# frozen_string_literal: true
module Projects
module Prometheus
class AlertPresenter < Gitlab::View::Presenter::Delegated
GENERIC_ALERT_SUMMARY_ANNOTATIONS = %w(monitoring_tool service hosts).freeze
MARKDOWN_LINE_BREAK = " \n".freeze
INCIDENT_LABEL_NAME = ::IncidentManagement::CreateIncidentLabelService::LABEL_PROPERTIES[:title].freeze
METRIC_TIME_WINDOW = 30.minutes
def full_title
[environment_name, alert_title].compact.join(': ')
end
def project_full_path
project.full_path
end
def metric_query
gitlab_alert&.full_query
end
def environment_name
environment&.name
end
def performance_dashboard_link
if environment
metrics_project_environment_url(project, environment)
else
metrics_project_environments_url(project)
end
end
def show_performance_dashboard_link?
gitlab_alert.present?
end
def show_incident_issues_link?
project.incident_management_setting&.create_issue?
end
def incident_issues_link
project_issues_url(project, label_name: INCIDENT_LABEL_NAME)
end
def start_time
starts_at&.strftime('%d %B %Y, %-l:%M%p (%Z)')
end
def issue_summary_markdown
<<~MARKDOWN.chomp
#{metadata_list}
#{metric_embed_for_alert}
MARKDOWN
end
def metric_embed_for_alert
"\n[](#{metrics_dashboard_url})" if metrics_dashboard_url
end
def metrics_dashboard_url
strong_memoize(:metrics_dashboard_url) do
embed_url_for_gitlab_alert || embed_url_for_self_managed_alert
end
end
def details_url
return unless am_alert
::Gitlab::Routing.url_helpers.details_project_alert_management_url(
project,
am_alert.iid
)
end
private
def alert_title
query_title || title
end
def query_title
return unless gitlab_alert
"#{gitlab_alert.title} #{gitlab_alert.computed_operator} #{gitlab_alert.threshold} for 5 minutes"
end
def metadata_list
metadata = []
metadata << list_item('Start time', start_time) if start_time
metadata << list_item('full_query', backtick(full_query)) if full_query
metadata << list_item(service.label.humanize, service.value) if service
metadata << list_item(monitoring_tool.label.humanize, monitoring_tool.value) if monitoring_tool
metadata << list_item(hosts.label.humanize, host_links) if hosts
metadata << list_item('GitLab alert', details_url) if details_url
metadata.join(MARKDOWN_LINE_BREAK)
end
def details
Gitlab::Utils::InlineHash.merge_keys(payload)
end
def list_item(key, value)
"**#{key}:** #{value}".strip
end
def backtick(value)
"`#{value}`"
end
GENERIC_ALERT_SUMMARY_ANNOTATIONS.each do |annotation_name|
define_method(annotation_name) do
annotations.find { |a| a.label == annotation_name }
end
end
def host_links
Array(hosts.value).join(' ')
end
def embed_url_for_gitlab_alert
return unless gitlab_alert
metrics_dashboard_project_prometheus_alert_url(
project,
gitlab_alert.prometheus_metric_id,
environment_id: environment.id,
embedded: true,
**alert_embed_window_params(embed_time)
)
end
def embed_url_for_self_managed_alert
return unless environment && full_query && title
metrics_dashboard_project_environment_url(
project,
environment,
embed_json: dashboard_for_self_managed_alert.to_json,
embedded: true,
**alert_embed_window_params(embed_time)
)
end
def embed_time
starts_at || Time.current
end
def alert_embed_window_params(time)
{
start: format_embed_timestamp(time - METRIC_TIME_WINDOW),
end: format_embed_timestamp(time + METRIC_TIME_WINDOW)
}
end
def format_embed_timestamp(time)
time.utc.strftime('%FT%TZ')
end
def dashboard_for_self_managed_alert
{
panel_groups: [{
panels: [{
type: 'area-chart',
title: title,
y_label: y_label,
metrics: [{
query_range: full_query
}]
}]
}]
}
end
end
end
end
......@@ -47,7 +47,7 @@ module AlertManagement
def create_alert_management_alert
if alert.save
alert.execute_services
SystemNoteService.create_new_alert(alert, Gitlab::AlertManagement::AlertParams::MONITORING_TOOLS[:prometheus])
SystemNoteService.create_new_alert(alert, Gitlab::AlertManagement::Payload::MONITORING_TOOLS[:prometheus])
return
end
......
......@@ -7,43 +7,34 @@ module Projects
include ::IncidentManagement::Settings
def execute(token)
return bad_request unless valid_payload_size?
return forbidden unless alerts_service_activated?
return unauthorized unless valid_token?(token)
alert = process_alert
process_alert
return bad_request unless alert.persisted?
process_incident_issues(alert) if process_issues?
process_incident_issues if process_issues?
send_alert_email if send_email?
ServiceResponse.success
rescue Gitlab::Alerting::NotificationPayloadParser::BadPayloadError
bad_request
end
private
delegate :alerts_service, :alerts_service_activated?, to: :project
def am_alert_params
strong_memoize(:am_alert_params) do
Gitlab::AlertManagement::AlertParams.from_generic_alert(project: project, payload: params.to_h)
end
end
def process_alert
existing_alert = find_alert_by_fingerprint(am_alert_params[:fingerprint])
if existing_alert
process_existing_alert(existing_alert)
if alert.persisted?
process_existing_alert
else
create_alert
end
end
def process_existing_alert(alert)
if am_alert_params[:ended_at].present?
process_resolved_alert(alert)
def process_existing_alert
if incoming_payload.ends_at.present?
process_resolved_alert
else
alert.register_new_event!
end
......@@ -51,10 +42,10 @@ module Projects
alert
end
def process_resolved_alert(alert)
def process_resolved_alert
return unless auto_close_incident?
if alert.resolve(am_alert_params[:ended_at])
if alert.resolve(incoming_payload.ends_at)
close_issue(alert.issue)
end
......@@ -72,20 +63,13 @@ module Projects
end
def create_alert
alert = AlertManagement::Alert.create(am_alert_params.except(:ended_at))
alert.execute_services if alert.persisted?
SystemNoteService.create_new_alert(alert, 'Generic Alert Endpoint')
return unless alert.save
alert
end
def find_alert_by_fingerprint(fingerprint)
return unless fingerprint
AlertManagement::Alert.not_resolved.for_fingerprint(project, fingerprint).first
alert.execute_services
SystemNoteService.create_new_alert(alert, 'Generic Alert Endpoint')
end
def process_incident_issues(alert)
def process_incident_issues
return if alert.issue
::IncidentManagement::ProcessAlertWorker.perform_async(nil, nil, alert.id)
......@@ -94,11 +78,33 @@ module Projects
def send_alert_email
notification_service
.async
.prometheus_alerts_fired(project, [parsed_payload])
.prometheus_alerts_fired(project, [alert.attributes])
end
def alert
strong_memoize(:alert) do
existing_alert || new_alert
end
end
def existing_alert
return unless incoming_payload.gitlab_fingerprint
AlertManagement::Alert.not_resolved.for_fingerprint(project, incoming_payload.gitlab_fingerprint).first
end
def new_alert
AlertManagement::Alert.new(**incoming_payload.alert_params, ended_at: nil)
end
def incoming_payload
strong_memoize(:incoming_payload) do
Gitlab::AlertManagement::Payload.parse(project, params.to_h)
end
end
def parsed_payload
Gitlab::Alerting::NotificationPayloadParser.call(params.to_h, project)
def valid_payload_size?
Gitlab::Utils::DeepSize.new(params).valid?
end
def valid_token?(token)
......
......@@ -125,7 +125,7 @@ module Projects
notification_service
.async
.prometheus_alerts_fired(project, firings)
.prometheus_alerts_fired(project, alerts_attributes)
end
def process_prometheus_alerts
......@@ -136,6 +136,18 @@ module Projects
end
end
def alerts_attributes
firings.map do |payload|
alert_params = Gitlab::AlertManagement::Payload.parse(
project,
payload,
monitoring_tool: Gitlab::AlertManagement::Payload::MONITORING_TOOLS[:prometheus]
).alert_params
AlertManagement::Alert.new(alert_params).attributes
end
end
def bad_request
ServiceResponse.error(message: 'Bad Request', http_status: :bad_request)
end
......
%p
= _('An alert has been triggered in %{project_path}.') % { project_path: @alert.project_full_path }
= _('An alert has been triggered in %{project_path}.') % { project_path: @alert.project.full_path }
- if description = @alert.description
%p
= _('Description:')
= description
- if env_name = @alert.environment_name
- if env_name = @alert.environment&.name
%p
= _('Environment:')
= env_name
- if metric_query = @alert.metric_query
- if metric_query = @alert.prometheus_alert&.full_query
%p
= _('Metric:')
......@@ -25,4 +25,3 @@
- if @alert.show_performance_dashboard_link?
%p
= link_to(_('View performance dashboard.'), @alert.performance_dashboard_link)
<%= _('An alert has been triggered in %{project_path}.') % { project_path: @alert.project_full_path } %>.
<%= _('An alert has been triggered in %{project_path}.') % { project_path: @alert.project.full_path } %>.
<% if description = @alert.description %>
<%= _('Description:') %> <%= description %>
<% end %>
<% if env_name = @alert.environment_name %>
<% if env_name = @alert.environment&.name %>
<%= _('Environment:') %> <%= env_name %>
<% end %>
<% if metric_query = @alert.metric_query %>
<% if metric_query = @alert.prometheus_alert&.full_query %>
<%= _('Metric:') %> <%= metric_query %>
<% end %>
......
# frozen_string_literal: true
# Attribute mapping for alerts via generic alerting integration.
module EE
module Gitlab
module AlertManagement
module Payload
module Generic
extend ::Gitlab::Utils::Override
EXCLUDED_PAYLOAD_FINGERPRINT_PARAMS = %w(start_time end_time hosts).freeze
private
# Currently we use full payloads, when generating a fingerprint.
# This results in a quite strict fingerprint.
# Over time we can relax these rules.
# See https://gitlab.com/gitlab-org/gitlab/-/issues/214557#note_362795447
override :plain_gitlab_fingerprint
def plain_gitlab_fingerprint
strong_memoize(:plain_gitlab_fingerprint) do
next super if super.present?
next unless generic_alert_fingerprinting_enabled?
payload_excluding_params = payload.excluding(EXCLUDED_PAYLOAD_FINGERPRINT_PARAMS)
next if payload_excluding_params.none? { |_, v| v.present? }
payload_excluding_params
end
end
def generic_alert_fingerprinting_enabled?
project.feature_available?(:generic_alert_fingerprinting)
end
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::AlertManagement::Payload::Generic do
let_it_be(:project) { build_stubbed(:project) }
let(:raw_payload) { {} }
let(:parsed_payload) { described_class.new(project: project, payload: raw_payload) }
describe '#gitlab_fingerprint' do
subject { parsed_payload.gitlab_fingerprint }
context 'with fingerprint defined in payload' do
let(:expected_fingerprint) { Digest::SHA1.hexdigest(plain_fingerprint) }
let(:plain_fingerprint) { 'fingerprint' }
let(:raw_payload) { { 'fingerprint' => plain_fingerprint } }
it { is_expected.to eq(expected_fingerprint) }
end
context 'license feature enabled' do
let(:expected_fingerprint) { Gitlab::AlertManagement::Fingerprint.generate(plain_fingerprint) }
let(:plain_fingerprint) { raw_payload.except('hosts', 'start_time') }
let(:raw_payload) do
{
'keep-this' => 'attribute',
'hosts' => 'remove me',
'start_time' => 'remove me'
}
end
before do
stub_licensed_features(generic_alert_fingerprinting: true)
end
it { is_expected.to eq(expected_fingerprint) }
context 'payload has no values' do
let(:raw_payload) do
{
'start_time' => '2020-09-17 12:49:54 -0400',
'hosts' => ['gitlab.com'],
'end_time' => '2020-09-17 12:59:54 -0400',
'title' => ' '
}
end
it { is_expected.to be_nil }
end
end
context 'license feature not enabled' do
it { is_expected.to be_nil }
end
end
end
......@@ -10,7 +10,7 @@ RSpec.describe Projects::Alerting::NotifyService do
let(:token) { alerts_service.token }
let(:payload) do
{
title: 'Test alert title'
'title' => 'Test alert title'
}
end
......@@ -19,14 +19,11 @@ RSpec.describe Projects::Alerting::NotifyService do
subject { service.execute(token) }
context 'existing alert with same payload fingerprint' do
let(:existing_alert) do
service.execute(token)
AlertManagement::Alert.last!
end
let(:existing_alert) { create(:alert_management_alert, :from_payload, project: project, payload: payload) }
before do
stub_licensed_features(generic_alert_fingerprinting: fingerprinting_enabled)
existing_alert # create existing alert
existing_alert # create existing alert after enabling flag
end
context 'generic fingerprinting license not enabled' do
......@@ -53,16 +50,8 @@ RSpec.describe Projects::Alerting::NotifyService do
end
context 'end_time provided for subsequent alert' do
before do
payload[:end_time] = Time.current.change(usec: 0).iso8601
end
let(:existing_alert) do
existing_payload = payload.except(:end_time)
described_class.new(project, nil, existing_payload).execute(token)
AlertManagement::Alert.last!
end
let(:existing_alert) { create(:alert_management_alert, :from_payload, project: project, payload: payload.except('end_time')) }
let(:payload) { { 'title' => 'title', 'end_time' => Time.current.change(usec: 0).iso8601 } }
it 'does not create AlertManagement::Alert' do
expect { subject }.not_to change(AlertManagement::Alert, :count)
......@@ -70,7 +59,7 @@ RSpec.describe Projects::Alerting::NotifyService do
it 'resolves the existing alert', :aggregate_failures do
expect { subject }.to change { existing_alert.reload.resolved? }.from(false).to(true)
expect(existing_alert.ended_at).to eq(payload[:end_time])
expect(existing_alert.ended_at).to eq(payload['end_time'])
end
end
end
......
# frozen_string_literal: true
module Gitlab
module AlertManagement
class AlertParams
MONITORING_TOOLS = {
prometheus: 'Prometheus'
}.freeze
def self.from_generic_alert(project:, payload:)
parsed_payload = Gitlab::Alerting::NotificationPayloadParser.call(payload, project).with_indifferent_access
annotations = parsed_payload[:annotations]
{
project_id: project.id,
title: annotations[:title],
description: annotations[:description],
monitoring_tool: annotations[:monitoring_tool],
service: annotations[:service],
hosts: Array(annotations[:hosts]),
payload: payload,
started_at: parsed_payload['startsAt'],
ended_at: parsed_payload['endsAt'],
severity: annotations[:severity],
fingerprint: annotations[:fingerprint],
environment: annotations[:environment]
}
end
def self.from_prometheus_alert(project:, parsed_alert:)
{
project_id: project.id,
title: parsed_alert.title,
description: parsed_alert.description,
monitoring_tool: MONITORING_TOOLS[:prometheus],
payload: parsed_alert.payload,
started_at: parsed_alert.starts_at,
ended_at: parsed_alert.ends_at,
fingerprint: parsed_alert.gitlab_fingerprint,
environment: parsed_alert.environment,
prometheus_alert: parsed_alert.gitlab_alert
}
end
end
end
end
......@@ -8,6 +8,8 @@ module Gitlab
DEFAULT_TITLE = 'New: Incident'
DEFAULT_SEVERITY = 'critical'
attribute :description, paths: 'description'
attribute :ends_at, paths: 'end_time', type: :time
attribute :environment_name, paths: 'gitlab_environment_name'
attribute :hosts, paths: 'hosts'
attribute :monitoring_tool, paths: 'monitoring_tool'
......@@ -23,3 +25,5 @@ module Gitlab
end
end
end
Gitlab::AlertManagement::Payload::Generic.prepend_if_ee('EE::Gitlab::AlertManagement::Payload::Generic')
# frozen_string_literal: true
module Gitlab
module Alerting
class Alert
include ActiveModel::Model
include Gitlab::Utils::StrongMemoize
include Presentable
attr_accessor :project, :payload, :am_alert
def self.for_alert_management_alert(project:, alert:)
params = if alert.prometheus?
alert.payload
else
Gitlab::Alerting::NotificationPayloadParser.call(alert.payload.to_h, alert.project)
end
self.new(project: project, payload: params, am_alert: alert)
end
def gitlab_alert
strong_memoize(:gitlab_alert) do
parse_gitlab_alert_from_payload
end
end
def metric_id
strong_memoize(:metric_id) do
payload&.dig('labels', 'gitlab_alert_id')
end
end
def gitlab_prometheus_alert_id
strong_memoize(:gitlab_prometheus_alert_id) do
payload&.dig('labels', 'gitlab_prometheus_alert_id')
end
end
def title
strong_memoize(:title) do
gitlab_alert&.title || parse_title_from_payload
end
end
def description
strong_memoize(:description) do
parse_description_from_payload
end
end
def environment
strong_memoize(:environment) do
gitlab_alert&.environment || parse_environment_from_payload
end
end
def annotations
strong_memoize(:annotations) do
parse_annotations_from_payload || []
end
end
def starts_at
strong_memoize(:starts_at) do
parse_datetime_from_payload('startsAt')
end
end
def starts_at_raw
strong_memoize(:starts_at_raw) do
payload&.dig('startsAt')
end
end
def ends_at
strong_memoize(:ends_at) do
parse_datetime_from_payload('endsAt')
end
end
def full_query
strong_memoize(:full_query) do
gitlab_alert&.full_query || parse_expr_from_payload
end
end
def y_label
strong_memoize(:y_label) do
parse_y_label_from_payload || title
end
end
def alert_markdown
strong_memoize(:alert_markdown) do
parse_alert_markdown_from_payload
end
end
def status
strong_memoize(:status) do
payload&.dig('status')
end
end
def firing?
status == 'firing'
end
def resolved?
status == 'resolved'
end
def gitlab_managed?
metric_id.present?
end
def gitlab_fingerprint
Gitlab::AlertManagement::Fingerprint.generate(plain_gitlab_fingerprint)
end
def valid?
payload.respond_to?(:dig) && project && title && starts_at
end
def present
super(presenter_class: Projects::Prometheus::AlertPresenter)
end
private
def plain_gitlab_fingerprint
if gitlab_managed?
[metric_id, starts_at_raw].join('/')
else # self managed
[starts_at_raw, title, full_query].join('/')
end
end
def parse_environment_from_payload
environment_name = payload&.dig('labels', 'gitlab_environment_name')
return unless environment_name
EnvironmentsFinder.new(project, nil, { name: environment_name })
.find
&.first
end
def parse_gitlab_alert_from_payload
alerts_found = matching_gitlab_alerts
return if alerts_found.blank? || alerts_found.size > 1
alerts_found.first
end
def matching_gitlab_alerts
return unless metric_id || gitlab_prometheus_alert_id
Projects::Prometheus::AlertsFinder
.new(project: project, metric: metric_id, id: gitlab_prometheus_alert_id)
.execute
end
def parse_title_from_payload
payload&.dig('annotations', 'title') ||
payload&.dig('annotations', 'summary') ||
payload&.dig('labels', 'alertname')
end
def parse_description_from_payload
payload&.dig('annotations', 'description')
end
def parse_annotations_from_payload
payload&.dig('annotations')&.map do |label, value|
Alerting::AlertAnnotation.new(label: label, value: value)
end
end
def parse_datetime_from_payload(field)
value = payload&.dig(field)
return unless value
# value is a rfc3339 timestamp
# Timestamps from Prometheus and Alertmanager are UTC RFC3339 timestamps like: '2018-03-12T09:06:00Z' (Z represents 0 offset or UTC)
# .utc sets the datetime zone to `UTC`
Time.rfc3339(value).utc
rescue ArgumentError
end
# Parses `g0.expr` from `generatorURL`.
#
# Example: http://localhost:9090/graph?g0.expr=vector%281%29&g0.tab=1
def parse_expr_from_payload
url = payload&.dig('generatorURL')
return unless url
uri = URI(url)
Rack::Utils.parse_query(uri.query).fetch('g0.expr')
rescue URI::InvalidURIError, KeyError
end
def parse_alert_markdown_from_payload
payload&.dig('annotations', 'gitlab_incident_markdown')
end
def parse_y_label_from_payload
payload&.dig('annotations', 'gitlab_y_label')
end
end
end
end
......@@ -100,7 +100,7 @@ FactoryBot.define do
end
trait :prometheus do
monitoring_tool { Gitlab::AlertManagement::AlertParams::MONITORING_TOOLS[:prometheus] }
monitoring_tool { Gitlab::AlertManagement::Payload::MONITORING_TOOLS[:prometheus] }
payload do
{
annotations: {
......@@ -123,5 +123,17 @@ FactoryBot.define do
with_description
low
end
trait :from_payload do
after(:build) do |alert|
alert_params = ::Gitlab::AlertManagement::Payload.parse(
alert.project,
alert.payload,
monitoring_tool: alert.monitoring_tool
).alert_params
alert.assign_attributes(alert_params)
end
end
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :alerting_alert, class: 'Gitlab::Alerting::Alert' do
project
payload { {} }
transient do
metric_id { nil }
after(:build) do |alert, evaluator|
unless alert.payload.key?('startsAt')
alert.payload['startsAt'] = Time.now.rfc3339
end
if metric_id = evaluator.metric_id
alert.payload['labels'] ||= {}
alert.payload['labels']['gitlab_alert_id'] = metric_id.to_s
end
end
end
skip_create
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::AlertManagement::AlertParams do
let_it_be(:project) { create(:project, :repository, :private) }
describe '.from_generic_alert' do
let(:started_at) { Time.current.change(usec: 0).rfc3339 }
let(:default_payload) do
{
'title' => 'Alert title',
'description' => 'Description',
'monitoring_tool' => 'Monitoring tool name',
'service' => 'Service',
'hosts' => ['gitlab.com'],
'start_time' => started_at,
'some' => { 'extra' => { 'payload' => 'here' } }
}
end
let(:payload) { default_payload }
subject { described_class.from_generic_alert(project: project, payload: payload) }
it 'returns Alert compatible parameters' do
is_expected.to eq(
project_id: project.id,
title: 'Alert title',
description: 'Description',
monitoring_tool: 'Monitoring tool name',
service: 'Service',
severity: 'critical',
hosts: ['gitlab.com'],
payload: payload,
started_at: started_at,
ended_at: nil,
fingerprint: nil,
environment: nil
)
end
context 'when severity given' do
let(:payload) { default_payload.merge(severity: 'low') }
it 'returns Alert compatible parameters' do
expect(subject[:severity]).to eq('low')
end
end
context 'when there are no hosts in the payload' do
let(:payload) { {} }
it 'hosts param is an empty array' do
expect(subject[:hosts]).to be_empty
end
end
end
describe '.from_prometheus_alert' do
let(:payload) do
{
'status' => 'firing',
'labels' => {
'alertname' => 'GitalyFileServerDown',
'channel' => 'gitaly',
'pager' => 'pagerduty',
'severity' => 's1'
},
'annotations' => {
'description' => 'Alert description',
'runbook' => 'troubleshooting/gitaly-down.md',
'title' => 'Alert title'
},
'startsAt' => '2020-04-27T10:10:22.265949279Z',
'endsAt' => '0001-01-01T00:00:00Z',
'generatorURL' => 'http://8d467bd4607a:9090/graph?g0.expr=vector%281%29&g0.tab=1',
'fingerprint' => 'b6ac4d42057c43c1'
}
end
let(:parsed_alert) { Gitlab::Alerting::Alert.new(project: project, payload: payload) }
subject { described_class.from_prometheus_alert(project: project, parsed_alert: parsed_alert) }
it 'returns Alert-compatible params' do
is_expected.to eq(
project_id: project.id,
title: 'Alert title',
description: 'Alert description',
monitoring_tool: 'Prometheus',
payload: payload,
started_at: parsed_alert.starts_at,
ended_at: parsed_alert.ends_at,
fingerprint: parsed_alert.gitlab_fingerprint,
environment: parsed_alert.environment,
prometheus_alert: parsed_alert.gitlab_alert
)
end
end
end
......@@ -86,4 +86,34 @@ RSpec.describe Gitlab::AlertManagement::Payload::Generic do
it_behaves_like 'parsable alert payload field', 'gitlab_environment_name'
end
describe '#description' do
subject { parsed_payload.description }
it_behaves_like 'parsable alert payload field', 'description'
end
describe '#ends_at' do
let(:current_time) { Time.current.change(usec: 0).utc }
subject { parsed_payload.ends_at }
around do |example|
Timecop.freeze(current_time) { example.run }
end
context 'without end_time' do
it { is_expected.to be_nil }
end
context "with end_time" do
let(:value) { 10.minutes.ago.change(usec: 0).utc }
before do
raw_payload['end_time'] = value.to_s
end
it { is_expected.to eq(value) }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Alerting::Alert do
let_it_be(:project) { create(:project) }
let(:alert) { build(:alerting_alert, project: project, payload: payload) }
let(:payload) { {} }
shared_context 'gitlab alert' do
let!(:gitlab_alert) { create(:prometheus_alert, project: project) }
let(:gitlab_alert_id) { gitlab_alert.id }
before do
payload['labels'] = {
'gitlab_alert_id' => gitlab_alert.prometheus_metric_id.to_s,
'gitlab_prometheus_alert_id' => gitlab_alert_id
}
end
end
shared_context 'full query' do
before do
payload['generatorURL'] = 'http://localhost:9090/graph?g0.expr=vector%281%29'
end
end
shared_examples 'invalid alert' do
it 'is invalid' do
expect(alert).not_to be_valid
end
end
shared_examples 'parse payload' do |*pairs|
context 'without payload' do
it { is_expected.to be_nil }
end
pairs.each do |pair|
context "with #{pair}" do
let(:value) { 'some value' }
before do
section, name = pair.split('/')
payload[section] = { name => value }
end
it { is_expected.to eq(value) }
end
end
end
describe '#gitlab_alert' do
subject { alert.gitlab_alert }
context 'without payload' do
it { is_expected.to be_nil }
end
context 'with gitlab alert' do
include_context 'gitlab alert'
it { is_expected.to eq(gitlab_alert) }
end
context 'with unknown gitlab alert' do
include_context 'gitlab alert' do
let(:gitlab_alert_id) { 'unknown' }
end
it { is_expected.to be_nil }
end
context 'when two alerts with the same metric exist' do
include_context 'gitlab alert'
let!(:second_gitlab_alert) do
create(:prometheus_alert,
project: project,
prometheus_metric_id: gitlab_alert.prometheus_metric_id
)
end
context 'alert id given in params' do
before do
payload['labels'] = {
'gitlab_alert_id' => gitlab_alert.prometheus_metric_id.to_s,
'gitlab_prometheus_alert_id' => second_gitlab_alert.id
}
end
it { is_expected.to eq(second_gitlab_alert) }
end
context 'metric id given in params' do
# This tests the case when two alerts are found, as metric id
# is not unique.
# Note the metric id was incorrectly named as 'gitlab_alert_id'
# in PrometheusAlert#to_param.
before do
payload['labels'] = { 'gitlab_alert_id' => gitlab_alert.prometheus_metric_id }
end
it { is_expected.to be_nil }
end
end
end
describe '#title' do
subject { alert.title }
it_behaves_like 'parse payload',
'annotations/title',
'annotations/summary',
'labels/alertname'
context 'with gitlab alert' do
include_context 'gitlab alert'
context 'with annotations/title' do
let(:value) { 'annotation title' }
before do
payload['annotations'] = { 'title' => value }
end
it { is_expected.to eq(gitlab_alert.title) }
end
end
end
describe '#description' do
subject { alert.description }
it_behaves_like 'parse payload', 'annotations/description'
end
describe '#annotations' do
subject { alert.annotations }
context 'without payload' do
it { is_expected.to eq([]) }
end
context 'with payload' do
before do
payload['annotations'] = { 'foo' => 'value1', 'bar' => 'value2' }
end
it 'parses annotations' do
expect(subject.size).to eq(2)
expect(subject.map(&:label)).to eq(%w[foo bar])
expect(subject.map(&:value)).to eq(%w[value1 value2])
end
end
end
describe '#environment' do
subject { alert.environment }
context 'without gitlab_alert' do
it { is_expected.to be_nil }
end
context 'with gitlab alert' do
include_context 'gitlab alert'
it { is_expected.to eq(gitlab_alert.environment) }
end
end
describe '#starts_at' do
subject { alert.starts_at }
context 'with empty startsAt' do
before do
payload['startsAt'] = nil
end
it { is_expected.to be_nil }
end
context 'with invalid startsAt' do
before do
payload['startsAt'] = 'invalid'
end
it { is_expected.to be_nil }
end
context 'with payload' do
let(:time) { Time.current.change(usec: 0) }
before do
payload['startsAt'] = time.rfc3339
end
it { is_expected.to eq(time) }
end
end
describe '#full_query' do
using RSpec::Parameterized::TableSyntax
subject { alert.full_query }
where(:generator_url, :expected_query) do
nil | nil
'http://localhost' | nil
'invalid url' | nil
'http://localhost:9090/graph?g1.expr=vector%281%29' | nil
'http://localhost:9090/graph?g0.expr=vector%281%29' | 'vector(1)'
end
with_them do
before do
payload['generatorURL'] = generator_url
end
it { is_expected.to eq(expected_query) }
end
context 'with gitlab alert' do
include_context 'gitlab alert'
include_context 'full query'
it { is_expected.to eq(gitlab_alert.full_query) }
end
end
describe '#y_label' do
subject { alert.y_label }
it_behaves_like 'parse payload', 'annotations/gitlab_y_label'
context 'when y_label is not included in the payload' do
it_behaves_like 'parse payload', 'annotations/title'
end
end
describe '#alert_markdown' do
subject { alert.alert_markdown }
it_behaves_like 'parse payload', 'annotations/gitlab_incident_markdown'
end
describe '#gitlab_fingerprint' do
subject { alert.gitlab_fingerprint }
context 'when the alert is a GitLab managed alert' do
include_context 'gitlab alert'
it 'returns a fingerprint' do
plain_fingerprint = [alert.metric_id, alert.starts_at_raw].join('/')
is_expected.to eq(Digest::SHA1.hexdigest(plain_fingerprint))
end
end
context 'when the alert is from self managed Prometheus' do
include_context 'full query'
it 'returns a fingerprint' do
plain_fingerprint = [alert.starts_at_raw, alert.title, alert.full_query].join('/')
is_expected.to eq(Digest::SHA1.hexdigest(plain_fingerprint))
end
end
end
describe '#valid?' do
before do
payload.update(
'annotations' => { 'title' => 'some title' },
'startsAt' => Time.current.rfc3339
)
end
subject { alert }
it { is_expected.to be_valid }
context 'without project' do
let(:project) { nil }
it { is_expected.not_to be_valid }
end
context 'without starts_at' do
before do
payload['startsAt'] = nil
end
it { is_expected.not_to be_valid }
end
end
end
......@@ -30,107 +30,118 @@ RSpec.describe Emails::Projects do
let_it_be(:user) { create(:user) }
describe '#prometheus_alert_fired_email' do
let(:default_title) { Gitlab::AlertManagement::Payload::Generic::DEFAULT_TITLE }
let(:payload) { { 'startsAt' => Time.now.rfc3339 } }
let(:alert_attributes) { build(:alert_management_alert, :from_payload, payload: payload, project: project).attributes }
subject do
Notify.prometheus_alert_fired_email(project.id, user.id, alert_params)
Notify.prometheus_alert_fired_email(project.id, user.id, alert_attributes)
end
let(:alert_params) do
{ 'startsAt' => Time.now.rfc3339 }
context 'missing required attributes' do
let(:alert_attributes) { build(:alert_management_alert, :prometheus, :from_payload, payload: payload, project: project).attributes }
it_behaves_like 'no email'
end
context 'with a gitlab alert' do
before do
alert_params['labels'] = { 'gitlab_alert_id' => alert.prometheus_metric_id.to_s }
end
context 'with minimum required attributes' do
let(:payload) { {} }
let(:title) do
"#{alert.title} #{alert.computed_operator} #{alert.threshold}"
end
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like 'a user cannot unsubscribe through footer link'
let(:metrics_url) do
metrics_project_environment_url(project, environment)
it 'has expected subject' do
is_expected.to have_subject("#{project.name} | Alert: #{default_title}")
end
let(:environment) { alert.environment }
it 'has expected content' do
is_expected.to have_body_text('An alert has been triggered')
is_expected.to have_body_text(project.full_path)
is_expected.not_to have_body_text('Description:')
is_expected.not_to have_body_text('Environment:')
is_expected.not_to have_body_text('Metric:')
end
end
let!(:alert) { create(:prometheus_alert, project: project) }
context 'with description' do
let(:payload) { { 'description' => 'alert description' } }
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like 'a user cannot unsubscribe through footer link'
it 'has expected subject' do
is_expected.to have_subject("#{project.name} | Alert: #{environment.name}: #{title} for 5 minutes")
is_expected.to have_subject("#{project.name} | Alert: #{default_title}")
end
it 'has expected content' do
is_expected.to have_body_text('An alert has been triggered')
is_expected.to have_body_text(project.full_path)
is_expected.to have_body_text('Environment:')
is_expected.to have_body_text(environment.name)
is_expected.to have_body_text('Metric:')
is_expected.to have_body_text(alert.full_query)
is_expected.to have_body_text(metrics_url)
is_expected.to have_body_text('Description:')
is_expected.to have_body_text('alert description')
is_expected.not_to have_body_text('Environment:')
is_expected.not_to have_body_text('Metric:')
end
it_behaves_like 'shows the incident issues url'
end
context 'with no payload' do
let(:alert_params) { {} }
context 'with environment' do
let_it_be(:environment) { create(:environment, project: project) }
let(:payload) { { 'gitlab_environment_name' => environment.name } }
let(:metrics_url) { metrics_project_environment_url(project, environment) }
it_behaves_like 'no email'
end
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like 'a user cannot unsubscribe through footer link'
context 'with an unknown alert' do
before do
alert_params['labels'] = { 'gitlab_alert_id' => 'unknown' }
it 'has expected subject' do
is_expected.to have_subject("#{project.name} | Alert: #{environment.name}: #{default_title}")
end
it_behaves_like 'no email'
it 'has expected content' do
is_expected.to have_body_text('An alert has been triggered')
is_expected.to have_body_text(project.full_path)
is_expected.to have_body_text('Environment:')
is_expected.to have_body_text(environment.name)
is_expected.not_to have_body_text('Description:')
is_expected.not_to have_body_text('Metric:')
end
end
context 'with an external alert' do
let(:title) { 'alert title' }
context 'with gitlab alerting rule' do
let_it_be(:prometheus_alert) { create(:prometheus_alert, project: project) }
let_it_be(:environment) { prometheus_alert.environment }
let(:metrics_url) do
metrics_project_environments_url(project)
end
let(:alert_attributes) { build(:alert_management_alert, :prometheus, :from_payload, payload: payload, project: project).attributes }
let(:title) { "#{prometheus_alert.title} #{prometheus_alert.computed_operator} #{prometheus_alert.threshold}" }
let(:metrics_url) { metrics_project_environment_url(project, environment) }
before do
alert_params['annotations'] = { 'title' => title }
alert_params['generatorURL'] = 'http://localhost:9090/graph?g0.expr=vector%281%29&g0.tab=1'
payload['labels'] = {
'gitlab_alert_id' => prometheus_alert.prometheus_metric_id,
'alertname' => prometheus_alert.title
}
end
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like 'a user cannot unsubscribe through footer link'
it_behaves_like 'shows the incident issues url'
it 'has expected subject' do
is_expected.to have_subject("#{project.name} | Alert: #{title}")
is_expected.to have_subject("#{project.name} | Alert: #{environment.name}: #{title} for 5 minutes")
end
it 'has expected content' do
is_expected.to have_body_text('An alert has been triggered')
is_expected.to have_body_text(project.full_path)
is_expected.to have_body_text('Environment:')
is_expected.to have_body_text(environment.name)
is_expected.to have_body_text('Metric:')
is_expected.to have_body_text(prometheus_alert.full_query)
is_expected.to have_body_text(metrics_url)
is_expected.not_to have_body_text('Description:')
is_expected.not_to have_body_text('Environment:')
end
context 'with annotated description' do
let(:description) { 'description' }
before do
alert_params['annotations']['description'] = description
end
it 'shows the description' do
is_expected.to have_body_text('Description:')
is_expected.to have_body_text(description)
end
end
it_behaves_like 'shows the incident issues url'
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::Prometheus::AlertPresenter do
include Gitlab::Routing.url_helpers
let_it_be(:project, reload: true) { create(:project) }
let(:presenter) { described_class.new(alert) }
let(:payload) { {} }
let(:alert) { create(:alerting_alert, project: project, payload: payload) }
shared_context 'gitlab alert' do
let(:gitlab_alert) { create(:prometheus_alert, project: project) }
let(:metric_id) { gitlab_alert.prometheus_metric_id }
let(:alert) do
create(:alerting_alert, project: project, metric_id: metric_id, payload: payload)
end
end
describe '#project_full_path' do
subject { presenter.project_full_path }
it { is_expected.to eq(project.full_path) }
end
describe '#start_time' do
subject { presenter.start_time }
let(:starts_at) { '2020-10-31T14:02:04Z' }
before do
payload['startsAt'] = starts_at
end
context 'with valid utc datetime' do
it { is_expected.to eq('31 October 2020, 2:02PM (UTC)') }
context 'with admin time zone not UTC' do
before do
allow(Time).to receive(:zone).and_return(ActiveSupport::TimeZone.new('Perth'))
end
it { is_expected.to eq('31 October 2020, 2:02PM (UTC)') }
end
end
context 'with invalid datetime' do
let(:starts_at) { 'invalid' }
it { is_expected.to be_nil }
end
end
describe '#issue_summary_markdown' do
let(:markdown_line_break) { ' ' }
subject { presenter.issue_summary_markdown }
context 'without default payload' do
it do
is_expected.to eq(
<<~MARKDOWN.chomp
**Start time:** #{presenter.start_time}
MARKDOWN
)
end
end
context 'with optional attributes' do
before do
payload['annotations'] = {
'title' => 'Alert Title',
'foo' => 'value1',
'bar' => 'value2',
'description' => 'Alert Description',
'monitoring_tool' => 'monitoring_tool_name',
'service' => 'service_name',
'hosts' => ['http://localhost:3000', 'http://localhost:3001']
}
payload['generatorURL'] = 'http://host?g0.expr=query'
end
it do
is_expected.to eq(
<<~MARKDOWN.chomp
**Start time:** #{presenter.start_time}#{markdown_line_break}
**full_query:** `query`#{markdown_line_break}
**Service:** service_name#{markdown_line_break}
**Monitoring tool:** monitoring_tool_name#{markdown_line_break}
**Hosts:** http://localhost:3000 http://localhost:3001
MARKDOWN
)
end
end
context 'when hosts is a string' do
before do
payload['annotations'] = { 'hosts' => 'http://localhost:3000' }
end
it do
is_expected.to eq(
<<~MARKDOWN.chomp
**Start time:** #{presenter.start_time}#{markdown_line_break}
**Hosts:** http://localhost:3000
MARKDOWN
)
end
end
context 'with embedded metrics' do
let(:starts_at) { '2018-03-12T09:06:00Z' }
shared_examples_for 'markdown with metrics embed' do
let(:embed_regex) { /\n\[\]\(#{Regexp.quote(presenter.metrics_dashboard_url)}\)\z/ }
context 'without a starting time available' do
around do |example|
Timecop.freeze(starts_at) { example.run }
end
before do
payload.delete('startsAt')
end
it { is_expected.to match(embed_regex) }
end
context 'with a starting time available' do
it { is_expected.to match(embed_regex) }
end
end
context 'for gitlab-managed prometheus alerts' do
include_context 'gitlab-managed prometheus alert attributes'
let(:alert) do
create(:alerting_alert, project: project, metric_id: prometheus_metric_id, payload: payload)
end
it_behaves_like 'markdown with metrics embed'
end
context 'for alerts from a self-managed prometheus' do
include_context 'self-managed prometheus alert attributes'
it_behaves_like 'markdown with metrics embed'
context 'without y_label' do
let(:y_label) { title }
before do
payload['annotations'].delete('gitlab_y_label')
end
it_behaves_like 'markdown with metrics embed'
end
context 'when not enough information is present for an embed' do
shared_examples_for 'does not include an embed' do
it { is_expected.not_to match(/\[\]\(.+\)/) }
end
context 'without title' do
before do
payload['annotations'].delete('title')
end
it_behaves_like 'does not include an embed'
end
context 'without environment' do
before do
payload['labels'].delete('gitlab_environment_name')
end
it_behaves_like 'does not include an embed'
end
context 'without full_query' do
before do
payload.delete('generatorURL')
end
it_behaves_like 'does not include an embed'
end
end
end
end
end
describe '#show_performance_dashboard_link?' do
subject { presenter.show_performance_dashboard_link? }
it { is_expected.to be_falsey }
context 'with gitlab alert' do
include_context 'gitlab alert'
it { is_expected.to eq(true) }
end
end
describe '#show_incident_issues_link?' do
subject { presenter.show_incident_issues_link? }
it { is_expected.to be_falsey }
context 'create issue setting enabled' do
before do
create(:project_incident_management_setting, project: project, create_issue: true)
end
it { is_expected.to eq(true) }
end
end
describe '#details_url' do
subject { presenter.details_url }
it { is_expected.to eq(nil) }
context 'alert management alert present' do
let_it_be(:am_alert) { create(:alert_management_alert, project: project) }
let(:alert) { create(:alerting_alert, project: project, payload: payload, am_alert: am_alert) }
it { is_expected.to eq("http://localhost/#{project.full_path}/-/alert_management/#{am_alert.iid}/details") }
end
end
context 'with gitlab alert' do
include_context 'gitlab alert'
describe '#full_title' do
let(:query_title) do
"#{gitlab_alert.title} #{gitlab_alert.computed_operator} #{gitlab_alert.threshold} for 5 minutes"
end
let(:expected_subject) do
"#{alert.environment.name}: #{query_title}"
end
subject { presenter.full_title }
it { is_expected.to eq(expected_subject) }
end
describe '#metric_query' do
subject { presenter.metric_query }
it { is_expected.to eq(gitlab_alert.full_query) }
end
describe '#environment_name' do
subject { presenter.environment_name }
it { is_expected.to eq(alert.environment.name) }
end
describe '#performance_dashboard_link' do
let(:expected_link) { metrics_project_environment_url(project, alert.environment) }
subject { presenter.performance_dashboard_link }
it { is_expected.to eq(expected_link) }
end
describe '#incident_issues_link' do
let(:expected_link) { project_issues_url(project, label_name: described_class::INCIDENT_LABEL_NAME) }
subject { presenter.incident_issues_link }
it { is_expected.to eq(expected_link) }
end
end
context 'without gitlab alert' do
describe '#full_title' do
subject { presenter.full_title }
context 'with title' do
let(:title) { 'some title' }
before do
expect(alert).to receive(:title).and_return(title)
end
it { is_expected.to eq(title) }
end
context 'without title' do
it { is_expected.to eq('') }
end
end
describe '#metric_query' do
subject { presenter.metric_query }
it { is_expected.to be_nil }
end
describe '#environment_name' do
subject { presenter.environment_name }
it { is_expected.to be_nil }
end
describe '#performance_dashboard_link' do
let(:expected_link) { metrics_project_environments_url(project) }
subject { presenter.performance_dashboard_link }
it { is_expected.to eq(expected_link) }
end
end
describe '#metrics_dashboard_url' do
subject { presenter.metrics_dashboard_url }
context 'for a non-prometheus alert' do
it { is_expected.to be_nil }
end
context 'for a self-managed prometheus alert' do
include_context 'self-managed prometheus alert attributes'
let(:prometheus_payload) { payload }
it { is_expected.to eq(dashboard_url_for_alert) }
end
context 'for a gitlab-managed prometheus alert' do
include_context 'gitlab-managed prometheus alert attributes'
let(:prometheus_payload) { payload }
it { is_expected.to eq(dashboard_url_for_alert) }
end
end
end
......@@ -3082,32 +3082,25 @@ RSpec.describe NotificationService, :mailer do
describe '#prometheus_alerts_fired' do
let!(:project) { create(:project) }
let!(:prometheus_alert) { create(:prometheus_alert, project: project) }
let!(:master) { create(:user) }
let!(:developer) { create(:user) }
let(:alert_attributes) { build(:alert_management_alert, project: project).attributes }
before do
project.add_maintainer(master)
end
it 'sends the email to owners and masters' do
expect(Notify).to receive(:prometheus_alert_fired_email).with(project.id, master.id, prometheus_alert).and_call_original
expect(Notify).to receive(:prometheus_alert_fired_email).with(project.id, project.owner.id, prometheus_alert).and_call_original
expect(Notify).not_to receive(:prometheus_alert_fired_email).with(project.id, developer.id, prometheus_alert)
expect(Notify).to receive(:prometheus_alert_fired_email).with(project.id, master.id, alert_attributes).and_call_original
expect(Notify).to receive(:prometheus_alert_fired_email).with(project.id, project.owner.id, alert_attributes).and_call_original
expect(Notify).not_to receive(:prometheus_alert_fired_email).with(project.id, developer.id, alert_attributes)
subject.prometheus_alerts_fired(prometheus_alert.project, [prometheus_alert])
subject.prometheus_alerts_fired(project, [alert_attributes])
end
it_behaves_like 'project emails are disabled' do
before do
allow_next_instance_of(::Gitlab::Alerting::Alert) do |instance|
allow(instance).to receive(:valid?).and_return(true)
end
end
let(:alert_params) { { 'labels' => { 'gitlab_alert_id' => 'unknown' } } }
let(:notification_target) { prometheus_alert.project }
let(:notification_trigger) { subject.prometheus_alerts_fired(prometheus_alert.project, [alert_params]) }
let(:notification_target) { project }
let(:notification_trigger) { subject.prometheus_alerts_fired(project, [alert_attributes]) }
around do |example|
perform_enqueued_jobs { example.run }
......
......@@ -197,11 +197,10 @@ RSpec.describe Projects::Alerting::NotifyService do
end
context 'with overlong payload' do
let(:payload_raw) do
{
title: 'a' * Gitlab::Utils::DeepSize::DEFAULT_MAX_SIZE,
start_time: starts_at.rfc3339
}
let(:deep_size_object) { instance_double(Gitlab::Utils::DeepSize, valid?: false) }
before do
allow(Gitlab::Utils::DeepSize).to receive(:new).and_return(deep_size_object)
end
it_behaves_like 'does not process incident issues due to error', http_status: :bad_request
......@@ -215,17 +214,6 @@ RSpec.describe Projects::Alerting::NotifyService do
it_behaves_like 'processes incident issues'
context 'with an invalid payload' do
before do
allow(Gitlab::Alerting::NotificationPayloadParser)
.to receive(:call)
.and_raise(Gitlab::Alerting::NotificationPayloadParser::BadPayloadError)
end
it_behaves_like 'does not process incident issues due to error', http_status: :bad_request
it_behaves_like 'does not an create alert management alert'
end
context 'when alert already exists' do
let(:fingerprint_sha) { Digest::SHA1.hexdigest(fingerprint) }
let!(:alert) { create(:alert_management_alert, project: project, fingerprint: fingerprint_sha) }
......
......@@ -6,7 +6,7 @@ RSpec.describe IncidentManagement::ProcessPrometheusAlertWorker do
describe '#perform' do
let_it_be(:project) { create(:project) }
let_it_be(:prometheus_alert) { create(:prometheus_alert, project: project) }
let(:payload_key) { Gitlab::Alerting::Alert.new(project: project, payload: alert_params).gitlab_fingerprint }
let(:payload_key) { Gitlab::AlertManagement::Payload::Prometheus.new(project: project, payload: alert_params).gitlab_fingerprint }
let!(:prometheus_alert_event) { create(:prometheus_alert_event, prometheus_alert: prometheus_alert, payload_key: payload_key) }
let!(:settings) { create(:project_incident_management_setting, project: project, create_issue: true) }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment