diff options
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 |