summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsyasonik <syasonik@gitlab.com>2019-08-15 16:58:13 +0300
committersyasonik <syasonik@gitlab.com>2019-09-06 16:13:34 +0300
commitca082c1dce9838b7a2cc8afe1582cb22a3f06134 (patch)
tree1c0e95dff0782323c6a9d126b3db5d93c0cfc4a8
parent3441092b3840cecb913068542ee9242ea19a2017 (diff)
downloadgitlab-ce-add-metrics-finder.tar.gz
Add finder for prometheus metricsadd-metrics-finder
Adds a finder for the PrometheusMetric class to create a unified way to retrieve instances of the class. Note that this should not be confused with the PrometheusMetric model used for importing common metrics into the database. Updates all retrievals of PrometheusMetric instances to use the new PrometheusMetricFinder class.
-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