summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/finders/prometheus_metrics_finder.rb92
-rw-r--r--app/models/prometheus_metric.rb7
-rw-r--r--app/services/metrics/dashboard/custom_metric_embed_service.rb7
-rw-r--r--lib/gitlab/metrics/dashboard/stages/common_metrics_inserter.rb2
-rw-r--r--lib/gitlab/metrics/dashboard/stages/project_metrics_inserter.rb2
-rw-r--r--lib/gitlab/prometheus/metric_group.rb18
-rw-r--r--lib/gitlab/prometheus/queries/knative_invocation_query.rb12
-rw-r--r--spec/finders/prometheus_metrics_finder_spec.rb122
-rw-r--r--spec/lib/gitlab/prometheus/queries/knative_invocation_query_spec.rb4
9 files changed, 247 insertions, 19 deletions
diff --git a/app/finders/prometheus_metrics_finder.rb b/app/finders/prometheus_metrics_finder.rb
new file mode 100644
index 00000000000..579862b6b76
--- /dev/null
+++ b/app/finders/prometheus_metrics_finder.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+class PrometheusMetricsFinder
+ ACCEPTED_PARAMS = [
+ :project,
+ :group,
+ :title,
+ :y_label,
+ :identifier,
+ :id,
+ :common,
+ :ordered
+ ].freeze
+
+ def initialize(params = {})
+ validate_params!(params)
+
+ @params = params
+ end
+
+ # @return [PrometheusMetric, PrometheusMetric::ActiveRecord_Relation]
+ def execute
+ metrics = by_project(::PrometheusMetric)
+ metrics = by_group(metrics)
+ metrics = by_title(metrics)
+ metrics = by_y_label(metrics)
+ metrics = common(metrics)
+ metrics = ordered(metrics)
+ metrics = find_identifier(metrics)
+ metrics = find_id(metrics)
+
+ metrics
+ end
+
+ private
+
+ attr_reader :params
+
+ def by_project(metrics)
+ return metrics unless params[:project]
+
+ metrics.for_project(params[:project])
+ end
+
+ def by_group(metrics)
+ return metrics unless params[:group]
+
+ metrics.for_group(params[:group])
+ end
+
+ def by_title(metrics)
+ return metrics unless params[:title]
+
+ metrics.for_title(params[:title])
+ end
+
+ def by_y_label(metrics)
+ return metrics unless params[:y_label]
+
+ metrics.for_y_label(params[:y_label])
+ end
+
+ def common(metrics)
+ return metrics unless params[:common]
+
+ metrics.common
+ end
+
+ def ordered(metrics)
+ return metrics unless params[:ordered]
+
+ metrics.order_created_at_asc
+ end
+
+ def find_identifier(metrics)
+ return metrics unless params[:identifier]
+
+ metrics.for_identifier(params[:identifier]).first
+ end
+
+ def find_id(metrics)
+ return metrics unless params[:id]
+
+ metrics.find(params[:id])
+ end
+
+ def validate_params!(params)
+ raise ArgumentError, "Please provide one or more of: #{ACCEPTED_PARAMS}" if params.blank?
+ raise ArgumentError, 'Only one of :identifier, :id is permitted' if params[:identifier] && params[:id]
+ raise ArgumentError, ':identifier must be scoped to a :project or :common' if params[:identifier] && !(params[:project] || params[:common])
+ end
+end
diff --git a/app/models/prometheus_metric.rb b/app/models/prometheus_metric.rb
index c7786500c5c..b29c054cb4d 100644
--- a/app/models/prometheus_metric.rb
+++ b/app/models/prometheus_metric.rb
@@ -14,7 +14,14 @@ class PrometheusMetric < ApplicationRecord
validates :project, presence: true, unless: :common?
validates :project, absence: true, if: :common?
+ scope :for_project, -> (project) { where(project_id: project) }
+ scope :for_group, -> (group) { where(group: group) }
+ scope :for_title, -> (title) { where(title: title) }
+ scope :for_y_label, -> (y_label) { where(y_label: y_label) }
+ scope :for_identifier, -> (identifier) { where(identifier: identifier) }
+ scope :for_identifier, -> (identifier) { where(identifier: identifier) }
scope :common, -> { where(common: true) }
+ scope :order_created_at_asc, -> { reorder(created_at: :asc) }
def priority
group_details(group).fetch(:priority)
diff --git a/app/services/metrics/dashboard/custom_metric_embed_service.rb b/app/services/metrics/dashboard/custom_metric_embed_service.rb
index 50f070989fc..f543bd39e84 100644
--- a/app/services/metrics/dashboard/custom_metric_embed_service.rb
+++ b/app/services/metrics/dashboard/custom_metric_embed_service.rb
@@ -77,15 +77,14 @@ module Metrics
# There may be multiple metrics, but they should be
# displayed in a single panel/chart.
# @return [ActiveRecord::AssociationRelation<PromtheusMetric>]
- # rubocop: disable CodeReuse/ActiveRecord
def metrics
- project.prometheus_metrics.where(
+ PrometheusMetricsFinder.new(
+ project: project,
group: group_key,
title: title,
y_label: y_label
- )
+ ).execute
end
- # rubocop: enable CodeReuse/ActiveRecord
# Returns a symbol representing the group that
# the dashboard's group title belongs to.
diff --git a/lib/gitlab/metrics/dashboard/stages/common_metrics_inserter.rb b/lib/gitlab/metrics/dashboard/stages/common_metrics_inserter.rb
index 188912bedb4..62479ed6de4 100644
--- a/lib/gitlab/metrics/dashboard/stages/common_metrics_inserter.rb
+++ b/lib/gitlab/metrics/dashboard/stages/common_metrics_inserter.rb
@@ -9,7 +9,7 @@ module Gitlab
# find a corresponding database record. If found,
# includes the record's id in the dashboard config.
def transform!
- common_metrics = ::PrometheusMetric.common
+ common_metrics = ::PrometheusMetricsFinder.new(common: true).execute
for_metrics do |metric|
metric_record = common_metrics.find { |m| m.identifier == metric[:id] }
diff --git a/lib/gitlab/metrics/dashboard/stages/project_metrics_inserter.rb b/lib/gitlab/metrics/dashboard/stages/project_metrics_inserter.rb
index 643be309992..c0f67d445f8 100644
--- a/lib/gitlab/metrics/dashboard/stages/project_metrics_inserter.rb
+++ b/lib/gitlab/metrics/dashboard/stages/project_metrics_inserter.rb
@@ -9,7 +9,7 @@ module Gitlab
# config. If there are no project-specific metrics,
# this will have no effect.
def transform!
- project.prometheus_metrics.each do |project_metric|
+ PrometheusMetricsFinder.new(project: project).execute.each do |project_metric|
group = find_or_create_panel_group(dashboard[:panel_groups], project_metric)
panel = find_or_create_panel(group[:panels], project_metric)
find_or_create_metric(panel[:metrics], project_metric)
diff --git a/lib/gitlab/prometheus/metric_group.rb b/lib/gitlab/prometheus/metric_group.rb
index 394556e8708..6263750e4d4 100644
--- a/lib/gitlab/prometheus/metric_group.rb
+++ b/lib/gitlab/prometheus/metric_group.rb
@@ -10,13 +10,17 @@ module Gitlab
validates :name, :priority, :metrics, presence: true
def self.common_metrics
- all_groups = ::PrometheusMetric.common.group_by(&:group_title).map do |name, metrics|
- MetricGroup.new(
- name: name,
- priority: metrics.map(&:priority).max,
- metrics: metrics.map(&:to_query_metric)
- )
- end
+ all_groups = ::PrometheusMetricsFinder
+ .new(common: true)
+ .execute
+ .group_by(&:group_title)
+ .map do |name, metrics|
+ MetricGroup.new(
+ name: name,
+ priority: metrics.map(&:priority).max,
+ metrics: metrics.map(&:to_query_metric)
+ )
+ end
all_groups.sort_by(&:priority).reverse
end
diff --git a/lib/gitlab/prometheus/queries/knative_invocation_query.rb b/lib/gitlab/prometheus/queries/knative_invocation_query.rb
index 2691abe46d6..3d10cadfb5c 100644
--- a/lib/gitlab/prometheus/queries/knative_invocation_query.rb
+++ b/lib/gitlab/prometheus/queries/knative_invocation_query.rb
@@ -7,11 +7,13 @@ module Gitlab
include QueryAdditionalMetrics
def query(serverless_function_id)
- PrometheusMetric
- .find_by_identifier(:system_metrics_knative_function_invocation_count)
- .to_query_metric.tap do |q|
- q.queries[0][:result] = run_query(q.queries[0][:query_range], context(serverless_function_id))
- end
+ PrometheusMetricsFinder
+ .new(identifier: :system_metrics_knative_function_invocation_count, common: true)
+ .execute
+ .to_query_metric
+ .tap do |q|
+ q.queries[0][:result] = run_query(q.queries[0][:query_range], context(serverless_function_id))
+ end
end
protected
diff --git a/spec/finders/prometheus_metrics_finder_spec.rb b/spec/finders/prometheus_metrics_finder_spec.rb
new file mode 100644
index 00000000000..f76acfb3a32
--- /dev/null
+++ b/spec/finders/prometheus_metrics_finder_spec.rb
@@ -0,0 +1,122 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe PrometheusMetricsFinder do
+ describe '#execute' do
+ let(:finder) { described_class.new(params) }
+ let(:params) { {} }
+
+ subject { finder.execute }
+
+ context 'with params' do
+ set(:project) { create(:project) }
+ set(:project_metric) { create(:prometheus_metric, project: project) }
+ set(:common_metric) { create(:prometheus_metric, :common) }
+ set(:unique_metric) do
+ create(
+ :prometheus_metric,
+ :common,
+ title: 'Unique title',
+ y_label: 'Unique y_label',
+ group: :kubernetes,
+ identifier: 'identifier',
+ created_at: 5.minutes.ago
+ )
+ end
+
+ context 'with project' do
+ let(:params) { { project: project } }
+
+ it { is_expected.to eq([project_metric]) }
+ end
+
+ context 'with group' do
+ let(:params) { { group: project_metric.group } }
+
+ it { is_expected.to contain_exactly(common_metric, project_metric) }
+ end
+
+ context 'with title' do
+ let(:params) { { title: project_metric.title } }
+
+ it { is_expected.to contain_exactly(project_metric, common_metric) }
+ end
+
+ context 'with y_label' do
+ let(:params) { { y_label: project_metric.y_label } }
+
+ it { is_expected.to contain_exactly(project_metric, common_metric) }
+ end
+
+ context 'common' do
+ let(:params) { { common: true } }
+
+ it { is_expected.to contain_exactly(common_metric, unique_metric) }
+ end
+
+ context 'ordered' do
+ let(:params) { { ordered: true } }
+
+ it { is_expected.to eq([unique_metric, project_metric, common_metric]) }
+ end
+
+ context 'find_indentifier' do
+ let(:params) { { identifier: unique_metric.identifier, common: true } }
+
+ it { is_expected.to eq(unique_metric) }
+
+ context 'without identifier scope' do
+ let(:params) { { identifier: unique_metric.identifier } }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(
+ ArgumentError,
+ ':identifier must be scoped to a :project or :common'
+ )
+ end
+ end
+
+ context 'with id' do
+ let(:params) { { id: 14, identifier: 'string' } }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(
+ ArgumentError,
+ 'Only one of :identifier, :id is permitted'
+ )
+ end
+ end
+ end
+
+ context 'find_id' do
+ let(:params) { { id: common_metric.id } }
+
+ it { is_expected.to eq(common_metric) }
+ end
+
+ context 'with multiple params' do
+ let(:params) do
+ {
+ group: project_metric.group,
+ title: project_metric.title,
+ y_label: project_metric.y_label,
+ common: true,
+ order_by: { created_at: :desc }
+ }
+ end
+
+ it { is_expected.to eq([common_metric]) }
+ end
+ end
+
+ context 'without params' do
+ it 'raises an error' do
+ expect { subject }.to raise_error(
+ ArgumentError,
+ 'Please provide one or more of: [:project, :group, :title, :y_label, :identifier, :id, :common, :ordered]'
+ )
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/prometheus/queries/knative_invocation_query_spec.rb b/spec/lib/gitlab/prometheus/queries/knative_invocation_query_spec.rb
index 7f6283715f2..e721dbe4b33 100644
--- a/spec/lib/gitlab/prometheus/queries/knative_invocation_query_spec.rb
+++ b/spec/lib/gitlab/prometheus/queries/knative_invocation_query_spec.rb
@@ -13,7 +13,9 @@ describe Gitlab::Prometheus::Queries::KnativeInvocationQuery do
context 'verify queries' do
before do
- allow(PrometheusMetric).to receive(:find_by_identifier).and_return(create(:prometheus_metric, query: prometheus_istio_query('test-name', 'test-ns')))
+ allow_any_instance_of(PrometheusMetricsFinder)
+ .to receive(:execute)
+ .and_return(create(:prometheus_metric, query: prometheus_istio_query('test-name', 'test-ns')))
allow(client).to receive(:query_range)
end