Commit 5630b850 authored by Imre Farkas's avatar Imre Farkas

Merge branch '228758-create-a-new-endpoint-that-converts-yml-panels-to-json' into 'master'

Process panels YAML to JSON

See merge request gitlab-org/gitlab!38279
parents 29777f4a 54617a2a
......@@ -9,7 +9,13 @@ module Projects
def panel_preview
respond_to do |format|
format.json { render json: render_panel }
format.json do
if rendered_panel.success?
render json: rendered_panel.payload
else
render json: { message: rendered_panel.message }, status: :unprocessable_entity
end
end
end
end
......@@ -19,25 +25,21 @@ module Projects
render_404 unless Feature.enabled?(:metrics_dashboard_new_panel_page, project)
end
def render_panel
{
"title": "Memory Usage (Total)",
"type": "area-chart",
"y_label": "Total Memory Used (GB)",
"weight": 4,
"metrics": [
{
"id": "system_metrics_kubernetes_container_memory_total",
"query_range": "avg(sum(container_memory_usage_bytes{container_name!=\"POD\",pod_name=~\"^{{ci_environment_slug}}-(.*)\",namespace=\"{{kube_namespace}}\"}) by (job)) without (job) /1024/1024/1024",
"label": "Total (GB)",
"unit": "GB",
"metric_id": 15,
"edit_path": nil,
"prometheus_endpoint_path": "/root/autodevops-deploy/-/environments/29/prometheus/api/v1/query_range?query=avg%28sum%28container_memory_usage_bytes%7Bcontainer_name%21%3D%22POD%22%2Cpod_name%3D~%22%5E%7B%7Bci_environment_slug%7D%7D-%28.%2A%29%22%2Cnamespace%3D%22%7B%7Bkube_namespace%7D%7D%22%7D%29+by+%28job%29%29+without+%28job%29++%2F1024%2F1024%2F1024"
}
],
"id": "4570deed516d0bf93fb42879004117009ab456ced27393ec8dce5b6960438132"
}
def rendered_panel
@panel_preview ||= ::Metrics::Dashboard::PanelPreviewService.new(project, panel_yaml, environment).execute
end
def panel_yaml
params.require(:panel_yaml)
end
def environment
@environment ||=
if params[:environment]
project.environments.find(params[:environment])
else
project.default_environment
end
end
end
end
......
# frozen_string_literal: true
# Ingest YAML fragment with metrics dashboard panel definition
# https://docs.gitlab.com/ee/operations/metrics/dashboards/yaml.html#panel-panels-properties
# process it and returns renderable json version
module Metrics
module Dashboard
class PanelPreviewService
SEQUENCE = [
::Gitlab::Metrics::Dashboard::Stages::CommonMetricsInserter,
::Gitlab::Metrics::Dashboard::Stages::MetricEndpointInserter,
::Gitlab::Metrics::Dashboard::Stages::PanelIdsInserter,
::Gitlab::Metrics::Dashboard::Stages::AlertsInserter,
::Gitlab::Metrics::Dashboard::Stages::UrlValidator
].freeze
HANDLED_PROCESSING_ERRORS = [
Gitlab::Metrics::Dashboard::Errors::DashboardProcessingError,
Gitlab::Config::Loader::Yaml::NotHashError,
Gitlab::Config::Loader::Yaml::DataTooLargeError,
Gitlab::Config::Loader::FormatError
].freeze
def initialize(project, panel_yaml, environment)
@project, @panel_yaml, @environment = project, panel_yaml, environment
end
def execute
dashboard = ::Gitlab::Metrics::Dashboard::Processor.new(project, dashboard_structure, SEQUENCE, environment: environment).process
ServiceResponse.success(payload: dashboard[:panel_groups][0][:panels][0])
rescue *HANDLED_PROCESSING_ERRORS => error
ServiceResponse.error(message: error.message)
end
private
attr_accessor :project, :panel_yaml, :environment
def dashboard_structure
{
panel_groups: [
{
panels: [panel_hash]
}
]
}
end
def panel_hash
::Gitlab::Config::Loader::Yaml.new(panel_yaml).load_raw!
end
end
end
end
......@@ -6,6 +6,42 @@ RSpec.describe 'Projects::Metrics::Dashboards::BuilderController' do
let_it_be(:project) { create(:project) }
let_it_be(:environment) { create(:environment, project: project) }
let_it_be(:user) { create(:user) }
let_it_be(:valid_panel_yml) do
<<~YML
---
title: "Super Chart A1"
type: "area-chart"
y_label: "y_label"
weight: 1
max_value: 1
metrics:
- id: metric_a1
query_range: |+
avg(
sum(
container_memory_usage_bytes{
container_name!="POD",
pod_name=~"^{{ci_environment_slug}}-(.*)",
namespace="{{kube_namespace}}",
user_def_variable="{{user_def_variable}}"
}
) by (job)
) without (job)
/1024/1024/1024
unit: unit
label: Legend Label
YML
end
let_it_be(:invalid_panel_yml) do
<<~YML
---
title: "Super Chart A1"
type: "area-chart"
y_label: "y_label"
weight: 1
max_value: 1
YML
end
def send_request(params = {})
post namespace_project_metrics_dashboards_builder_path(namespace_id: project.namespace, project_id: project, format: :json, **params)
......@@ -17,14 +53,14 @@ RSpec.describe 'Projects::Metrics::Dashboards::BuilderController' do
stub_feature_flags(metrics_dashboard_new_panel_page: true)
end
it 'redirects to sign in' do
it 'redirects user to sign in page' do
send_request
expect(response).to redirect_to(new_user_session_path)
end
end
context 'as user with reporter access' do
context 'as user with guest access' do
before do
stub_feature_flags(metrics_dashboard_new_panel_page: true)
project.add_guest(user)
......@@ -49,10 +85,31 @@ RSpec.describe 'Projects::Metrics::Dashboards::BuilderController' do
stub_feature_flags(metrics_dashboard_new_panel_page: true)
end
it 'returns success' do
send_request
context 'valid yaml panel is supplied' do
it 'returns success' do
send_request(panel_yaml: valid_panel_yml)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to include('title' => 'Super Chart A1', 'type' => 'area-chart')
end
end
context 'invalid yaml panel is supplied' do
it 'returns unprocessable entity' do
send_request(panel_yaml: invalid_panel_yml)
expect(response).to have_gitlab_http_status(:unprocessable_entity)
expect(json_response['message']).to eq('Each "panel" must define an array :metrics')
end
end
context 'invalid panel_yaml is not a yaml string' do
it 'returns unprocessable entity' do
send_request(panel_yaml: 1)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to have_gitlab_http_status(:unprocessable_entity)
expect(json_response['message']).to eq('Invalid configuration format')
end
end
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Metrics::Dashboard::PanelPreviewService do
let_it_be(:project) { create(:project) }
let_it_be(:environment) { create(:environment, project: project) }
let_it_be(:panel_yml) do
<<~YML
---
title: test panel
YML
end
let_it_be(:dashboard) do
{
panel_groups: [
{
panels: [{ 'title' => 'test panel' }]
}
]
}
end
describe '#execute' do
subject(:service_response) { described_class.new(project, panel_yml, environment).execute }
context "valid panel's yaml" do
before do
allow_next_instance_of(::Gitlab::Metrics::Dashboard::Processor) do |processor|
allow(processor).to receive(:process).and_return(dashboard)
end
end
it 'returns success service response' do
expect(service_response.success?).to be_truthy
end
it 'returns processed panel' do
expect(service_response.payload).to eq('title' => 'test panel')
end
it 'uses dashboard processor' do
sequence = [
::Gitlab::Metrics::Dashboard::Stages::CommonMetricsInserter,
::Gitlab::Metrics::Dashboard::Stages::MetricEndpointInserter,
::Gitlab::Metrics::Dashboard::Stages::PanelIdsInserter,
::Gitlab::Metrics::Dashboard::Stages::AlertsInserter,
::Gitlab::Metrics::Dashboard::Stages::UrlValidator
]
processor_params = [project, dashboard, sequence, environment: environment]
expect_next_instance_of(::Gitlab::Metrics::Dashboard::Processor, *processor_params) do |processor|
expect(processor).to receive(:process).and_return(dashboard)
end
service_response
end
end
context "invalid panel's yaml" do
[
Gitlab::Metrics::Dashboard::Errors::DashboardProcessingError,
Gitlab::Config::Loader::Yaml::NotHashError,
Gitlab::Config::Loader::Yaml::DataTooLargeError,
Gitlab::Config::Loader::FormatError
].each do |error_class|
before do
allow_next_instance_of(::Gitlab::Metrics::Dashboard::Processor) do |processor|
allow(processor).to receive(:process).and_raise(error_class.new('error'))
end
end
it 'returns error service response' do
expect(service_response.error?).to be_truthy
end
it 'returns error message' do
expect(service_response.message).to eq('error')
end
end
end
end
end
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