Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-07-03 00:09:23 +00:00
parent 2c0e92d031
commit 57b359e90c
19 changed files with 426 additions and 784 deletions

View File

@ -0,0 +1,53 @@
# frozen_string_literal: true
module Metrics::Dashboard::PrometheusApiProxy
extend ActiveSupport::Concern
include RenderServiceResults
included do
before_action :authorize_read_prometheus!, only: [:prometheus_proxy]
end
def prometheus_proxy
variable_substitution_result =
proxy_variable_substitution_service.new(proxyable, permit_params).execute
if variable_substitution_result[:status] == :error
return error_response(variable_substitution_result)
end
prometheus_result = Prometheus::ProxyService.new(
proxyable,
proxy_method,
proxy_path,
variable_substitution_result[:params]
).execute
return continue_polling_response if prometheus_result.nil?
return error_response(prometheus_result) if prometheus_result[:status] == :error
success_response(prometheus_result)
end
private
def proxyable
raise NotImplementedError, "#{self.class} must implement method: #{__callee__}"
end
def proxy_variable_substitution_service
raise NotImplementedError, "#{self.class} must implement method: #{__callee__}"
end
def permit_params
params.permit!
end
def proxy_method
request.method
end
def proxy_path
params[:proxy_path]
end
end

View File

@ -1,51 +1,17 @@
# frozen_string_literal: true # frozen_string_literal: true
class Projects::Environments::PrometheusApiController < Projects::ApplicationController class Projects::Environments::PrometheusApiController < Projects::ApplicationController
include RenderServiceResults include Metrics::Dashboard::PrometheusApiProxy
before_action :authorize_read_prometheus! before_action :proxyable
before_action :environment
def proxy
variable_substitution_result =
variable_substitution_service.new(environment, permit_params).execute
if variable_substitution_result[:status] == :error
return error_response(variable_substitution_result)
end
prometheus_result = Prometheus::ProxyService.new(
environment,
proxy_method,
proxy_path,
variable_substitution_result[:params]
).execute
return continue_polling_response if prometheus_result.nil?
return error_response(prometheus_result) if prometheus_result[:status] == :error
success_response(prometheus_result)
end
private private
def variable_substitution_service def proxyable
@proxyable ||= project.environments.find(params[:id])
end
def proxy_variable_substitution_service
Prometheus::ProxyVariableSubstitutionService Prometheus::ProxyVariableSubstitutionService
end end
def permit_params
params.permit!
end
def environment
@environment ||= project.environments.find(params[:id])
end
def proxy_method
request.method
end
def proxy_path
params[:proxy_path]
end
end end

View File

@ -4,7 +4,7 @@ class AuditEvent < ApplicationRecord
include CreatedAtFilterable include CreatedAtFilterable
include IgnorableColumns include IgnorableColumns
ignore_column :updated_at, remove_with: '13.3', remove_after: '2020-08-22' ignore_column :updated_at, remove_with: '13.4', remove_after: '2020-09-22'
serialize :details, Hash # rubocop:disable Cop/ActiveRecordSerialize serialize :details, Hash # rubocop:disable Cop/ActiveRecordSerialize

View File

@ -69,8 +69,6 @@ class User < ApplicationRecord
MINIMUM_INACTIVE_DAYS = 180 MINIMUM_INACTIVE_DAYS = 180
ignore_column :ghost, remove_with: '13.2', remove_after: '2020-06-22'
# Override Devise::Models::Trackable#update_tracked_fields! # Override Devise::Models::Trackable#update_tracked_fields!
# to limit database writes to at most once every hour # to limit database writes to at most once every hour
# rubocop: disable CodeReuse/ServiceClass # rubocop: disable CodeReuse/ServiceClass

View File

