summaryrefslogtreecommitdiff
path: root/app/services/projects/alerting/notify_service.rb
blob: 014fb0e3ed307d73b41bbcc99fbbf64a83e4b0db (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
# frozen_string_literal: true

module Projects
  module Alerting
    class NotifyService
      include BaseServiceUtility
      include Gitlab::Utils::StrongMemoize
      include ::IncidentManagement::Settings

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

      def execute(token, integration = nil)
        @integration = integration

        return bad_request unless valid_payload_size?
        return forbidden unless active_integration?
        return unauthorized unless valid_token?(token)

        process_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, :integration

      def process_alert
        if alert.persisted?
          process_existing_alert
        else
          create_alert
        end
      end

      def process_existing_alert
        if incoming_payload.ends_at.present?
          process_resolved_alert
        else
          alert.register_new_event!
        end

        alert
      end

      def process_resolved_alert
        return unless auto_close_incident?

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

        alert
      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 create_alert
        return unless alert.save

        alert.execute_services
        SystemNoteService.create_new_alert(alert, notification_source)
      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 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, payload.to_h)
        end
      end

      def notification_source
        alert.monitoring_tool || integration&.name || 'Generic Alert Endpoint'
      end

      def valid_payload_size?
        Gitlab::Utils::DeepSize.new(payload).valid?
      end

      def active_integration?
        integration&.active?
      end

      def valid_token?(token)
        token == integration.token
      end

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

      def unauthorized
        ServiceResponse.error(message: 'Unauthorized', http_status: :unauthorized)
      end

      def forbidden
        ServiceResponse.error(message: 'Forbidden', http_status: :forbidden)
      end
    end
  end
end