Commit 101c30f4 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent e1549c75
......@@ -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)
......
# 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
---
title: Improve API response for archived project searchs
merge_request: 27717
author:
type: performance
---
title: Add healthy column to clusters_applications_prometheus table
merge_request: 26168
author:
type: added
# 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
# 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:
![Broadcast Message](img/broadcast_messages.png)
- 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.
![Broadcast Message Banner](img/broadcast_messages_banner_v12_10.png)
## 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 Message Notification](img/broadcast_messages_notification_v12_10.png)
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
......
......@@ -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)
......
# 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
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