@ -1,291 +0,0 @@
#!/usr/bin/env ruby
#
# Generate a feature flag entry file in the correct location.
#
# Automatically stages the file and amends the previous commit if the `--amend`
# argument is used.
require 'optparse'
require 'yaml'
require 'fileutils'
require 'cgi'
require_relative '../lib/feature/shared' unless defined?(Feature::Shared)
Options = Struct.new(
:name,
:type,
:group,
:ee,
:amend,
:dry_run,
:force,
:introduced_by_url,
:rollout_issue_url
)
module FeatureFlagHelpers
Abort = Class.new(StandardError)
Done = Class.new(StandardError)
def capture_stdout(cmd)
output = IO.popen(cmd, &:read)
fail_with "command failed: #{cmd.join(' ')}" unless $?.success?
output
end
def fail_with(message)
raise Abort, "\e[31merror\e[0m #{message}"
end
end
class FeatureFlagOptionParser
extend FeatureFlagHelpers
extend ::Feature::Shared
class << self
def parse(argv)
options = Options.new
parser = OptionParser.new do |opts|
opts.banner = "Usage: #{__FILE__} [options] <feature-flag>\n\n"
# Note: We do not provide a shorthand for this in order to match the `git
# commit` interface
opts.on('--amend', 'Amend the previous commit') do |value|
options.amend = value
end
opts.on('-f', '--force', 'Overwrite an existing entry') do |value|
options.force = value
end
opts.on('-m', '--introduced-by-url [string]', String, 'URL to Merge Request introducing Feature Flag') do |value|
options.introduced_by_url = value
end
opts.on('-i', '--rollout-issue-url [string]', String, 'URL to Issue rolling out Feature Flag') do |value|
options.rollout_issue_url = value
end
opts.on('-n', '--dry-run', "Don't actually write anything, just print") do |value|
options.dry_run = value
end
opts.on('-g', '--group [string]', String, "The group introducing a feature flag, like: `group::apm`") do |value|
options.group = value if value.start_with?('group::')
end
opts.on('-t', '--type [string]', String, "The category of the feature flag, valid options are: #{TYPES.keys.map(&:to_s).join(', ')}") do |value|
options.type = value.to_sym if TYPES[value.to_sym]
end
opts.on('-e', '--ee', 'Generate a feature flag entry for GitLab EE') do |value|
options.ee = value
end
opts.on('-h', '--help', 'Print help message') do
$stdout.puts opts
raise Done.new
end
end
parser.parse!(argv)
unless argv.one?
$stdout.puts parser.help
$stdout.puts
raise Abort, 'Feature flag name is required'
end
# Name is a first name
options.name = argv.first
options
end
def read_group
$stdout.puts ">> Please specify the group introducing feature flag, like `group::apm`:"
loop do
$stdout.print "\n?> "
group = $stdin.gets.strip
group = nil if group.empty?
return group if group.nil? || group.start_with?('group::')
$stderr.puts "Group needs to include `group::`"
end
end
def read_type
$stdout.puts ">> Please specify the type of your feature flag:"
$stdout.puts
TYPES.each do |type, data|
$stdout.puts "#{type.to_s.rjust(15)}#{' '*6}#{data[:description]}"
end
loop do
$stdout.print "\n?> "
type = $stdin.gets.strip.to_sym
return type if TYPES[type]
$stderr.puts "Invalid type specified '#{type}'"
end
end
def read_issue_url(options)
return unless TYPES.dig(options.type, :rollout_issue)
url = "https://gitlab.com/gitlab-org/gitlab/-/issues/new"
title = "[Feature flag] Rollout of `#{options.name}`"
description = File.read('.gitlab/issue_templates/Feature Flag Roll Out.md')
description.sub!(':feature_name', options.name)
issue_new_url = url + "?" +
"issue[title]=" + CGI.escape(title) + "&"
# TODO: We should be able to pick `issueable_template`
# + "issue[description]=" + CGI.escape(description)
$stdout.puts ">> Open this URL and fill the rest of details:"
$stdout.puts issue_new_url
$stdout.puts
$stdout.puts ">> Paste URL here, or enter to skip:"
loop do
$stdout.print "\n?> "
created_url = $stdin.gets.strip
created_url = nil if created_url.empty?
return created_url if created_url.nil? || created_url.start_with?('https://')
$stderr.puts "URL needs to start with https://"
end
end
end
end
class FeatureFlagCreator
include FeatureFlagHelpers
attr_reader :options
def initialize(options)
@options = options
end
def execute
assert_feature_branch!
assert_name!
assert_existing_feature_flag!
# Read type from $stdin unless is already set
options.type ||= FeatureFlagOptionParser.read_type
options.group ||= FeatureFlagOptionParser.read_group
options.rollout_issue_url ||= FeatureFlagOptionParser.read_issue_url(options)
$stdout.puts "\e[32mcreate\e[0m #{file_path}"
$stdout.puts contents
unless options.dry_run
write
amend_commit if options.amend
end
if editor
system("#{editor} '#{file_path}'")
end
end
private
def contents
YAML.dump(
'name' => options.name,
'introduced_by_url' => options.introduced_by_url,
'rollout_issue_url' => options.rollout_issue_url,
'group' => options.group.to_s,
'type' => options.type.to_s,
'default_enabled' => false
).strip
end
def write
FileUtils.mkdir_p(File.dirname(file_path))
File.write(file_path, contents)
end
def editor
ENV['EDITOR']
end
def amend_commit
fail_with "git add failed" unless system(*%W[git add #{file_path}])
Kernel.exec(*%w[git commit --amend])
end
def assert_feature_branch!
return unless branch_name == 'master'
fail_with "Create a branch first!"
end
def assert_existing_feature_flag!
existing_path = all_feature_flag_names[options.name]
return unless existing_path
return if options.force
fail_with "#{existing_path} already exists! Use `--force` to overwrite."
end
def assert_name!
return if options.name.match(/\A[a-z0-9_-]+\Z/)
fail_with "Provide a name for the feature flag that is [a-z0-9_-]"
end
def file_path
feature_flags_paths.last
.sub('**', options.type.to_s)
.sub('*.yml', options.name + '.yml')
end
def all_feature_flag_names
@all_feature_flag_names ||=
feature_flags_paths.map do |glob_path|
Dir.glob(glob_path).map do |path|
[File.basename(path, '.yml'), path]
end
end.flatten(1).to_h
end
def feature_flags_paths
paths = []
paths << File.join('config', 'feature_flags', '**', '*.yml')
paths << File.join('ee', 'config', 'feature_flags', '**', '*.yml') if ee?
paths
end
def ee?
options.ee
end
def branch_name
@branch_name ||= capture_stdout(%w[git symbolic-ref --short HEAD]).strip
end
end
if $0 == __FILE__
begin
options = FeatureFlagOptionParser.parse(ARGV)
FeatureFlagCreator.new(options).execute
rescue FeatureFlagHelpers::Abort => ex
$stderr.puts ex.message
exit 1
rescue FeatureFlagHelpers::Done
exit
end
end
# vim: ft=ruby

View File

@ -0,0 +1,5 @@
---
title: Add custom Dockerfile paths to Auto DevOps Build stage with DOCKERFILE_PATH
merge_request: 35662
author: thklein
type: added

View File

@ -257,7 +257,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
# This route is also defined in gitlab-workhorse. Make sure to update accordingly. # This route is also defined in gitlab-workhorse. Make sure to update accordingly.
get '/terminal.ws/authorize', to: 'environments#terminal_websocket_authorize', format: false get '/terminal.ws/authorize', to: 'environments#terminal_websocket_authorize', format: false
get '/prometheus/api/v1/*proxy_path', to: 'environments/prometheus_api#proxy', as: :prometheus_api get '/prometheus/api/v1/*proxy_path', to: 'environments/prometheus_api#prometheus_proxy', as: :prometheus_api
get '/sample_metrics', to: 'environments/sample_metrics#query' get '/sample_metrics', to: 'environments/sample_metrics#query'
end end

View File

@ -35,7 +35,11 @@ def check_changelog_yaml(path)
fail "`title` should be set, in #{gitlab.html_link(path)}! #{SEE_DOC}" if yaml["title"].nil? fail "`title` should be set, in #{gitlab.html_link(path)}! #{SEE_DOC}" if yaml["title"].nil?
fail "`type` should be set, in #{gitlab.html_link(path)}! #{SEE_DOC}" if yaml["type"].nil? fail "`type` should be set, in #{gitlab.html_link(path)}! #{SEE_DOC}" if yaml["type"].nil?
if yaml["merge_request"].nil? && !helper.security_mr? return if helper.security_mr?
cherry_pick_against_stable_branch = helper.cherry_pick_mr? && helper.stable_branch?
if yaml["merge_request"].nil?
mr_line = raw_file.lines.find_index("merge_request:\n") mr_line = raw_file.lines.find_index("merge_request:\n")
if mr_line if mr_line
@ -43,7 +47,7 @@ def check_changelog_yaml(path)
else else
message "Consider setting `merge_request` to #{gitlab.mr_json["iid"]} in #{gitlab.html_link(path)}. #{SEE_DOC}" message "Consider setting `merge_request` to #{gitlab.mr_json["iid"]} in #{gitlab.html_link(path)}. #{SEE_DOC}"
end end
elsif yaml["merge_request"] != gitlab.mr_json["iid"] && !helper.security_mr? elsif yaml["merge_request"] != gitlab.mr_json["iid"] && !cherry_pick_against_stable_branch
fail "Merge request ID was not set to #{gitlab.mr_json["iid"]}! #{SEE_DOC}" fail "Merge request ID was not set to #{gitlab.mr_json["iid"]}! #{SEE_DOC}"
end end
rescue Psych::SyntaxError, Psych::DisallowedClass, Psych::BadAlias rescue Psych::SyntaxError, Psych::DisallowedClass, Psych::BadAlias

View File

@ -6,85 +6,90 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Grafana Configuration # Grafana Configuration
[Grafana](https://grafana.com/) is a tool that allows you to visualize time [Grafana](https://grafana.com/) is a tool that enables you to visualize time
series metrics through graphs and dashboards. GitLab writes performance data to Prometheus series metrics through graphs and dashboards. GitLab writes performance data to Prometheus,
and Grafana will allow you to query to display useful graphs. and Grafana allows you to query the data to display useful graphs.
## Installation ## Installation
[Omnibus GitLab can help you install Grafana (recommended)](https://docs.gitlab.com/omnibus/settings/grafana.html) Omnibus GitLab can [help you install Grafana (recommended)](https://docs.gitlab.com/omnibus/settings/grafana.html)
or Grafana supplies package repositories (Yum/Apt) for easy installation. or Grafana supplies package repositories (Yum/Apt) for easy installation.
See [Grafana installation documentation](https://grafana.com/docs/grafana/latest/installation/) See [Grafana installation documentation](https://grafana.com/docs/grafana/latest/installation/)
for detailed steps. for detailed steps.
NOTE: **Note:** NOTE: **Note:**
Before starting Grafana for the first time, set the admin user Before starting Grafana for the first time, set the admin user
and password in `/etc/grafana/grafana.ini`. Otherwise, the default password and password in `/etc/grafana/grafana.ini`. If you don't, the default password
will be `admin`. is `admin`.
## Configuration ## Configuration
Login as the admin user. Expand the menu by clicking the Grafana logo in the 1. Log in to Grafana as the admin user.
top left corner. Choose 'Data Sources' from the menu. Then, click 'Add new' 1. Expand the menu by clicking the Grafana logo in the top left corner.
in the top bar. 1. Choose **Data Sources** from the menu.
1. Click **Add new** in the top bar:
![Grafana empty data source page](img/grafana_data_source_empty.png) ![Grafana empty data source page](img/grafana_data_source_empty.png)
1. Edit the data source to fit your needs:
![Grafana data source configurations](img/grafana_data_source_configuration.png) ![Grafana data source configurations](img/grafana_data_source_configuration.png)
1. Click **Save**.
## Import Dashboards ## Import Dashboards
You can now import a set of default dashboards that will give you a good You can now import a set of default dashboards to start displaying useful information.
start on displaying useful information. GitLab has published a set of default GitLab has published a set of default
[Grafana dashboards](https://gitlab.com/gitlab-org/grafana-dashboards) to get you started. Clone the [Grafana dashboards](https://gitlab.com/gitlab-org/grafana-dashboards) to get you started.
repository or download a zip/tarball, then follow these steps to import each Clone the repository, or download a ZIP file or tarball, then follow these steps to import each
JSON file. JSON file individually:
Open the dashboard dropdown menu and click 'Import' 1. Log in to Grafana as the admin user.
1. Open the dashboard dropdown menu and click **Import**:
![Grafana dashboard dropdown](img/grafana_dashboard_dropdown.png)
1. Click **Choose file**, and browse to the location where you downloaded or
cloned the dashboard repository. Select a JSON file to import:
![Grafana dashboard import](img/grafana_dashboard_import.png)
1. After the dashboard is imported, click the **Save dashboard** icon in the top bar:
![Grafana save icon](img/grafana_save_icon.png)
![Grafana dashboard dropdown](img/grafana_dashboard_dropdown.png) NOTE: **Note:**
If you don't save the dashboard after importing it, the dashboard is removed
Click 'Choose file' and browse to the location where you downloaded or cloned when you navigate away from the page.
the dashboard repository. Pick one of the JSON files to import.
![Grafana dashboard import](img/grafana_dashboard_import.png)
Once the dashboard is imported, be sure to click save icon in the top bar. If
you do not save the dashboard after importing it will be removed when you
navigate away.
![Grafana save icon](img/grafana_save_icon.png)
Repeat this process for each dashboard you wish to import. Repeat this process for each dashboard you wish to import.
Alternatively you can automatically import all the dashboards into your Grafana Alternatively, you can import all the dashboards into your Grafana
instance. See the README of the [Grafana dashboards](https://gitlab.com/gitlab-org/grafana-dashboards) instance. For more information about this process, see the
repository for more information on this process. [README of the Grafana dashboards](https://gitlab.com/gitlab-org/grafana-dashboards)
repository.
## Integration with GitLab UI ## Integration with GitLab UI
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/61005) in GitLab 12.1. > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/61005) in GitLab 12.1.
If you have set up Grafana, you can enable a link to access it easily from the sidebar: After setting up Grafana, you can enable a link to access it easily from the
GitLab sidebar:
1. Go to the **Admin Area > Settings > Metrics and profiling**. 1. Navigate to the **{admin}** **Admin Area > Settings > Metrics and profiling**.
1. Expand **Metrics - Grafana**. 1. Expand **Metrics - Grafana**.
1. Check the "Enable access to Grafana" checkbox. 1. Check the **Enable access to Grafana** checkbox.
1. If Grafana is enabled through Omnibus GitLab and on the same server, 1. Configure the **Grafana URL**:
leave **Grafana URL** unchanged. It should be `/-/grafana`. - *If Grafana is enabled through Omnibus GitLab and on the same server,*
leave **Grafana URL** unchanged. It should be `/-/grafana`.
In any other case, enter the full URL of the Grafana instance. - *Otherwise,* enter the full URL of the Grafana instance.
1. Click **Save changes**. 1. Click **Save changes**.
1. The new link will be available in the **Admin Area > Monitoring > Metrics Dashboard**.
GitLab displays your link in the **{admin}** **Admin Area > Monitoring > Metrics Dashboard**.
## Security Update ## Security Update
Users running GitLab version 12.0 or later should immediately upgrade to one of the following security releases due to a known vulnerability with the embedded Grafana dashboard: Users running GitLab version 12.0 or later should immediately upgrade to one of the
following security releases due to a known vulnerability with the embedded Grafana dashboard:
- 12.0.6 - 12.0.6
- 12.1.6 - 12.1.6
After upgrading, the Grafana dashboard will be disabled and the location of your existing Grafana data will be changed from `/var/opt/gitlab/grafana/data/` to `/var/opt/gitlab/grafana/data.bak.#{Date.today}/`. After upgrading, the Grafana dashboard is disabled, and the location of your
existing Grafana data is changed from `/var/opt/gitlab/grafana/data/` to
`/var/opt/gitlab/grafana/data.bak.#{Date.today}/`.
To prevent the data from being relocated, you can run the following command prior to upgrading: To prevent the data from being relocated, you can run the following command prior to upgrading:
@ -100,19 +105,23 @@ sudo mv /var/opt/gitlab/grafana/data.bak.xxxx/ /var/opt/gitlab/grafana/data/
However, you should **not** reinstate your old data _except_ under one of the following conditions: However, you should **not** reinstate your old data _except_ under one of the following conditions:
1. If you are certain that you changed your default admin password when you enabled Grafana 1. If you're certain that you changed your default admin password when you enabled Grafana.
1. If you run GitLab in a private network, accessed only by trusted users, and your Grafana login page has not been exposed to the internet 1. If you run GitLab in a private network, accessed only by trusted users, and your
Grafana login page has not been exposed to the internet.
If you require access to your old Grafana data but do not meet one of these criteria, you may consider: If you require access to your old Grafana data but don't meet one of these criteria, you may consider:
1. Reinstating it temporarily. 1. Reinstating it temporarily.
1. [Exporting the dashboards](https://grafana.com/docs/grafana/latest/reference/export_import/#exporting-a-dashboard) you need. 1. [Exporting the dashboards](https://grafana.com/docs/grafana/latest/reference/export_import/#exporting-a-dashboard) you need.
1. Refreshing the data and [re-importing your dashboards](https://grafana.com/docs/grafana/latest/reference/export_import/#importing-a-dashboard). 1. Refreshing the data and [re-importing your dashboards](https://grafana.com/docs/grafana/latest/reference/export_import/#importing-a-dashboard).
DANGER: **Danger:** DANGER: **Danger:**
This poses a temporary vulnerability while your old Grafana data is in use and the decision to do so should be weighed carefully with your need to access existing data and dashboards. These actions pose a temporary vulnerability while your old Grafana data is in use.
Deciding to take any of these actions should be weighed carefully with your need to access
existing data and dashboards.
For more information and further mitigation details, please refer to our [blog post on the security release](https://about.gitlab.com/releases/2019/08/12/critical-security-release-gitlab-12-dot-1-dot-6-released/). For more information and further mitigation details, please refer to our
[blog post on the security release](https://about.gitlab.com/releases/2019/08/12/critical-security-release-gitlab-12-dot-1-dot-6-released/).
--- ---

View File

@ -9,27 +9,25 @@ info: To determine the technical writer assigned to the Stage/Group associated w
>- Available since [Omnibus GitLab 8.17](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/1132). >- Available since [Omnibus GitLab 8.17](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/1132).
>- Renamed from `GitLab monitor exporter` to `GitLab exporter` in [GitLab 12.3](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16511). >- Renamed from `GitLab monitor exporter` to `GitLab exporter` in [GitLab 12.3](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16511).
The [GitLab exporter](https://gitlab.com/gitlab-org/gitlab-exporter) allows you to The [GitLab exporter](https://gitlab.com/gitlab-org/gitlab-exporter) enables you to
measure various GitLab metrics, pulled from Redis and the database, in Omnibus GitLab measure various GitLab metrics pulled from Redis and the database in Omnibus GitLab
instances. instances.
NOTE: **Note:** NOTE: **Note:**
For installations from source you'll have to install and configure it yourself. For installations from source you must install and configure it yourself.
To enable the GitLab exporter in an Omnibus GitLab instance: To enable the GitLab exporter in an Omnibus GitLab instance:
1. [Enable Prometheus](index.md#configuring-prometheus) 1. [Enable Prometheus](index.md#configuring-prometheus).
1. Edit `/etc/gitlab/gitlab.rb` 1. Edit `/etc/gitlab/gitlab.rb`.
1. Add or find and uncomment the following line, making sure it's set to `true`: 1. Add, or find and uncomment, the following line, making sure it's set to `true`:
```ruby ```ruby
gitlab_exporter['enable'] = true gitlab_exporter['enable'] = true
``` ```
1. Save the file and [reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure) 1. Save the file and [reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure)
for the changes to take effect for the changes to take effect.
Prometheus will now automatically begin collecting performance data from Prometheus automatically begins collecting performance data from
the GitLab exporter exposed under `localhost:9168`. the GitLab exporter exposed at `localhost:9168`.
[← Back to the main Prometheus page](index.md)

View File

@ -14,18 +14,18 @@ To enable the GitLab Prometheus metrics:
1. [Restart GitLab](../../restart_gitlab.md#omnibus-gitlab-restart) for the changes to take effect. 1. [Restart GitLab](../../restart_gitlab.md#omnibus-gitlab-restart) for the changes to take effect.
NOTE: **Note:** NOTE: **Note:**
For installations from source you'll have to configure it yourself. For installations from source you must configure it yourself.
## Collecting the metrics ## Collecting the metrics
GitLab monitors its own internal service metrics, and makes them available at the GitLab monitors its own internal service metrics, and makes them available at the
`/-/metrics` endpoint. Unlike other [Prometheus](https://prometheus.io) exporters, to access `/-/metrics` endpoint. Unlike other [Prometheus](https://prometheus.io) exporters, to access
it, the client IP address needs to be [explicitly allowed](../ip_whitelist.md). the metrics, the client IP address must be [explicitly allowed](../ip_whitelist.md).
For [Omnibus GitLab](https://docs.gitlab.com/omnibus/) and Chart installations, For [Omnibus GitLab](https://docs.gitlab.com/omnibus/) and Chart installations,
these metrics are enabled and collected as of these metrics are enabled and collected as of
[GitLab 9.4](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/1702). [GitLab 9.4](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/1702).
For source installations or earlier versions, these metrics must be enabled For source installations, these metrics must be enabled
manually and collected by a Prometheus server. manually and collected by a Prometheus server.
For enabling and viewing metrics from Sidekiq nodes, see [Sidekiq metrics](#sidekiq-metrics). For enabling and viewing metrics from Sidekiq nodes, see [Sidekiq metrics](#sidekiq-metrics).
@ -40,7 +40,7 @@ The following metrics are available:
| `gitlab_banzai_cacheless_render_real_duration_seconds` | Histogram | 9.4 | Duration of rendering Markdown into HTML when cached output does not exist | `controller`, `action` | | `gitlab_banzai_cacheless_render_real_duration_seconds` | Histogram | 9.4 | Duration of rendering Markdown into HTML when cached output does not exist | `controller`, `action` |
| `gitlab_cache_misses_total` | Counter | 10.2 | Cache read miss | `controller`, `action` | | `gitlab_cache_misses_total` | Counter | 10.2 | Cache read miss | `controller`, `action` |
| `gitlab_cache_operation_duration_seconds` | Histogram | 10.2 | Cache access time | | | `gitlab_cache_operation_duration_seconds` | Histogram | 10.2 | Cache access time | |
| `gitlab_cache_operations_total` | Counter | 12.2 | Cache operations by controller/action | `controller`, `action`, `operation` | | `gitlab_cache_operations_total` | Counter | 12.2 | Cache operations by controller or action | `controller`, `action`, `operation` |
| `gitlab_ci_pipeline_creation_duration_seconds` | Histogram | 13.0 | Time in seconds it takes to create a CI/CD pipeline | | | `gitlab_ci_pipeline_creation_duration_seconds` | Histogram | 13.0 | Time in seconds it takes to create a CI/CD pipeline | |
| `gitlab_ci_pipeline_size_builds` | Histogram | 13.1 | Total number of builds within a pipeline grouped by a pipeline source | `source` | | `gitlab_ci_pipeline_size_builds` | Histogram | 13.1 | Total number of builds within a pipeline grouped by a pipeline source | `source` |
| `job_waiter_started_total` | Counter | 12.9 | Number of batches of jobs started where a web request is waiting for the jobs to complete | `worker` | | `job_waiter_started_total` | Counter | 12.9 | Number of batches of jobs started where a web request is waiting for the jobs to complete | `worker` |
@ -92,9 +92,9 @@ The following metrics are available:
| `gitlab_view_rendering_duration_seconds` | Histogram | 10.2 | Duration for views (histogram) | `controller`, `action`, `view` | | `gitlab_view_rendering_duration_seconds` | Histogram | 10.2 | Duration for views (histogram) | `controller`, `action`, `view` |
| `http_requests_total` | Counter | 9.4 | Rack request count | `method` | | `http_requests_total` | Counter | 9.4 | Rack request count | `method` |
| `http_request_duration_seconds` | Histogram | 9.4 | HTTP response time from rack middleware | `method`, `status` | | `http_request_duration_seconds` | Histogram | 9.4 | HTTP response time from rack middleware | `method`, `status` |
| `gitlab_transaction_db_count_total` | Counter | 13.1 | Counter for total number of sql calls | `controller`, `action` | | `gitlab_transaction_db_count_total` | Counter | 13.1 | Counter for total number of SQL calls | `controller`, `action` |
| `gitlab_transaction_db_write_count_total` | Counter | 13.1 | Counter for total number of write sql calls | `controller`, `action` | | `gitlab_transaction_db_write_count_total` | Counter | 13.1 | Counter for total number of write SQL calls | `controller`, `action` |
| `gitlab_transaction_db_cached_count_total` | Counter | 13.1 | Counter for total number of cached sql calls | `controller`, `action` | | `gitlab_transaction_db_cached_count_total` | Counter | 13.1 | Counter for total number of cached SQL calls | `controller`, `action` |
| `http_redis_requests_duration_seconds` | Histogram | 13.1 | Redis requests duration during web transactions | `controller`, `action` | | `http_redis_requests_duration_seconds` | Histogram | 13.1 | Redis requests duration during web transactions | `controller`, `action` |
| `http_redis_requests_total` | Counter | 13.1 | Redis requests count during web transactions | `controller`, `action` | | `http_redis_requests_total` | Counter | 13.1 | Redis requests count during web transactions | `controller`, `action` |
| `http_elasticsearch_requests_duration_seconds` **(STARTER)** | Histogram | 13.1 | Elasticsearch requests duration during web transactions | `controller`, `action` | | `http_elasticsearch_requests_duration_seconds` **(STARTER)** | Histogram | 13.1 | Elasticsearch requests duration during web transactions | `controller`, `action` |
@ -120,7 +120,7 @@ The following metrics can be controlled by feature flags:
## Sidekiq metrics ## Sidekiq metrics
Sidekiq jobs may also gather metrics, and these metrics can be accessed if the Sidekiq jobs may also gather metrics, and these metrics can be accessed if the
Sidekiq exporter is enabled (for example, using the `monitoring.sidekiq_exporter` Sidekiq exporter is enabled: for example, using the `monitoring.sidekiq_exporter`
configuration option in `gitlab.yml`. These metrics are served from the configuration option in `gitlab.yml`. These metrics are served from the
`/metrics` path on the configured port. `/metrics` path on the configured port.
@ -187,16 +187,16 @@ The following metrics are available:
## Connection pool metrics ## Connection pool metrics
These metrics record the status of the database [connection pools](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/ConnectionPool.html). These metrics record the status of the database
[connection pools](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/ConnectionPool.html),
and the metrics all have these labels:
They all have these labels: - `class` - the Ruby class being recorded.
- `ActiveRecord::Base` is the main database connection.
1. `class` - the Ruby class being recorded. - `Geo::TrackingBase` is the connection to the Geo tracking database, if
1. `ActiveRecord::Base` is the main database connection. enabled.
1. `Geo::TrackingBase` is the connection to the Geo tracking database, if - `host` - the host name used to connect to the database.
enabled. - `port` - the port used to connect to the database.
1. `host` - the host name used to connect to the database.
1. `port` - the port used to connect to the database.
| Metric | Type | Since | Description | | Metric | Type | Since | Description |
|:----------------------------------------------|:------|:------|:--------------------------------------------------| |:----------------------------------------------|:------|:------|:--------------------------------------------------|
@ -256,10 +256,10 @@ When Puma is used instead of Unicorn, the following metrics are available:
GitLab's Prometheus client requires a directory to store metrics data shared between multi-process services. GitLab's Prometheus client requires a directory to store metrics data shared between multi-process services.
Those files are shared among all instances running under Unicorn server. Those files are shared among all instances running under Unicorn server.
The directory must be accessible to all running Unicorn's processes, or The directory must be accessible to all running Unicorn's processes, or
metrics won't function correctly. metrics can't function correctly.
This directory's location is configured using environment variable `prometheus_multiproc_dir`. This directory's location is configured using environment variable `prometheus_multiproc_dir`.
For best performance, create this directory in `tmpfs`. For best performance, create this directory in `tmpfs`.
If GitLab is installed using [Omnibus GitLab](https://docs.gitlab.com/omnibus/) If GitLab is installed using [Omnibus GitLab](https://docs.gitlab.com/omnibus/)
and `tmpfs` is available, then the metrics directory will be configured for you. and `tmpfs` is available, then GitLab configures the metrics directory for you.

View File

@ -41,11 +41,16 @@ If your goal is to use only a single custom buildpack, you should provide the pr
## Custom `Dockerfile` ## Custom `Dockerfile`
> Support for `DOCKERFILE_PATH` was [added in GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35662)
If your project has a `Dockerfile` in the root of the project repository, Auto DevOps If your project has a `Dockerfile` in the root of the project repository, Auto DevOps
builds a Docker image based on the Dockerfile, rather than using buildpacks. builds a Docker image based on the Dockerfile, rather than using buildpacks.
This can be much faster and result in smaller images, especially if your This can be much faster and result in smaller images, especially if your
Dockerfile is based on [Alpine](https://hub.docker.com/_/alpine/). Dockerfile is based on [Alpine](https://hub.docker.com/_/alpine/).
If you set the `DOCKERFILE_PATH` CI variable, Auto Build looks for a Dockerfile there
instead.
## Passing arguments to `docker build` ## Passing arguments to `docker build`
Arguments can be passed to the `docker build` command using the Arguments can be passed to the `docker build` command using the
@ -311,6 +316,7 @@ applications.
| `CANARY_ENABLED` | From GitLab 11.0, used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments-premium). | | `CANARY_ENABLED` | From GitLab 11.0, used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments-premium). |
| `CANARY_PRODUCTION_REPLICAS` | Number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md) in the production environment. Takes precedence over `CANARY_REPLICAS`. Defaults to 1. | | `CANARY_PRODUCTION_REPLICAS` | Number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md) in the production environment. Takes precedence over `CANARY_REPLICAS`. Defaults to 1. |
| `CANARY_REPLICAS` | Number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md). Defaults to 1. | | `CANARY_REPLICAS` | Number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md). Defaults to 1. |
| `DOCKERFILE_PATH` | From GitLab 13.2, allows overriding the [default Dockerfile path for the build stage](#custom-dockerfile) |
| `HELM_RELEASE_NAME` | From GitLab 12.1, allows the `helm` release name to be overridden. Can be used to assign unique release names when deploying multiple projects to a single namespace. | | `HELM_RELEASE_NAME` | From GitLab 12.1, allows the `helm` release name to be overridden. Can be used to assign unique release names when deploying multiple projects to a single namespace. |
| `HELM_UPGRADE_VALUES_FILE` | From GitLab 12.6, allows the `helm upgrade` values file to be overridden. Defaults to `.gitlab/auto-deploy-values.yaml`. | | `HELM_UPGRADE_VALUES_FILE` | From GitLab 12.6, allows the `helm upgrade` values file to be overridden. Defaults to `.gitlab/auto-deploy-values.yaml`. |
| `HELM_UPGRADE_EXTRA_ARGS` | From GitLab 11.11, allows extra arguments in `helm` commands when deploying the application. Note that using quotes won't prevent word splitting. | | `HELM_UPGRADE_EXTRA_ARGS` | From GitLab 11.11, allows extra arguments in `helm` commands when deploying the application. Note that using quotes won't prevent word splitting. |

View File

@ -667,6 +667,11 @@ To install applications using GitLab CI/CD:
- template: Managed-Cluster-Applications.gitlab-ci.yml - template: Managed-Cluster-Applications.gitlab-ci.yml
``` ```
NOTE: **Note:**
The job provided by this template connects to the cluster using tools provided
in a custom Docker image. It requires that you have a runner registered with the Docker,
Kubernetes, or Docker Machine executor.
1. Add a `.gitlab/managed-apps/config.yaml` file to define which 1. Add a `.gitlab/managed-apps/config.yaml` file to define which
applications you would like to install. Define the `installed` key as applications you would like to install. Define the `installed` key as
`true` to install the application and `false` to uninstall the `true` to install the application and `false` to uninstall the

View File

@ -1,6 +1,6 @@
build: build:
stage: build stage: build
image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-build-image:v0.2.3" image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-build-image:v0.3.0"
variables: variables:
DOCKER_TLS_CERTDIR: "" DOCKER_TLS_CERTDIR: ""
services: services:

View File

@ -196,6 +196,18 @@ module Gitlab
gitlab_helper.mr_json['web_url'].include?('/gitlab-org/security/') gitlab_helper.mr_json['web_url'].include?('/gitlab-org/security/')
end end
def cherry_pick_mr?
return false unless gitlab_helper
/cherry[\s-]*pick/i.match?(gitlab_helper.mr_json['title'])
end
def stable_branch?
return false unless gitlab_helper
/\A\d+-\d+-stable-ee/i.match?(gitlab_helper.mr_json['target_branch'])
end
def mr_has_labels?(*labels) def mr_has_labels?(*labels)
return false unless gitlab_helper return false unless gitlab_helper

View File

@ -1,191 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
load File.expand_path('../../bin/feature-flag', __dir__)
RSpec.describe 'bin/feature-flag' do
using RSpec::Parameterized::TableSyntax
describe FeatureFlagCreator do
let(:argv) { %w[feature-flag-name -t development -g group::memory -i https://url] }
let(:options) { FeatureFlagOptionParser.parse(argv) }
let(:creator) { described_class.new(options) }
let(:existing_flag) { File.join('config', 'feature_flags', 'development', 'existing-feature-flag.yml') }
before do
# create a dummy feature flag
FileUtils.mkdir_p(File.dirname(existing_flag))
File.write(existing_flag, '{}')
# ignore writes
allow(File).to receive(:write).and_return(true)
# ignore stdin
allow($stdin).to receive(:gets).and_raise('EOF')
# ignore Git commands
allow(creator).to receive(:branch_name) { 'feature-branch' }
end
after do
FileUtils.rm_f(existing_flag)
end
subject { creator.execute }
it 'properly creates a feature flag' do
expect(File).to receive(:write).with(
File.join('config', 'feature_flags', 'development', 'feature-flag-name.yml'),
anything)
expect do
subject
end.to output(/name: feature-flag-name/).to_stdout
end
context 'when running on master' do
it 'requires feature branch' do
expect(creator).to receive(:branch_name) { 'master' }
expect { subject }.to raise_error(FeatureFlagHelpers::Abort, /Create a branch first/)
end
end
context 'validates feature flag name' do
where(:argv, :ex) do
%w[.invalid.feature.flag] | /Provide a name for the feature flag that is/
%w[existing-feature-flag] | /already exists!/
end
with_them do
it do
expect { subject }.to raise_error(ex)
end
end
end
end
describe FeatureFlagOptionParser do
describe '.parse' do
where(:param, :argv, :result) do
:name | %w[foo] | 'foo'
:amend | %w[foo --amend] | true
:force | %w[foo -f] | true
:force | %w[foo --force] | true
:ee | %w[foo -e] | true
:ee | %w[foo --ee] | true
:introduced_by_url | %w[foo -m https://url] | 'https://url'
:introduced_by_url | %w[foo --introduced-by-url https://url] | 'https://url'
:rollout_issue_url | %w[foo -i https://url] | 'https://url'
:rollout_issue_url | %w[foo --rollout-issue-url https://url] | 'https://url'
:dry_run | %w[foo -n] | true
:dry_run | %w[foo --dry-run] | true
:type | %w[foo -t development] | :development
:type | %w[foo --type development] | :development
:type | %w[foo -t invalid] | nil
:type | %w[foo --type invalid] | nil
:group | %w[foo -g group::memory] | 'group::memory'
:group | %w[foo --group group::memory] | 'group::memory'
:group | %w[foo -g invalid] | nil
:group | %w[foo --group invalid] | nil
end
with_them do
it do
options = described_class.parse(Array(argv))
expect(options.public_send(param)).to eq(result)
end
end
it 'missing feature flag name' do
expect do
expect { described_class.parse(%w[--amend]) }.to output(/Feature flag name is required/).to_stdout
end.to raise_error(FeatureFlagHelpers::Abort)
end
it 'parses -h' do
expect do
expect { described_class.parse(%w[foo -h]) }.to output(/Usage:/).to_stdout
end.to raise_error(FeatureFlagHelpers::Done)
end
end
describe '.read_type' do
let(:type) { 'development' }
it 'reads type from $stdin' do
expect($stdin).to receive(:gets).and_return(type)
expect do
expect(described_class.read_type).to eq(:development)
end.to output(/specify the type/).to_stdout
end
context 'invalid type given' do
let(:type) { 'invalid' }
it 'shows error message and retries' do
expect($stdin).to receive(:gets).and_return(type)
expect($stdin).to receive(:gets).and_raise('EOF')
expect do
expect { described_class.read_type }.to raise_error(/EOF/)
end.to output(/specify the type/).to_stdout
.and output(/Invalid type specified/).to_stderr
end
end
end
describe '.read_group' do
let(:group) { 'group::memory' }
it 'reads type from $stdin' do
expect($stdin).to receive(:gets).and_return(group)
expect do
expect(described_class.read_group).to eq('group::memory')
end.to output(/specify the group/).to_stdout
end
context 'invalid group given' do
let(:type) { 'invalid' }
it 'shows error message and retries' do
expect($stdin).to receive(:gets).and_return(type)
expect($stdin).to receive(:gets).and_raise('EOF')
expect do
expect { described_class.read_group }.to raise_error(/EOF/)
end.to output(/specify the group/).to_stdout
.and output(/Group needs to include/).to_stderr
end
end
end
describe '.rollout_issue_url' do
let(:options) { OpenStruct.new(name: 'foo', type: :development) }
let(:url) { 'https://issue' }
it 'reads type from $stdin' do
expect($stdin).to receive(:gets).and_return(url)
expect do
expect(described_class.read_issue_url(options)).to eq('https://issue')
end.to output(/Paste URL here/).to_stdout
end
context 'invalid URL given' do
let(:type) { 'invalid' }
it 'shows error message and retries' do
expect($stdin).to receive(:gets).and_return(type)
expect($stdin).to receive(:gets).and_raise('EOF')
expect do
expect { described_class.read_issue_url(options) }.to raise_error(/EOF/)
end.to output(/Paste URL here/).to_stdout
.and output(/URL needs to start/).to_stderr
end
end
end
end
end

View File

@ -3,215 +3,73 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Projects::Environments::PrometheusApiController do RSpec.describe Projects::Environments::PrometheusApiController do
let_it_be(:project) { create(:project) }
let_it_be(:environment) { create(:environment, project: project) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:proxyable) { create(:environment, project: project) }
before do before do
project.add_reporter(user) project.add_reporter(user)
sign_in(user) sign_in(user)
end end
describe 'GET #proxy' do describe 'GET #prometheus_proxy' do
let(:prometheus_proxy_service) { instance_double(Prometheus::ProxyService) } it_behaves_like 'metrics dashboard prometheus api proxy' do
let(:proxyable_params) do
let(:expected_params) do {
ActionController::Parameters.new( id: proxyable.id.to_s,
environment_params( namespace_id: project.namespace.full_path,
proxy_path: 'query', project_id: project.name
controller: 'projects/environments/prometheus_api', }
action: 'proxy'
)
).permit!
end
context 'with valid requests' do
before do
allow(Prometheus::ProxyService).to receive(:new)
.with(environment, 'GET', 'query', expected_params)
.and_return(prometheus_proxy_service)
allow(prometheus_proxy_service).to receive(:execute)
.and_return(service_result)
end end
context 'with success result' do context 'with variables' do
let(:service_result) { { status: :success, body: prometheus_body } }
let(:prometheus_body) { '{"status":"success"}' } let(:prometheus_body) { '{"status":"success"}' }
let(:prometheus_json_body) { Gitlab::Json.parse(prometheus_body) } let(:pod_name) { "pod1" }
it 'returns prometheus response' do before do
get :proxy, params: environment_params expected_params[:query] = %{up{pod_name="#{pod_name}"}}
expected_params[:variables] = { 'pod_name' => pod_name }
end
it 'replaces variables with values' do
get :prometheus_proxy, params: prometheus_proxy_params.merge(
query: 'up{pod_name="{{pod_name}}"}', variables: { 'pod_name' => pod_name }
)
expect(response).to have_gitlab_http_status(:success)
expect(Prometheus::ProxyService).to have_received(:new) expect(Prometheus::ProxyService).to have_received(:new)
.with(environment, 'GET', 'query', expected_params) .with(proxyable, 'GET', 'query', expected_params)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq(prometheus_json_body)
end end
context 'with format string' do context 'with invalid variables' do
before do let(:params_with_invalid_variables) do
expected_params[:query] = %{up{environment="#{environment.slug}"}} prometheus_proxy_params.merge(
end query: 'up{pod_name="{{pod_name}}"}', variables: ['a']
it 'replaces variables with values' do
get :proxy, params: environment_params.merge(query: 'up{environment="{{ci_environment_slug}}"}')
expect(Prometheus::ProxyService).to have_received(:new)
.with(environment, 'GET', 'query', expected_params)
end
context 'with nil query' do
let(:params_without_query) do
environment_params.except(:query)
end
before do
expected_params.delete(:query)
end
it 'does not raise error' do
get :proxy, params: params_without_query
expect(Prometheus::ProxyService).to have_received(:new)
.with(environment, 'GET', 'query', expected_params)
end
end
end
context 'with variables' do
let(:pod_name) { "pod1" }
before do
expected_params[:query] = %{up{pod_name="#{pod_name}"}}
expected_params[:variables] = { 'pod_name' => pod_name }
end
it 'replaces variables with values' do
get :proxy, params: environment_params.merge(
query: 'up{pod_name="{{pod_name}}"}', variables: { 'pod_name' => pod_name }
) )
expect(response).to have_gitlab_http_status(:success)
expect(Prometheus::ProxyService).to have_received(:new)
.with(environment, 'GET', 'query', expected_params)
end end
context 'with invalid variables' do it 'returns 400' do
let(:params_with_invalid_variables) do get :prometheus_proxy, params: params_with_invalid_variables
environment_params.merge(
query: 'up{pod_name="{{pod_name}}"}', variables: ['a']
)
end
it 'returns 400' do
get :proxy, params: params_with_invalid_variables
expect(response).to have_gitlab_http_status(:bad_request)
expect(Prometheus::ProxyService).not_to receive(:new)
end
end
end
end
context 'with nil result' do
let(:service_result) { nil }
it 'returns 204 no_content' do
get :proxy, params: environment_params
expect(json_response['status']).to eq(_('processing'))
expect(json_response['message']).to eq(_('Not ready yet. Try again later.'))
expect(response).to have_gitlab_http_status(:no_content)
end
end
context 'with 404 result' do
let(:service_result) { { http_status: 404, status: :success, body: '{"body": "value"}' } }
it 'returns body' do
get :proxy, params: environment_params
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['body']).to eq('value')
end
end
context 'with error result' do
context 'with http_status' do
let(:service_result) do
{ http_status: :service_unavailable, status: :error, message: 'error message' }
end
it 'sets the http response status code' do
get :proxy, params: environment_params
expect(response).to have_gitlab_http_status(:service_unavailable)
expect(json_response['status']).to eq('error')
expect(json_response['message']).to eq('error message')
end
end
context 'without http_status' do
let(:service_result) { { status: :error, message: 'error message' } }
it 'returns bad_request' do
get :proxy, params: environment_params
expect(response).to have_gitlab_http_status(:bad_request) expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['status']).to eq('error') expect(Prometheus::ProxyService).not_to receive(:new)
expect(json_response['message']).to eq('error message')
end end
end end
end end
end
context 'with inappropriate requests' do
context 'with anonymous user' do context 'with anonymous user' do
let(:prometheus_body) { nil }
before do before do
sign_out(user) sign_out(user)
end end
it 'redirects to signin page' do it 'redirects to signin page' do
get :proxy, params: environment_params get :prometheus_proxy, params: prometheus_proxy_params
expect(response).to redirect_to(new_user_session_path) expect(response).to redirect_to(new_user_session_path)
end end
end end
context 'without correct permissions' do
before do
project.team.truncate
end
it 'returns 404' do
get :proxy, params: environment_params
expect(response).to have_gitlab_http_status(:not_found)
end
end
end end
context 'with invalid environment id' do
let(:other_environment) { create(:environment) }
it 'returns 404' do
get :proxy, params: environment_params(id: other_environment.id)
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
private
def environment_params(params = {})
{
id: environment.id.to_s,
namespace_id: project.namespace.full_path,
project_id: project.name,
proxy_path: 'query',
query: '1'
}.merge(params)
end end
end end

View File

@ -369,6 +369,69 @@ RSpec.describe Gitlab::Danger::Helper do
end end
end end
describe '#cherry_pick_mr?' do
it 'returns false when `gitlab_helper` is unavailable' do
expect(helper).to receive(:gitlab_helper).and_return(nil)
expect(helper).not_to be_cherry_pick_mr
end
context 'when MR title does not mention a cherry-pick' do
it 'returns false' do
expect(fake_gitlab).to receive(:mr_json)
.and_return('title' => 'Add feature xyz')
expect(helper).not_to be_cherry_pick_mr
end
end
context 'when MR title mentions a cherry-pick' do
[
'Cherry Pick !1234',
'cherry-pick !1234',
'CherryPick !1234'
].each do |mr_title|
it 'returns true' do
expect(fake_gitlab).to receive(:mr_json)
.and_return('title' => mr_title)
expect(helper).to be_cherry_pick_mr
end
end
end
end
describe '#stable_branch?' do
it 'returns false when `gitlab_helper` is unavailable' do
expect(helper).to receive(:gitlab_helper).and_return(nil)
expect(helper).not_to be_stable_branch
end
context 'when MR target branch is not a stable branch' do
it 'returns false' do
expect(fake_gitlab).to receive(:mr_json)
.and_return('target_branch' => 'my-feature-branch')
expect(helper).not_to be_stable_branch
end
end
context 'when MR target branch is a stable branch' do
%w[
13-1-stable-ee
13-1-stable-ee-patch-1
].each do |target_branch|
it 'returns true' do
expect(fake_gitlab).to receive(:mr_json)
.and_return('target_branch' => target_branch)
expect(helper).to be_stable_branch
end
end
end
end
describe '#mr_has_label?' do describe '#mr_has_label?' do
it 'returns false when `gitlab_helper` is unavailable' do it 'returns false when `gitlab_helper` is unavailable' do
expect(helper).to receive(:gitlab_helper).and_return(nil) expect(helper).to receive(:gitlab_helper).and_return(nil)

View File

@ -0,0 +1,147 @@
# frozen_string_literal: true
RSpec.shared_examples_for 'metrics dashboard prometheus api proxy' do
let(:service_params) { [proxyable, 'GET', 'query', expected_params] }
let(:service_result) { { status: :success, body: prometheus_body } }
let(:prometheus_proxy_service) { instance_double(Prometheus::ProxyService) }
let(:proxyable_params) do
{
id: proxyable.id.to_s
}
end
let(:expected_params) do
ActionController::Parameters.new(
prometheus_proxy_params(
proxy_path: 'query',
controller: described_class.controller_path,
action: 'prometheus_proxy'
)
).permit!
end
before do
allow_next_instance_of(Prometheus::ProxyService, *service_params) do |proxy_service|
allow(proxy_service).to receive(:execute).and_return(service_result)
end
end
context 'with valid requests' do
context 'with success result' do
let(:prometheus_body) { '{"status":"success"}' }
let(:prometheus_json_body) { Gitlab::Json.parse(prometheus_body) }
it 'returns prometheus response' do
get :prometheus_proxy, params: prometheus_proxy_params
expect(Prometheus::ProxyService).to have_received(:new).with(*service_params)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq(prometheus_json_body)
end
context 'with nil query' do
let(:params_without_query) do
prometheus_proxy_params.except(:query)
end
before do
expected_params.delete(:query)
end
it 'does not raise error' do
get :prometheus_proxy, params: params_without_query
expect(Prometheus::ProxyService).to have_received(:new).with(*service_params)
end
end
end
context 'with nil result' do
let(:service_result) { nil }
it 'returns 204 no_content' do
get :prometheus_proxy, params: prometheus_proxy_params
expect(json_response['status']).to eq(_('processing'))
expect(json_response['message']).to eq(_('Not ready yet. Try again later.'))
expect(response).to have_gitlab_http_status(:no_content)
end
end
context 'with 404 result' do
let(:service_result) { { http_status: 404, status: :success, body: '{"body": "value"}' } }
it 'returns body' do
get :prometheus_proxy, params: prometheus_proxy_params
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['body']).to eq('value')
end
end
context 'with error result' do
context 'with http_status' do
let(:service_result) do
{ http_status: :service_unavailable, status: :error, message: 'error message' }
end
it 'sets the http response status code' do
get :prometheus_proxy, params: prometheus_proxy_params
expect(response).to have_gitlab_http_status(:service_unavailable)
expect(json_response['status']).to eq('error')
expect(json_response['message']).to eq('error message')
end
end
context 'without http_status' do
let(:service_result) { { status: :error, message: 'error message' } }
it 'returns bad_request' do
get :prometheus_proxy, params: prometheus_proxy_params
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['status']).to eq('error')
expect(json_response['message']).to eq('error message')
end
end
end
end
context 'with inappropriate requests' do
let(:prometheus_body) { nil }
context 'without correct permissions' do
let(:user2) { create(:user) }
before do
sign_out(user)
sign_in(user2)
end
it 'returns 404' do
get :prometheus_proxy, params: prometheus_proxy_params
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
context 'with invalid proxyable id' do
let(:prometheus_body) { nil }
it 'returns 404' do
get :prometheus_proxy, params: prometheus_proxy_params(id: proxyable.id + 1)
expect(response).to have_gitlab_http_status(:not_found)
end
end
private
def prometheus_proxy_params(params = {})
{
proxy_path: 'query',
query: '1'
}.merge(proxyable_params).merge(params)
end
end