Commit 2a56df68 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 73fe2ca0 0535308b
<script> <script>
import { GlDropdown, GlTooltipDirective, GlIcon, GlLink, GlSprintf } from '@gitlab/ui'; import { GlDropdown, GlTooltipDirective, GlIcon, GlLink, GlSprintf, GlBadge } from '@gitlab/ui';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { __, s__, sprintf } from '~/locale'; import { __, s__, sprintf } from '~/locale';
...@@ -30,6 +30,7 @@ export default { ...@@ -30,6 +30,7 @@ export default {
CommitComponent, CommitComponent,
ExternalUrlComponent, ExternalUrlComponent,
GlDropdown, GlDropdown,
GlBadge,
GlIcon, GlIcon,
GlLink, GlLink,
GlSprintf, GlSprintf,
...@@ -621,9 +622,9 @@ export default { ...@@ -621,9 +622,9 @@ export default {
<span v-if="model.size === 1">{{ model.name }}</span> <span v-if="model.size === 1">{{ model.name }}</span>
<span v-else>{{ model.name_without_type }}</span> <span v-else>{{ model.name_without_type }}</span>
</a> </a>
<span v-if="isProtected" class="badge badge-success"> <gl-badge v-if="isProtected" variant="success">{{
{{ s__('Environments|protected') }} s__('Environments|protected')
</span> }}</gl-badge>
</span> </span>
<span <span
v-else v-else
...@@ -639,7 +640,7 @@ export default { ...@@ -639,7 +640,7 @@ export default {
<span> {{ model.folderName }} </span> <span> {{ model.folderName }} </span>
<span class="badge badge-pill"> {{ model.size }} </span> <gl-badge>{{ model.size }}</gl-badge>
</span> </span>
</div> </div>
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
- if @user.groups.any? - if @user.groups.any?
.card .card
.card-header= _('Group projects') .card-header= _('Groups')
%ul.hover-list %ul.hover-list
- @user.group_members.includes(:source).each do |group_member| # rubocop: disable CodeReuse/ActiveRecord - @user.group_members.includes(:source).each do |group_member| # rubocop: disable CodeReuse/ActiveRecord
- group = group_member.group - group = group_member.group
......
...@@ -4,6 +4,8 @@ group: Distribution ...@@ -4,6 +4,8 @@ group: Distribution
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
--- ---
<!-- any changes made to this page should be reflected in https://about.gitlab.com/support/statement-of-support.html#alpha--beta-features and https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha-beta-ga -->
# Support for Alpha, Beta, and Generally Available Features **(PREMIUM)** # Support for Alpha, Beta, and Generally Available Features **(PREMIUM)**
Some GitLab features are released as [Alpha or Beta versions](https://about.gitlab.com/support/statement-of-support.html#alpha--beta-features) which are not fully supported. All other features are considered to be Generally Available (GA). Some GitLab features are released as [Alpha or Beta versions](https://about.gitlab.com/support/statement-of-support.html#alpha--beta-features) which are not fully supported. All other features are considered to be Generally Available (GA).
...@@ -30,7 +32,7 @@ Characteristics of beta features: ...@@ -30,7 +32,7 @@ Characteristics of beta features:
- Features and functions are not likely to change. - Features and functions are not likely to change.
- Data loss is not likely. - Data loss is not likely.
Your Support Contract provides **best-efforts** support for Beta features, with the expectation that issues will require extra time and assistance from development to troubleshoot. Your Support Contract provides **commercially-reasonable effort** support for Beta features, with the expectation that issues will require extra time and assistance from development to troubleshoot.
## Generally Available (GA) ## Generally Available (GA)
......
...@@ -1305,9 +1305,11 @@ If you're using backup restore procedures, you may encounter the following ...@@ -1305,9 +1305,11 @@ If you're using backup restore procedures, you may encounter the following
warning messages: warning messages:
```plaintext ```plaintext
psql:/var/opt/gitlab/backups/db/database.sql:22: ERROR: must be owner of extension plpgsql ERROR: must be owner of extension pg_trgm
psql:/var/opt/gitlab/backups/db/database.sql:2931: WARNING: no privileges could be revoked for "public" (two occurrences) ERROR: must be owner of extension btree_gist
psql:/var/opt/gitlab/backups/db/database.sql:2933: WARNING: no privileges were granted for "public" (two occurrences) ERROR: must be owner of extension plpgsql
WARNING: no privileges could be revoked for "public" (two occurrences)
WARNING: no privileges were granted for "public" (two occurrences)
``` ```
Be advised that the backup is successfully restored in spite of these warning Be advised that the backup is successfully restored in spite of these warning
......
...@@ -3,6 +3,7 @@ import { GlDropdownDivider, GlDropdownItem, GlTruncate } from '@gitlab/ui'; ...@@ -3,6 +3,7 @@ import { GlDropdownDivider, GlDropdownItem, GlTruncate } from '@gitlab/ui';
import { union, without, get, set, keyBy } from 'lodash'; import { union, without, get, set, keyBy } from 'lodash';
import { DEFAULT_SCANNER, SCANNER_ID_PREFIX } from 'ee/security_dashboard/constants'; import { DEFAULT_SCANNER, SCANNER_ID_PREFIX } from 'ee/security_dashboard/constants';
import { createScannerOption } from 'ee/security_dashboard/helpers'; import { createScannerOption } from 'ee/security_dashboard/helpers';
import { REPORT_TYPE_CLUSTER_IMAGE_SCANNING } from '~/vue_shared/security_reports/constants';
import FilterBody from './filter_body.vue'; import FilterBody from './filter_body.vue';
import FilterItem from './filter_item.vue'; import FilterItem from './filter_item.vue';
import SimpleFilter from './simple_filter.vue'; import SimpleFilter from './simple_filter.vue';
...@@ -53,7 +54,11 @@ export default { ...@@ -53,7 +54,11 @@ export default {
const options = keyBy(this.filter.options, 'reportType'); const options = keyBy(this.filter.options, 'reportType');
const groups = { GitLab: options }; const groups = { GitLab: options };
this.scanners.forEach((scanner) => { const scanners = this.scanners.filter(
(x) => x.report_type.toLowerCase() !== REPORT_TYPE_CLUSTER_IMAGE_SCANNING,
);
scanners.forEach((scanner) => {
const vendor = scanner.vendor || DEFAULT_SCANNER; // Default to GitLab if there's no vendor. const vendor = scanner.vendor || DEFAULT_SCANNER; // Default to GitLab if there's no vendor.
const reportType = scanner.report_type; const reportType = scanner.report_type;
const id = `${vendor}.${reportType}`; const id = `${vendor}.${reportType}`;
......
...@@ -5,7 +5,7 @@ import VueRouter from 'vue-router'; ...@@ -5,7 +5,7 @@ import VueRouter from 'vue-router';
import FilterItem from 'ee/security_dashboard/components/shared/filters/filter_item.vue'; import FilterItem from 'ee/security_dashboard/components/shared/filters/filter_item.vue';
import ScannerFilter from 'ee/security_dashboard/components/shared/filters/scanner_filter.vue'; import ScannerFilter from 'ee/security_dashboard/components/shared/filters/scanner_filter.vue';
import { DEFAULT_SCANNER, SCANNER_ID_PREFIX } from 'ee/security_dashboard/constants'; import { DEFAULT_SCANNER, SCANNER_ID_PREFIX } from 'ee/security_dashboard/constants';
import { vendorScannerFilter } from 'ee/security_dashboard/helpers'; import { vendorScannerFilterNoClusterImage } from 'ee/security_dashboard/helpers';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(VueRouter); localVue.use(VueRouter);
...@@ -41,7 +41,7 @@ describe('Scanner Filter component', () => { ...@@ -41,7 +41,7 @@ describe('Scanner Filter component', () => {
let filter; let filter;
const createWrapper = ({ scanners = customScanners } = {}) => { const createWrapper = ({ scanners = customScanners } = {}) => {
filter = cloneDeep(vendorScannerFilter); filter = cloneDeep(vendorScannerFilterNoClusterImage);
wrapper = shallowMount(ScannerFilter, { wrapper = shallowMount(ScannerFilter, {
localVue, localVue,
...@@ -125,7 +125,7 @@ describe('Scanner Filter component', () => { ...@@ -125,7 +125,7 @@ describe('Scanner Filter component', () => {
it('emits filter-changed event with expected data for selected options', async () => { it('emits filter-changed event with expected data for selected options', async () => {
const ids = ['GitLab.SAST', 'Custom.SAST', 'GitLab.API_FUZZING', 'GitLab.COVERAGE_FUZZING']; const ids = ['GitLab.SAST', 'Custom.SAST', 'GitLab.API_FUZZING', 'GitLab.COVERAGE_FUZZING'];
router.replace({ query: { [vendorScannerFilter.id]: ids } }); router.replace({ query: { [vendorScannerFilterNoClusterImage.id]: ids } });
const selectedScanners = customScanners.filter((x) => const selectedScanners = customScanners.filter((x) =>
ids.includes(`${x.vendor}.${x.report_type}`), ids.includes(`${x.vendor}.${x.report_type}`),
); );
......
...@@ -39,13 +39,8 @@ module Gitlab ...@@ -39,13 +39,8 @@ module Gitlab
@server = ::WEBrick::HTTPServer.new( @server = ::WEBrick::HTTPServer.new(
Port: settings.port, BindAddress: settings.address, Port: settings.port, BindAddress: settings.address,
Logger: logger, AccessLog: access_log) Logger: logger, AccessLog: access_log
server.mount_proc '/readiness' do |req, res| )
render_probe(readiness_probe, req, res)
end
server.mount_proc '/liveness' do |req, res|
render_probe(liveness_probe, req, res)
end
server.mount '/', Rack::Handler::WEBrick, rack_app server.mount '/', Rack::Handler::WEBrick, rack_app
true true
...@@ -72,8 +67,14 @@ module Gitlab ...@@ -72,8 +67,14 @@ module Gitlab
end end
def rack_app def rack_app
readiness = readiness_probe
liveness = liveness_probe
pid = thread_name
Rack::Builder.app do Rack::Builder.app do
use Rack::Deflater use Rack::Deflater
use Gitlab::Metrics::Exporter::MetricsMiddleware, pid
use Gitlab::Metrics::Exporter::HealthChecksMiddleware, readiness, liveness
use ::Prometheus::Client::Rack::Exporter if ::Gitlab::Metrics.metrics_folder_present? use ::Prometheus::Client::Rack::Exporter if ::Gitlab::Metrics.metrics_folder_present?
run -> (env) { [404, {}, ['']] } run -> (env) { [404, {}, ['']] }
end end
...@@ -86,14 +87,6 @@ module Gitlab ...@@ -86,14 +87,6 @@ module Gitlab
def liveness_probe def liveness_probe
::Gitlab::HealthChecks::Probes::Collection.new ::Gitlab::HealthChecks::Probes::Collection.new
end end
def render_probe(probe, req, res)
result = probe.execute
res.status = result.http_status
res.content_type = 'application/json; charset=utf-8'
res.body = result.json.to_json
end
end end
end end
end end
......
# frozen_string_literal: true
module Gitlab
module Metrics
module Exporter
class HealthChecksMiddleware
def initialize(app, readiness_probe, liveness_probe)
@app = app
@readiness_probe = readiness_probe
@liveness_probe = liveness_probe
end
def call(env)
case env['PATH_INFO']
when '/readiness' then render_probe(@readiness_probe)
when '/liveness' then render_probe(@liveness_probe)
else @app.call(env)
end
end
private
def render_probe(probe)
result = probe.execute
[
result.http_status,
{ 'Content-Type' => 'application/json; charset=utf-8' },
[result.json.to_json]
]
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module Metrics
module Exporter
class MetricsMiddleware
def initialize(app, pid)
@app = app
default_labels = {
pid: pid
}
@requests_total = Gitlab::Metrics.counter(
:exporter_http_requests_total, 'Total number of HTTP requests', default_labels
)
@request_durations = Gitlab::Metrics.histogram(
:exporter_http_request_duration_seconds,
'HTTP request duration histogram (seconds)',
default_labels,
[0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]
)
end
def call(env)
start = Gitlab::Metrics::System.monotonic_time
@app.call(env).tap do |response|
duration = Gitlab::Metrics::System.monotonic_time - start
labels = {
method: env['REQUEST_METHOD'].downcase,
path: env['PATH_INFO'].to_s,
code: response.first.to_s
}
@requests_total.increment(labels)
@request_durations.observe(labels, duration)
end
end
end
end
end
end
...@@ -16787,9 +16787,6 @@ msgstr "" ...@@ -16787,9 +16787,6 @@ msgstr ""
msgid "Group project URLs are prefixed with the group namespace" msgid "Group project URLs are prefixed with the group namespace"
msgstr "" msgstr ""
msgid "Group projects"
msgstr ""
msgid "Group requires separate account" msgid "Group requires separate account"
msgstr "" msgstr ""
......
...@@ -24,6 +24,8 @@ require_relative '../lib/gitlab/metrics/samplers/base_sampler' ...@@ -24,6 +24,8 @@ require_relative '../lib/gitlab/metrics/samplers/base_sampler'
require_relative '../lib/gitlab/metrics/samplers/ruby_sampler' require_relative '../lib/gitlab/metrics/samplers/ruby_sampler'
require_relative '../lib/gitlab/metrics/exporter/base_exporter' require_relative '../lib/gitlab/metrics/exporter/base_exporter'
require_relative '../lib/gitlab/metrics/exporter/sidekiq_exporter' require_relative '../lib/gitlab/metrics/exporter/sidekiq_exporter'
require_relative '../lib/gitlab/metrics/exporter/metrics_middleware'
require_relative '../lib/gitlab/metrics/exporter/health_checks_middleware'
require_relative '../lib/gitlab/health_checks/probes/collection' require_relative '../lib/gitlab/health_checks/probes/collection'
require_relative '../lib/gitlab/health_checks/probes/status' require_relative '../lib/gitlab/health_checks/probes/status'
require_relative '../lib/gitlab/process_management' require_relative '../lib/gitlab/process_management'
......
...@@ -23,6 +23,8 @@ RSpec.describe 'bin/metrics-server', :aggregate_failures do ...@@ -23,6 +23,8 @@ RSpec.describe 'bin/metrics-server', :aggregate_failures do
end end
context 'with a running server' do context 'with a running server' do
let(:metrics_dir) { Dir.mktmpdir }
before do before do
# We need to send a request to localhost # We need to send a request to localhost
WebMock.allow_net_connect! WebMock.allow_net_connect!
...@@ -33,7 +35,8 @@ RSpec.describe 'bin/metrics-server', :aggregate_failures do ...@@ -33,7 +35,8 @@ RSpec.describe 'bin/metrics-server', :aggregate_failures do
env = { env = {
'GITLAB_CONFIG' => config_file.path, 'GITLAB_CONFIG' => config_file.path,
'METRICS_SERVER_TARGET' => 'sidekiq', 'METRICS_SERVER_TARGET' => 'sidekiq',
'WIPE_METRICS_DIR' => '1' 'WIPE_METRICS_DIR' => '1',
'prometheus_multiproc_dir' => metrics_dir
} }
@pid = Process.spawn(env, 'bin/metrics-server', pgroup: true) @pid = Process.spawn(env, 'bin/metrics-server', pgroup: true)
end end
...@@ -55,6 +58,7 @@ RSpec.describe 'bin/metrics-server', :aggregate_failures do ...@@ -55,6 +58,7 @@ RSpec.describe 'bin/metrics-server', :aggregate_failures do
# 'No such process' means the process died before # 'No such process' means the process died before
ensure ensure
config_file.unlink config_file.unlink
FileUtils.rm_rf(metrics_dir, secure: true)
end end
it 'serves /metrics endpoint' do it 'serves /metrics endpoint' do
......
...@@ -462,9 +462,9 @@ RSpec.describe 'Admin::Users' do ...@@ -462,9 +462,9 @@ RSpec.describe 'Admin::Users' do
visit projects_admin_user_path(user) visit projects_admin_user_path(user)
end end
it 'lists group projects' do it 'lists groups' do
within(:css, '.gl-mb-3 + .card') do within(:css, '.gl-mb-3 + .card') do
expect(page).to have_content 'Group projects' expect(page).to have_content 'Groups'
expect(page).to have_link group.name, href: admin_group_path(group) expect(page).to have_link group.name, href: admin_group_path(group)
end end
end end
......
...@@ -111,6 +111,18 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do ...@@ -111,6 +111,18 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do
describe 'request handling' do describe 'request handling' do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
let(:fake_collector) do
Class.new do
def initialize(app, ...)
@app = app
end
def call(env)
@app.call(env)
end
end
end
where(:method_class, :path, :http_status) do where(:method_class, :path, :http_status) do
Net::HTTP::Get | '/metrics' | 200 Net::HTTP::Get | '/metrics' | 200
Net::HTTP::Get | '/liveness' | 200 Net::HTTP::Get | '/liveness' | 200
...@@ -123,6 +135,8 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do ...@@ -123,6 +135,8 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do
allow(settings).to receive(:port).and_return(0) allow(settings).to receive(:port).and_return(0)
allow(settings).to receive(:address).and_return('127.0.0.1') allow(settings).to receive(:address).and_return('127.0.0.1')
stub_const('Gitlab::Metrics::Exporter::MetricsMiddleware', fake_collector)
# We want to wrap original method # We want to wrap original method
# and run handling of requests # and run handling of requests
# in separate thread # in separate thread
...@@ -134,8 +148,6 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do ...@@ -134,8 +148,6 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do
# is raised as we close listeners # is raised as we close listeners
end end
end end
exporter.start.join
end end
after do after do
...@@ -146,12 +158,25 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do ...@@ -146,12 +158,25 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do
let(:config) { exporter.server.config } let(:config) { exporter.server.config }
let(:request) { method_class.new(path) } let(:request) { method_class.new(path) }
it 'responds with proper http_status' do subject(:response) do
http = Net::HTTP.new(config[:BindAddress], config[:Port]) http = Net::HTTP.new(config[:BindAddress], config[:Port])
response = http.request(request) http.request(request)
end
it 'responds with proper http_status' do
exporter.start.join
expect(response.code).to eq(http_status.to_s) expect(response.code).to eq(http_status.to_s)
end end
it 'collects request metrics' do
expect_next_instance_of(fake_collector) do |instance|
expect(instance).to receive(:call).and_call_original
end
exporter.start.join
response
end
end end
end end
......
# frozen_string_literal: true
require 'fast_spec_helper'
RSpec.describe Gitlab::Metrics::Exporter::HealthChecksMiddleware do
let(:app) { double(:app) }
let(:env) { { 'PATH_INFO' => path } }
let(:readiness_probe) { double(:readiness_probe) }
let(:liveness_probe) { double(:liveness_probe) }
let(:probe_result) { Gitlab::HealthChecks::Probes::Status.new(200, { status: 'ok' }) }
subject(:middleware) { described_class.new(app, readiness_probe, liveness_probe) }
describe '#call' do
context 'handling /readiness requests' do
let(:path) { '/readiness' }
it 'handles the request' do
expect(readiness_probe).to receive(:execute).and_return(probe_result)
response = middleware.call(env)
expect(response).to eq([200, { 'Content-Type' => 'application/json; charset=utf-8' }, ['{"status":"ok"}']])
end
end
context 'handling /liveness requests' do
let(:path) { '/liveness' }
it 'handles the request' do
expect(liveness_probe).to receive(:execute).and_return(probe_result)
response = middleware.call(env)
expect(response).to eq([200, { 'Content-Type' => 'application/json; charset=utf-8' }, ['{"status":"ok"}']])
end
end
context 'handling other requests' do
let(:path) { '/other_path' }
it 'forwards them to the next middleware' do
expect(app).to receive(:call).with(env).and_return([201, {}, []])
response = middleware.call(env)
expect(response).to eq([201, {}, []])
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Metrics::Exporter::MetricsMiddleware do
let(:app) { double(:app) }
let(:pid) { 'fake_exporter' }
let(:env) { { 'PATH_INFO' => '/path', 'REQUEST_METHOD' => 'GET' } }
subject(:middleware) { described_class.new(app, pid) }
def metric(name, method, path, status)
metric = ::Prometheus::Client.registry.get(name)
return unless metric
values = metric.values.transform_keys { |k| k.slice(:method, :path, :pid, :code) }
values[{ method: method, path: path, pid: pid, code: status.to_s }]&.get
end
before do
expect(app).to receive(:call).with(env).and_return([200, {}, []])
end
describe '#call', :prometheus do
it 'records a total requests metric' do
response = middleware.call(env)
expect(response).to eq([200, {}, []])
expect(metric(:exporter_http_requests_total, 'get', '/path', 200)).to eq(1.0)
end
it 'records a request duration histogram' do
response = middleware.call(env)
expect(response).to eq([200, {}, []])
expect(metric(:exporter_http_request_duration_seconds, 'get', '/path', 200)).to be_a(Hash)
end
end
end
...@@ -24,14 +24,14 @@ RSpec.describe Gitlab::Metrics::Exporter::WebExporter do ...@@ -24,14 +24,14 @@ RSpec.describe Gitlab::Metrics::Exporter::WebExporter do
exporter.stop exporter.stop
end end
context 'when running server' do context 'when running server', :prometheus do
it 'readiness probe returns succesful status' do it 'readiness probe returns succesful status' do
expect(readiness_probe.http_status).to eq(200) expect(readiness_probe.http_status).to eq(200)
expect(readiness_probe.json).to include(status: 'ok') expect(readiness_probe.json).to include(status: 'ok')
expect(readiness_probe.json).to include('web_exporter' => [{ 'status': 'ok' }]) expect(readiness_probe.json).to include('web_exporter' => [{ 'status': 'ok' }])
end end
it 'initializes request metrics', :prometheus do it 'initializes request metrics' do
expect(Gitlab::Metrics::RailsSlis).to receive(:initialize_request_slis_if_needed!).and_call_original expect(Gitlab::Metrics::RailsSlis).to receive(:initialize_request_slis_if_needed!).and_call_original
http = Net::HTTP.new(exporter.server.config[:BindAddress], exporter.server.config[:Port]) http = Net::HTTP.new(exporter.server.config[:BindAddress], exporter.server.config[:Port])
...@@ -42,7 +42,7 @@ RSpec.describe Gitlab::Metrics::Exporter::WebExporter do ...@@ -42,7 +42,7 @@ RSpec.describe Gitlab::Metrics::Exporter::WebExporter do
end end
describe '#mark_as_not_running!' do describe '#mark_as_not_running!' do
it 'readiness probe returns a failure status' do it 'readiness probe returns a failure status', :prometheus do
exporter.mark_as_not_running! exporter.mark_as_not_running!
expect(readiness_probe.http_status).to eq(503) expect(readiness_probe.http_status).to eq(503)
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment