Add embedding flag and filter to CPU/Mem

This commits adds support for metrics dashboards
for embedding. If the flag 'embedded' is provided
to the environments/id/metrics_dashboard endpoint,
the response will be suitable for embedding in
issues or other content.

This is a precursor for support for embedding
metrics in GFM.
This commit is contained in:
Sarah Yasonik 2019-06-20 14:06:18 +00:00 committed by Sean McGivern
parent 429f6b629b
commit f49dd76a44
10 changed files with 195 additions and 16 deletions

View file

@ -162,9 +162,17 @@ class Projects::EnvironmentsController < Projects::ApplicationController
return render_403 unless Feature.enabled?(:environment_metrics_use_prometheus_endpoint, project) return render_403 unless Feature.enabled?(:environment_metrics_use_prometheus_endpoint, project)
if Feature.enabled?(:environment_metrics_show_multiple_dashboards, project) if Feature.enabled?(:environment_metrics_show_multiple_dashboards, project)
result = dashboard_finder.find(project, current_user, environment, params[:dashboard]) result = dashboard_finder.find(
project,
current_user,
environment,
dashboard_path: params[:dashboard],
embedded: params[:embedded]
)
unless params[:embedded]
result[:all_dashboards] = dashboard_finder.find_all_paths(project) result[:all_dashboards] = dashboard_finder.find_all_paths(project)
end
else else
result = dashboard_finder.find(project, current_user, environment) result = dashboard_finder.find(project, current_user, environment)
end end

View file

@ -23,6 +23,11 @@ module Gitlab
raise NotImplementedError raise NotImplementedError
end end
# Returns an un-processed dashboard from the cache.
def raw_dashboard
Gitlab::Metrics::Dashboard::Cache.fetch(cache_key) { get_raw_dashboard }
end
private private
# Returns a new dashboard Hash, supplemented with DB info # Returns a new dashboard Hash, supplemented with DB info
@ -37,11 +42,6 @@ module Gitlab
params[:dashboard_path] params[:dashboard_path]
end end
# Returns an un-processed dashboard from the cache.
def raw_dashboard
Gitlab::Metrics::Dashboard::Cache.fetch(cache_key) { get_raw_dashboard }
end
# @return [Hash] an unmodified dashboard # @return [Hash] an unmodified dashboard
def get_raw_dashboard def get_raw_dashboard
raise NotImplementedError raise NotImplementedError
@ -54,6 +54,7 @@ module Gitlab
# Determines whether custom metrics should be included # Determines whether custom metrics should be included
# in the processed output. # in the processed output.
# @return [Boolean]
def insert_project_metrics? def insert_project_metrics?
false false
end end

View file

@ -0,0 +1,65 @@
# frozen_string_literal: true
# Responsible for returning a filtered system dashboard
# containing only the default embedded metrics. In future,
# this class may be updated to support filtering to
# alternate metrics/panels.
#
# Why isn't this filtering in a processing stage? By filtering
# here, we ensure the dynamically-determined dashboard is cached.
#
# Use Gitlab::Metrics::Dashboard::Finder to retrive dashboards.
module Gitlab
module Metrics
module Dashboard
class DynamicDashboardService < Gitlab::Metrics::Dashboard::BaseService
# For the default filtering for embedded metrics,
# uses the 'id' key in dashboard-yml definition for
# identification.
DEFAULT_EMBEDDED_METRICS_IDENTIFIERS = %w(
system_metrics_kubernetes_container_memory_total
system_metrics_kubernetes_container_cores_total
).freeze
# Returns a new dashboard with only the matching
# metrics from the system dashboard, stripped of groups.
# @return [Hash]
def raw_dashboard
panels = panel_groups.each_with_object([]) do |group, panels|
matched_panels = group['panels'].select { |panel| matching_panel?(panel) }
panels.concat(matched_panels)
end
{ 'panel_groups' => [{ 'panels' => panels }] }
end
def cache_key
"dynamic_metrics_dashboard_#{metric_identifiers.join('_')}"
end
private
# Returns an array of the panels groups on the
# system dashboard
def panel_groups
Gitlab::Metrics::Dashboard::SystemDashboardService
.new(project, nil)
.raw_dashboard['panel_groups']
end
# Identifies a panel as "matching" if any metric ids in
# the panel is in the list of identifiers to collect.
def matching_panel?(panel)
panel['metrics'].any? do |metric|
metric_identifiers.include?(metric['id'])
end
end
def metric_identifiers
DEFAULT_EMBEDDED_METRICS_IDENTIFIERS
end
end
end
end
end

View file

