Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
e1549c7584
commit
101c30f4df
|
|
@ -189,7 +189,7 @@ class MergeRequest < ApplicationRecord
|
|||
end
|
||||
|
||||
# rubocop: disable CodeReuse/ServiceClass
|
||||
after_transition unchecked: :cannot_be_merged do |merge_request, transition|
|
||||
after_transition [:unchecked, :checking] => :cannot_be_merged do |merge_request, transition|
|
||||
if merge_request.notify_conflict?
|
||||
NotificationService.new.merge_request_unmergeable(merge_request)
|
||||
TodoService.new.merge_request_became_unmergeable(merge_request)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Clusters
|
||||
module Applications
|
||||
class PrometheusHealthCheckService
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
include Gitlab::Routing
|
||||
|
||||
def initialize(cluster)
|
||||
@cluster = cluster
|
||||
@logger = Gitlab::AppJsonLogger.build
|
||||
end
|
||||
|
||||
def execute
|
||||
raise 'Invalid cluster type. Only project types are allowed.' unless @cluster.project_type?
|
||||
|
||||
return unless prometheus_application.installed?
|
||||
|
||||
project = @cluster.clusterable
|
||||
|
||||
@logger.info(
|
||||
message: 'Prometheus health check',
|
||||
cluster_id: @cluster.id,
|
||||
newly_unhealthy: became_unhealthy?,
|
||||
currently_healthy: currently_healthy?,
|
||||
was_healthy: was_healthy?
|
||||
)
|
||||
|
||||
send_notification(project) if became_unhealthy?
|
||||
|
||||
prometheus_application.update_columns(healthy: currently_healthy?) if health_changed?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def prometheus_application
|
||||
strong_memoize(:prometheus_application) do
|
||||
@cluster.application_prometheus
|
||||
end
|
||||
end
|
||||
|
||||
def currently_healthy?
|
||||
strong_memoize(:currently_healthy) do
|
||||
prometheus_application.prometheus_client.healthy?
|
||||
end
|
||||
end
|
||||
|
||||
def became_unhealthy?
|
||||
strong_memoize(:became_unhealthy) do
|
||||
(was_healthy? || was_healthy?.nil?) && !currently_healthy?
|
||||
end
|
||||
end
|
||||
|
||||
def was_healthy?
|
||||
strong_memoize(:was_healthy) do
|
||||
prometheus_application.healthy
|
||||
end
|
||||
end
|
||||
|
||||
def health_changed?
|
||||
was_healthy? != currently_healthy?
|
||||
end
|
||||
|
||||
def send_notification(project)
|
||||
notification_payload = build_notification_payload(project)
|
||||
token = project.alerts_service.data.token
|
||||
Projects::Alerting::NotifyService.new(project, nil, notification_payload).execute(token)
|
||||
@logger.info(message: 'Successfully notified of Prometheus newly unhealthy', cluster_id: @cluster.id, project_id: project.id)
|
||||
end
|
||||
|
||||
def build_notification_payload(project)
|
||||
cluster_path = namespace_project_cluster_path(
|
||||
project_id: project.path,
|
||||
namespace_id: project.namespace.path,
|
||||
id: @cluster.id
|
||||
)
|
||||
|
||||
{
|
||||
title: "Prometheus is Unhealthy. Cluster Name: #{@cluster.name}",
|
||||
description: "Prometheus is unhealthy for the cluster: [#{@cluster.name}](#{cluster_path}) attached to project #{project.name}."
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Improve API response for archived project searchs
|
||||
merge_request: 27717
|
||||
author:
|
||||
type: performance
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add healthy column to clusters_applications_prometheus table
|
||||
merge_request: 26168
|
||||
author:
|
||||
type: added
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddHealthyToClustersApplicationsPrometheus < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
# Default is null to indicate that a health check has not run for a project
|
||||
# For now, health checks will only run on monitor demo projects
|
||||
add_column :clusters_applications_prometheus, :healthy, :boolean
|
||||
end
|
||||
|
||||
def down
|
||||
remove_column :clusters_applications_prometheus, :healthy
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddApiIndexesForArchivedProjects < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
PUBLIC_AND_ARCHIVED_INDEX_NAME = "index_projects_api_created_at_id_for_archived_vis20"
|
||||
ARCHIVED_INDEX_NAME = "index_projects_api_created_at_id_for_archived"
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_index :projects, [:created_at, :id],
|
||||
where: "archived = true AND visibility_level = 20 AND pending_delete = false",
|
||||
name: PUBLIC_AND_ARCHIVED_INDEX_NAME
|
||||
|
||||
add_concurrent_index :projects, [:created_at, :id], where: "archived = true AND pending_delete = false",
|
||||
name: ARCHIVED_INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :projects, ARCHIVED_INDEX_NAME
|
||||
|
||||
remove_concurrent_index_by_name :projects, PUBLIC_AND_ARCHIVED_INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -1748,7 +1748,8 @@ CREATE TABLE public.clusters_applications_prometheus (
|
|||
updated_at timestamp with time zone NOT NULL,
|
||||
last_update_started_at timestamp with time zone,
|
||||
encrypted_alert_manager_token character varying,
|
||||
encrypted_alert_manager_token_iv character varying
|
||||
encrypted_alert_manager_token_iv character varying,
|
||||
healthy boolean
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.clusters_applications_prometheus_id_seq
|
||||
|
|
@ -9654,6 +9655,10 @@ CREATE UNIQUE INDEX index_project_tracing_settings_on_project_id ON public.proje
|
|||
|
||||
CREATE INDEX index_projects_api_created_at_id_desc ON public.projects USING btree (created_at, id DESC);
|
||||
|
||||
CREATE INDEX index_projects_api_created_at_id_for_archived ON public.projects USING btree (created_at, id) WHERE ((archived = true) AND (pending_delete = false));
|
||||
|
||||
CREATE INDEX index_projects_api_created_at_id_for_archived_vis20 ON public.projects USING btree (created_at, id) WHERE ((archived = true) AND (visibility_level = 20) AND (pending_delete = false));
|
||||
|
||||
CREATE INDEX index_projects_api_last_activity_at_id_desc ON public.projects USING btree (last_activity_at, id DESC);
|
||||
|
||||
CREATE INDEX index_projects_api_name_id_desc ON public.projects USING btree (name, id DESC);
|
||||
|
|
@ -12731,6 +12736,7 @@ COPY "schema_migrations" (version) FROM STDIN;
|
|||
20200302152516
|
||||
20200303055348
|
||||
20200303074328
|
||||
20200303181648
|
||||
20200304085423
|
||||
20200304090155
|
||||
20200304121828
|
||||
|
|
@ -12793,6 +12799,7 @@ COPY "schema_migrations" (version) FROM STDIN;
|
|||
20200320123839
|
||||
20200323075043
|
||||
20200323122201
|
||||
20200323134519
|
||||
20200324115359
|
||||
\.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,14 +4,46 @@ type: reference, howto
|
|||
|
||||
# Broadcast Messages **(CORE ONLY)**
|
||||
|
||||
GitLab can display messages to all users of a GitLab instance in a banner that appears in the UI.
|
||||
GitLab can display broadcast messages to all users of a GitLab instance. There are two types of broadcast messages:
|
||||
|
||||

|
||||
- banners
|
||||
- notifications
|
||||
|
||||
You can style a message's content using the `a` and `br` HTML tags. The `br` tag inserts a line break. The `a` HTML tag accepts `class` and `style` attributes with the following CSS properties:
|
||||
|
||||
- `color`
|
||||
- `border`
|
||||
- `background`
|
||||
- `padding`
|
||||
- `margin`
|
||||
- `text-decoration`
|
||||
|
||||
## Banners
|
||||
|
||||
Banners are shown on the top of a page.
|
||||
|
||||

|
||||
|
||||
## Notifications
|
||||
|
||||
Notifications are shown on the bottom right of a page and can contain placeholders. A placeholder is replaced with an attribute of the active user. Placeholders must be surrounded by curly braces, for example `{{name}}`.
|
||||
The available placeholders are:
|
||||
|
||||
- `{{email}}`
|
||||
- `{{name}}`
|
||||
- `{{user_id}}`
|
||||
- `{{username}}`
|
||||
- `{{instance_id}}`
|
||||
|
||||
If the user is not signed in, user related values will be empty.
|
||||
|
||||

|
||||
|
||||
Broadcast messages can be managed using the [broadcast messages API](../../api/broadcast_messages.md).
|
||||
|
||||
NOTE: **Note:**
|
||||
If more than one banner message is active at one time, they are displayed in a stack in order of creation.
|
||||
If more than one notification message is active at one time, only the newest is shown.
|
||||
|
||||
## Adding a broadcast message
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 21 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
|
|
@ -87,6 +87,9 @@ or over the size limit, you can [reduce your repository size with Git](../projec
|
|||
| ----------- | ----------------- | ------------- |
|
||||
| Repository size including LFS | 10G | Unlimited |
|
||||
|
||||
NOTE: **Note:**
|
||||
A single `git push` is limited to 5GB. LFS is not affected by this limit.
|
||||
|
||||
## IP range
|
||||
|
||||
GitLab.com is using the IP range `34.74.90.64/28` for traffic from its Web/API
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ module Gitlab
|
|||
include Gitlab::Utils::StrongMemoize
|
||||
Error = Class.new(StandardError)
|
||||
QueryError = Class.new(Gitlab::PrometheusClient::Error)
|
||||
HEALTHY_RESPONSE = "Prometheus is Healthy.\n"
|
||||
|
||||
# Target number of data points for `query_range`.
|
||||
# Please don't exceed the limit of 11000 data points
|
||||
|
|
@ -32,13 +33,20 @@ module Gitlab
|
|||
json_api_get('query', query: '1')
|
||||
end
|
||||
|
||||
def healthy?
|
||||
response_body = handle_management_api_response(get(health_url, {}))
|
||||
|
||||
# From Prometheus docs: This endpoint always returns 200 and should be used to check Prometheus health.
|
||||
response_body == HEALTHY_RESPONSE
|
||||
end
|
||||
|
||||
def proxy(type, args)
|
||||
path = api_path(type)
|
||||
get(path, args)
|
||||
rescue Gitlab::HTTP::ResponseError => ex
|
||||
raise PrometheusClient::Error, "Network connection error" unless ex.response && ex.response.try(:code)
|
||||
|
||||
handle_response(ex.response)
|
||||
handle_querying_api_response(ex.response)
|
||||
end
|
||||
|
||||
def query(query, time: Time.now)
|
||||
|
|
@ -79,6 +87,10 @@ module Gitlab
|
|||
[QUERY_RANGE_MIN_STEP, step].max
|
||||
end
|
||||
|
||||
def health_url
|
||||
[api_url, '-/healthy'].join('/')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def api_path(type)
|
||||
|
|
@ -88,11 +100,11 @@ module Gitlab
|
|||
def json_api_get(type, args = {})
|
||||
path = api_path(type)
|
||||
response = get(path, args)
|
||||
handle_response(response)
|
||||
handle_querying_api_response(response)
|
||||
rescue Gitlab::HTTP::ResponseError => ex
|
||||
raise PrometheusClient::Error, "Network connection error" unless ex.response && ex.response.try(:code)
|
||||
|
||||
handle_response(ex.response)
|
||||
handle_querying_api_response(ex.response)
|
||||
end
|
||||
|
||||
def gitlab_http_key(key)
|
||||
|
|
@ -119,7 +131,15 @@ module Gitlab
|
|||
raise PrometheusClient::Error, 'Connection refused'
|
||||
end
|
||||
|
||||
def handle_response(response)
|
||||
def handle_management_api_response(response)
|
||||
if response.code == 200
|
||||
response.body
|
||||
else
|
||||
raise PrometheusClient::Error, "#{response.code} - #{response.body}"
|
||||
end
|
||||
end
|
||||
|
||||
def handle_querying_api_response(response)
|
||||
response_code = response.try(:code)
|
||||
response_body = response.try(:body)
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,26 @@ describe Gitlab::PrometheusClient do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#healthy?' do
|
||||
it 'returns true when status code is 200 and healthy response body' do
|
||||
stub_request(:get, subject.health_url).to_return(status: 200, body: described_class::HEALTHY_RESPONSE)
|
||||
|
||||
expect(subject.healthy?).to eq(true)
|
||||
end
|
||||
|
||||
it 'returns false when status code is 200 and unhealthy response body' do
|
||||
stub_request(:get, subject.health_url).to_return(status: 200, body: '')
|
||||
|
||||
expect(subject.healthy?).to eq(false)
|
||||
end
|
||||
|
||||
it 'raises error when status code not 200' do
|
||||
stub_request(:get, subject.health_url).to_return(status: 500, body: '')
|
||||
|
||||
expect { subject.healthy? }.to raise_error(Gitlab::PrometheusClient::Error)
|
||||
end
|
||||
end
|
||||
|
||||
# This shared examples expect:
|
||||
# - query_url: A query URL
|
||||
# - execute_query: A query call
|
||||
|
|
|
|||
|
|
@ -3223,6 +3223,14 @@ describe MergeRequest do
|
|||
subject.mark_as_unmergeable
|
||||
end
|
||||
|
||||
it 'notifies conflict, with enabled async mergability check' do
|
||||
expect(notification_service).to receive(:merge_request_unmergeable).with(subject).once
|
||||
expect(todo_service).to receive(:merge_request_became_unmergeable).with(subject).once
|
||||
|
||||
subject.mark_as_checking
|
||||
subject.mark_as_unmergeable
|
||||
end
|
||||
|
||||
it 'does not notify whenever merge request is newly unmergeable due to other reasons' do
|
||||
allow(subject.project.repository).to receive(:can_be_merged?).and_return(true)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Clusters::Applications::PrometheusHealthCheckService, '#execute' do
|
||||
let(:service) { described_class.new(cluster) }
|
||||
|
||||
subject { service.execute }
|
||||
|
||||
RSpec.shared_examples 'no alert' do
|
||||
it 'does not send alert' do
|
||||
expect(Projects::Alerting::NotifyService).not_to receive(:new)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'sends alert' do
|
||||
it 'sends an alert' do
|
||||
expect_next_instance_of(Projects::Alerting::NotifyService) do |notify_service|
|
||||
expect(notify_service).to receive(:execute).with(alerts_service.token)
|
||||
end
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'correct health stored' do
|
||||
it 'stores the correct health of prometheus app' do
|
||||
subject
|
||||
|
||||
expect(prometheus.healthy).to eq(client_healthy)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when cluster is not project_type' do
|
||||
let(:cluster) { create(:cluster, :instance) }
|
||||
|
||||
it { expect { subject }.to raise_error(RuntimeError, 'Invalid cluster type. Only project types are allowed.') }
|
||||
end
|
||||
|
||||
context 'when cluster is project_type' do
|
||||
let_it_be(:alerts_service) { create(:alerts_service) }
|
||||
let_it_be(:project) { create(:project, alerts_service: alerts_service) }
|
||||
let(:applications_prometheus_healthy) { true }
|
||||
let(:prometheus) { create(:clusters_applications_prometheus, status: prometheus_status_value, healthy: applications_prometheus_healthy) }
|
||||
let(:cluster) { create(:cluster, :project, application_prometheus: prometheus, projects: [project]) }
|
||||
|
||||
context 'when prometheus not installed' do
|
||||
let(:prometheus_status_value) { Clusters::Applications::Prometheus.state_machine.states[:installing].value }
|
||||
|
||||
it { expect(subject).to eq(nil) }
|
||||
include_examples 'no alert'
|
||||
end
|
||||
|
||||
context 'when prometheus installed' do
|
||||
let(:prometheus_status_value) { Clusters::Applications::Prometheus.state_machine.states[:installed].value }
|
||||
|
||||
before do
|
||||
client = instance_double('PrometheusClient', healthy?: client_healthy)
|
||||
expect(prometheus).to receive(:prometheus_client).and_return(client)
|
||||
end
|
||||
|
||||
context 'when newly unhealthy' do
|
||||
let(:applications_prometheus_healthy) { true }
|
||||
let(:client_healthy) { false }
|
||||
|
||||
include_examples 'sends alert'
|
||||
include_examples 'correct health stored'
|
||||
end
|
||||
|
||||
context 'when newly healthy' do
|
||||
let(:applications_prometheus_healthy) { false }
|
||||
let(:client_healthy) { true }
|
||||
|
||||
include_examples 'no alert'
|
||||
include_examples 'correct health stored'
|
||||
end
|
||||
|
||||
context 'when continuously unhealthy' do
|
||||
let(:applications_prometheus_healthy) { false }
|
||||
let(:client_healthy) { false }
|
||||
|
||||
include_examples 'no alert'
|
||||
include_examples 'correct health stored'
|
||||
end
|
||||
|
||||
context 'when continuously healthy' do
|
||||
let(:applications_prometheus_healthy) { true }
|
||||
let(:client_healthy) { true }
|
||||
|
||||
include_examples 'no alert'
|
||||
include_examples 'correct health stored'
|
||||
end
|
||||
|
||||
context 'when first health check and healthy' do
|
||||
let(:applications_prometheus_healthy) { nil }
|
||||
let(:client_healthy) { true }
|
||||
|
||||
include_examples 'no alert'
|
||||
include_examples 'correct health stored'
|
||||
end
|
||||
|
||||
context 'when first health check and not healthy' do
|
||||
let(:applications_prometheus_healthy) { nil }
|
||||
let(:client_healthy) { false }
|
||||
|
||||
include_examples 'sends alert'
|
||||
include_examples 'correct health stored'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue