diff options
author | syasonik <syasonik@gitlab.com> | 2019-08-15 16:58:13 +0300 |
---|---|---|
committer | syasonik <syasonik@gitlab.com> | 2019-09-06 16:13:34 +0300 |
commit | ca082c1dce9838b7a2cc8afe1582cb22a3f06134 (patch) | |
tree | 1c0e95dff0782323c6a9d126b3db5d93c0cfc4a8 | |
parent | 3441092b3840cecb913068542ee9242ea19a2017 (diff) | |
download | gitlab-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.
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 |