@ -9,17 +9,28 @@ module Gitlab
class Finder class Finder
class << self class << self
# Returns a formatted dashboard packed with DB info. # Returns a formatted dashboard packed with DB info.
# @param project [Project]
# @param user [User]
# @param environment [Environment]
# @param opts - dashboard_path [String] Path at which the
# dashboard can be found. Nil values will
# default to the system dashboard.
# @param opts - embedded [Boolean] Determines whether the
# dashboard is to be rendered as part of an
# issue or location other than the primary
# metrics dashboard UI. Returns only the
# Memory/CPU charts of the system dash.
# @return [Hash] # @return [Hash]
def find(project, user, environment, dashboard_path = nil) def find(project, user, environment, dashboard_path: nil, embedded: false)
service = system_dashboard?(dashboard_path) ? system_service : project_service service_for_path(dashboard_path, embedded: embedded)
service
.new(project, user, environment: environment, dashboard_path: dashboard_path) .new(project, user, environment: environment, dashboard_path: dashboard_path)
.get_dashboard .get_dashboard
end end
# Summary of all known dashboards. # Summary of all known dashboards.
# @return [Array<Hash>] ex) [{ path: String, default: Boolean }] # @return [Array<Hash>] ex) [{ path: String,
# display_name: String,
# default: Boolean }]
def find_all_paths(project) def find_all_paths(project)
project.repository.metrics_dashboard_paths project.repository.metrics_dashboard_paths
end end
@ -35,6 +46,13 @@ module Gitlab
private private
def service_for_path(dashboard_path, embedded:)
return dynamic_service if embedded
return system_service if system_dashboard?(dashboard_path)
project_service
end
def system_service def system_service
Gitlab::Metrics::Dashboard::SystemDashboardService Gitlab::Metrics::Dashboard::SystemDashboardService
end end
@ -43,6 +61,10 @@ module Gitlab
Gitlab::Metrics::Dashboard::ProjectDashboardService Gitlab::Metrics::Dashboard::ProjectDashboardService
end end
def dynamic_service
Gitlab::Metrics::Dashboard::DynamicDashboardService
end
def system_dashboard?(filepath) def system_dashboard?(filepath)
!filepath || system_service.system_dashboard?(filepath) !filepath || system_service.system_dashboard?(filepath)
end end

View file

@ -556,6 +556,19 @@ describe Projects::EnvironmentsController do
it_behaves_like 'has all dashboards' it_behaves_like 'has all dashboards'
end end
end end
context 'when the dashboard is intended for embedding' do
let(:dashboard_params) { { format: :json, embedded: true } }
it_behaves_like '200 response'
context 'when a dashboard path is provided' do
let(:dashboard_params) { { format: :json, dashboard: '.gitlab/dashboards/test.yml', embedded: true } }
# The dashboard path should simple be ignored.
it_behaves_like '200 response'
end
end
end end
end end

View file

@ -0,0 +1,13 @@
{
"type": "object",
"required": ["panel_groups"],
"properties": {
"panel_groups": {
"type": "array",
"items": {
"$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/embedded_panel_groups.json"
}
}
},
"additionalProperties": false
}

View file

@ -0,0 +1,11 @@
{
"type": "object",
"required": ["panels"],
"properties": {
"panels": {
"type": "array",
"items": { "$ref": "panels.json" }
}
},
"additionalProperties": false
}

View file

@ -0,0 +1,30 @@
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Metrics::Dashboard::DynamicDashboardService, :use_clean_rails_memory_store_caching do
include MetricsDashboardHelpers
set(:project) { build(:project) }
set(:environment) { create(:environment, project: project) }
describe '#get_dashboard' do
let(:service_params) { [project, nil, { environment: environment, dashboard_path: nil }] }
let(:service_call) { described_class.new(*service_params).get_dashboard }
it_behaves_like 'valid embedded dashboard service response'
it 'caches the unprocessed dashboard for subsequent calls' do
expect(YAML).to receive(:safe_load).once.and_call_original
described_class.new(*service_params).get_dashboard
described_class.new(*service_params).get_dashboard
end
context 'when called with a non-system dashboard' do
let(:dashboard_path) { 'garbage/dashboard/path' }
it_behaves_like 'valid embedded dashboard service response'
end
end
end

View file

@ -11,7 +11,7 @@ describe Gitlab::Metrics::Dashboard::Finder, :use_clean_rails_memory_store_cachi
describe '.find' do describe '.find' do
let(:dashboard_path) { '.gitlab/dashboards/test.yml' } let(:dashboard_path) { '.gitlab/dashboards/test.yml' }
let(:service_call) { described_class.find(project, nil, environment, dashboard_path) } let(:service_call) { described_class.find(project, nil, environment, dashboard_path: dashboard_path) }
it_behaves_like 'misconfigured dashboard service response', :not_found it_behaves_like 'misconfigured dashboard service response', :not_found
@ -45,6 +45,12 @@ describe Gitlab::Metrics::Dashboard::Finder, :use_clean_rails_memory_store_cachi
it_behaves_like 'valid dashboard service response' it_behaves_like 'valid dashboard service response'
end end
context 'when the dashboard is expected to be embedded' do
let(:service_call) { described_class.find(project, nil, environment, dashboard_path: nil, embedded: true) }
it_behaves_like 'valid embedded dashboard service response'
end
end end
describe '.find_all_paths' do describe '.find_all_paths' do

View file

@ -28,9 +28,7 @@ module MetricsDashboardHelpers
end end
end end
shared_examples_for 'valid dashboard service response' do shared_examples_for 'valid dashboard service response for schema' do
let(:dashboard_schema) { JSON.parse(fixture_file('lib/gitlab/metrics/dashboard/schemas/dashboard.json')) }
it 'returns a json representation of the dashboard' do it 'returns a json representation of the dashboard' do
result = service_call result = service_call
@ -40,4 +38,16 @@ module MetricsDashboardHelpers
expect(JSON::Validator.fully_validate(dashboard_schema, result[:dashboard])).to be_empty expect(JSON::Validator.fully_validate(dashboard_schema, result[:dashboard])).to be_empty
end end
end end
shared_examples_for 'valid dashboard service response' do
let(:dashboard_schema) { JSON.parse(fixture_file('lib/gitlab/metrics/dashboard/schemas/dashboard.json')) }
it_behaves_like 'valid dashboard service response for schema'
end
shared_examples_for 'valid embedded dashboard service response' do
let(:dashboard_schema) { JSON.parse(fixture_file('lib/gitlab/metrics/dashboard/schemas/embedded_dashboard.json')) }
it_behaves_like 'valid dashboard service response for schema'
end
end end