debian-mirror-gitlab/lib/gitlab/metrics/dashboard/stages/grafana_formatter.rb
2021-04-29 21:17:54 +05:30

164 lines
5.4 KiB
Ruby

# frozen_string_literal: true
module Gitlab
module Metrics
module Dashboard
module Stages
class GrafanaFormatter < BaseStage
include Gitlab::Utils::StrongMemoize
CHART_TYPE = 'area-chart'
PROXY_PATH = 'api/v1/query_range'
# Reformats the specified panel in the Gitlab
# dashboard-yml format
def transform!
validate_input!
new_dashboard = formatted_dashboard
dashboard.clear
dashboard.merge!(new_dashboard)
end
private
def validate_input!
::Grafana::Validator.new(
grafana_dashboard,
datasource,
panel,
query_params
).validate!
rescue ::Grafana::Validator::Error => e
raise ::Gitlab::Metrics::Dashboard::Errors::DashboardProcessingError, e.message
end
def formatted_dashboard
{ panel_groups: [{ panels: [formatted_panel] }] }
end
def formatted_panel
{
title: panel[:title],
type: CHART_TYPE,
y_label: '', # Grafana panels do not include a Y-Axis label
metrics: panel[:targets].map.with_index do |target, idx|
formatted_metric(target, idx)
end
}
end
def formatted_metric(metric, idx)
{
id: "#{metric[:legendFormat]}_#{idx}",
query_range: format_query(metric),
label: replace_variables(metric[:legendFormat]),
prometheus_endpoint_path: prometheus_endpoint_for_metric(metric)
}.compact
end
# Panel specified by the url from the Grafana dashboard
def panel
strong_memoize(:panel) do
grafana_dashboard[:dashboard][:panels].find do |panel|
query_params[:panelId] ? matching_panel?(panel) : valid_panel?(panel)
end
end
end
# Determines whether a given panel is the one
# specified by the linked grafana url
def matching_panel?(panel)
panel[:id].to_s == query_params[:panelId]
end
# Determines whether any given panel has the potenial
# to return valid results from grafana/prometheus
def valid_panel?(panel)
::Grafana::Validator
.new(grafana_dashboard, datasource, panel, query_params)
.valid?
end
# Grafana url query parameters. Includes information
# on which panel to select and time range.
def query_params
strong_memoize(:query_params) do
Gitlab::Metrics::Dashboard::Url.parse_query(grafana_url)
end
end
# Endpoint which will return prometheus metric data
# for the metric
def prometheus_endpoint_for_metric(metric)
Gitlab::Routing.url_helpers.project_grafana_api_path(
project,
datasource_id: datasource[:id],
proxy_path: PROXY_PATH,
query: format_query(metric)
)
end
# Reformats query for compatibility with prometheus api.
def format_query(metric)
expression = remove_new_lines(metric[:expr])
expression = replace_variables(expression)
replace_global_variables(expression, metric)
end
# Accomodates instance-defined Grafana variables.
# These are variables defined by users, and values
# must be provided in the query parameters.
def replace_variables(expression)
return expression unless grafana_dashboard[:dashboard][:templating]
grafana_dashboard[:dashboard][:templating][:list]
.sort_by { |variable| variable[:name].length }
.each do |variable|
variable_value = query_params[:"var-#{variable[:name]}"]
expression = expression.gsub("$#{variable[:name]}", variable_value)
expression = expression.gsub("[[#{variable[:name]}]]", variable_value)
expression = expression.gsub("{{#{variable[:name]}}}", variable_value)
end
expression
end
# Replaces Grafana global built-in variables with values.
# Only $__interval and $__from and $__to are supported.
#
# See https://grafana.com/docs/reference/templating/#global-built-in-variables
def replace_global_variables(expression, metric)
expression = expression.gsub('$__interval', metric[:interval]) if metric[:interval]
expression = expression.gsub('$__from', query_params[:from])
expression.gsub('$__to', query_params[:to])
end
# Removes new lines from expression.
def remove_new_lines(expression)
expression.gsub(/\R+/, '')
end
# Grafana datasource object corresponding to the
# specified dashboard
def datasource
params[:datasource]
end
# The specified Grafana dashboard
def grafana_dashboard
params[:grafana_dashboard]
end
# The URL specifying which Grafana panel to embed
def grafana_url
params[:grafana_url]
end
end
end
end
end
end