summaryrefslogtreecommitdiff
path: root/app/services/prometheus/create_default_alerts_service.rb
blob: 53baf6a650e092a134a6f6185c6eb7bdcd18a973 (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
# frozen_string_literal: true

module Prometheus
  class CreateDefaultAlertsService < BaseService
    include Gitlab::Utils::StrongMemoize

    attr_reader :project

    DEFAULT_ALERTS = [
      {
        identifier: 'response_metrics_nginx_ingress_16_http_error_rate',
        operator: 'gt',
        threshold: 0.1
      },
      {
        identifier: 'response_metrics_nginx_ingress_http_error_rate',
        operator: 'gt',
        threshold: 0.1
      },
      {
        identifier: 'response_metrics_nginx_http_error_percentage',
        operator: 'gt',
        threshold: 0.1
      }
    ].freeze

    def initialize(project:)
      @project = project
    end

    def execute
      return ServiceResponse.error(message: 'Invalid project') unless project
      return ServiceResponse.error(message: 'Invalid environment') unless environment

      create_alerts
      schedule_prometheus_update

      ServiceResponse.success
    end

    private

    def create_alerts
      DEFAULT_ALERTS.each do |alert_hash|
        identifier = alert_hash[:identifier]
        next if alerts_by_identifier(environment).key?(identifier)

        metric = metrics_by_identifier[identifier]
        next unless metric

        create_alert(alert: alert_hash, metric: metric)
      end
    end

    def schedule_prometheus_update
      return unless prometheus_application

      ::Clusters::Applications::ScheduleUpdateService.new(prometheus_application, project).execute
    end

    def prometheus_application
      environment.cluster_prometheus_adapter
    end

    def metrics_by_identifier
      strong_memoize(:metrics_by_identifier) do
        metric_identifiers = DEFAULT_ALERTS.map { |alert| alert[:identifier] }

        PrometheusMetricsFinder
          .new(identifier: metric_identifiers, common: true)
          .execute
          .index_by(&:identifier)
      end
    end

    def alerts_by_identifier(environment)
      strong_memoize(:alerts_by_identifier) do
        Projects::Prometheus::AlertsFinder
          .new(project: project, metric: metrics_by_identifier.values, environment: environment)
          .execute
          .index_by { |alert| alert.prometheus_metric.identifier }
      end
    end

    def environment
      strong_memoize(:environment) do
        EnvironmentsFinder.new(project, nil, name: 'production').find.first ||
          project.environments.first
      end
    end

    def create_alert(alert:, metric:)
      PrometheusAlert.create!(
        project: project,
        prometheus_metric: metric,
        environment: environment,
        threshold: alert[:threshold],
        operator: alert[:operator]
      )
    rescue ActiveRecord::RecordNotUnique
      # Ignore duplicate creations although it unlikely to happen
    end
  end
end