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

module Projects
  module Alerting
    class NotifyService < BaseService
      include Gitlab::Utils::StrongMemoize
      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)

        process_alert
        return bad_request unless alert.persisted?

        process_incident_issues if process_issues?
        send_alert_email if send_email?

        ServiceResponse.success
      end

      private

      delegate :alerts_service, :alerts_service_activated?, to: :project

      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,
          alert.monitoring_tool || 'Generic Alert Endpoint'
        )
      end

      def process_incident_issues
        return if alert.issue

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

      def send_alert_email
        notification_service
          .async
          .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 valid_payload_size?
        Gitlab::Utils::DeepSize.new(params).valid?
      end

      def valid_token?(token)
        token == alerts_service.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