Commit 71282101 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge branch '5029-support-cluster-metrics' into 'master'

Add CPU & Memory metrics to k8s cluster page

Closes #5029

See merge request gitlab-org/gitlab-ee!4701
parents 915db3d5 642a9adb
......@@ -574,3 +574,17 @@
}
}
}
// EE-only
.cluster-health-graphs {
.prometheus-state {
.state-svg img {
max-height: 120px;
}
.state-description,
.state-button {
display: none;
}
}
}
......@@ -64,6 +64,22 @@ class Projects::ClustersController < Projects::ApplicationController
end
end
def metrics
return render_404 unless prometheus_adapter&.can_query?
respond_to do |format|
format.json do
metrics = prometheus_adapter.query(:cluster) || {}
if metrics.any?
render json: metrics
else
head :no_content
end
end
end
end
private
def cluster
......@@ -71,6 +87,12 @@ class Projects::ClustersController < Projects::ApplicationController
.present(current_user: current_user)
end
def prometheus_adapter
return unless cluster&.application_prometheus&.installed?
cluster.application_prometheus
end
def update_params
if cluster.managed?
params.require(:cluster).permit(
......
......@@ -22,6 +22,10 @@
.js-cluster-application-notice
.flash-container
-# EE-specific
- if @cluster.project.feature_available?(:cluster_health)
= render 'health'
%section.settings.no-animate.expanded#cluster-integration
= render 'banner'
= render 'integration_form'
......
- group: Cluster Health
priority: 1
metrics:
- title: "CPU Usage"
y_label: "CPU"
required_metrics: ['container_cpu_usage_seconds_total']
weight: 1
queries:
- query_range: 'avg(sum(rate(container_cpu_usage_seconds_total{id="/"}[15m])) by (job)) without (job)'
label: Usage
unit: "cores"
- query_range: 'sum(kube_node_status_capacity_cpu_cores{kubernetes_namespace="gitlab-managed-apps"})'
label: Capacity
unit: "cores"
- title: "Memory usage"
y_label: "Memory"
required_metrics: ['container_memory_usage_bytes']
weight: 1
queries:
- query_range: 'avg(sum(container_memory_usage_bytes{id="/"}) by (job)) without (job) / 2^30'
label: Usage
unit: "GiB"
- query_range: 'sum(kube_node_status_capacity_memory_bytes{kubernetes_namespace="gitlab-managed-apps"})/2^30'
label: Capacity
unit: "GiB"
\ No newline at end of file
......@@ -244,6 +244,7 @@ constraints(ProjectUrlConstrainer.new) do
member do
get :status, format: :json
get :metrics, format: :json
scope :applications do
post '/:application', to: 'clusters/applications#create', as: :install_applications
......
import Vue from 'vue';
import Dashboard from '~/monitoring/components/dashboard.vue';
export default () => {
const el = document.getElementById('prometheus-graphs');
if (el && el.dataset) {
// eslint-disable-next-line no-new
new Vue({
el,
render(createElement) {
return createElement(Dashboard, {
props: {
...el.dataset,
showLegend: false,
showPanels: false,
forceSmallGraph: true,
},
});
},
});
}
};
import '~/pages/projects/clusters/show';
import initClusterHealth from './cluster_health';
document.addEventListener('DOMContentLoaded', initClusterHealth);
......@@ -61,6 +61,7 @@ class License < ActiveRecord::Base
EEU_FEATURES = EEP_FEATURES + %i[
sast
sast_container
cluster_health
dast
epics
ide
......
%section.settings.no-animate.expanded.cluster-health-graphs#cluster-health
%h4= s_('ClusterIntegration|Kubernetes cluster health')
- if @cluster&.application_prometheus&.installed?
#prometheus-graphs{ data: { "settings-path": edit_project_service_path(@project, 'prometheus'),
"clusters-path": project_clusters_path(@project),
"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-unable-to-connect-svg-path": image_path('illustrations/monitoring/unable_to_connect.svg'),
"metrics-endpoint": metrics_namespace_project_cluster_path( format: :json ),
"project-path": project_path(@project),
"tags-path": project_tags_path(@project) } }
- else
.settings-content
%p= s_("ClusterIntegration|In order to show the health of the cluster, we'll need to provision your cluster with Prometheus to collect the required data.")
%a.btn.btn-default{ href: '#cluster-applications' }
= s_('ClusterIntegration|Install Prometheus')
---
title: Query cluster status
merge_request: 4701
author:
type: added
module Gitlab
module Prometheus
module Queries
class ClusterQuery < BaseQuery
include QueryAdditionalMetrics
def query
AdditionalMetricsParser.load_groups_from_yaml('cluster_metrics.yml')
.map(&query_group(base_query_context(8.hours.ago, Time.now)))
end
end
end
end
end
require 'spec_helper'
describe Gitlab::Prometheus::Queries::ClusterQuery do
let(:client) { double('prometheus_client', query_range: nil) }
subject { described_class.new(client) }
around do |example|
Timecop.freeze { example.run }
end
it 'load cluster metrics from yaml' do
expect(Gitlab::Prometheus::AdditionalMetricsParser).to receive(:load_groups_from_yaml).with('cluster_metrics.yml').and_call_original
subject.query
end
it 'sends queries to prometheus' do
subject.query
expect(client).to have_received(:query_range).with(anything, start: 8.hours.ago, stop: Time.now).at_least(1)
end
end
......@@ -155,6 +155,93 @@ describe Projects::ClustersController do
end
end
describe 'GET metrics' do
let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
describe 'functionality' do
let(:user) { create(:user) }
before do
project.add_master(user)
sign_in(user)
end
context "Can't query Prometheus" do
it 'returns not found' do
go
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'can query Prometheus' do
let(:prometheus_adapter) { double('prometheus_adapter', can_query?: true, query: nil) }
before do
allow(controller).to receive(:prometheus_adapter).and_return(prometheus_adapter)
end
it 'queries cluster metrics' do
go
expect(prometheus_adapter).to have_received(:query).with(:cluster)
end
context 'when response has content' do
let(:query_response) { { response: nil } }
before do
allow(prometheus_adapter).to receive(:query).and_return(query_response)
end
it 'returns prometheus query response' do
go
expect(response).to have_gitlab_http_status(:ok)
expect(response.body).to eq(query_response.to_json)
end
end
context 'when response has no content' do
let(:query_response) { {} }
before do
allow(prometheus_adapter).to receive(:query).and_return(query_response)
end
it 'returns prometheus query response' do
go
expect(response).to have_gitlab_http_status(:no_content)
end
end
end
end
def go
get :metrics, format: :json,
namespace_id: project.namespace,
project_id: project,
id: cluster
end
describe 'security' do
let(:prometheus_adapter) { double('prometheus_adapter', can_query?: true, query: nil) }
before do
allow(controller).to receive(:prometheus_adapter).and_return(prometheus_adapter)
end
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
it { expect { go }.to be_allowed_for(:master).of(project) }
it { expect { go }.to be_denied_for(:developer).of(project) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) }
end
end
describe 'PUT update' do
context 'when cluster is provided by GCP' do
let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
......
......@@ -7,6 +7,8 @@ describe Projects::Prometheus::MetricsController do
let(:prometheus_adapter) { double('prometheus_adapter', can_query?: true) }
before do
allow(controller).to receive(:project).and_return(project)
project.add_master(user)
sign_in(user)
end
......
......@@ -4,14 +4,15 @@ describe PrometheusAdapter, :use_clean_rails_memory_store_caching do
include PrometheusHelpers
include ReactiveCachingHelpers
class TestClass
include PrometheusAdapter
end
let(:project) { create(:prometheus_project) }
let(:service) { project.prometheus_service }
let(:described_class) { TestClass }
let(:described_class) do
Class.new do
include PrometheusAdapter
end
end
let(:environment_query) { Gitlab::Prometheus::Queries::EnvironmentQuery }
describe '#query' do
......
......@@ -547,7 +547,7 @@ describe Environment do
let(:project) { create(:prometheus_project) }
subject { environment.additional_metrics }
context 'when the environment has additional metrics' do
context 'when the environment has metrics' do
before do
allow(environment).to receive(:has_metrics?).and_return(true)
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