diff options
author | syasonik <syasonik@gitlab.com> | 2019-04-25 11:03:50 +0800 |
---|---|---|
committer | syasonik <syasonik@gitlab.com> | 2019-04-25 13:35:05 +0800 |
commit | 4a5c48c47cccd8ce2b6bd6912ecff925122778f0 (patch) | |
tree | d67a72b736ed069afd1c11fa14b4a789bc3d5b0c | |
parent | dde709912f0c186d569ec5fb3d1986b8184d81f1 (diff) | |
download | gitlab-ce-4a5c48c47cccd8ce2b6bd6912ecff925122778f0.tar.gz |
Move MetricsDashboard to Metrics::Dashboard
20 files changed, 312 insertions, 304 deletions
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 30cbe35edf5..1f619c8fd2f 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -160,7 +160,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController def metrics_dashboard return render_403 unless Feature.enabled?(:environment_metrics_use_prometheus_endpoint, @project) - result = Gitlab::MetricsDashboard::Service.new(@project, @current_user, environment: environment).get_dashboard + result = Gitlab::Metrics::Dashboard::Service.new(@project, @current_user, environment: environment).get_dashboard respond_to do |format| if result[:status] == :success diff --git a/lib/gitlab/metrics/dashboard/processor.rb b/lib/gitlab/metrics/dashboard/processor.rb new file mode 100644 index 00000000000..36de1d033f2 --- /dev/null +++ b/lib/gitlab/metrics/dashboard/processor.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Gitlab + module Metrics + module Dashboard + # Responsible for processesing a dashboard hash, inserting + # relevant DB records & sorting for proper rendering in + # the UI. These includes shared metric info, custom metrics + # info, and alerts (only in EE). + class Processor + SEQUENCE = [ + Stages::CommonMetricsInserter, + Stages::ProjectMetricsInserter, + Stages::Sorter + ].freeze + + def initialize(project, environment) + @project = project + @environment = environment + end + + # Returns a new dashboard hash with the results of + # running transforms on the dashboard. + def process(dashboard) + dashboard = dashboard.deep_symbolize_keys + + stage_params = [@project, @environment] + sequence.each { |stage| stage.new(*stage_params).transform!(dashboard) } + + dashboard + end + + private + + def sequence + SEQUENCE + end + end + end + end +end diff --git a/lib/gitlab/metrics/dashboard/service.rb b/lib/gitlab/metrics/dashboard/service.rb new file mode 100644 index 00000000000..966d7279aef --- /dev/null +++ b/lib/gitlab/metrics/dashboard/service.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +# Fetches the metrics dashboard layout and supplemented the output with DB info. +module Gitlab + module Metrics + module Dashboard + class Service < ::BaseService + SYSTEM_DASHBOARD_NAME = 'common_metrics' + SYSTEM_DASHBOARD_PATH = Rails.root.join('config', 'prometheus', "#{SYSTEM_DASHBOARD_NAME}.yml") + + # Returns a DB-supplemented json representation of a dashboard config file. + def get_dashboard + dashboard_string = Rails.cache.fetch(cache_key) { system_dashboard } + + dashboard = process_dashboard(dashboard_string) + + success(dashboard: dashboard) + rescue Gitlab::Metrics::Dashboard::Stages::BaseStage::DashboardLayoutError => e + error(e.message, :unprocessable_entity) + end + + private + + # Returns the base metrics shipped with every GitLab service. + def system_dashboard + YAML.load_file(SYSTEM_DASHBOARD_PATH) + end + + def cache_key + "metrics_dashboard_#{SYSTEM_DASHBOARD_NAME}" + end + + # Returns a new dashboard Hash, supplemented with DB info + def process_dashboard(dashboard) + Processor.new(project, params[:environment]).process(dashboard) + end + end + end + end +end diff --git a/lib/gitlab/metrics/dashboard/stages/base_stage.rb b/lib/gitlab/metrics/dashboard/stages/base_stage.rb new file mode 100644 index 00000000000..daecb2698ae --- /dev/null +++ b/lib/gitlab/metrics/dashboard/stages/base_stage.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +module Gitlab + module Metrics + module Dashboard + module Stages + class BaseStage + DashboardLayoutError = Class.new(StandardError) + + DEFAULT_PANEL_TYPE = 'area-chart' + + attr_reader :project, :environment + + def initialize(project, environment) + @project = project + @environment = environment + end + + # Entry-point to the stage + # @param dashboard [Hash] + # @param project [Project] + # @param environment [Environment] + def transform!(_dashboard) + raise NotImplementedError + end + + protected + + def missing_panel_groups! + raise DashboardLayoutError.new('Top-level key :panel_groups must be an array') + end + + def missing_panels! + raise DashboardLayoutError.new('Each "panel_group" must define an array :panels') + end + + def missing_metrics! + raise DashboardLayoutError.new('Each "panel" must define an array :metrics') + end + + def for_metrics(dashboard) + missing_panel_groups! unless dashboard[:panel_groups].is_a?(Array) + + dashboard[:panel_groups].each do |panel_group| + missing_panels! unless panel_group[:panels].is_a?(Array) + + panel_group[:panels].each do |panel| + missing_metrics! unless panel[:metrics].is_a?(Array) + + panel[:metrics].each do |metric| + yield metric + end + end + end + end + end + end + end + end +end diff --git a/lib/gitlab/metrics/dashboard/stages/common_metrics_inserter.rb b/lib/gitlab/metrics/dashboard/stages/common_metrics_inserter.rb new file mode 100644 index 00000000000..20e6b98e20b --- /dev/null +++ b/lib/gitlab/metrics/dashboard/stages/common_metrics_inserter.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Gitlab + module Metrics + module Dashboard + module Stages + class CommonMetricsInserter < BaseStage + # For each metric in the dashboard config, attempts to + # find a corresponding database record. If found, + # includes the record's id in the dashboard config. + def transform!(dashboard) + common_metrics = ::PrometheusMetric.common + + for_metrics(dashboard) do |metric| + metric_record = common_metrics.find { |m| m.identifier == metric[:id] } + metric[:metric_id] = metric_record.id if metric_record + end + end + end + end + end + end +end diff --git a/lib/gitlab/metrics/dashboard/stages/project_metrics_inserter.rb b/lib/gitlab/metrics/dashboard/stages/project_metrics_inserter.rb new file mode 100644 index 00000000000..0dc87f1ec16 --- /dev/null +++ b/lib/gitlab/metrics/dashboard/stages/project_metrics_inserter.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +module Gitlab + module Metrics + module Dashboard + module Stages + class ProjectMetricsInserter < BaseStage + # Inserts project-specific metrics into the dashboard + # config. If there are no project-specific metrics, + # this will have no effect. + def transform!(dashboard) + project.prometheus_metrics.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) + end + end + + private + + # Looks for a panel_group corresponding to the + # provided metric object. If unavailable, inserts one. + # @param panel_groups [Array<Hash>] + # @param metric [PrometheusMetric] + def find_or_create_panel_group(panel_groups, metric) + panel_group = find_panel_group(panel_groups, metric) + return panel_group if panel_group + + panel_group = new_panel_group(metric) + panel_groups << panel_group + + panel_group + end + + # Looks for a panel corresponding to the provided + # metric object. If unavailable, inserts one. + # @param panels [Array<Hash>] + # @param metric [PrometheusMetric] + def find_or_create_panel(panels, metric) + panel = find_panel(panels, metric) + return panel if panel + + panel = new_panel(metric) + panels << panel + + panel + end + + # Looks for a metric corresponding to the provided + # metric object. If unavailable, inserts one. + # @param metrics [Array<Hash>] + # @param metric [PrometheusMetric] + def find_or_create_metric(metrics, metric) + target_metric = find_metric(metrics, metric) + return target_metric if target_metric + + target_metric = new_metric(metric) + metrics << target_metric + + target_metric + end + + def find_panel_group(panel_groups, metric) + return unless panel_groups + + panel_groups.find { |group| group[:group] == metric.group_title } + end + + def find_panel(panels, metric) + return unless panels + + panel_identifiers = [DEFAULT_PANEL_TYPE, metric.title, metric.y_label] + panels.find { |panel| panel.values_at(:type, :title, :y_label) == panel_identifiers } + end + + def find_metric(metrics, metric) + return unless metrics + + metrics.find { |m| m[:id] == metric.identifier } + end + + def new_panel_group(metric) + { + group: metric.group_title, + priority: metric.priority, + panels: [] + } + end + + def new_panel(metric) + { + type: DEFAULT_PANEL_TYPE, + title: metric.title, + y_label: metric.y_label, + metrics: [] + } + end + + def new_metric(metric) + metric.queries.first.merge(metric_id: metric.id) + end + end + end + end + end +end diff --git a/lib/gitlab/metrics/dashboard/stages/sorter.rb b/lib/gitlab/metrics/dashboard/stages/sorter.rb new file mode 100644 index 00000000000..9c4183fff99 --- /dev/null +++ b/lib/gitlab/metrics/dashboard/stages/sorter.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Gitlab + module Metrics + module Dashboard + module Stages + class Sorter < BaseStage + def transform!(dashboard) + missing_panel_groups! unless dashboard[:panel_groups].is_a? Array + + sort_groups!(dashboard) + sort_panels!(dashboard) + end + + private + + # Sorts the groups in the dashboard by the :priority key + def sort_groups!(dashboard) + dashboard[:panel_groups] = dashboard[:panel_groups].sort_by { |group| -group[:priority].to_i } + end + + # Sorts the panels in the dashboard by the :weight key + def sort_panels!(dashboard) + dashboard[:panel_groups].each do |group| + missing_panels! unless group[:panels].is_a? Array + + group[:panels] = group[:panels].sort_by { |panel| -panel[:weight].to_i } + end + end + end + end + end + end +end diff --git a/lib/gitlab/metrics_dashboard/processor.rb b/lib/gitlab/metrics_dashboard/processor.rb deleted file mode 100644 index 8fa95f1ee2d..00000000000 --- a/lib/gitlab/metrics_dashboard/processor.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module MetricsDashboard - # Responsible for processesing a dashboard hash, inserting - # relevant DB records & sorting for proper rendering in - # the UI. These includes shared metric info, custom metrics - # info, and alerts (only in EE). - class Processor - SEQUENCE = [ - Stages::CommonMetricsInserter, - Stages::ProjectMetricsInserter, - Stages::Sorter - ].freeze - - def initialize(project, environment) - @project = project - @environment = environment - end - - # Returns a new dashboard hash with the results of - # running transforms on the dashboard. - def process(dashboard) - dashboard = dashboard.deep_transform_keys(&:to_sym) - - stage_params = [@project, @environment] - sequence.each { |stage| stage.new(*stage_params).transform!(dashboard) } - - dashboard - end - - private - - def sequence - SEQUENCE - end - end - end -end diff --git a/lib/gitlab/metrics_dashboard/service.rb b/lib/gitlab/metrics_dashboard/service.rb deleted file mode 100644 index 84f174c7d1b..00000000000 --- a/lib/gitlab/metrics_dashboard/service.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -# Fetches the metrics dashboard layout and supplemented the output with DB info. -module Gitlab - module MetricsDashboard - class Service < ::BaseService - SYSTEM_DASHBOARD_NAME = 'common_metrics' - SYSTEM_DASHBOARD_PATH = Rails.root.join('config', 'prometheus', "#{SYSTEM_DASHBOARD_NAME}.yml") - - # Returns a DB-supplemented json representation of a dashboard config file. - def get_dashboard - dashboard_string = Rails.cache.fetch(cache_key) { system_dashboard } - - dashboard = process_dashboard(dashboard_string) - - success(dashboard: dashboard) - rescue Gitlab::MetricsDashboard::Stages::BaseStage::DashboardLayoutError => e - error(e.message, :unprocessable_entity) - end - - private - - # Returns the base metrics shipped with every GitLab service. - def system_dashboard - YAML.load_file(SYSTEM_DASHBOARD_PATH) - end - - def cache_key - "metrics_dashboard_#{SYSTEM_DASHBOARD_NAME}" - end - - # Returns a new dashboard Hash, supplemented with DB info - def process_dashboard(dashboard) - Processor.new(project, params[:environment]).process(dashboard) - end - end - end -end diff --git a/lib/gitlab/metrics_dashboard/stages/base_stage.rb b/lib/gitlab/metrics_dashboard/stages/base_stage.rb deleted file mode 100644 index df49126a07b..00000000000 --- a/lib/gitlab/metrics_dashboard/stages/base_stage.rb +++ /dev/null @@ -1,58 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module MetricsDashboard - module Stages - class BaseStage - DashboardLayoutError = Class.new(StandardError) - - DEFAULT_PANEL_TYPE = 'area-chart' - - attr_reader :project, :environment - - def initialize(project, environment) - @project = project - @environment = environment - end - - # Entry-point to the stage - # @param dashboard [Hash] - # @param project [Project] - # @param environment [Environment] - def transform!(_dashboard) - raise NotImplementedError - end - - protected - - def missing_panel_groups! - raise DashboardLayoutError.new('Top-level key :panel_groups must be an array') - end - - def missing_panels! - raise DashboardLayoutError.new('Each "panel_group" must define an array :panels') - end - - def missing_metrics! - raise DashboardLayoutError.new('Each "panel" must define an array :metrics') - end - - def for_metrics(dashboard) - missing_panel_groups! unless dashboard[:panel_groups].is_a?(Array) - - dashboard[:panel_groups].each do |panel_group| - missing_panels! unless panel_group[:panels].is_a?(Array) - - panel_group[:panels].each do |panel| - missing_metrics! unless panel[:metrics].is_a?(Array) - - panel[:metrics].each do |metric| - yield metric - end - end - end - end - end - end - end -end diff --git a/lib/gitlab/metrics_dashboard/stages/common_metrics_inserter.rb b/lib/gitlab/metrics_dashboard/stages/common_metrics_inserter.rb deleted file mode 100644 index ef70347c6b2..00000000000 --- a/lib/gitlab/metrics_dashboard/stages/common_metrics_inserter.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module MetricsDashboard - module Stages - class CommonMetricsInserter < BaseStage - # For each metric in the dashboard config, attempts to - # find a corresponding database record. If found, - # includes the record's id in the dashboard config. - def transform!(dashboard) - common_metrics = ::PrometheusMetric.common - - for_metrics(dashboard) do |metric| - metric_record = common_metrics.find { |m| m.identifier == metric[:id] } - metric[:metric_id] = metric_record.id if metric_record - end - end - end - end - end -end diff --git a/lib/gitlab/metrics_dashboard/stages/project_metrics_inserter.rb b/lib/gitlab/metrics_dashboard/stages/project_metrics_inserter.rb deleted file mode 100644 index ab33ee75cce..00000000000 --- a/lib/gitlab/metrics_dashboard/stages/project_metrics_inserter.rb +++ /dev/null @@ -1,104 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module MetricsDashboard - module Stages - class ProjectMetricsInserter < BaseStage - # Inserts project-specific metrics into the dashboard - # config. If there are no project-specific metrics, - # this will have no effect. - def transform!(dashboard) - project.prometheus_metrics.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) - end - end - - private - - # Looks for a panel_group corresponding to the - # provided metric object. If unavailable, inserts one. - # @param panel_groups [Array<Hash>] - # @param metric [PrometheusMetric] - def find_or_create_panel_group(panel_groups, metric) - panel_group = find_panel_group(panel_groups, metric) - return panel_group if panel_group - - panel_group = new_panel_group(metric) - panel_groups << panel_group - - panel_group - end - - # Looks for a panel corresponding to the provided - # metric object. If unavailable, inserts one. - # @param panels [Array<Hash>] - # @param metric [PrometheusMetric] - def find_or_create_panel(panels, metric) - panel = find_panel(panels, metric) - return panel if panel - - panel = new_panel(metric) - panels << panel - - panel - end - - # Looks for a metric corresponding to the provided - # metric object. If unavailable, inserts one. - # @param metrics [Array<Hash>] - # @param metric [PrometheusMetric] - def find_or_create_metric(metrics, metric) - target_metric = find_metric(metrics, metric) - return target_metric if target_metric - - target_metric = new_metric(metric) - metrics << target_metric - - target_metric - end - - def find_panel_group(panel_groups, metric) - return unless panel_groups - - panel_groups.find { |group| group[:group] == metric.group_title } - end - - def find_panel(panels, metric) - return unless panels - - panel_identifiers = [DEFAULT_PANEL_TYPE, metric.title, metric.y_label] - panels.find { |panel| panel.values_at(:type, :title, :y_label) == panel_identifiers } - end - - def find_metric(metrics, metric) - return unless metrics - - metrics.find { |m| m[:id] == metric.identifier } - end - - def new_panel_group(metric) - { - group: metric.group_title, - priority: metric.priority, - panels: [] - } - end - - def new_panel(metric) - { - type: DEFAULT_PANEL_TYPE, - title: metric.title, - y_label: metric.y_label, - metrics: [] - } - end - - def new_metric(metric) - metric.queries.first.merge(metric_id: metric.id) - end - end - end - end -end diff --git a/lib/gitlab/metrics_dashboard/stages/sorter.rb b/lib/gitlab/metrics_dashboard/stages/sorter.rb deleted file mode 100644 index ca310c9637a..00000000000 --- a/lib/gitlab/metrics_dashboard/stages/sorter.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module MetricsDashboard - module Stages - class Sorter < BaseStage - def transform!(dashboard) - missing_panel_groups! unless dashboard[:panel_groups].is_a? Array - - sort_groups!(dashboard) - sort_panels!(dashboard) - end - - private - - # Sorts the groups in the dashboard by the :priority key - def sort_groups!(dashboard) - dashboard[:panel_groups] = dashboard[:panel_groups].sort_by { |group| -group[:priority].to_i } - end - - # Sorts the panels in the dashboard by the :weight key - def sort_panels!(dashboard) - dashboard[:panel_groups].each do |group| - missing_panels! unless group[:panels].is_a? Array - - group[:panels] = group[:panels].sort_by { |panel| -panel[:weight].to_i } - end - end - end - end - end -end diff --git a/spec/fixtures/lib/gitlab/metrics_dashboard/sample_dashboard.yml b/spec/fixtures/lib/gitlab/metrics/dashboard/sample_dashboard.yml index c2d3d3d8aca..c2d3d3d8aca 100644 --- a/spec/fixtures/lib/gitlab/metrics_dashboard/sample_dashboard.yml +++ b/spec/fixtures/lib/gitlab/metrics/dashboard/sample_dashboard.yml diff --git a/spec/fixtures/lib/gitlab/metrics_dashboard/schemas/dashboard.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/dashboard.json index 2d7601b64b1..1ee1205e29a 100644 --- a/spec/fixtures/lib/gitlab/metrics_dashboard/schemas/dashboard.json +++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/dashboard.json @@ -1,16 +1,12 @@ { "type": "object", - "required": [ - "dashboard", - "priority", - "panel_groups" - ], + "required": ["dashboard", "priority", "panel_groups"], "properties": { "dashboard": { "type": "string" }, "priority": { "type": "number" }, "panel_groups": { "type": "array", - "items": { "$ref": "spec/fixtures/lib/gitlab/metrics_dashboard/schemas/panel_groups.json" } + "items": { "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panel_groups.json" } } }, "additionalProperties": false diff --git a/spec/fixtures/lib/gitlab/metrics_dashboard/schemas/metrics.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json index 2d0af57ec2c..2d0af57ec2c 100644 --- a/spec/fixtures/lib/gitlab/metrics_dashboard/schemas/metrics.json +++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json diff --git a/spec/fixtures/lib/gitlab/metrics_dashboard/schemas/panel_groups.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panel_groups.json index d7a390adcdc..d7a390adcdc 100644 --- a/spec/fixtures/lib/gitlab/metrics_dashboard/schemas/panel_groups.json +++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panel_groups.json diff --git a/spec/fixtures/lib/gitlab/metrics_dashboard/schemas/panels.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panels.json index 1548daacd64..1548daacd64 100644 --- a/spec/fixtures/lib/gitlab/metrics_dashboard/schemas/panels.json +++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panels.json diff --git a/spec/lib/gitlab/metrics_dashboard/processor_spec.rb b/spec/lib/gitlab/metrics/dashboard/processor_spec.rb index 9a4897944c6..e27ba2f1f4e 100644 --- a/spec/lib/gitlab/metrics_dashboard/processor_spec.rb +++ b/spec/lib/gitlab/metrics/dashboard/processor_spec.rb @@ -2,10 +2,10 @@ require 'spec_helper' -describe Gitlab::MetricsDashboard::Processor do +describe Gitlab::Metrics::Dashboard::Processor do let(:project) { build(:project) } let(:environment) { build(:environment) } - let(:dashboard_yml) { YAML.load_file('spec/fixtures/lib/gitlab/metrics_dashboard/sample_dashboard.yml') } + let(:dashboard_yml) { YAML.load_file('spec/fixtures/lib/gitlab/metrics/dashboard/sample_dashboard.yml') } describe 'process' do let(:process_params) { [project, environment] } @@ -50,7 +50,7 @@ describe Gitlab::MetricsDashboard::Processor do shared_examples_for 'errors with message' do |expected_message| it 'raises a DashboardLayoutError' do - error_class = Gitlab::MetricsDashboard::Stages::BaseStage::DashboardLayoutError + error_class = Gitlab::Metrics::Dashboard::Stages::BaseStage::DashboardLayoutError expect { dashboard }.to raise_error(error_class, expected_message) end diff --git a/spec/lib/gitlab/metrics_dashboard/service_spec.rb b/spec/lib/gitlab/metrics/dashboard/service_spec.rb index d2b8231fbfb..3f82fd7ebf8 100644 --- a/spec/lib/gitlab/metrics_dashboard/service_spec.rb +++ b/spec/lib/gitlab/metrics/dashboard/service_spec.rb @@ -2,12 +2,12 @@ require 'spec_helper' -describe Gitlab::MetricsDashboard::Service, :use_clean_rails_memory_store_caching do +describe Gitlab::Metrics::Dashboard::Service, :use_clean_rails_memory_store_caching do let(:project) { build(:project) } let(:environment) { build(:environment) } describe 'get_dashboard' do - let(:dashboard_schema) { JSON.parse(fixture_file('lib/gitlab/metrics_dashboard/schemas/dashboard.json')) } + let(:dashboard_schema) { JSON.parse(fixture_file('lib/gitlab/metrics/dashboard/schemas/dashboard.json')) } it 'returns a json representation of the environment dashboard' do result = described_class.new(project, environment).get_dashboard |