diff options
author | Sean McGivern <sean@gitlab.com> | 2019-04-05 09:21:08 +0000 |
---|---|---|
committer | Sean McGivern <sean@gitlab.com> | 2019-04-05 09:21:08 +0000 |
commit | 5b273d355f47baa27b365e22b96a07187c78e1a7 (patch) | |
tree | 817e1bf2e3e9ac677ed7735b9df06c7c72d52359 | |
parent | eff97941248e1a8fc3a693325eb1267ea196adda (diff) | |
parent | 20594de8cc3311ddc53b29ffdf60bcf4a580a5b3 (diff) | |
download | gitlab-ce-5b273d355f47baa27b365e22b96a07187c78e1a7.tar.gz |
Merge branch '58375-api-controller' into 'master'
Add a prometheus proxy API per environment
Closes #58375
See merge request gitlab-org/gitlab-ce!26841
-rw-r--r-- | app/controllers/projects/environments/prometheus_api_controller.rb | 37 | ||||
-rw-r--r-- | app/policies/project_policy.rb | 1 | ||||
-rw-r--r-- | changelogs/unreleased/58375-api-controller.yml | 5 | ||||
-rw-r--r-- | config/routes/project.rb | 2 | ||||
-rw-r--r-- | locale/gitlab.pot | 6 | ||||
-rw-r--r-- | spec/controllers/projects/environments/prometheus_api_controller_spec.rb | 152 | ||||
-rw-r--r-- | spec/support/shared_context/policies/project_policy_shared_context.rb | 1 |
7 files changed, 204 insertions, 0 deletions
diff --git a/app/controllers/projects/environments/prometheus_api_controller.rb b/app/controllers/projects/environments/prometheus_api_controller.rb new file mode 100644 index 00000000000..fd3320637b0 --- /dev/null +++ b/app/controllers/projects/environments/prometheus_api_controller.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +class Projects::Environments::PrometheusApiController < Projects::ApplicationController + before_action :authorize_read_prometheus! + before_action :environment + + def proxy + result = Prometheus::ProxyService.new( + environment, + request.method, + params[:proxy_path], + params.permit! + ).execute + + if result.nil? + return render status: :accepted, 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 environment + @environment ||= project.environments.find(params[:id]) + end +end diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 75825c8fac0..26d7d6e84c4 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -204,6 +204,7 @@ class ProjectPolicy < BasePolicy enable :read_merge_request enable :read_sentry_issue enable :read_release + enable :read_prometheus end # We define `:public_user_access` separately because there are cases in gitlab-ee diff --git a/changelogs/unreleased/58375-api-controller.yml b/changelogs/unreleased/58375-api-controller.yml new file mode 100644 index 00000000000..60f21b37ae7 --- /dev/null +++ b/changelogs/unreleased/58375-api-controller.yml @@ -0,0 +1,5 @@ +--- +title: Add a Prometheus API per environment +merge_request: 26841 +author: +type: added diff --git a/config/routes/project.rb b/config/routes/project.rb index d60a5cc9ae8..1cb8f331f6f 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -219,6 +219,8 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do get :metrics get :additional_metrics get '/terminal.ws/authorize', to: 'environments#terminal_websocket_authorize', constraints: { format: nil } + + get '/prometheus/api/v1/*proxy_path', to: 'environments/prometheus_api#proxy' end collection do diff --git a/locale/gitlab.pot b/locale/gitlab.pot index a0168dec980..ea08f468616 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -5492,6 +5492,9 @@ msgstr "" msgid "Not now" msgstr "" +msgid "Not ready yet. Try again later." +msgstr "" + msgid "Not started" msgstr "" @@ -10049,6 +10052,9 @@ msgstr "" msgid "private" msgstr "" +msgid "processing" +msgstr "" + msgid "project" msgstr "" diff --git a/spec/controllers/projects/environments/prometheus_api_controller_spec.rb b/spec/controllers/projects/environments/prometheus_api_controller_spec.rb new file mode 100644 index 00000000000..5a0b92c2514 --- /dev/null +++ b/spec/controllers/projects/environments/prometheus_api_controller_spec.rb @@ -0,0 +1,152 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Projects::Environments::PrometheusApiController do + set(:project) { create(:project) } + set(:environment) { create(:environment, project: project) } + set(:user) { create(:user) } + + before do + project.add_reporter(user) + sign_in(user) + end + + describe 'GET #proxy' do + let(:prometheus_proxy_service) { instance_double(Prometheus::ProxyService) } + let(:expected_params) do + ActionController::Parameters.new( + environment_params( + proxy_path: 'query', + controller: 'projects/environments/prometheus_api', + action: 'proxy' + ) + ).permit! + end + + context 'with valid requests' do + before do + allow(Prometheus::ProxyService).to receive(:new) + .with(environment, '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"}' } + let(:prometheus_json_body) { JSON.parse(prometheus_body) } + + it 'returns prometheus response' do + get :proxy, params: environment_params + + expect(Prometheus::ProxyService).to have_received(:new) + .with(environment, '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 202 accepted' do + get :proxy, params: environment_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(:accepted) + end + end + + context 'with 404 result' do + let(:service_result) { { http_status: 404, status: :success, body: '{"body": "value"}' } } + + it 'returns body' do + get :proxy, params: environment_params + + 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 :proxy, params: environment_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 :proxy, params: environment_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 + + context 'with inappropriate requests' do + context 'with anonymous user' do + before do + sign_out(user) + end + + it 'redirects to signin page' do + get :proxy, params: environment_params + + expect(response).to redirect_to(new_user_session_path) + end + end + + context 'without correct permissions' do + before do + project.team.truncate + end + + it 'returns 404' do + get :proxy, params: environment_params + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end + + context 'with invalid environment id' do + let(:other_environment) { create(:environment) } + + it 'returns 404' do + get :proxy, params: environment_params(id: other_environment.id) + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end + + private + + def environment_params(params = {}) + { + id: environment.id.to_s, + namespace_id: project.namespace.name, + project_id: project.name, + proxy_path: 'query', + query: '1' + }.merge(params) + end +end diff --git a/spec/support/shared_context/policies/project_policy_shared_context.rb b/spec/support/shared_context/policies/project_policy_shared_context.rb index 3ad6e067674..ee5cfcd850d 100644 --- a/spec/support/shared_context/policies/project_policy_shared_context.rb +++ b/spec/support/shared_context/policies/project_policy_shared_context.rb @@ -25,6 +25,7 @@ RSpec.shared_context 'ProjectPolicy context' do admin_issue admin_label admin_list read_commit_status read_build read_container_image read_pipeline read_environment read_deployment read_merge_request download_wiki_code read_sentry_issue read_release + read_prometheus ] end |