Commit 3b9425e0 authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch '63044-add-prom-proxies' into 'master'

Add prometheus proxies

See merge request gitlab-org/gitlab-ee!15700
parents 882d22c9 6ce79b1a
......@@ -31,6 +31,10 @@ class Clusters::BaseController < ApplicationController
access_denied! unless can?(current_user, :create_cluster, clusterable)
end
def authorize_read_prometheus!
access_denied! unless can?(current_user, :read_prometheus, clusterable)
end
def clusterable
raise NotImplementedError
end
......
......@@ -8,6 +8,7 @@ module Clusters
enable :create_cluster
enable :update_cluster
enable :admin_cluster
enable :read_prometheus
end
end
end
......@@ -141,6 +141,7 @@ Rails.application.routes.draw do
member do
Gitlab.ee do
get :metrics, format: :json
get '/prometheus/api/v1/*proxy_path', to: 'clusters#prometheus_proxy', as: :prometheus_api
end
scope :applications do
......
......@@ -3,6 +3,12 @@
module EE
module Clusters
module ClustersController
extend ActiveSupport::Concern
prepended do
before_action :authorize_read_prometheus!, only: :prometheus_proxy
end
def metrics
return render_404 unless prometheus_adapter&.can_query?
......@@ -19,6 +25,31 @@ module EE
end
end
def prometheus_proxy
result = ::Prometheus::ProxyService.new(
clusterable.clusterable,
proxy_method,
proxy_path,
proxy_params
).execute
if result.nil?
return render status: :no_content, json: {
status: _('processing'),
message: _('Not ready yet. Try again later.')
}
end
if result[:status] == :success
render status: result[:http_status], json: result[:body]
else
render(
status: result[:http_status] || :bad_request,
json: { status: result[:status], message: result[:message] }
)
end
end
private
def prometheus_adapter
......@@ -26,6 +57,18 @@ module EE
cluster.application_prometheus
end
def proxy_method
request.method
end
def proxy_path
params[:proxy_path]
end
def proxy_params
params.permit!
end
end
end
end
......@@ -48,6 +48,7 @@ module EE
rule { reporter }.policy do
enable :admin_list
enable :admin_board
enable :read_prometheus
end
rule { maintainer }.policy do
......
# frozen_string_literal: true
require 'spec_helper'
describe Admin::ClustersController do
it_behaves_like 'cluster metrics' do
let(:user) { create(:admin) }
let(:clusterable) { Clusters::Instance.new }
let(:cluster) do
create(:cluster, :instance, :provided_by_gcp)
end
let(:metrics_params) do
{
id: cluster
}
end
before do
allow(::Clusters::Instance).to receive(:new).and_return(cluster.instance)
end
context 'with inappropriate requests' do
context 'with anoymous user' do
before do
sign_out(user)
end
it 'renders not found' do
get :prometheus_proxy, params: prometheus_proxy_params
expect(response).to have_gitlab_http_status(404)
end
end
context 'with non-admin user' do
let(:user) { create(:user) }
before do
sign_in(user)
end
it 'renders not found' do
get :prometheus_proxy, params: prometheus_proxy_params
expect(response).to have_gitlab_http_status(404)
end
end
end
end
private
def prometheus_proxy_params(params = {})
{
id: cluster.id.to_s,
proxy_path: 'query',
query: '1'
}.merge(params)
end
end
......@@ -15,6 +15,7 @@ describe Groups::ClustersController do
end
it_behaves_like 'cluster metrics' do
let(:user) { create(:user) }
let(:clusterable) { group }
let(:cluster) do
......@@ -27,6 +28,67 @@ describe Groups::ClustersController do
id: cluster
}
end
before do
clusterable.add_maintainer(user)
end
context 'with inappropriate requests' do
context 'with anoymous user' do
before do
sign_out(user)
end
it 'renders not found' do
get :prometheus_proxy, params: prometheus_proxy_params
expect(response).to have_gitlab_http_status(404)
end
context 'with invalid clusterable id' do
before do
sign_in(user)
end
let(:other_clusterable) { create(:group) }
it 'returns 404' do
get :prometheus_proxy, params: prometheus_proxy_params(id: other_clusterable.id)
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end
describe 'security' do
let(:prometheus_adapter) { double(:prometheus_adapter, can_query?: true, query: nil) }
before do
sign_in(user)
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(clusterable) }
it { expect { go }.to be_allowed_for(:maintainer).of(clusterable) }
it { expect { go }.to be_denied_for(:developer).of(clusterable) }
it { expect { go }.to be_denied_for(:reporter).of(clusterable) }
it { expect { go }.to be_denied_for(:guest).of(clusterable) }
it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) }
end
end
private
def prometheus_proxy_params(params = {})
{
id: cluster.id.to_s,
group_id: group.name,
proxy_path: 'query',
query: '1'
}.merge(params)
end
describe 'GET environments' do
......
......@@ -6,6 +6,7 @@ describe Projects::ClustersController do
set(:project) { create(:project) }
it_behaves_like 'cluster metrics' do
let(:user) { create(:user) }
let(:clusterable) { project }
let(:cluster) do
......@@ -19,5 +20,67 @@ describe Projects::ClustersController do
id: cluster
}
end
before do
clusterable.add_maintainer(user)
end
context 'with inappropriate requests' do
context 'with annoymous user' do
before do
sign_out(user)
end
it 'redirects to signin page' do
get :prometheus_proxy, params: prometheus_proxy_params
expect(response).to redirect_to(new_user_session_path)
end
end
context 'with invalid clusterable id' do
before do
sign_in(user)
end
let(:other_clusterable) { create(:project) }
it 'returns 404' do
get :prometheus_proxy, params: prometheus_proxy_params(id: other_clusterable.id)
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
describe 'security' do
let(:prometheus_adapter) { double(:prometheus_adapter, can_query?: true, query: nil) }
before do
sign_in(user)
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(clusterable) }
it { expect { go }.to be_allowed_for(:maintainer).of(clusterable) }
it { expect { go }.to be_denied_for(:developer).of(clusterable) }
it { expect { go }.to be_denied_for(:reporter).of(clusterable) }
it { expect { go }.to be_denied_for(:guest).of(clusterable) }
it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) }
end
end
private
def prometheus_proxy_params(params = {})
{
id: cluster.id.to_s,
namespace_id: project.namespace.full_path,
project_id: project.name,
proxy_path: 'query',
query: '1'
}.merge(params)
end
end
......@@ -11,10 +11,7 @@ shared_examples 'cluster metrics' do
end
describe 'functionality' do
let(:user) { create(:user) }
before do
clusterable.add_maintainer(user)
sign_in(user)
end
......@@ -77,24 +74,123 @@ shared_examples 'cluster metrics' do
end
end
end
end
describe 'GET #prometheus_proxy' do
let(:prometheus_proxy_service) { instance_double(Prometheus::ProxyService) }
let(:expected_params) do
ActionController::Parameters.new(
prometheus_proxy_params(
proxy_path: 'query',
controller: subject.controller_path,
action: 'prometheus_proxy'
)
).permit!
end
before do
sign_in(user)
end
context 'with valid requests' do
before do
allow(Prometheus::ProxyService).to receive(:new)
.with(clusterable, 'GET', 'query', expected_params)
.and_return(prometheus_proxy_service)
allow(prometheus_proxy_service).to receive(:execute)
.and_return(service_result)
end
context 'with success result' do
let(:service_result) { { status: :success, body: prometheus_body } }
let(:prometheus_body) { '{"status":"success"}' }
it 'returns prometheus response' do
prometheus_json_body = JSON.parse(prometheus_body)
get :prometheus_proxy, params: prometheus_proxy_params
expect(Prometheus::ProxyService).to have_received(:new)
.with(clusterable, 'GET', 'query', expected_params)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq(prometheus_json_body)
end
end
context 'with nil result' do
let(:service_result) { nil }
it 'returns 204 no content' do
get :prometheus_proxy, params: prometheus_proxy_params
expect(json_response['status']).to eq('processing')
expect(json_response['message']).to eq('Not ready yet. Try again later.')
expect(response).to have_gitlab_http_status(:no_content)
end
end
context 'with 404 result' do
let(:service_result) { { http_status: 404, status: :success, body: '{"body": "value"}' } }
it 'returns body' do
get :prometheus_proxy, params: prometheus_proxy_params
describe 'security' do
let(:prometheus_adapter) { double(:prometheus_adapter, can_query?: true, query: nil) }
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(clusterable) }
it { expect { go }.to be_allowed_for(:maintainer).of(clusterable) }
it { expect { go }.to be_denied_for(:developer).of(clusterable) }
it { expect { go }.to be_denied_for(:reporter).of(clusterable) }
it { expect { go }.to be_denied_for(:guest).of(clusterable) }
it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) }
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response['body']).to eq('value')
end
end
context 'with error result' do
context 'with http_status' do
let(:service_result) do
{ http_status: :service_unavailable, status: :error, message: 'error message' }
end
it 'sets the http response status code' do
get :prometheus_proxy, params: prometheus_proxy_params
expect(response).to have_gitlab_http_status(:service_unavailable)
expect(json_response['status']).to eq('error')
expect(json_response['message']).to eq('error message')
end
end
context 'without http_status' do
let(:service_result) { { status: :error, message: 'error message' } }
it 'returns bad_request' do
get :prometheus_proxy, params: prometheus_proxy_params
expect(response). to have_gitlab_http_status(:bad_request)
expect(json_response['status']).to eq('error')
expect(json_response['message']).to eq('error message')
end
end
end
end
private
context 'with inappropriate requests' do
context 'without correct permissions' do
let(:user2) { create(:user) }
before do
sign_out(user)
sign_in(user2)
end
it 'returns 404' do
get :prometheus_proxy, params: prometheus_proxy_params
def go
get :metrics, params: metrics_params, format: :json
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end
private
def go
get :metrics, params: metrics_params, format: :json
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