summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean McGivern <sean@gitlab.com>2019-04-05 09:21:08 +0000
committerSean McGivern <sean@gitlab.com>2019-04-05 09:21:08 +0000
commit5b273d355f47baa27b365e22b96a07187c78e1a7 (patch)
tree817e1bf2e3e9ac677ed7735b9df06c7c72d52359
parenteff97941248e1a8fc3a693325eb1267ea196adda (diff)
parent20594de8cc3311ddc53b29ffdf60bcf4a580a5b3 (diff)
downloadgitlab-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.rb37
-rw-r--r--app/policies/project_policy.rb1
-rw-r--r--changelogs/unreleased/58375-api-controller.yml5
-rw-r--r--config/routes/project.rb2
-rw-r--r--locale/gitlab.pot6
-rw-r--r--spec/controllers/projects/environments/prometheus_api_controller_spec.rb152
-rw-r--r--spec/support/shared_context/policies/project_policy_shared_context.rb1
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