summaryrefslogtreecommitdiff
path: root/app/services/alert_management/process_prometheus_alert_service.rb
blob: 753162bfdbf502fedfd478714189fb0c89366da4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# frozen_string_literal: true

module AlertManagement
  class ProcessPrometheusAlertService
    include BaseServiceUtility
    include Gitlab::Utils::StrongMemoize
    include ::IncidentManagement::Settings

    def initialize(project, payload)
      @project = project
      @payload = payload
    end

    def execute
      return bad_request unless incoming_payload.has_required_attributes?

      process_alert_management_alert
      return bad_request unless alert.persisted?

      process_incident_issues if process_issues?
      send_alert_email if send_email?

      ServiceResponse.success
    end

    private

    attr_reader :project, :payload

    def process_alert_management_alert
      if incoming_payload.resolved?
        process_resolved_alert_management_alert
      else
        process_firing_alert_management_alert
      end
    end

    def process_firing_alert_management_alert
      if alert.persisted?
        alert.register_new_event!
        reset_alert_management_alert_status
      else
        create_alert_management_alert
      end
    end

    def reset_alert_management_alert_status
      return if alert.trigger

      logger.warn(
        message: 'Unable to update AlertManagement::Alert status to triggered',
        project_id: project.id,
        alert_id: alert.id
      )
    end

    def create_alert_management_alert
      if alert.save
        alert.execute_services
        SystemNoteService.create_new_alert(alert, Gitlab::AlertManagement::Payload::MONITORING_TOOLS[:prometheus])
        return
      end

      logger.warn(
        message: 'Unable to create AlertManagement::Alert',
        project_id: project.id,
        alert_errors: alert.errors.messages
      )
    end

    def process_resolved_alert_management_alert
      return unless alert.persisted?
      return unless auto_close_incident?

      if alert.resolve(incoming_payload.ends_at)
        close_issue(alert.issue)
        return
      end

      logger.warn(
        message: 'Unable to update AlertManagement::Alert status to resolved',
        project_id: project.id,
        alert_id: alert.id
      )
    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 process_incident_issues
      return if alert.issue || alert.resolved?

      IncidentManagement::ProcessAlertWorker.perform_async(nil, nil, alert.id)
    end

    def send_alert_email
      notification_service
        .async
        .prometheus_alerts_fired(project, [alert])
    end

    def logger
      @logger ||= Gitlab::AppLogger
    end

    def alert
      strong_memoize(:alert) do
        existing_alert || new_alert
      end
    end

    def existing_alert
      strong_memoize(:existing_alert) do
        AlertManagement::Alert.not_resolved.for_fingerprint(project, incoming_payload.gitlab_fingerprint).first
      end
    end

    def new_alert
      strong_memoize(:new_alert) do
        AlertManagement::Alert.new(
          **incoming_payload.alert_params,
          ended_at: nil
        )
      end
    end

    def incoming_payload
      strong_memoize(:incoming_payload) do
        Gitlab::AlertManagement::Payload.parse(
          project,
          payload,
          monitoring_tool: Gitlab::AlertManagement::Payload::MONITORING_TOOLS[:prometheus]
        )
      end
    end

    def bad_request
      ServiceResponse.error(message: 'Bad Request', http_status: :bad_request)
    end
  end
end