Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-07-20 15:26:25 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-07-20 15:26:25 +0300
commita09983ae35713f5a2bbb100981116d31ce99826e (patch)
tree2ee2af7bd104d57086db360a7e6d8c9d5d43667a /app/services/alert_management
parent18c5ab32b738c0b6ecb4d0df3994000482f34bd8 (diff)
Add latest changes from gitlab-org/gitlab@13-2-stable-ee
Diffstat (limited to 'app/services/alert_management')
-rw-r--r--app/services/alert_management/alerts/todo/create_service.rb51
-rw-r--r--app/services/alert_management/alerts/update_service.rb123
-rw-r--r--app/services/alert_management/create_alert_issue_service.rb65
-rw-r--r--app/services/alert_management/process_prometheus_alert_service.rb18
-rw-r--r--app/services/alert_management/update_alert_status_service.rb63
5 files changed, 215 insertions, 105 deletions
diff --git a/app/services/alert_management/alerts/todo/create_service.rb b/app/services/alert_management/alerts/todo/create_service.rb
new file mode 100644
index 00000000000..87af943fdc2
--- /dev/null
+++ b/app/services/alert_management/alerts/todo/create_service.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+module AlertManagement
+ module Alerts
+ module Todo
+ class CreateService
+ # @param alert [AlertManagement::Alert]
+ # @param current_user [User]
+ def initialize(alert, current_user)
+ @alert = alert
+ @current_user = current_user
+ end
+
+ def execute
+ return error_no_permissions unless allowed?
+
+ todos = TodoService.new.mark_todo(alert, current_user)
+ todo = todos&.first
+
+ return error_existing_todo unless todo
+
+ success(todo)
+ end
+
+ private
+
+ attr_reader :alert, :current_user
+
+ def allowed?
+ current_user&.can?(:update_alert_management_alert, alert)
+ end
+
+ def error(message)
+ ServiceResponse.error(payload: { alert: alert, todo: nil }, message: message)
+ end
+
+ def success(todo)
+ ServiceResponse.success(payload: { alert: alert, todo: todo })
+ end
+
+ def error_no_permissions
+ error(_('You have insufficient permissions to create a Todo for this alert'))
+ end
+
+ def error_existing_todo
+ error(_('You already have pending todo for this alert'))
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/alert_management/alerts/update_service.rb b/app/services/alert_management/alerts/update_service.rb
index ffabbb37289..0b7216cd9f8 100644
--- a/app/services/alert_management/alerts/update_service.rb
+++ b/app/services/alert_management/alerts/update_service.rb
@@ -12,17 +12,20 @@ module AlertManagement
@alert = alert
@current_user = current_user
@params = params
+ @param_errors = []
end
def execute
return error_no_permissions unless allowed?
- return error_no_updates if params.empty?
- filter_assignees
+ filter_params
+ return error_invalid_params if param_errors.any?
+
+ # Save old assignees for system notes
old_assignees = alert.assignees.to_a
if alert.update(params)
- process_assignement(old_assignees)
+ handle_changes(old_assignees: old_assignees)
success
else
@@ -32,16 +35,13 @@ module AlertManagement
private
- attr_reader :alert, :current_user, :params
+ attr_reader :alert, :current_user, :params, :param_errors
+ delegate :resolved?, to: :alert
def allowed?
current_user&.can?(:update_alert_management_alert, alert)
end
- def assignee_todo_allowed?
- assignee&.can?(:read_alert_management_alert, alert)
- end
-
def todo_service
strong_memoize(:todo_service) do
TodoService.new
@@ -60,39 +60,122 @@ module AlertManagement
error(_('You have no permissions'))
end
- def error_no_updates
- error(_('Please provide attributes to update'))
+ def error_invalid_params
+ error(param_errors.to_sentence)
+ end
+
+ def add_param_error(message)
+ param_errors << message
+ end
+
+ def filter_params
+ param_errors << _('Please provide attributes to update') if params.empty?
+
+ filter_status
+ filter_assignees
+ filter_duplicate
+ end
+
+ def handle_changes(old_assignees:)
+ handle_assignement(old_assignees) if params[:assignees]
+ handle_status_change if params[:status_event]
end
# ----- Assignee-related behavior ------
def filter_assignees
return if params[:assignees].nil?
- params[:assignees] = Array(assignee)
+ # Always take first assignee while multiple are not currently supported
+ params[:assignees] = Array(params[:assignees].first)
+
+ param_errors << _('Assignee has no permissions') if unauthorized_assignees?
end
- def assignee
- strong_memoize(:assignee) do
- # Take first assignee while multiple are not currently supported
- params[:assignees]&.first
- end
+ def unauthorized_assignees?
+ params[:assignees]&.any? { |user| !user.can?(:read_alert_management_alert, alert) }
end
- def process_assignement(old_assignees)
+ def handle_assignement(old_assignees)
assign_todo
add_assignee_system_note(old_assignees)
end
def assign_todo
- # Remove check in follow-up issue https://gitlab.com/gitlab-org/gitlab/-/issues/222672
- return unless assignee_todo_allowed?
-
todo_service.assign_alert(alert, current_user)
end
def add_assignee_system_note(old_assignees)
SystemNoteService.change_issuable_assignees(alert, alert.project, current_user, old_assignees)
end
+
+ # ------ Status-related behavior -------
+ def filter_status
+ return unless params[:status]
+
+ status_event = AlertManagement::Alert::STATUS_EVENTS[status_key]
+
+ unless status_event
+ param_errors << _('Invalid status')
+ return
+ end
+
+ params[:status_event] = status_event
+ end
+
+ def status_key
+ strong_memoize(:status_key) do
+ status = params.delete(:status)
+ AlertManagement::Alert::STATUSES.key(status)
+ end
+ end
+
+ def handle_status_change
+ add_status_change_system_note
+ resolve_todos if resolved?
+ end
+
+ def add_status_change_system_note
+ SystemNoteService.change_alert_status(alert, current_user)
+ end
+
+ def resolve_todos
+ todo_service.resolve_todos_for_target(alert, current_user)
+ end
+
+ def filter_duplicate
+ # Only need to check if changing to an open status
+ return unless params[:status_event] && AlertManagement::Alert::OPEN_STATUSES.include?(status_key)
+
+ param_errors << unresolved_alert_error if duplicate_alert?
+ end
+
+ def duplicate_alert?
+ return if alert.fingerprint.blank?
+
+ open_alerts.any? && open_alerts.exclude?(alert)
+ end
+
+ def open_alerts
+ strong_memoize(:open_alerts) do
+ AlertManagement::Alert.for_fingerprint(alert.project, alert.fingerprint).open
+ end
+ end
+
+ def unresolved_alert_error
+ _('An %{link_start}alert%{link_end} with the same fingerprint is already open. ' \
+ 'To change the status of this alert, resolve the linked alert.'
+ ) % open_alert_url_params
+ end
+
+ def open_alert_url_params
+ open_alert = open_alerts.first
+ alert_path = Gitlab::Routing.url_helpers.details_project_alert_management_path(alert.project, open_alert)
+
+ {
+ link_start: '<a href="%{url}">'.html_safe % { url: alert_path },
+ link_end: '</a>'.html_safe
+ }
+ end
end
end
end
diff --git a/app/services/alert_management/create_alert_issue_service.rb b/app/services/alert_management/create_alert_issue_service.rb
index beacd240b08..6ea3fd867ef 100644
--- a/app/services/alert_management/create_alert_issue_service.rb
+++ b/app/services/alert_management/create_alert_issue_service.rb
@@ -2,6 +2,8 @@
module AlertManagement
class CreateAlertIssueService
+ include Gitlab::Utils::StrongMemoize
+
# @param alert [AlertManagement::Alert]
# @param user [User]
def initialize(alert, user)
@@ -13,18 +15,20 @@ module AlertManagement
return error_no_permissions unless allowed?
return error_issue_already_exists if alert.issue
- result = create_issue(alert, user, alert_payload)
- @issue = result[:issue]
+ result = create_issue
+ issue = result.payload[:issue]
+
+ return error(result.message, issue) if result.error?
+ return error(object_errors(alert), issue) unless associate_alert_with_issue(issue)
- return error(result[:message]) if result[:status] == :error
- return error(alert.errors.full_messages.to_sentence) unless update_alert_issue_id
+ SystemNoteService.new_alert_issue(alert, issue, user)
- success
+ result
end
private
- attr_reader :alert, :user, :issue
+ attr_reader :alert, :user
delegate :project, to: :alert
@@ -32,29 +36,36 @@ module AlertManagement
user.can?(:create_issue, project)
end
- def create_issue(alert, user, alert_payload)
- ::IncidentManagement::CreateIssueService
- .new(project, alert_payload, user)
- .execute(skip_settings_check: true)
- end
+ def create_issue
+ label_result = find_or_create_incident_label
- def alert_payload
- if alert.prometheus?
- alert.payload
- else
- Gitlab::Alerting::NotificationPayloadParser.call(alert.payload.to_h)
- end
+ # Create an unlabelled issue if we couldn't create the label
+ # due to a race condition.
+ # See https://gitlab.com/gitlab-org/gitlab-foss/issues/65042
+ extra_params = label_result.success? ? { label_ids: [label_result.payload[:label].id] } : {}
+
+ issue = Issues::CreateService.new(
+ project,
+ user,
+ title: alert_presenter.title,
+ description: alert_presenter.issue_description,
+ **extra_params
+ ).execute
+
+ return error(object_errors(issue), issue) unless issue.valid?
+
+ success(issue)
end
- def update_alert_issue_id
+ def associate_alert_with_issue(issue)
alert.update(issue_id: issue.id)
end
- def success
+ def success(issue)
ServiceResponse.success(payload: { issue: issue })
end
- def error(message)
+ def error(message, issue = nil)
ServiceResponse.error(payload: { issue: issue }, message: message)
end
@@ -65,5 +76,19 @@ module AlertManagement
def error_no_permissions
error(_('You have no permissions'))
end
+
+ def alert_presenter
+ strong_memoize(:alert_presenter) do
+ alert.present
+ end
+ end
+
+ def find_or_create_incident_label
+ IncidentManagement::CreateIncidentLabelService.new(project, user).execute
+ end
+
+ def object_errors(object)
+ object.errors.full_messages.to_sentence
+ end
end
end
diff --git a/app/services/alert_management/process_prometheus_alert_service.rb b/app/services/alert_management/process_prometheus_alert_service.rb
index 90fcbd95e4b..573d3914c05 100644
--- a/app/services/alert_management/process_prometheus_alert_service.rb
+++ b/app/services/alert_management/process_prometheus_alert_service.rb
@@ -66,7 +66,11 @@ module AlertManagement
def process_resolved_alert_management_alert
return if am_alert.blank?
- return if am_alert.resolve(ends_at)
+
+ if am_alert.resolve(ends_at)
+ close_issue(am_alert.issue)
+ return
+ end
logger.warn(
message: 'Unable to update AlertManagement::Alert status to resolved',
@@ -75,12 +79,22 @@ module AlertManagement
)
end
+ def close_issue(issue)
+ return if issue.blank? || issue.closed?
+
+ Issues::CloseService
+ .new(project, User.alert_bot)
+ .execute(issue, system_note: false)
+
+ SystemNoteService.auto_resolve_prometheus_alert(issue, project, User.alert_bot) if issue.reset.closed?
+ end
+
def logger
@logger ||= Gitlab::AppLogger
end
def am_alert
- @am_alert ||= AlertManagement::Alert.for_fingerprint(project, gitlab_fingerprint).first
+ @am_alert ||= AlertManagement::Alert.not_resolved.for_fingerprint(project, gitlab_fingerprint).first
end
def bad_request
diff --git a/app/services/alert_management/update_alert_status_service.rb b/app/services/alert_management/update_alert_status_service.rb
deleted file mode 100644
index a7ebddb82e0..00000000000
--- a/app/services/alert_management/update_alert_status_service.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-# frozen_string_literal: true
-
-module AlertManagement
- class UpdateAlertStatusService
- include Gitlab::Utils::StrongMemoize
-
- # @param alert [AlertManagement::Alert]
- # @param user [User]
- # @param status [Integer] Must match a value from AlertManagement::Alert::STATUSES
- def initialize(alert, user, status)
- @alert = alert
- @user = user
- @status = status
- end
-
- def execute
- return error_no_permissions unless allowed?
- return error_invalid_status unless status_key
-
- if alert.update(status_event: status_event)
- success
- else
- error(alert.errors.full_messages.to_sentence)
- end
- end
-
- private
-
- attr_reader :alert, :user, :status
-
- delegate :project, to: :alert
-
- def allowed?
- user.can?(:update_alert_management_alert, project)
- end
-
- def status_key
- strong_memoize(:status_key) do
- AlertManagement::Alert::STATUSES.key(status)
- end
- end
-
- def status_event
- AlertManagement::Alert::STATUS_EVENTS[status_key]
- end
-
- def success
- ServiceResponse.success(payload: { alert: alert })
- end
-
- def error_no_permissions
- error(_('You have no permissions'))
- end
-
- def error_invalid_status
- error(_('Invalid status'))
- end
-
- def error(message)
- ServiceResponse.error(payload: { alert: alert }, message: message)
- end
- end
-end