Commit 2b92cf29 authored by Kamil Trzciński's avatar Kamil Trzciński

Extend prometheus metrics for EE

parent ec870aca
......@@ -82,7 +82,7 @@ module Projects
end
def alert
@alert ||= project.prometheus_alerts.find_by(prometheus_metric: params[:id]) || render_404
@alert ||= project.prometheus_alerts.find_by(prometheus_metric_id: params[:id]) || render_404
end
def application
......
module EE
module PrometheusMetric
extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override
prepended do
has_many :prometheus_alerts, inverse_of: :prometheus_metric
end
end
end
......@@ -7,11 +7,14 @@ class PrometheusAlert < ActiveRecord::Base
belongs_to :environment, required: true, validate: true, inverse_of: :prometheus_alerts
belongs_to :project, required: true, validate: true, inverse_of: :prometheus_alerts
belongs_to :prometheus_metric, required: true, validate: true, inverse_of: :prometheus_alert
belongs_to :prometheus_metric, required: true, validate: true, inverse_of: :prometheus_alerts
after_save :clear_prometheus_adapter_cache!
after_destroy :clear_prometheus_adapter_cache!
validate :require_valid_environment_project!
validate :require_valid_metric_project!
enum operator: [:lt, :eq, :gt]
delegate :title, :query, to: :prometheus_metric
......@@ -45,4 +48,17 @@ class PrometheusAlert < ActiveRecord::Base
def clear_prometheus_adapter_cache!
environment.clear_prometheus_reactive_cache!(:additional_metrics_environment)
end
def require_valid_environment_project!
return if project == environment.project
errors.add(:environment, "invalid project")
end
def require_valid_metric_project!
return if prometheus_metric.default?
return if project == prometheus_metric.project
errors.add(:prometheus_metric, "invalid project")
end
end
class PrometheusMetric < ActiveRecord::Base
belongs_to :project, required: true, validate: true, inverse_of: :prometheus_metrics
has_one :prometheus_alert, inverse_of: :prometheus_metric
enum group: [:business, :response, :system]
validates :title, presence: true
validates :query, presence: true
validates :group, presence: true
validates :y_label, presence: true
validates :unit, presence: true
GROUP_TITLES = {
business: _('Business metrics (Custom)'),
response: _('Response metrics (Custom)'),
system: _('System metrics (Custom)')
}.freeze
def group_title
GROUP_TITLES[group.to_sym]
end
def to_query_metric
Gitlab::Prometheus::Metric.new(id: id, title: title, required_metrics: [], weight: 0, y_label: y_label, queries: build_queries)
end
private
def build_queries
[
{
query_range: query,
unit: unit,
label: legend
}
]
end
end
......@@ -2,6 +2,8 @@ module Projects
module Prometheus
module Metrics
class BaseService
include StrongMemoize
def initialize(metric, params = {})
@metric = metric
@project = metric.project
......@@ -13,15 +15,25 @@ module Projects
attr_reader :metric, :project, :params
def application
metric.prometheus_alert.environment.cluster_prometheus_adapter
alert.environment.cluster_prometheus_adapter
end
def schedule_alert_update
::Clusters::Applications::ScheduleUpdateService.new(application, project).execute
return unless alert
return unless alert.environment
::Clusters::Applications::ScheduleUpdateService.new(
alert.environment.cluster_prometheus_adapter, project).execute
end
def alert
strong_memoize(:alert) do
metric.prometheus_alerts.find_by(project: project)
end
end
def has_alert?
metric.prometheus_alert.present?
alert.present?
end
end
end
......
class CreatePrometheusMetrics < ActiveRecord::Migration
DOWNTIME = false
def change
create_table :prometheus_metrics do |t|
t.references :project, index: true, foreign_key: { on_delete: :cascade }, null: false
t.string :title, null: false
t.string :query, null: false
t.string :y_label
t.string :unit
t.string :legend
t.integer :group, null: false, index: true
t.timestamps_with_timezone null: false
end
end
end
class AllowManyPrometheusAlerts < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :prometheus_alerts, [:project_id, :prometheus_metric_id], unique: true
remove_concurrent_index :prometheus_alerts, :prometheus_metric_id, unique: true
end
def down
add_concurrent_index :prometheus_alerts, :prometheus_metric_id, unique: true
remove_concurrent_index :prometheus_alerts, [:project_id, :prometheus_metric_id], unique: true
end
end
module EE
module Gitlab
module Prometheus
module MetricGroup
extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override
prepended do
def self.custom_metrics(project)
project.prometheus_metrics.all.group_by(&:group_title).map do |name, metrics|
MetricGroup.new(name: name, priority: 0, metrics: metrics.map(&:to_query_metric))
end
end
override :for_project
def self.for_project(project)
super + custom_metrics(project)
end
end
end
end
end
end
FactoryBot.define do
factory :prometheus_metric, class: PrometheusMetric do
title 'title'
query 'avg(metric)'
y_label 'y_label'
unit 'm/s'
group :business
project
legend 'legend'
end
end
require 'spec_helper'
describe PrometheusMetric do
subject { build(:prometheus_metric) }
it { is_expected.to belong_to(:project) }
it { is_expected.to validate_presence_of(:title) }
it { is_expected.to validate_presence_of(:query) }
it { is_expected.to validate_presence_of(:group) }
describe '#group_title' do
shared_examples 'group_title' do |group, title|
subject { build(:prometheus_metric, group: group).group_title }
it "returns text #{title} for group #{group}" do
expect(subject).to eq(title)
end
end
it_behaves_like 'group_title', :business, 'Business metrics (Custom)'
it_behaves_like 'group_title', :response, 'Response metrics (Custom)'
it_behaves_like 'group_title', :system, 'System metrics (Custom)'
end
describe '#to_query_metric' do
it 'converts to queryable metric object' do
expect(subject.to_query_metric).to be_instance_of(Gitlab::Prometheus::Metric)
end
it 'queryable metric object has title' do
expect(subject.to_query_metric.title).to eq(subject.title)
end
it 'queryable metric object has y_label' do
expect(subject.to_query_metric.y_label).to eq(subject.y_label)
end
it 'queryable metric has no required_metric' do
expect(subject.to_query_metric.required_metrics).to eq([])
end
it 'queryable metric has weight 0' do
expect(subject.to_query_metric.weight).to eq(0)
end
it 'queryable metrics has query description' do
queries = [
{
query_range: subject.query,
unit: subject.unit,
label: subject.legend
}
]
expect(subject.to_query_metric.queries).to eq(queries)
end
end
end
module Gitlab
module Prometheus
class MetricGroup
prepend EE::Gitlab::Prometheus::MetricGroup
include ActiveModel::Model
attr_accessor :name, :priority, :metrics
......@@ -13,14 +14,9 @@ module Gitlab
end
end
def self.for_project(project)
common_metrics + custom_metrics(project)
end
def self.custom_metrics(project)
project.prometheus_metrics.all.group_by(&:group_title).map do |name, metrics|
MetricGroup.new(name: name, priority: 0, metrics: metrics.map(&:to_query_metric))
end
# EE only
def self.for_project(_)
common_metrics
end
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment