Commit 5f366e2b authored by Thong Kuah's avatar Thong Kuah

Add experimental internal Kubernetes API

- Return agent_id in response if present
- Receive token via Authorization header

Use standard way for Authorization which is the header. We currently not
care about the
Authorization type but may enforce this in future

Adds /project_info endpoint. Only for public projects. We will sort out
authorization for internal and private projects later

Remove need to hack around InternalHelpers. InternalHelpers assumes an
actor (we don't have it) and params (don't have that either). So get the
required data directly instead.
parent 2f61a0b5
...@@ -237,6 +237,7 @@ module API ...@@ -237,6 +237,7 @@ module API
mount ::API::Internal::Base mount ::API::Internal::Base
mount ::API::Internal::Pages mount ::API::Internal::Pages
mount ::API::Internal::Kubernetes
route :any, '*path' do route :any, '*path' do
error!('404 Not Found', 404) error!('404 Not Found', 404)
......
# frozen_string_literal: true
module API
# Kubernetes Internal API
module Internal
class Kubernetes < Grape::API::Instance
helpers do
def repo_type
Gitlab::GlRepository::PROJECT
end
def gl_repository(project)
repo_type.identifier_for_container(project)
end
def gl_repository_path(project)
repo_type.repository_for(project).full_path
end
def authorization_header
strong_memoize(:authorization_header) do
request.headers['Authorization']
end
end
def authorization_token
unless authorization_header.present?
unauthorized!
end
_token_type, authorization_token = authorization_header.split(' ', 2)
authorization_token
end
def check_feature_enabled
not_found! unless Feature.enabled?(:kubernetes_agent_internal_api)
end
end
namespace 'internal' do
namespace 'kubernetes' do
desc 'Gets agent info' do
detail 'Retrieves agent info for the given token'
end
get '/agent_info' do
check_feature_enabled
agent_token = Clusters::AgentToken.find_by_token(authorization_token)
if agent_token
agent = agent_token.agent
project = agent.project
@gl_project_string = "project-#{project.id}"
status 200
{
project_id: project.id,
agent_id: agent.id,
agent_name: agent.name,
storage_name: project.repository_storage,
relative_path: project.disk_path + '.git',
gl_repository: gl_repository(project),
gl_project_path: gl_repository_path(project)
}
else
status 403
end
end
desc 'Gets project info' do
detail 'Retrieves project info (if authorized)'
end
get '/project_info' do
check_feature_enabled
agent_token = Clusters::AgentToken.find_by_token(authorization_token)
if agent_token
project = find_project(params[:id])
# TODO sort out authorization for real
# https://gitlab.com/gitlab-org/gitlab/-/issues/220912
if !project || !project.public?
not_found!
end
@gl_project_string = "project-#{project.id}"
status 200
{
project_id: project.id,
storage_name: project.repository_storage,
relative_path: project.disk_path + '.git',
gl_repository: gl_repository(project),
gl_project_path: gl_repository_path(project)
}
else
status 403
end
end
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe API::Internal::Kubernetes do
describe "GET /internal/kubernetes/agent_info" do
context 'kubernetes_agent_internal_api feature flag disabled' do
before do
stub_feature_flags(kubernetes_agent_internal_api: false)
end
it 'returns 404' do
get api('/internal/kubernetes/agent_info')
expect(response).to have_gitlab_http_status(:not_found)
end
end
it 'returns 401 if Authorization header not sent' do
get api('/internal/kubernetes/agent_info')
expect(response).to have_gitlab_http_status(:unauthorized)
end
context 'an agent is found' do
let!(:agent_token) { create(:cluster_agent_token) }
let(:agent) { agent_token.agent }
let(:project) { agent.project }
it 'returns expected data', :aggregate_failures do
get api('/internal/kubernetes/agent_info'), headers: { 'Authorization' => "Bearer #{agent_token.token}" }
expect(response).to have_gitlab_http_status(:success)
expect(json_response['project_id']).to eq(project.id)
expect(json_response['agent_id']).to eq(agent.id)
expect(json_response['agent_name']).to eq(agent.name)
expect(json_response['storage_name']).to eq(project.repository_storage)
expect(json_response['relative_path']).to eq(project.disk_path + '.git')
expect(json_response['gl_repository']).to eq("project-#{project.id}")
expect(json_response['gl_project_path']).to eq(project.full_path)
end
end
context 'no such agent exists' do
it 'returns 404' do
get api('/internal/kubernetes/agent_info'), headers: { 'Authorization' => 'Bearer ABCD' }
expect(response).to have_gitlab_http_status(:forbidden)
end
end
end
describe 'GET /internal/kubernetes/project_info' do
context 'kubernetes_agent_internal_api feature flag disabled' do
before do
stub_feature_flags(kubernetes_agent_internal_api: false)
end
it 'returns 404' do
get api('/internal/kubernetes/project_info')
expect(response).to have_gitlab_http_status(:not_found)
end
end
it 'returns 401 if Authorization header not sent' do
get api('/internal/kubernetes/project_info')
expect(response).to have_gitlab_http_status(:unauthorized)
end
context 'no such agent exists' do
it 'returns 404' do
get api('/internal/kubernetes/project_info'), headers: { 'Authorization' => 'Bearer ABCD' }
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'an agent is found' do
let!(:agent_token) { create(:cluster_agent_token) }
let(:agent) { agent_token.agent }
context 'project is public' do
let(:project) { create(:project, :public) }
it 'returns expected data', :aggregate_failures do
get api('/internal/kubernetes/project_info'), params: { id: project.id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" }
expect(response).to have_gitlab_http_status(:success)
expect(json_response['project_id']).to eq(project.id)
expect(json_response['storage_name']).to eq(project.repository_storage)
expect(json_response['relative_path']).to eq(project.disk_path + '.git')
expect(json_response['gl_repository']).to eq("project-#{project.id}")
expect(json_response['gl_project_path']).to eq(project.full_path)
end
end
context 'project is private' do
let(:project) { create(:project, :private) }
it 'returns 404' do
get api('/internal/kubernetes/project_info'), params: { id: project.id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" }
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'project is internal' do
let(:project) { create(:project, :internal) }
it 'returns 404' do
get api('/internal/kubernetes/project_info'), params: { id: project.id }, headers: { 'Authorization' => "Bearer #{agent_token.token}" }
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'project does not exist' do
it 'returns 404' do
get api('/internal/kubernetes/project_info'), params: { id: 0 }, headers: { 'Authorization' => "Bearer #{agent_token.token}" }
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
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