Commit 33aa3ca8 authored by Sean McGivern's avatar Sean McGivern

Merge branch 'ce-5158-metrics-alerting' into 'master'

Backport 5158-metrics-alerting to CE

See merge request gitlab-org/gitlab-ce!20148
parents ffbfd18c fab7dacc
...@@ -4,4 +4,23 @@ module EnvironmentsHelper ...@@ -4,4 +4,23 @@ module EnvironmentsHelper
endpoint: project_environments_path(@project, format: :json) endpoint: project_environments_path(@project, format: :json)
} }
end end
def metrics_data(project, environment)
{
"settings-path" => edit_project_service_path(project, 'prometheus'),
"clusters-path" => project_clusters_path(project),
"current-environment-name": environment.name,
"documentation-path" => help_page_path('administration/monitoring/prometheus/index.md'),
"empty-getting-started-svg-path" => image_path('illustrations/monitoring/getting_started.svg'),
"empty-loading-svg-path" => image_path('illustrations/monitoring/loading.svg'),
"empty-no-data-svg-path" => image_path('illustrations/monitoring/no_data.svg'),
"empty-unable-to-connect-svg-path" => image_path('illustrations/monitoring/unable_to_connect.svg'),
"metrics-endpoint" => additional_metrics_project_environment_path(project, environment, format: :json),
"deployment-endpoint" => project_environment_deployments_path(project, environment, format: :json),
"environments-endpoint": project_environments_path(project, format: :json),
"project-path" => project_path(project),
"tags-path" => project_tags_path(project),
"has-metrics" => "#{environment.has_metrics?}"
}
end
end end
...@@ -21,6 +21,14 @@ module Clusters ...@@ -21,6 +21,14 @@ module Clusters
end end
end end
def ready_status
[:installed]
end
def ready?
ready_status.include?(status_name)
end
def chart def chart
'stable/prometheus' 'stable/prometheus'
end end
......
...@@ -24,11 +24,10 @@ module PrometheusAdapter ...@@ -24,11 +24,10 @@ module PrometheusAdapter
def query(query_name, *args) def query(query_name, *args)
return unless can_query? return unless can_query?
query_class = Gitlab::Prometheus::Queries.const_get("#{query_name.to_s.classify}Query") query_class = query_klass_for(query_name)
query_args = build_query_args(*args)
args.map!(&:id) with_reactive_cache(query_class.name, *query_args, &query_class.method(:transform_reactive_result))
with_reactive_cache(query_class.name, *args, &query_class.method(:transform_reactive_result))
end end
# Cache metrics for specific environment # Cache metrics for specific environment
...@@ -44,5 +43,13 @@ module PrometheusAdapter ...@@ -44,5 +43,13 @@ module PrometheusAdapter
rescue Gitlab::PrometheusClient::Error => err rescue Gitlab::PrometheusClient::Error => err
{ success: false, result: err.message } { success: false, result: err.message }
end end
def query_klass_for(query_name)
Gitlab::Prometheus::Queries.const_get("#{query_name.to_s.classify}Query")
end
def build_query_args(*args)
args.map(&:id)
end
end end
end end
...@@ -50,17 +50,17 @@ module Clusters ...@@ -50,17 +50,17 @@ module Clusters
end end
def remove_installation_pod def remove_installation_pod
helm_api.delete_installation_pod!(install_command.pod_name) helm_api.delete_pod!(install_command.pod_name)
rescue rescue
# no-op # no-op
end end
def installation_phase def installation_phase
helm_api.installation_status(install_command.pod_name) helm_api.status(install_command.pod_name)
end end
def installation_errors def installation_errors
helm_api.installation_log(install_command.pod_name) helm_api.log(install_command.pod_name)
end end
end end
end end
......
...@@ -30,7 +30,7 @@ module Prometheus ...@@ -30,7 +30,7 @@ module Prometheus
return unless deployment_platform.respond_to?(:cluster) return unless deployment_platform.respond_to?(:cluster)
cluster = deployment_platform.cluster cluster = deployment_platform.cluster
return unless cluster.application_prometheus&.installed? return unless cluster.application_prometheus&.ready?
cluster.application_prometheus cluster.application_prometheus
end end
......
...@@ -2,17 +2,11 @@ ...@@ -2,17 +2,11 @@
- page_title "Metrics for environment", @environment.name - page_title "Metrics for environment", @environment.name
.prometheus-container{ class: container_class } .prometheus-container{ class: container_class }
#prometheus-graphs{ data: { "settings-path": edit_project_service_path(@project, 'prometheus'), .top-area
"clusters-path": project_clusters_path(@project), .row
"current-environment-name": @environment.name, .col-sm-6
"documentation-path": help_page_path('administration/monitoring/prometheus/index.md'), %h3
"empty-getting-started-svg-path": image_path('illustrations/monitoring/getting_started.svg'), Environment:
"empty-loading-svg-path": image_path('illustrations/monitoring/loading.svg'), = link_to @environment.name, environment_path(@environment)
"empty-no-data-svg-path": image_path('illustrations/monitoring/no_data.svg'),
"empty-unable-to-connect-svg-path": image_path('illustrations/monitoring/unable_to_connect.svg'), #prometheus-graphs{ data: metrics_data(@project, @environment) }
"metrics-endpoint": additional_metrics_project_environment_path(@project, @environment, format: :json),
"deployment-endpoint": project_environment_deployments_path(@project, @environment, format: :json),
"environments-endpoint": project_environments_path(@project, format: :json),
"project-path": project_path(@project),
"tags-path": project_tags_path(@project),
"has-metrics": "#{@environment.has_metrics?}" } }
module Gitlab module Gitlab
module Kubernetes module Kubernetes
class ConfigMap class ConfigMap
def initialize(name, values) def initialize(name, values = "")
@name = name @name = name
@values = values @values = values
end end
...@@ -13,6 +13,10 @@ module Gitlab ...@@ -13,6 +13,10 @@ module Gitlab
resource resource
end end
def config_map_name
"values-content-configuration-#{name}"
end
private private
attr_reader :name, :values attr_reader :name, :values
...@@ -25,10 +29,6 @@ module Gitlab ...@@ -25,10 +29,6 @@ module Gitlab
} }
end end
def config_map_name
"values-content-configuration-#{name}"
end
def namespace def namespace
Gitlab::Kubernetes::Helm::NAMESPACE Gitlab::Kubernetes::Helm::NAMESPACE
end end
......
...@@ -8,9 +8,9 @@ module Gitlab ...@@ -8,9 +8,9 @@ module Gitlab
end end
def install(command) def install(command)
@namespace.ensure_exists! namespace.ensure_exists!
create_config_map(command) if command.config_map? create_config_map(command) if command.config_map?
@kubeclient.create_pod(command.pod_resource) kubeclient.create_pod(command.pod_resource)
end end
## ##
...@@ -20,23 +20,25 @@ module Gitlab ...@@ -20,23 +20,25 @@ module Gitlab
# #
# values: "Pending", "Running", "Succeeded", "Failed", "Unknown" # values: "Pending", "Running", "Succeeded", "Failed", "Unknown"
# #
def installation_status(pod_name) def status(pod_name)
@kubeclient.get_pod(pod_name, @namespace.name).status.phase kubeclient.get_pod(pod_name, namespace.name).status.phase
end end
def installation_log(pod_name) def log(pod_name)
@kubeclient.get_pod_log(pod_name, @namespace.name).body kubeclient.get_pod_log(pod_name, namespace.name).body
end end
def delete_installation_pod!(pod_name) def delete_pod!(pod_name)
@kubeclient.delete_pod(pod_name, @namespace.name) kubeclient.delete_pod(pod_name, namespace.name)
end end
private private
attr_reader :kubeclient, :namespace
def create_config_map(command) def create_config_map(command)
command.config_map_resource.tap do |config_map_resource| command.config_map_resource.tap do |config_map_resource|
@kubeclient.create_config_map(config_map_resource) kubeclient.create_config_map(config_map_resource)
end end
end end
end end
......
...@@ -3,7 +3,7 @@ module Gitlab ...@@ -3,7 +3,7 @@ module Gitlab
class Metric class Metric
include ActiveModel::Model include ActiveModel::Model
attr_accessor :title, :required_metrics, :weight, :y_label, :queries attr_accessor :id, :title, :required_metrics, :weight, :y_label, :queries
validates :title, :required_metrics, :weight, :y_label, :queries, presence: true validates :title, :required_metrics, :weight, :y_label, :queries, presence: true
......
...@@ -8,6 +8,7 @@ module Gitlab ...@@ -8,6 +8,7 @@ module Gitlab
Deployment.find_by(id: deployment_id).try do |deployment| Deployment.find_by(id: deployment_id).try do |deployment|
query_metrics( query_metrics(
deployment.project, deployment.project,
deployment.environment,
common_query_context( common_query_context(
deployment.environment, deployment.environment,
timeframe_start: (deployment.created_at - 30.minutes).to_f, timeframe_start: (deployment.created_at - 30.minutes).to_f,
......
...@@ -8,6 +8,7 @@ module Gitlab ...@@ -8,6 +8,7 @@ module Gitlab
::Environment.find_by(id: environment_id).try do |environment| ::Environment.find_by(id: environment_id).try do |environment|
query_metrics( query_metrics(
environment.project, environment.project,
environment,
common_query_context(environment, timeframe_start: 8.hours.ago.to_f, timeframe_end: Time.now.to_f) common_query_context(environment, timeframe_start: 8.hours.ago.to_f, timeframe_end: Time.now.to_f)
) )
end end
......
...@@ -2,7 +2,7 @@ module Gitlab ...@@ -2,7 +2,7 @@ module Gitlab
module Prometheus module Prometheus
module Queries module Queries
module QueryAdditionalMetrics module QueryAdditionalMetrics
def query_metrics(project, query_context) def query_metrics(project, environment, query_context)
matched_metrics(project).map(&query_group(query_context)) matched_metrics(project).map(&query_group(query_context))
.select(&method(:group_with_any_metrics)) .select(&method(:group_with_any_metrics))
end end
...@@ -14,12 +14,16 @@ module Gitlab ...@@ -14,12 +14,16 @@ module Gitlab
lambda do |group| lambda do |group|
metrics = group.metrics.map do |metric| metrics = group.metrics.map do |metric|
{ metric_hsh = {
title: metric.title, title: metric.title,
weight: metric.weight, weight: metric.weight,
y_label: metric.y_label, y_label: metric.y_label,
queries: metric.queries.map(&query_processor).select(&method(:query_with_result)) queries: metric.queries.map(&query_processor).select(&method(:query_with_result))
} }
metric_hsh[:id] = metric.id if metric.id
metric_hsh
end end
{ {
......
...@@ -22,4 +22,10 @@ describe Gitlab::Kubernetes::ConfigMap do ...@@ -22,4 +22,10 @@ describe Gitlab::Kubernetes::ConfigMap do
is_expected.to eq(resource) is_expected.to eq(resource)
end end
end end
describe '#config_map_name' do
it 'returns the config_map name' do
expect(config_map.config_map_name).to eq("values-content-configuration-#{application.name}")
end
end
end end
...@@ -49,33 +49,33 @@ describe Gitlab::Kubernetes::Helm::Api do ...@@ -49,33 +49,33 @@ describe Gitlab::Kubernetes::Helm::Api do
end end
end end
describe '#installation_status' do describe '#status' do
let(:phase) { Gitlab::Kubernetes::Pod::RUNNING } let(:phase) { Gitlab::Kubernetes::Pod::RUNNING }
let(:pod) { Kubeclient::Resource.new(status: { phase: phase }) } # partial representation let(:pod) { Kubeclient::Resource.new(status: { phase: phase }) } # partial representation
it 'fetches POD phase from kubernetes cluster' do it 'fetches POD phase from kubernetes cluster' do
expect(client).to receive(:get_pod).with(command.pod_name, gitlab_namespace).once.and_return(pod) expect(client).to receive(:get_pod).with(command.pod_name, gitlab_namespace).once.and_return(pod)
expect(subject.installation_status(command.pod_name)).to eq(phase) expect(subject.status(command.pod_name)).to eq(phase)
end end
end end
describe '#installation_log' do describe '#log' do
let(:log) { 'some output' } let(:log) { 'some output' }
let(:response) { RestClient::Response.new(log) } let(:response) { RestClient::Response.new(log) }
it 'fetches POD phase from kubernetes cluster' do it 'fetches POD phase from kubernetes cluster' do
expect(client).to receive(:get_pod_log).with(command.pod_name, gitlab_namespace).once.and_return(response) expect(client).to receive(:get_pod_log).with(command.pod_name, gitlab_namespace).once.and_return(response)
expect(subject.installation_log(command.pod_name)).to eq(log) expect(subject.log(command.pod_name)).to eq(log)
end end
end end
describe '#delete_installation_pod!' do describe '#delete_pod!' do
it 'deletes the POD from kubernetes cluster' do it 'deletes the POD from kubernetes cluster' do
expect(client).to receive(:delete_pod).with(command.pod_name, gitlab_namespace).once expect(client).to receive(:delete_pod).with(command.pod_name, gitlab_namespace).once
subject.delete_installation_pod!(command.pod_name) subject.delete_pod!(command.pod_name)
end end
end end
end end
...@@ -34,6 +34,47 @@ describe Clusters::Applications::Prometheus do ...@@ -34,6 +34,47 @@ describe Clusters::Applications::Prometheus do
end end
end end
describe '#ready' do
let(:project) { create(:project) }
let(:cluster) { create(:cluster, projects: [project]) }
it 'returns true when installed' do
application = build(:clusters_applications_prometheus, :installed, cluster: cluster)
expect(application).to be_ready
end
it 'returns false when not_installable' do
application = build(:clusters_applications_prometheus, :not_installable, cluster: cluster)
expect(application).not_to be_ready
end
it 'returns false when installable' do
application = build(:clusters_applications_prometheus, :installable, cluster: cluster)
expect(application).not_to be_ready
end
it 'returns false when scheduled' do
application = build(:clusters_applications_prometheus, :scheduled, cluster: cluster)
expect(application).not_to be_ready
end
it 'returns false when installing' do
application = build(:clusters_applications_prometheus, :installing, cluster: cluster)
expect(application).not_to be_ready
end
it 'returns false when errored' do
application = build(:clusters_applications_prometheus, :errored, cluster: cluster)
expect(application).not_to be_ready
end
end
describe '#prometheus_client' do describe '#prometheus_client' do
context 'cluster is nil' do context 'cluster is nil' do
it 'returns nil' do it 'returns nil' do
...@@ -102,15 +143,17 @@ describe Clusters::Applications::Prometheus do ...@@ -102,15 +143,17 @@ describe Clusters::Applications::Prometheus do
let(:kubeclient) { double('kubernetes client') } let(:kubeclient) { double('kubernetes client') }
let(:prometheus) { create(:clusters_applications_prometheus) } let(:prometheus) { create(:clusters_applications_prometheus) }
subject { prometheus.install_command } it 'returns an instance of Gitlab::Kubernetes::Helm::InstallCommand' do
expect(prometheus.install_command).to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand)
it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand) } end
it 'should be initialized with 3 arguments' do it 'should be initialized with 3 arguments' do
expect(subject.name).to eq('prometheus') command = prometheus.install_command
expect(subject.chart).to eq('stable/prometheus')
expect(subject.version).to eq('6.7.3') expect(command.name).to eq('prometheus')
expect(subject.values).to eq(prometheus.values) expect(command.chart).to eq('stable/prometheus')
expect(command.version).to eq('6.7.3')
expect(command.values).to eq(prometheus.values)
end end
end end
......
...@@ -25,7 +25,7 @@ RSpec.shared_examples 'additional metrics query' do ...@@ -25,7 +25,7 @@ RSpec.shared_examples 'additional metrics query' do
shared_examples 'query context containing environment slug and filter' do shared_examples 'query context containing environment slug and filter' do
it 'contains ci_environment_slug' do it 'contains ci_environment_slug' do
expect(subject).to receive(:query_metrics).with(project, hash_including(ci_environment_slug: environment.slug)) expect(subject).to receive(:query_metrics).with(project, environment, hash_including(ci_environment_slug: environment.slug))
subject.query(*query_params) subject.query(*query_params)
end end
...@@ -33,6 +33,7 @@ RSpec.shared_examples 'additional metrics query' do ...@@ -33,6 +33,7 @@ RSpec.shared_examples 'additional metrics query' do
it 'contains environment filter' do it 'contains environment filter' do
expect(subject).to receive(:query_metrics).with( expect(subject).to receive(:query_metrics).with(
project, project,
environment,
hash_including( hash_including(
environment_filter: "container_name!=\"POD\",environment=\"#{environment.slug}\"" environment_filter: "container_name!=\"POD\",environment=\"#{environment.slug}\""
) )
...@@ -50,7 +51,7 @@ RSpec.shared_examples 'additional metrics query' do ...@@ -50,7 +51,7 @@ RSpec.shared_examples 'additional metrics query' do
it_behaves_like 'query context containing environment slug and filter' it_behaves_like 'query context containing environment slug and filter'
it 'query context contains kube_namespace' do it 'query context contains kube_namespace' do
expect(subject).to receive(:query_metrics).with(project, hash_including(kube_namespace: kube_namespace)) expect(subject).to receive(:query_metrics).with(project, environment, hash_including(kube_namespace: kube_namespace))
subject.query(*query_params) subject.query(*query_params)
end end
...@@ -74,7 +75,7 @@ RSpec.shared_examples 'additional metrics query' do ...@@ -74,7 +75,7 @@ RSpec.shared_examples 'additional metrics query' do
it_behaves_like 'query context containing environment slug and filter' it_behaves_like 'query context containing environment slug and filter'
it 'query context contains empty kube_namespace' do it 'query context contains empty kube_namespace' do
expect(subject).to receive(:query_metrics).with(project, hash_including(kube_namespace: '')) expect(subject).to receive(:query_metrics).with(project, environment, hash_including(kube_namespace: ''))
subject.query(*query_params) subject.query(*query_params)
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