Commit 0cd08998 authored by Dylan Griffith's avatar Dylan Griffith

Merge branch 'sy-remove-notification-payload-parser' into 'master'

Remove usage of Gitlab::Alerting::NotificationPayloadParser

See merge request gitlab-org/gitlab!42527
parents a5d499cc 53e520a5
......@@ -211,7 +211,7 @@ module EE
end
def generic_alert_with_default_title?
title == ::Gitlab::Alerting::NotificationPayloadParser::DEFAULT_TITLE &&
title == ::Gitlab::AlertManagement::Payload::Generic::DEFAULT_TITLE &&
project.alerts_service_activated? &&
author == ::User.alert_bot
end
......
# frozen_string_literal: true
module EE
module Gitlab
module Alerting
module NotificationPayloadParser
extend ::Gitlab::Utils::Override
EXCLUDED_PAYLOAD_FINGERPRINT_PARAMS = %w(start_time end_time hosts).freeze
# 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 :fingerprint
def fingerprint
return super if payload[:fingerprint].present? || !generic_alert_fingerprinting_enabled?
payload_excluding_params = payload.excluding(EXCLUDED_PAYLOAD_FINGERPRINT_PARAMS)
return if payload_excluding_params.none? { |_, v| v.present? }
::Gitlab::AlertManagement::Fingerprint.generate(payload_excluding_params)
end
private
def generic_alert_fingerprinting_enabled?
project.feature_available?(:generic_alert_fingerprinting)
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Alerting::NotificationPayloadParser do
let(:project) { build_stubbed(:project) }
describe '.call' do
subject(:parsed) { described_class.call(payload, project) }
let(:payload) do
{
'title' => 'alert title',
'start_time' => Time.current,
'description' => 'Description',
'monitoring_tool' => 'Monitoring tool name',
'service' => 'Service',
'hosts' => ['gitlab.com'],
'severity' => 'low'
}
end
describe 'fingerprint' do
subject(:fingerprint) { parsed.dig('annotations', 'fingerprint') }
context 'license feature enabled' do
before do
stub_licensed_features(generic_alert_fingerprinting: true)
end
it 'generates the fingerprint from the payload' do
fingerprint_payload = payload.excluding('start_time', 'hosts')
expected_fingerprint = Gitlab::AlertManagement::Fingerprint.generate(fingerprint_payload)
expect(fingerprint).to eq(expected_fingerprint)
end
context 'payload has no values' do
let(:payload) do
{
'start_time' => Time.current,
'hosts' => ['gitlab.com'],
'title' => ' '
}
end
it { is_expected.to eq(nil) }
end
end
context 'license feature not enabled' do
it { is_expected.to eq(nil) }
end
end
end
end
# frozen_string_literal: true
module Gitlab
module Alerting
class AlertAnnotation
include ActiveModel::Model
attr_accessor :label, :value
end
end
end
# frozen_string_literal: true
module Gitlab
module Alerting
class NotificationPayloadParser
BadPayloadError = Class.new(StandardError)
DEFAULT_TITLE = 'New: Incident'
DEFAULT_SEVERITY = 'critical'
def initialize(payload, project)
@payload = payload.to_h.with_indifferent_access
@project = project
end
def self.call(payload, project)
new(payload, project).call
end
def call
{
'annotations' => annotations,
'startsAt' => starts_at,
'endsAt' => ends_at
}.compact
end
private
attr_reader :payload, :project
def title
payload[:title].presence || DEFAULT_TITLE
end
def severity
payload[:severity].presence || DEFAULT_SEVERITY
end
def fingerprint
Gitlab::AlertManagement::Fingerprint.generate(payload[:fingerprint])
end
def annotations
primary_params
.reverse_merge(flatten_secondary_params)
.transform_values(&:presence)
.compact
end
def primary_params
{
'title' => title,
'description' => payload[:description],
'monitoring_tool' => payload[:monitoring_tool],
'service' => payload[:service],
'hosts' => hosts.presence,
'severity' => severity,
'fingerprint' => fingerprint,
'environment' => environment
}
end
def hosts
Array(payload[:hosts]).reject(&:blank?)
end
def current_time
Time.current.change(usec: 0).rfc3339
end
def starts_at
Time.parse(payload[:start_time].to_s).rfc3339
rescue ArgumentError
current_time
end
def ends_at
Time.parse(payload[:end_time].to_s).rfc3339
rescue ArgumentError
nil
end
def environment
environment_name = payload[:gitlab_environment_name]
return unless environment_name
EnvironmentsFinder.new(project, nil, { name: environment_name })
.find
&.first
end
def secondary_params
payload.except(:start_time, :end_time)
end
def flatten_secondary_params
Gitlab::Utils::SafeInlineHash.merge_keys!(secondary_params)
rescue ArgumentError
raise BadPayloadError, 'The payload is too big'
end
end
end
end
Gitlab::Alerting::NotificationPayloadParser.prepend_if_ee('EE::Gitlab::Alerting::NotificationPayloadParser')
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Alerting::NotificationPayloadParser do
let_it_be(:project) { build(:project) }
describe '.call' do
let(:starts_at) { Time.current.change(usec: 0) }
let(:ends_at) { Time.current.change(usec: 0) }
let(:payload) do
{
'title' => 'alert title',
'start_time' => starts_at.rfc3339,
'end_time' => ends_at.rfc3339,
'description' => 'Description',
'monitoring_tool' => 'Monitoring tool name',
'service' => 'Service',
'hosts' => ['gitlab.com'],
'severity' => 'low'
}
end
subject { described_class.call(payload, project) }
it 'returns Prometheus-like payload' do
is_expected.to eq(
{
'annotations' => {
'title' => 'alert title',
'description' => 'Description',
'monitoring_tool' => 'Monitoring tool name',
'service' => 'Service',
'hosts' => ['gitlab.com'],
'severity' => 'low'
},
'startsAt' => starts_at.rfc3339,
'endsAt' => ends_at.rfc3339
}
)
end
context 'when title is blank' do
before do
payload[:title] = ''
end
it 'sets a predefined title' do
expect(subject.dig('annotations', 'title')).to eq('New: Incident')
end
end
context 'when hosts attribute is a string' do
before do
payload[:hosts] = 'gitlab.com'
end
it 'returns hosts as an array of one element' do
expect(subject.dig('annotations', 'hosts')).to eq(['gitlab.com'])
end
end
context 'when the time is in unsupported format' do
before do
payload[:start_time] = 'invalid/date/format'
end
it 'sets startsAt to a current time in RFC3339 format' do
expect(subject['startsAt']).to eq(starts_at.rfc3339)
end
end
context 'when payload is blank' do
let(:payload) { {} }
it 'returns default parameters' do
is_expected.to match(
'annotations' => {
'title' => described_class::DEFAULT_TITLE,
'severity' => described_class::DEFAULT_SEVERITY
},
'startsAt' => starts_at.rfc3339
)
end
context 'when severity is blank' do
before do
payload[:severity] = ''
end
it 'sets severity to the default ' do
expect(subject.dig('annotations', 'severity')).to eq(described_class::DEFAULT_SEVERITY)
end
end
end
context 'with fingerprint' do
before do
payload[:fingerprint] = data
end
shared_examples 'fingerprint generation' do
it 'generates the fingerprint correctly' do
expect(result).to eq(Gitlab::AlertManagement::Fingerprint.generate(data))
end
end
context 'with blank fingerprint' do
it_behaves_like 'fingerprint generation' do
let(:data) { ' ' }
let(:result) { subject.dig('annotations', 'fingerprint') }
end
end
context 'with fingerprint given' do
it_behaves_like 'fingerprint generation' do
let(:data) { 'fingerprint' }
let(:result) { subject.dig('annotations', 'fingerprint') }
end
end
context 'with array fingerprint given' do
it_behaves_like 'fingerprint generation' do
let(:data) { [1, 'fingerprint', 'given'] }
let(:result) { subject.dig('annotations', 'fingerprint') }
end
end
end
context 'with environment' do
let(:environment) { create(:environment, project: project) }
before do
payload[:gitlab_environment_name] = environment.name
end
it 'sets the environment ' do
expect(subject.dig('annotations', 'environment')).to eq(environment)
end
end
context 'when payload attributes have blank lines' do
let(:payload) do
{
'title' => '',
'start_time' => '',
'end_time' => '',
'description' => '',
'monitoring_tool' => '',
'service' => '',
'hosts' => ['']
}
end
it 'returns default parameters' do
is_expected.to eq(
'annotations' => {
'title' => 'New: Incident',
'severity' => described_class::DEFAULT_SEVERITY
},
'startsAt' => starts_at.rfc3339
)
end
end
context 'when payload has secondary params' do
let(:payload) do
{
'description' => 'Description',
'additional' => {
'params' => {
'1' => 'Some value 1',
'2' => 'Some value 2',
'blank' => ''
}
}
}
end
it 'adds secondary params to annotations' do
is_expected.to eq(
'annotations' => {
'title' => 'New: Incident',
'severity' => described_class::DEFAULT_SEVERITY,
'description' => 'Description',
'additional.params.1' => 'Some value 1',
'additional.params.2' => 'Some value 2'
},
'startsAt' => starts_at.rfc3339
)
end
end
context 'when secondary params hash is too big' do
before do
allow(Gitlab::Utils::SafeInlineHash).to receive(:merge_keys!).and_raise(ArgumentError)
end
it 'catches and re-raises an error' do
expect { subject }.to raise_error Gitlab::Alerting::NotificationPayloadParser::BadPayloadError, 'The payload is too big'
end
end
end
end
......@@ -9,7 +9,6 @@ RSpec.describe IncidentManagement::ProcessAlertWorker do
describe '#perform' do
let_it_be(:started_at) { Time.now.rfc3339 }
let_it_be(:payload) { { 'title' => 'title', 'start_time' => started_at } }
let_it_be(:parsed_payload) { Gitlab::Alerting::NotificationPayloadParser.call(payload, project) }
let_it_be(:alert) { create(:alert_management_alert, project: project, payload: payload, started_at: started_at) }
let(:created_issue) { Issue.last! }
......@@ -68,7 +67,6 @@ RSpec.describe IncidentManagement::ProcessAlertWorker do
context 'prometheus alert' do
let_it_be(:alert) { create(:alert_management_alert, :prometheus, project: project, started_at: started_at) }
let_it_be(:parsed_payload) { alert.payload }
it_behaves_like 'creates issue successfully'
end
......
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