Commit 35c41232 authored by syasonik's avatar syasonik

Refactor dashboard proccesing into stages

parent d307f446
......@@ -162,7 +162,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
respond_to do |format|
format.json do
dashboard = MetricsDashboardService.new(@project).get_dashboard
dashboard = Gitlab::MetricsDashboard::Service.new(@project).get_dashboard
render json: dashboard, status: :ok
end
......
# frozen_string_literal: true
# Fetches the metrics dashboard layout and supplemented the output with DB info.
class MetricsDashboardService
SYSTEM_DASHBOARD_NAME = 'common_metrics'
SYSTEM_DASHBOARD_PATH = Rails.root.join('config', 'prometheus', "#{SYSTEM_DASHBOARD_NAME}.yml")
def initialize(project)
@project = project
end
# Returns a DB-supplemented json representation of a dashboard config file.
def get_dashboard
dashboard = Rails.cache.fetch(cache_key) { system_dashboard }
process_dashboard(dashboard)
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
def process_dashboard(dashboard)
MetricsDashboardProcessingService.new(dashboard, @project).process
end
end
# frozen_string_literal: true
module Gitlab
module MetricsDashboard
class CommonMetricsInserter
class << self
# 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, _project)
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
private
def for_metrics(dashboard)
dashboard[:panel_groups].each do |panel_group|
panel_group[:panels].each do |panel|
panel[:metrics].each do |metric|
yield metric
end
end
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module MetricsDashboard
class Processor
STAGES = [CommonMetricsInserter, Sorter, ProjectMetricsInserter]
def initialize(dashboard, project)
@dashboard = dashboard.deep_transform_keys(&:to_sym)
@project = project
end
def process
STAGES.each { |stage| stage.transform!(@dashboard, @project) }
@dashboard.to_json
end
end
end
end
# frozen_string_literal: true
module Gitlab
module MetricsDashboard
class ProjectMetricsInserter
DEFAULT_PANEL_TYPE = 'area-chart'
class << self
# Inserts project-specific metrics into the dashboard config.
# If there are no project-specific metrics, this will have no effect.
def transform!(dashboard, project)
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
# Looks for an instance of the named resource corresponding to the provided
# metric object. If unavailable, inserts one.
# @param name [Symbol, String] One of :panel_group, :panel, or :metric
# @param existing_resources [Array<Hash>]
# @param metric [PrometheusMetric]
def find_or_create(name, existing_resources, metric)
target = self.send("find_#{name}", existing_resources, metric)
return target if target
target = self.send("new_#{name}", metric)
existing_resources << target
target
end
def find_panel_group(panel_groups, metric)
panel_groups.find { |group| group[:group] == metric.group_title }
end
def find_panel(panels, metric)
panel_identifiers = [DEFAULT_PANEL_TYPE, metric.title, metric.y_label]
target_panel = panels.find { |panel| panel.values_at(:type, :title, :y_label) == panel_identifiers }
end
def find_metric(metrics, metric)
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
# frozen_string_literal: true
# Fetches the metrics dashboard layout and supplemented the output with DB info.
module Gitlab
module MetricsDashboard
class Service
SYSTEM_DASHBOARD_NAME = 'common_metrics'
SYSTEM_DASHBOARD_PATH = Rails.root.join('config', 'prometheus', "#{SYSTEM_DASHBOARD_NAME}.yml")
def initialize(project)
@project = project
end
# Returns a DB-supplemented json representation of a dashboard config file.
def get_dashboard
dashboard = Rails.cache.fetch(cache_key) { system_dashboard }
process_dashboard(dashboard)
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
def process_dashboard(dashboard)
Processor.new(dashboard, @project).process
end
end
end
end
# frozen_string_literal: true
module Gitlab
module MetricsDashboard
class Sorter
class << self
def transform!(dashboard, _project)
sort_groups!(dashboard)
sort_panels!(dashboard)
end
# 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] }
end
# Sorts the panels in the dashboard by the :weight key
def sort_panels!(dashboard)
dashboard[:panel_groups].each do |group|
group[:panels] = group[:panels].sort_by { |panel| panel[:weight] }
end
end
end
end
end
end
require 'spec_helper'
describe MetricsDashboardProcessingService do
describe Gitlab::MetricsDashboard::Processor do
let(:project) { build(:project) }
let(:dashboard_yml) { YAML.load_file('spec/fixtures/services/metrics_dashboard_processing_service.yml') }
......@@ -40,7 +40,7 @@ describe MetricsDashboardProcessingService do
end
it 'orders groups by priority and panels by weight' do
expected_metrics_order = %w('metric_b metric_a2 metric_a1')
expected_metrics_order = %w(metric_b metric_a2 metric_a1)
actual_metrics_order = all_metrics.map { |m| m[:id] }
expect(actual_metrics_order).to eq expected_metrics_order
......
require 'spec_helper'
describe MetricsDashboardService, :use_clean_rails_memory_store_caching do
describe Gitlab::MetricsDashboard::Service, :use_clean_rails_memory_store_caching do
let(:project) { build(:project) }
describe 'get_dashboard' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment