Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
6791eefead
commit
664c4c7b49
|
@ -32,4 +32,5 @@ lib/gitlab/github_import/ @gitlab-org/maintainers/database
|
|||
/.gitlab/ci/ @gl-quality/eng-prod
|
||||
Dangerfile @gl-quality/eng-prod
|
||||
/danger/ @gl-quality/eng-prod
|
||||
/lib/gitlab/danger/ @gl-quality/eng-prod
|
||||
/scripts/ @gl-quality/eng-prod
|
||||
|
|
|
@ -97,7 +97,10 @@ schedule:review-build-cng:
|
|||
variables:
|
||||
HOST_SUFFIX: "${CI_ENVIRONMENT_SLUG}"
|
||||
DOMAIN: "-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}"
|
||||
GITLAB_HELM_CHART_REF: "v2.3.7"
|
||||
# v2.3.7 + some stability improvements not yet released:
|
||||
# - sidekiq readinessProbe should be `pgrep -f sidekiq`: https://gitlab.com/gitlab-org/charts/gitlab/merge_requests/991
|
||||
# - Allows livenessProbe and readinessProbe to be configured for unicorn: https://gitlab.com/gitlab-org/charts/gitlab/merge_requests/985
|
||||
GITLAB_HELM_CHART_REF: "df7c52dc69df441909880b8f2fd15e938cdb2047"
|
||||
GITLAB_EDITION: "ce"
|
||||
environment:
|
||||
name: review/${CI_COMMIT_REF_NAME}
|
||||
|
|
|
@ -29,7 +29,7 @@ Set the title to: `Description of the original issue`
|
|||
|
||||
#### Documentation and final details
|
||||
|
||||
- [ ] Check the topic on #security to see when the next release is going to happen and add a link to the [links section](#links)
|
||||
- [ ] Check the topic on #releases to see when the next release is going to happen and add a link to the [links section](#links)
|
||||
- [ ] Add links to this issue and your MRs in the description of the security release issue
|
||||
- [ ] Find out the versions affected (the Git history of the files affected may help you with this) and add them to the [details section](#details)
|
||||
- [ ] Fill in any upgrade notes that users may need to take into account in the [details section](#details)
|
||||
|
|
|
@ -14,7 +14,7 @@ class CreateBranchService < BaseService
|
|||
if new_branch
|
||||
success(new_branch)
|
||||
else
|
||||
error('Invalid reference name')
|
||||
error("Invalid reference name: #{branch_name}")
|
||||
end
|
||||
rescue Gitlab::Git::PreReceiveError => ex
|
||||
error(ex.message)
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Responsible for returning a gitlab-compatible dashboard
|
||||
# containing info based on a grafana dashboard and datasource.
|
||||
#
|
||||
# Use Gitlab::Metrics::Dashboard::Finder to retrive dashboards.
|
||||
module Metrics
|
||||
module Dashboard
|
||||
class GrafanaMetricEmbedService < ::Metrics::Dashboard::BaseService
|
||||
include ReactiveCaching
|
||||
|
||||
SEQUENCE = [
|
||||
::Gitlab::Metrics::Dashboard::Stages::GrafanaFormatter
|
||||
].freeze
|
||||
|
||||
self.reactive_cache_key = ->(service) { service.cache_key }
|
||||
self.reactive_cache_lease_timeout = 30.seconds
|
||||
self.reactive_cache_refresh_interval = 30.minutes
|
||||
self.reactive_cache_lifetime = 30.minutes
|
||||
self.reactive_cache_worker_finder = ->(_id, *args) { from_cache(*args) }
|
||||
|
||||
class << self
|
||||
# Determines whether the provided params are sufficient
|
||||
# to uniquely identify a grafana dashboard.
|
||||
def valid_params?(params)
|
||||
[
|
||||
params[:embedded],
|
||||
params[:grafana_url]
|
||||
].all?
|
||||
end
|
||||
|
||||
def from_cache(project_id, user_id, grafana_url)
|
||||
project = Project.find(project_id)
|
||||
user = User.find(user_id)
|
||||
|
||||
new(project, user, grafana_url: grafana_url)
|
||||
end
|
||||
end
|
||||
|
||||
def get_dashboard
|
||||
with_reactive_cache(*cache_key) { |result| result }
|
||||
end
|
||||
|
||||
# Inherits the primary logic from the parent class and
|
||||
# maintains the service's API while including ReactiveCache
|
||||
def calculate_reactive_cache(*)
|
||||
::Metrics::Dashboard::BaseService
|
||||
.instance_method(:get_dashboard)
|
||||
.bind(self)
|
||||
.call() # rubocop:disable Style/MethodCallWithoutArgsParentheses
|
||||
end
|
||||
|
||||
def cache_key(*args)
|
||||
[project.id, current_user.id, grafana_url]
|
||||
end
|
||||
|
||||
# Required for ReactiveCaching; Usage overridden by
|
||||
# self.reactive_cache_worker_finder
|
||||
def id
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_raw_dashboard
|
||||
raise MissingIntegrationError unless client
|
||||
|
||||
grafana_dashboard = fetch_dashboard
|
||||
datasource = fetch_datasource(grafana_dashboard)
|
||||
|
||||
params.merge!(grafana_dashboard: grafana_dashboard, datasource: datasource)
|
||||
|
||||
{}
|
||||
end
|
||||
|
||||
def fetch_dashboard
|
||||
uid = GrafanaUidParser.new(grafana_url, project).parse
|
||||
raise DashboardProcessingError.new('Dashboard uid not found') unless uid
|
||||
|
||||
response = client.get_dashboard(uid: uid)
|
||||
|
||||
parse_json(response.body)
|
||||
end
|
||||
|
||||
def fetch_datasource(dashboard)
|
||||
name = DatasourceNameParser.new(grafana_url, dashboard).parse
|
||||
raise DashboardProcessingError.new('Datasource name not found') unless name
|
||||
|
||||
response = client.get_datasource(name: name)
|
||||
|
||||
parse_json(response.body)
|
||||
end
|
||||
|
||||
def grafana_url
|
||||
params[:grafana_url]
|
||||
end
|
||||
|
||||
def client
|
||||
project.grafana_integration&.client
|
||||
end
|
||||
|
||||
def allowed?
|
||||
Ability.allowed?(current_user, :read_project, project)
|
||||
end
|
||||
|
||||
def sequence
|
||||
SEQUENCE
|
||||
end
|
||||
|
||||
def parse_json(json)
|
||||
JSON.parse(json, symbolize_names: true)
|
||||
rescue JSON::ParserError
|
||||
raise DashboardProcessingError.new('Grafana response contains invalid json')
|
||||
end
|
||||
end
|
||||
|
||||
# Identifies the uid of the dashboard based on url format
|
||||
class GrafanaUidParser
|
||||
def initialize(grafana_url, project)
|
||||
@grafana_url, @project = grafana_url, project
|
||||
end
|
||||
|
||||
def parse
|
||||
@grafana_url.match(uid_regex) { |m| m.named_captures['uid'] }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# URLs are expected to look like https://domain.com/d/:uid/other/stuff
|
||||
def uid_regex
|
||||
base_url = @project.grafana_integration.grafana_url.chomp('/')
|
||||
|
||||
%r{(#{Regexp.escape(base_url)}\/d\/(?<uid>\w+)\/)}x
|
||||
end
|
||||
end
|
||||
|
||||
# Identifies the name of the datasource for a dashboard
|
||||
# based on the panelId query parameter found in the url
|
||||
class DatasourceNameParser
|
||||
def initialize(grafana_url, grafana_dashboard)
|
||||
@grafana_url, @grafana_dashboard = grafana_url, grafana_dashboard
|
||||
end
|
||||
|
||||
def parse
|
||||
@grafana_dashboard[:dashboard][:panels]
|
||||
.find { |panel| panel[:id].to_s == query_params[:panelId] }
|
||||
.try(:[], :datasource)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def query_params
|
||||
Gitlab::Metrics::Dashboard::Url.parse_query(@grafana_url)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,4 +4,4 @@
|
|||
= password_field_tag :password, nil, class: 'form-control', required: true, title: _('This field is required.'), data: { qa_selector: 'password_field' }
|
||||
|
||||
.submit-container.move-submit-down
|
||||
= submit_tag _('Enter admin mode'), class: 'btn btn-success', data: { qa_selector: 'sign_in_button' }
|
||||
= submit_tag _('Enter Admin Mode'), class: 'btn btn-success', data: { qa_selector: 'sign_in_button' }
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
%ul.nav-links.new-session-tabs.nav-tabs.nav{ role: 'tablist' }
|
||||
%li.nav-item{ role: 'presentation' }
|
||||
%a.nav-link.active{ href: '#login-pane', data: { toggle: 'tab', qa_selector: 'sign_in_tab' }, role: 'tab' }= _('Enter admin mode')
|
||||
%a.nav-link.active{ href: '#login-pane', data: { toggle: 'tab', qa_selector: 'sign_in_tab' }, role: 'tab' }= _('Enter Admin Mode')
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
- @hide_breadcrumbs = true
|
||||
- page_title _('Enter admin mode')
|
||||
- page_title _('Enter Admin Mode')
|
||||
|
||||
.row.justify-content-center
|
||||
.col-6.new-session-forms-container
|
||||
|
|
|
@ -55,15 +55,15 @@
|
|||
= nav_link(controller: 'admin/dashboard') do
|
||||
= link_to admin_root_path, class: 'admin-icon qa-admin-area-link d-xl-none' do
|
||||
= _('Admin Area')
|
||||
- if Feature.enabled?(:user_mode_in_session)
|
||||
- if header_link?(:admin_mode)
|
||||
= nav_link(controller: 'admin/sessions') do
|
||||
= link_to destroy_admin_session_path, class: 'd-lg-none lock-open-icon' do
|
||||
= _('Leave admin mode')
|
||||
- elsif current_user.admin?
|
||||
= nav_link(controller: 'admin/sessions') do
|
||||
= link_to new_admin_session_path, class: 'd-lg-none lock-icon' do
|
||||
= _('Enter admin mode')
|
||||
- if Feature.enabled?(:user_mode_in_session)
|
||||
- if header_link?(:admin_mode)
|
||||
= nav_link(controller: 'admin/sessions') do
|
||||
= link_to destroy_admin_session_path, class: 'd-lg-none lock-open-icon' do
|
||||
= _('Leave Admin Mode')
|
||||
- elsif current_user.admin?
|
||||
= nav_link(controller: 'admin/sessions') do
|
||||
= link_to new_admin_session_path, class: 'd-lg-none lock-icon' do
|
||||
= _('Enter Admin Mode')
|
||||
- if Gitlab::Sherlock.enabled?
|
||||
%li
|
||||
= link_to sherlock_transactions_path, class: 'admin-icon' do
|
||||
|
@ -74,6 +74,15 @@
|
|||
= link_to admin_root_path, class: 'admin-icon qa-admin-area-link', title: _('Admin Area'), aria: { label: _('Admin Area') }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
|
||||
= sprite_icon('admin', size: 18)
|
||||
|
||||
- if Feature.enabled?(:user_mode_in_session)
|
||||
- if header_link?(:admin_mode)
|
||||
= nav_link(controller: 'admin/sessions', html_options: { class: "d-none d-lg-block d-xl-block"}) do
|
||||
= link_to destroy_admin_session_path, title: _('Leave Admin Mode'), aria: { label: _('Leave Admin Mode') }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
|
||||
= sprite_icon('lock-open', size: 18)
|
||||
- elsif current_user.admin?
|
||||
= nav_link(controller: 'admin/sessions', html_options: { class: "d-none d-lg-block d-xl-block"}) do
|
||||
= link_to new_admin_session_path, title: _('Enter Admin Mode'), aria: { label: _('Enter Admin Mode') }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
|
||||
= sprite_icon('lock', size: 18)
|
||||
|
||||
-# Shortcut to Dashboard > Projects
|
||||
- if dashboard_nav_link?(:projects)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Enable the color chip in AsciiDoc documents
|
||||
merge_request: 18723
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Resolve Error when uploading a few designs in a row
|
||||
merge_request: 18811
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix missing admin mode UI buttons on bigger screen sizes
|
||||
merge_request: 18585
|
||||
author: Diego Louzán
|
||||
type: fixed
|
|
@ -10,6 +10,7 @@ module Banzai
|
|||
Filter::SyntaxHighlightFilter,
|
||||
Filter::ExternalLinkFilter,
|
||||
Filter::PlantumlFilter,
|
||||
Filter::ColorFilter,
|
||||
Filter::AsciiDocPostProcessingFilter
|
||||
]
|
||||
end
|
||||
|
|
|
@ -9,6 +9,7 @@ module Gitlab
|
|||
module Errors
|
||||
DashboardProcessingError = Class.new(StandardError)
|
||||
PanelNotFoundError = Class.new(StandardError)
|
||||
MissingIntegrationError = Class.new(StandardError)
|
||||
LayoutError = Class.new(DashboardProcessingError)
|
||||
MissingQueryError = Class.new(DashboardProcessingError)
|
||||
|
||||
|
@ -22,6 +23,10 @@ module Gitlab
|
|||
error("#{dashboard_path} could not be found.", :not_found)
|
||||
when PanelNotFoundError
|
||||
error(error.message, :not_found)
|
||||
when ::Grafana::Client::Error
|
||||
error(error.message, :service_unavailable)
|
||||
when MissingIntegrationError
|
||||
error('Proxy support for this API is not available currently', :bad_request)
|
||||
else
|
||||
raise error
|
||||
end
|
||||
|
|
|
@ -17,7 +17,10 @@ module Gitlab
|
|||
|
||||
# Returns a new dashboard hash with the results of
|
||||
# running transforms on the dashboard.
|
||||
# @return [Hash, nil]
|
||||
def process
|
||||
return unless @dashboard
|
||||
|
||||
@dashboard.deep_symbolize_keys.tap do |dashboard|
|
||||
@sequence.each do |stage|
|
||||
stage.new(@project, dashboard, @params).transform!
|
||||
|
|
|
@ -0,0 +1,224 @@
|
|||
# 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!
|
||||
InputFormatValidator.new(
|
||||
grafana_dashboard,
|
||||
datasource,
|
||||
panel,
|
||||
query_params
|
||||
).validate!
|
||||
|
||||
new_dashboard = formatted_dashboard
|
||||
|
||||
dashboard.clear
|
||||
dashboard.merge!(new_dashboard)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
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|
|
||||
panel[:id].to_s == query_params[:panelId]
|
||||
end
|
||||
end
|
||||
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)
|
||||
expression = replace_global_variables(expression, metric)
|
||||
|
||||
expression
|
||||
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 = expression.gsub('$__to', query_params[:to])
|
||||
|
||||
expression
|
||||
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
|
||||
|
||||
class InputFormatValidator
|
||||
include ::Gitlab::Metrics::Dashboard::Errors
|
||||
|
||||
attr_reader :grafana_dashboard, :datasource, :panel, :query_params
|
||||
|
||||
UNSUPPORTED_GRAFANA_GLOBAL_VARS = %w(
|
||||
$__interval_ms
|
||||
$__timeFilter
|
||||
$__name
|
||||
$timeFilter
|
||||
$interval
|
||||
).freeze
|
||||
|
||||
def initialize(grafana_dashboard, datasource, panel, query_params)
|
||||
@grafana_dashboard = grafana_dashboard
|
||||
@datasource = datasource
|
||||
@panel = panel
|
||||
@query_params = query_params
|
||||
end
|
||||
|
||||
def validate!
|
||||
validate_query_params!
|
||||
validate_datasource!
|
||||
validate_panel_type!
|
||||
validate_variable_definitions!
|
||||
validate_global_variables!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_datasource!
|
||||
return if datasource[:access] == 'proxy' && datasource[:type] == 'prometheus'
|
||||
|
||||
raise_error 'Only Prometheus datasources with proxy access in Grafana are supported.'
|
||||
end
|
||||
|
||||
def validate_query_params!
|
||||
return if [:panelId, :from, :to].all? { |param| query_params.include?(param) }
|
||||
|
||||
raise_error 'Grafana query parameters must include panelId, from, and to.'
|
||||
end
|
||||
|
||||
def validate_panel_type!
|
||||
return if panel[:type] == 'graph' && panel[:lines]
|
||||
|
||||
raise_error 'Panel type must be a line graph.'
|
||||
end
|
||||
|
||||
def validate_variable_definitions!
|
||||
return unless grafana_dashboard[:dashboard][:templating]
|
||||
|
||||
return if grafana_dashboard[:dashboard][:templating][:list].all? do |variable|
|
||||
query_params[:"var-#{variable[:name]}"].present?
|
||||
end
|
||||
|
||||
raise_error 'All Grafana variables must be defined in the query parameters.'
|
||||
end
|
||||
|
||||
def validate_global_variables!
|
||||
return unless panel_contains_unsupported_vars?
|
||||
|
||||
raise_error 'Prometheus must not include'
|
||||
end
|
||||
|
||||
def panel_contains_unsupported_vars?
|
||||
panel[:targets].any? do |target|
|
||||
UNSUPPORTED_GRAFANA_GLOBAL_VARS.any? do |variable|
|
||||
target[:expr].include?(variable)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def raise_error(message)
|
||||
raise DashboardProcessingError.new(message)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -11,6 +11,18 @@ module Grafana
|
|||
@token = token
|
||||
end
|
||||
|
||||
# @param uid [String] Unique identifier for a Grafana dashboard
|
||||
def get_dashboard(uid:)
|
||||
http_get("#{@api_url}/api/dashboards/uid/#{uid}")
|
||||
end
|
||||
|
||||
# @param name [String] Unique identifier for a Grafana datasource
|
||||
def get_datasource(name:)
|
||||
# CGI#escape formats strings such that the Grafana endpoint
|
||||
# will not recognize the dashboard name. Preferring URI#escape.
|
||||
http_get("#{@api_url}/api/datasources/name/#{URI.escape(name)}") # rubocop:disable Lint/UriEscapeUnescape
|
||||
end
|
||||
|
||||
# @param datasource_id [String] Grafana ID for the datasource
|
||||
# @param proxy_path [String] Path to proxy - ex) 'api/v1/query_range'
|
||||
def proxy_datasource(datasource_id:, proxy_path:, query: {})
|
||||
|
@ -57,7 +69,7 @@ module Grafana
|
|||
def handle_response(response)
|
||||
return response if response.code == 200
|
||||
|
||||
raise_error "Grafana response status code: #{response.code}"
|
||||
raise_error "Grafana response status code: #{response.code}, Message: #{response.body}"
|
||||
end
|
||||
|
||||
def raise_error(message)
|
||||
|
|
|
@ -6099,15 +6099,15 @@ msgstr ""
|
|||
msgid "Ensure your %{linkStart}environment is part of the deploy stage%{linkEnd} of your CI pipeline to track deployments to your cluster."
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter Admin Mode"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter IP address range"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter a number"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter admin mode"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter at least three characters to search"
|
||||
msgstr ""
|
||||
|
||||
|
@ -9680,7 +9680,7 @@ msgstr ""
|
|||
msgid "Leave"
|
||||
msgstr ""
|
||||
|
||||
msgid "Leave admin mode"
|
||||
msgid "Leave Admin Mode"
|
||||
msgstr ""
|
||||
|
||||
msgid "Leave edit mode? All unsaved changes will be lost."
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
FactoryBot.define do
|
||||
factory :grafana_integration, class: GrafanaIntegration do
|
||||
project
|
||||
grafana_url { 'https://grafana.com' }
|
||||
grafana_url { 'https://grafana.example.com' }
|
||||
token { SecureRandom.hex(10) }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,6 +5,7 @@ require 'spec_helper'
|
|||
describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_not_mock_admin_mode do
|
||||
include StubENV
|
||||
include TermsHelper
|
||||
include MobileHelpers
|
||||
|
||||
let(:admin) { create(:admin) }
|
||||
|
||||
|
@ -450,6 +451,32 @@ describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_not_moc
|
|||
expect(page).to have_link(text: 'Support', href: new_support_url)
|
||||
end
|
||||
end
|
||||
|
||||
it 'Shows admin dashboard links on bigger screen' do
|
||||
visit root_dashboard_path
|
||||
|
||||
page.within '.navbar' do
|
||||
expect(page).to have_link(text: 'Admin Area', href: admin_root_path, visible: true)
|
||||
expect(page).to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true)
|
||||
end
|
||||
end
|
||||
|
||||
it 'Relocates admin dashboard links to dropdown list on smaller screen', :js do
|
||||
resize_screen_xs
|
||||
visit root_dashboard_path
|
||||
|
||||
page.within '.navbar' do
|
||||
expect(page).not_to have_link(text: 'Admin Area', href: admin_root_path, visible: true)
|
||||
expect(page).not_to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true)
|
||||
end
|
||||
|
||||
find('.header-more').click
|
||||
|
||||
page.within '.navbar' do
|
||||
expect(page).to have_link(text: 'Admin Area', href: admin_root_path, visible: true)
|
||||
expect(page).to have_link(text: 'Leave Admin Mode', href: destroy_admin_session_path, visible: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when in admin_mode' do
|
||||
|
@ -462,7 +489,7 @@ describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_not_moc
|
|||
it 'can leave admin mode' do
|
||||
page.within('.navbar-sub-nav') do
|
||||
# Select first, link is also included in mobile view list
|
||||
click_on 'Leave admin mode', match: :first
|
||||
click_on 'Leave Admin Mode', match: :first
|
||||
|
||||
expect(page).to have_link(href: new_admin_session_path)
|
||||
end
|
||||
|
@ -481,7 +508,7 @@ describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_not_moc
|
|||
before do
|
||||
page.within('.navbar-sub-nav') do
|
||||
# Select first, link is also included in mobile view list
|
||||
click_on 'Leave admin mode', match: :first
|
||||
click_on 'Leave Admin Mode', match: :first
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,764 @@
|
|||
{
|
||||
"meta": {
|
||||
"type": "db",
|
||||
"canSave": true,
|
||||
"canEdit": true,
|
||||
"canAdmin": true,
|
||||
"canStar": true,
|
||||
"slug": "gitlab-omnibus-redis",
|
||||
"url": "/-/grafana/d/XDaNK6amz/gitlab-omnibus-redis",
|
||||
"expires": "0001-01-01T00:00:00Z",
|
||||
"created": "2019-10-04T13:43:20Z",
|
||||
"updated": "2019-10-04T13:43:20Z",
|
||||
"updatedBy": "Anonymous",
|
||||
"createdBy": "Anonymous",
|
||||
"version": 1,
|
||||
"hasAcl": false,
|
||||
"isFolder": false,
|
||||
"folderId": 1,
|
||||
"folderTitle": "GitLab Omnibus",
|
||||
"folderUrl": "/-/grafana/dashboards/f/l2EpNh2Zk/gitlab-omnibus",
|
||||
"provisioned": true,
|
||||
"provisionedExternalId": "redis.json"
|
||||
},
|
||||
"dashboard": {
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations \u0026 Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "GitLab Omnibus dashboard for Redis servers",
|
||||
"editable": true,
|
||||
"gnetId": 763,
|
||||
"graphTooltip": 0,
|
||||
"id": 3,
|
||||
"iteration": 1556027798221,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"cacheTimeout": null,
|
||||
"colorBackground": false,
|
||||
"colorValue": false,
|
||||
"colors": ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
||||
"datasource": "GitLab Omnibus",
|
||||
"decimals": 0,
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"format": "dtdurations",
|
||||
"gauge": {
|
||||
"maxValue": 100,
|
||||
"minValue": 0,
|
||||
"show": false,
|
||||
"thresholdLabels": false,
|
||||
"thresholdMarkers": true
|
||||
},
|
||||
"gridPos": { "h": 3, "w": 4, "x": 0, "y": 0 },
|
||||
"id": 9,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
{ "name": "value to text", "value": 1 },
|
||||
{ "name": "range to text", "value": 2 }
|
||||
],
|
||||
"maxDataPoints": 100,
|
||||
"nullPointMode": "connected",
|
||||
"nullText": null,
|
||||
"postfix": "",
|
||||
"postfixFontSize": "50%",
|
||||
"prefix": "",
|
||||
"prefixFontSize": "50%",
|
||||
"rangeMaps": [{ "from": "null", "text": "N/A", "to": "null" }],
|
||||
"sparkline": {
|
||||
"fillColor": "rgba(31, 118, 189, 0.18)",
|
||||
"full": false,
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"tableColumn": "addr",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "avg(time() - redis_start_time_seconds{instance=~\"$instance\"})",
|
||||
"format": "time_series",
|
||||
"instant": true,
|
||||
"interval": "",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "",
|
||||
"metric": "",
|
||||
"refId": "A",
|
||||
"step": 1800
|
||||
}
|
||||
],
|
||||
"thresholds": "",
|
||||
"title": "Uptime",
|
||||
"type": "singlestat",
|
||||
"valueFontSize": "70%",
|
||||
"valueMaps": [{ "op": "=", "text": "N/A", "value": "null" }],
|
||||
"valueName": "current"
|
||||
},
|
||||
{
|
||||
"cacheTimeout": null,
|
||||
"colorBackground": false,
|
||||
"colorValue": false,
|
||||
"colors": ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
|
||||
"datasource": "GitLab Omnibus",
|
||||
"decimals": 0,
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"format": "none",
|
||||
"gauge": {
|
||||
"maxValue": 100,
|
||||
"minValue": 0,
|
||||
"show": false,
|
||||
"thresholdLabels": false,
|
||||
"thresholdMarkers": true
|
||||
},
|
||||
"gridPos": { "h": 3, "w": 4, "x": 4, "y": 0 },
|
||||
"hideTimeOverride": true,
|
||||
"id": 12,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
{ "name": "value to text", "value": 1 },
|
||||
{ "name": "range to text", "value": 2 }
|
||||
],
|
||||
"maxDataPoints": 100,
|
||||
"nullPointMode": "connected",
|
||||
"nullText": null,
|
||||
"postfix": "",
|
||||
"postfixFontSize": "50%",
|
||||
"prefix": "",
|
||||
"prefixFontSize": "50%",
|
||||
"rangeMaps": [{ "from": "null", "text": "N/A", "to": "null" }],
|
||||
"sparkline": {
|
||||
"fillColor": "rgba(31, 118, 189, 0.18)",
|
||||
"full": false,
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": true
|
||||
},
|
||||
"tableColumn": "",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(\n avg_over_time(redis_connected_clients{instance=~\"$instance\"}[$__interval])\n)",
|
||||
"format": "time_series",
|
||||
"interval": "1m",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "",
|
||||
"metric": "",
|
||||
"refId": "A",
|
||||
"step": 2
|
||||
}
|
||||
],
|
||||
"thresholds": "",
|
||||
"timeFrom": "1m",
|
||||
"timeShift": null,
|
||||
"title": "Clients",
|
||||
"type": "singlestat",
|
||||
"valueFontSize": "80%",
|
||||
"valueMaps": [{ "op": "=", "text": "N/A", "value": "null" }],
|
||||
"valueName": "avg"
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "GitLab Omnibus",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {},
|
||||
"gridPos": { "h": 6, "w": 8, "x": 8, "y": 0 },
|
||||
"id": 2,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": false,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 2,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"paceLength": 10,
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(\n rate(redis_commands_processed_total{instance=~\"$instance\"}[$__interval])\n)",
|
||||
"format": "time_series",
|
||||
"interval": "1m",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "",
|
||||
"metric": "A",
|
||||
"refId": "A",
|
||||
"step": 240,
|
||||
"target": ""
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Commands Executed",
|
||||
"tooltip": { "msResolution": false, "shared": true, "sort": 0, "value_type": "cumulative" },
|
||||
"type": "graph",
|
||||
"xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] },
|
||||
"yaxes": [
|
||||
{ "format": "reqps", "label": null, "logBase": 1, "max": null, "min": "0", "show": true },
|
||||
{ "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true }
|
||||
],
|
||||
"yaxis": { "align": false, "alignLevel": null }
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "GitLab Omnibus",
|
||||
"decimals": 2,
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {},
|
||||
"gridPos": { "h": 6, "w": 8, "x": 16, "y": 0 },
|
||||
"id": 1,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": false,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 2,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"paceLength": 10,
|
||||
"percentage": true,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(\n rate(redis_keyspace_hits_total{instance=~\"$instance\"}[$__interval])\n)",
|
||||
"format": "time_series",
|
||||
"hide": false,
|
||||
"interval": "1m",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "hits",
|
||||
"metric": "",
|
||||
"refId": "A",
|
||||
"step": 240,
|
||||
"target": ""
|
||||
},
|
||||
{
|
||||
"expr": "sum(\n rate(redis_keyspace_misses_total{instance=~\"$instance\"}[$__interval])\n)",
|
||||
"format": "time_series",
|
||||
"hide": false,
|
||||
"interval": "1m",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "misses",
|
||||
"metric": "",
|
||||
"refId": "B",
|
||||
"step": 240,
|
||||
"target": ""
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Hits, Misses per Second",
|
||||
"tooltip": { "msResolution": false, "shared": true, "sort": 0, "value_type": "individual" },
|
||||
"type": "graph",
|
||||
"xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] },
|
||||
"yaxes": [
|
||||
{ "format": "short", "label": "", "logBase": 1, "max": null, "min": 0, "show": true },
|
||||
{ "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true }
|
||||
],
|
||||
"yaxis": { "align": false, "alignLevel": null }
|
||||
},
|
||||
{
|
||||
"aliasColors": { "max": "#BF1B00" },
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "GitLab Omnibus",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {},
|
||||
"gridPos": { "h": 10, "w": 8, "x": 0, "y": 3 },
|
||||
"id": 7,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"hideEmpty": false,
|
||||
"hideZero": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 2,
|
||||
"links": [],
|
||||
"nullPointMode": "null as zero",
|
||||
"paceLength": 10,
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [{ "alias": "/max - .*/", "dashes": true }],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "redis_memory_used_bytes{instance=~\"$instance\"}",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "used - {{instance}}",
|
||||
"metric": "",
|
||||
"refId": "A",
|
||||
"step": 240,
|
||||
"target": ""
|
||||
},
|
||||
{
|
||||
"expr": "redis_config_maxmemory{instance=~\"$instance\"} \u003e 0",
|
||||
"format": "time_series",
|
||||
"hide": false,
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "max - {{instance}}",
|
||||
"refId": "B",
|
||||
"step": 240
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Memory Usage",
|
||||
"tooltip": { "msResolution": false, "shared": true, "sort": 0, "value_type": "cumulative" },
|
||||
"type": "graph",
|
||||
"xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] },
|
||||
"yaxes": [
|
||||
{ "format": "bytes", "label": null, "logBase": 1, "max": null, "min": 0, "show": true },
|
||||
{ "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true }
|
||||
],
|
||||
"yaxis": { "align": false, "alignLevel": null }
|
||||
},
|
||||
{
|
||||
"aliasColors": {
|
||||
"evicts": "#890F02",
|
||||
"memcached_items_evicted_total{instance=\"172.17.0.1:9150\",job=\"prometheus\"}": "#890F02",
|
||||
"reclaims": "#3F6833"
|
||||
},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "GitLab Omnibus",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {},
|
||||
"gridPos": { "h": 7, "w": 8, "x": 8, "y": 6 },
|
||||
"id": 8,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 2,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"paceLength": 10,
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [{ "alias": "reclaims", "yaxis": 2 }],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(redis_expired_keys_total{instance=~\"$instance\"}[$__interval]))",
|
||||
"format": "time_series",
|
||||
"interval": "1m",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "expired",
|
||||
"metric": "",
|
||||
"refId": "A",
|
||||
"step": 240,
|
||||
"target": ""
|
||||
},
|
||||
{
|
||||
"expr": "sum(rate(redis_evicted_keys_total{instance=~\"$instance\"}[$__interval]))",
|
||||
"format": "time_series",
|
||||
"interval": "1m",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "evicted",
|
||||
"refId": "B",
|
||||
"step": 240
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Expired / Evicted",
|
||||
"tooltip": { "msResolution": false, "shared": true, "sort": 0, "value_type": "cumulative" },
|
||||
"type": "graph",
|
||||
"xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] },
|
||||
"yaxes": [
|
||||
{ "format": "short", "label": null, "logBase": 1, "max": null, "min": "0", "show": true },
|
||||
{ "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true }
|
||||
],
|
||||
"yaxis": { "align": false, "alignLevel": null }
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "GitLab Omnibus",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {},
|
||||
"gridPos": { "h": 7, "w": 8, "x": 16, "y": 6 },
|
||||
"id": 10,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 2,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"paceLength": 10,
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(\n rate(redis_net_input_bytes_total{instance=~\"$instance\"}[$__interval])\n)",
|
||||
"format": "time_series",
|
||||
"interval": "1m",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "In",
|
||||
"refId": "A",
|
||||
"step": 240
|
||||
},
|
||||
{
|
||||
"expr": "sum(\n rate(redis_net_output_bytes_total{instance=~\"$instance\"}[$__interval])\n)",
|
||||
"format": "time_series",
|
||||
"interval": "1m",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "Out",
|
||||
"refId": "B",
|
||||
"step": 240
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Network I/O",
|
||||
"tooltip": { "msResolution": true, "shared": true, "sort": 0, "value_type": "cumulative" },
|
||||
"type": "graph",
|
||||
"xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] },
|
||||
"yaxes": [
|
||||
{ "format": "Bps", "label": null, "logBase": 1, "max": null, "min": "0", "show": true },
|
||||
{ "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true }
|
||||
],
|
||||
"yaxis": { "align": false, "alignLevel": null }
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "GitLab Omnibus",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 8,
|
||||
"grid": {},
|
||||
"gridPos": { "h": 7, "w": 16, "x": 0, "y": 13 },
|
||||
"id": 14,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": true,
|
||||
"current": true,
|
||||
"max": true,
|
||||
"min": false,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"paceLength": 10,
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": true,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum without (instance) (\n rate(redis_commands_total{instance=~\"$instance\"}[$__interval])\n) \u003e 0",
|
||||
"format": "time_series",
|
||||
"interval": "1m",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{ cmd }}",
|
||||
"metric": "redis_command_calls_total",
|
||||
"refId": "A",
|
||||
"step": 240
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Command Calls / sec",
|
||||
"tooltip": { "msResolution": true, "shared": true, "sort": 2, "value_type": "individual" },
|
||||
"type": "graph",
|
||||
"xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] },
|
||||
"yaxes": [
|
||||
{ "format": "short", "label": null, "logBase": 1, "max": null, "min": "0", "show": true },
|
||||
{ "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true }
|
||||
],
|
||||
"yaxis": { "align": false, "alignLevel": null }
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "GitLab Omnibus",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 7,
|
||||
"grid": {},
|
||||
"gridPos": { "h": 7, "w": 8, "x": 16, "y": 13 },
|
||||
"id": 13,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 2,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"paceLength": 10,
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": true,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(redis_db_keys{instance=~\"$instance\"} - redis_db_keys_expiring{instance=~\"$instance\"}) ",
|
||||
"format": "time_series",
|
||||
"interval": "",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "not expiring",
|
||||
"refId": "A",
|
||||
"step": 240,
|
||||
"target": ""
|
||||
},
|
||||
{
|
||||
"expr": "sum(redis_db_keys_expiring{instance=~\"$instance\"})",
|
||||
"format": "time_series",
|
||||
"interval": "",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "expiring",
|
||||
"metric": "",
|
||||
"refId": "B",
|
||||
"step": 240
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Expiring vs Not-Expiring Keys",
|
||||
"tooltip": { "msResolution": false, "shared": true, "sort": 0, "value_type": "individual" },
|
||||
"type": "graph",
|
||||
"xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] },
|
||||
"yaxes": [
|
||||
{ "format": "short", "label": null, "logBase": 1, "max": null, "min": "0", "show": true },
|
||||
{ "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true }
|
||||
],
|
||||
"yaxis": { "align": false, "alignLevel": null }
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "GitLab Omnibus",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 7,
|
||||
"grid": {},
|
||||
"gridPos": { "h": 7, "w": 16, "x": 0, "y": 20 },
|
||||
"id": 5,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": false,
|
||||
"current": true,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 2,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"paceLength": 10,
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": true,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum by (db) (\n redis_db_keys{instance=~\"$instance\"}\n)",
|
||||
"format": "time_series",
|
||||
"interval": "",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{ db }} ",
|
||||
"refId": "A",
|
||||
"step": 240,
|
||||
"target": ""
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeRegions": [],
|
||||
"timeShift": null,
|
||||
"title": "Items per DB",
|
||||
"tooltip": { "msResolution": false, "shared": true, "sort": 0, "value_type": "individual" },
|
||||
"type": "graph",
|
||||
"xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] },
|
||||
"yaxes": [
|
||||
{ "format": "none", "label": null, "logBase": 1, "max": null, "min": "0", "show": true },
|
||||
{ "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true }
|
||||
],
|
||||
"yaxis": { "align": false, "alignLevel": null }
|
||||
}
|
||||
],
|
||||
"refresh": "1m",
|
||||
"schemaVersion": 18,
|
||||
"style": "dark",
|
||||
"tags": ["redis"],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"allValue": null,
|
||||
"current": { "tags": [], "text": "All", "value": "$__all" },
|
||||
"datasource": "GitLab Omnibus",
|
||||
"definition": "",
|
||||
"hide": 0,
|
||||
"includeAll": true,
|
||||
"label": null,
|
||||
"multi": false,
|
||||
"name": "instance",
|
||||
"options": [],
|
||||
"query": "label_values(up{job=\"redis\"}, instance)",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"sort": 0,
|
||||
"tagValuesQuery": "",
|
||||
"tags": [],
|
||||
"tagsQuery": "",
|
||||
"type": "query",
|
||||
"useTags": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": { "from": "now-24h", "to": "now" },
|
||||
"timepicker": {
|
||||
"refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
|
||||
"time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
|
||||
},
|
||||
"timezone": "",
|
||||
"title": "GitLab Omnibus - Redis",
|
||||
"uid": "XDaNK6amz",
|
||||
"version": 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"id": 1,
|
||||
"orgId": 1,
|
||||
"name": "GitLab Omnibus",
|
||||
"type": "prometheus",
|
||||
"typeLogoUrl": "",
|
||||
"access": "proxy",
|
||||
"url": "http://localhost:9090",
|
||||
"password": "",
|
||||
"user": "",
|
||||
"database": "",
|
||||
"basicAuth": false,
|
||||
"basicAuthUser": "",
|
||||
"basicAuthPassword": "",
|
||||
"withCredentials": false,
|
||||
"isDefault": true,
|
||||
"jsonData": {},
|
||||
"secureJsonFields": {},
|
||||
"version": 1,
|
||||
"readOnly": true
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"panel_groups": [
|
||||
{
|
||||
"panels": [
|
||||
{
|
||||
"title": "Network I/O",
|
||||
"type": "area-chart",
|
||||
"y_label": "",
|
||||
"metrics": [
|
||||
{
|
||||
"id": "In_0",
|
||||
"query_range": "sum( rate(redis_net_input_bytes_total{instance=~\"localhost:9121\"}[1m]))",
|
||||
"label": "In",
|
||||
"prometheus_endpoint_path": "/foo/bar/-/grafana/proxy/1/api/v1/query_range?query=sum%28++rate%28redis_net_input_bytes_total%7Binstance%3D~%22localhost%3A9121%22%7D%5B1m%5D%29%29"
|
||||
},
|
||||
{
|
||||
"id": "Out_1",
|
||||
"query_range": "sum( rate(redis_net_output_bytes_total{instance=~\"localhost:9121\"}[1m]))",
|
||||
"label": "Out",
|
||||
"prometheus_endpoint_path": "/foo/bar/-/grafana/proxy/1/api/v1/query_range?query=sum%28++rate%28redis_net_output_bytes_total%7Binstance%3D~%22localhost%3A9121%22%7D%5B1m%5D%29%29"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"dashboard": {
|
||||
"panels": [
|
||||
{
|
||||
"datasource": "GitLab Omnibus",
|
||||
"id": 8,
|
||||
"lines": true,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(\n rate(redis_net_input_bytes_total{instance=~\"$instance\"}[$__interval])\n)",
|
||||
"format": "time_series",
|
||||
"interval": "1m",
|
||||
"legendFormat": "In",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"expr": "sum(\n rate(redis_net_output_bytes_total{instance=~\"[[instance]]\"}[$__interval])\n)",
|
||||
"format": "time_series",
|
||||
"interval": "1m",
|
||||
"legendFormat": "Out",
|
||||
"refId": "B"
|
||||
}
|
||||
],
|
||||
"title": "Network I/O",
|
||||
"type": "graph",
|
||||
"yaxes": [{ "format": "Bps" }, { "format": "short" }]
|
||||
}
|
||||
],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"current": {
|
||||
"value": "localhost:9121"
|
||||
},
|
||||
"name": "instance"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"unit",
|
||||
"label",
|
||||
"prometheus_endpoint_path"
|
||||
],
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
"required": [
|
||||
"title",
|
||||
"y_label",
|
||||
"weight",
|
||||
"metrics"
|
||||
],
|
||||
"properties": {
|
||||
|
|
|
@ -25,6 +25,14 @@ describe Gitlab::Metrics::Dashboard::Processor do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when the dashboard is not present' do
|
||||
let(:dashboard_yml) { nil }
|
||||
|
||||
it 'returns nil' do
|
||||
expect(dashboard).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when dashboard config corresponds to common metrics' do
|
||||
let!(:common_metric) { create(:prometheus_metric, :common, identifier: 'metric_a1') }
|
||||
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Metrics::Dashboard::Stages::GrafanaFormatter do
|
||||
include GrafanaApiHelpers
|
||||
|
||||
let_it_be(:namespace) { create(:namespace, name: 'foo') }
|
||||
let_it_be(:project) { create(:project, namespace: namespace, name: 'bar') }
|
||||
|
||||
describe '#transform!' do
|
||||
let(:grafana_dashboard) { JSON.parse(fixture_file('grafana/simplified_dashboard_response.json'), symbolize_names: true) }
|
||||
let(:datasource) { JSON.parse(fixture_file('grafana/datasource_response.json'), symbolize_names: true) }
|
||||
|
||||
let(:dashboard) { described_class.new(project, {}, params).transform! }
|
||||
|
||||
let(:params) do
|
||||
{
|
||||
grafana_dashboard: grafana_dashboard,
|
||||
datasource: datasource,
|
||||
grafana_url: valid_grafana_dashboard_link('https://grafana.example.com')
|
||||
}
|
||||
end
|
||||
|
||||
context 'when the query and resources are configured correctly' do
|
||||
let(:expected_dashboard) { JSON.parse(fixture_file('grafana/expected_grafana_embed.json'), symbolize_names: true) }
|
||||
|
||||
it 'generates a gitlab-yml formatted dashboard' do
|
||||
expect(dashboard).to eq(expected_dashboard)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the inputs are invalid' do
|
||||
shared_examples_for 'processing error' do
|
||||
it 'raises a processing error' do
|
||||
expect { dashboard }
|
||||
.to raise_error(Gitlab::Metrics::Dashboard::Stages::InputFormatValidator::DashboardProcessingError)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the datasource is not proxyable' do
|
||||
before do
|
||||
params[:datasource][:access] = 'not-proxy'
|
||||
end
|
||||
|
||||
it_behaves_like 'processing error'
|
||||
end
|
||||
|
||||
context 'when query param "panelId" is not specified' do
|
||||
before do
|
||||
params[:grafana_url].gsub!('panelId=8', '')
|
||||
end
|
||||
|
||||
it_behaves_like 'processing error'
|
||||
end
|
||||
|
||||
context 'when query param "from" is not specified' do
|
||||
before do
|
||||
params[:grafana_url].gsub!('from=1570397739557', '')
|
||||
end
|
||||
|
||||
it_behaves_like 'processing error'
|
||||
end
|
||||
|
||||
context 'when query param "to" is not specified' do
|
||||
before do
|
||||
params[:grafana_url].gsub!('to=1570484139557', '')
|
||||
end
|
||||
|
||||
it_behaves_like 'processing error'
|
||||
end
|
||||
|
||||
context 'when the panel is not a graph' do
|
||||
before do
|
||||
params[:grafana_dashboard][:dashboard][:panels][0][:type] = 'singlestat'
|
||||
end
|
||||
|
||||
it_behaves_like 'processing error'
|
||||
end
|
||||
|
||||
context 'when the panel is not a line graph' do
|
||||
before do
|
||||
params[:grafana_dashboard][:dashboard][:panels][0][:lines] = false
|
||||
end
|
||||
|
||||
it_behaves_like 'processing error'
|
||||
end
|
||||
|
||||
context 'when the query dashboard includes undefined variables' do
|
||||
before do
|
||||
params[:grafana_url].gsub!('&var-instance=localhost:9121', '')
|
||||
end
|
||||
|
||||
it_behaves_like 'processing error'
|
||||
end
|
||||
|
||||
context 'when the expression contains unsupported global variables' do
|
||||
before do
|
||||
params[:grafana_dashboard][:dashboard][:panels][0][:targets][0][:expr] = 'sum(important_metric[$__interval_ms])'
|
||||
end
|
||||
|
||||
it_behaves_like 'processing error'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -35,7 +35,7 @@ describe Grafana::Client do
|
|||
it 'does not follow redirects' do
|
||||
expect { subject }.to raise_exception(
|
||||
Grafana::Client::Error,
|
||||
'Grafana response status code: 302'
|
||||
'Grafana response status code: 302, Message: {}'
|
||||
)
|
||||
|
||||
expect(redirect_req_stub).to have_been_requested
|
||||
|
@ -67,6 +67,30 @@ describe Grafana::Client do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#get_dashboard' do
|
||||
let(:grafana_api_url) { 'https://grafanatest.com/-/grafana-project/api/dashboards/uid/FndfgnX' }
|
||||
|
||||
subject do
|
||||
client.get_dashboard(uid: 'FndfgnX')
|
||||
end
|
||||
|
||||
it_behaves_like 'calls grafana api'
|
||||
it_behaves_like 'no redirects'
|
||||
it_behaves_like 'handles exceptions'
|
||||
end
|
||||
|
||||
describe '#get_datasource' do
|
||||
let(:grafana_api_url) { 'https://grafanatest.com/-/grafana-project/api/datasources/name/Test%20Name' }
|
||||
|
||||
subject do
|
||||
client.get_datasource(name: 'Test Name')
|
||||
end
|
||||
|
||||
it_behaves_like 'calls grafana api'
|
||||
it_behaves_like 'no redirects'
|
||||
it_behaves_like 'handles exceptions'
|
||||
end
|
||||
|
||||
describe '#proxy_datasource' do
|
||||
let(:grafana_api_url) do
|
||||
'https://grafanatest.com/-/grafana-project/' \
|
||||
|
|
|
@ -602,7 +602,7 @@ describe API::Branches do
|
|||
post api(route, user), params: { branch: 'new_design3', ref: 'foo' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(400)
|
||||
expect(json_response['message']).to eq('Invalid reference name')
|
||||
expect(json_response['message']).to eq('Invalid reference name: new_design3')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -22,5 +22,20 @@ describe CreateBranchService do
|
|||
expect(project.repository.branch_exists?('my-feature')).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when creating a branch fails' do
|
||||
let(:project) { create(:project_empty_repo) }
|
||||
|
||||
before do
|
||||
allow(project.repository).to receive(:add_branch).and_return(false)
|
||||
end
|
||||
|
||||
it 'retruns an error with the branch name' do
|
||||
result = service.execute('my-feature', 'master')
|
||||
|
||||
expect(result[:status]).to eq(:error)
|
||||
expect(result[:message]).to eq("Invalid reference name: my-feature")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Metrics::Dashboard::GrafanaMetricEmbedService do
|
||||
include MetricsDashboardHelpers
|
||||
include ReactiveCachingHelpers
|
||||
include GrafanaApiHelpers
|
||||
|
||||
let_it_be(:project) { build(:project) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:grafana_integration) { create(:grafana_integration, project: project) }
|
||||
|
||||
let(:grafana_url) do
|
||||
valid_grafana_dashboard_link(grafana_integration.grafana_url)
|
||||
end
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
describe '.valid_params?' do
|
||||
let(:valid_params) { { embedded: true, grafana_url: grafana_url } }
|
||||
|
||||
subject { described_class.valid_params?(params) }
|
||||
|
||||
let(:params) { valid_params }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
|
||||
context 'not embedded' do
|
||||
let(:params) { valid_params.except(:embedded) }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
||||
context 'undefined grafana_url' do
|
||||
let(:params) { valid_params.except(:grafana_url) }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.from_cache' do
|
||||
let(:params) { [project.id, user.id, grafana_url] }
|
||||
|
||||
subject { described_class.from_cache(*params) }
|
||||
|
||||
it 'initializes an instance of GrafanaMetricEmbedService' do
|
||||
expect(subject).to be_an_instance_of(described_class)
|
||||
expect(subject.project).to eq(project)
|
||||
expect(subject.current_user).to eq(user)
|
||||
expect(subject.params[:grafana_url]).to eq(grafana_url)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#get_dashboard', :use_clean_rails_memory_store_caching do
|
||||
let(:service_params) do
|
||||
[
|
||||
project,
|
||||
user,
|
||||
{
|
||||
embedded: true,
|
||||
grafana_url: grafana_url
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
let(:service) { described_class.new(*service_params) }
|
||||
let(:service_call) { service.get_dashboard }
|
||||
|
||||
context 'without caching' do
|
||||
before do
|
||||
synchronous_reactive_cache(service)
|
||||
end
|
||||
|
||||
it_behaves_like 'raises error for users with insufficient permissions'
|
||||
|
||||
context 'without a grafana integration' do
|
||||
before do
|
||||
allow(project).to receive(:grafana_integration).and_return(nil)
|
||||
end
|
||||
|
||||
it_behaves_like 'misconfigured dashboard service response', :bad_request
|
||||
end
|
||||
|
||||
context 'when grafana cannot be reached' do
|
||||
before do
|
||||
allow(grafana_integration.client).to receive(:get_dashboard).and_raise(::Grafana::Client::Error)
|
||||
end
|
||||
|
||||
it_behaves_like 'misconfigured dashboard service response', :service_unavailable
|
||||
end
|
||||
|
||||
context 'when panelId is missing' do
|
||||
let(:grafana_url) do
|
||||
grafana_integration.grafana_url +
|
||||
'/d/XDaNK6amz/gitlab-omnibus-redis' \
|
||||
'?from=1570397739557&to=1570484139557'
|
||||
end
|
||||
|
||||
before do
|
||||
stub_dashboard_request(grafana_integration.grafana_url)
|
||||
end
|
||||
|
||||
it_behaves_like 'misconfigured dashboard service response', :unprocessable_entity
|
||||
end
|
||||
|
||||
context 'when uid is missing' do
|
||||
let(:grafana_url) { grafana_integration.grafana_url + '/d/' }
|
||||
|
||||
before do
|
||||
stub_dashboard_request(grafana_integration.grafana_url)
|
||||
end
|
||||
|
||||
it_behaves_like 'misconfigured dashboard service response', :unprocessable_entity
|
||||
end
|
||||
|
||||
context 'when the dashboard response contains misconfigured json' do
|
||||
before do
|
||||
stub_dashboard_request(grafana_integration.grafana_url, body: '')
|
||||
end
|
||||
|
||||
it_behaves_like 'misconfigured dashboard service response', :unprocessable_entity
|
||||
end
|
||||
|
||||
context 'when the datasource response contains misconfigured json' do
|
||||
before do
|
||||
stub_dashboard_request(grafana_integration.grafana_url)
|
||||
stub_datasource_request(grafana_integration.grafana_url, body: '')
|
||||
end
|
||||
|
||||
it_behaves_like 'misconfigured dashboard service response', :unprocessable_entity
|
||||
end
|
||||
|
||||
context 'when the embed was created successfully' do
|
||||
before do
|
||||
stub_dashboard_request(grafana_integration.grafana_url)
|
||||
stub_datasource_request(grafana_integration.grafana_url)
|
||||
end
|
||||
|
||||
it_behaves_like 'valid embedded dashboard service response'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with caching', :use_clean_rails_memory_store_caching do
|
||||
let(:cache_params) { [project.id, user.id, grafana_url] }
|
||||
|
||||
context 'when value not present in cache' do
|
||||
it 'returns nil' do
|
||||
expect(ReactiveCachingWorker)
|
||||
.to receive(:perform_async)
|
||||
.with(service.class, service.id, *cache_params)
|
||||
|
||||
expect(service_call).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when value present in cache' do
|
||||
let(:return_value) { { 'http_status' => :ok, 'dashboard' => '{}' } }
|
||||
|
||||
before do
|
||||
stub_reactive_cache(service, return_value, cache_params)
|
||||
end
|
||||
|
||||
it 'returns cached value' do
|
||||
expect(ReactiveCachingWorker)
|
||||
.not_to receive(:perform_async)
|
||||
.with(service.class, service.id, *cache_params)
|
||||
|
||||
expect(service_call[:http_status]).to eq(return_value[:http_status])
|
||||
expect(service_call[:dashboard]).to eq(return_value[:dashboard])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,32 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module GrafanaApiHelpers
|
||||
def valid_grafana_dashboard_link(base_url)
|
||||
base_url +
|
||||
'/d/XDaNK6amz/gitlab-omnibus-redis' \
|
||||
'?from=1570397739557&to=1570484139557' \
|
||||
'&var-instance=localhost:9121&panelId=8'
|
||||
end
|
||||
|
||||
def stub_dashboard_request(base_url, path: '/api/dashboards/uid/XDaNK6amz', body: nil)
|
||||
body ||= fixture_file('grafana/dashboard_response.json')
|
||||
|
||||
stub_request(:get, "#{base_url}#{path}")
|
||||
.to_return(
|
||||
status: 200,
|
||||
body: body,
|
||||
headers: { 'Content-Type' => 'application/json' }
|
||||
)
|
||||
end
|
||||
|
||||
def stub_datasource_request(base_url, path: '/api/datasources/name/GitLab%20Omnibus', body: nil)
|
||||
body ||= fixture_file('grafana/datasource_response.json')
|
||||
|
||||
stub_request(:get, "#{base_url}#{path}")
|
||||
.to_return(
|
||||
status: 200,
|
||||
body: body,
|
||||
headers: { 'Content-Type' => 'application/json' }
|
||||
)
|
||||
end
|
||||
end
|
|
@ -53,7 +53,7 @@ module LoginHelpers
|
|||
|
||||
fill_in 'password', with: user.password
|
||||
|
||||
click_button 'Enter admin mode'
|
||||
click_button 'Enter Admin Mode'
|
||||
end
|
||||
|
||||
def gitlab_sign_in_via(provider, user, uid, saml_response = nil)
|
||||
|
|
Loading…
Reference in New Issue