Commit 541a0852 authored by Alex Buijs's avatar Alex Buijs

Add client_id to context

Client ID will have one of these values:
- user/user.id
- runner/runner.id
- ip/request.ip
parent f29ef4fa
...@@ -313,7 +313,7 @@ gem 'pg_query', '~> 1.3.0' ...@@ -313,7 +313,7 @@ gem 'pg_query', '~> 1.3.0'
gem 'premailer-rails', '~> 1.10.3' gem 'premailer-rails', '~> 1.10.3'
# LabKit: Tracing and Correlation # LabKit: Tracing and Correlation
gem 'gitlab-labkit', '~> 0.16.0' gem 'gitlab-labkit', '~> 0.16.1'
# Thrift is a dependency of gitlab-labkit, we want a version higher than 0.14.0 # Thrift is a dependency of gitlab-labkit, we want a version higher than 0.14.0
# because of https://gitlab.com/gitlab-org/gitlab/-/issues/321900 # because of https://gitlab.com/gitlab-org/gitlab/-/issues/321900
gem 'thrift', '>= 0.14.0' gem 'thrift', '>= 0.14.0'
......
...@@ -447,7 +447,7 @@ GEM ...@@ -447,7 +447,7 @@ GEM
fog-xml (~> 0.1.0) fog-xml (~> 0.1.0)
google-api-client (>= 0.44.2, < 0.51) google-api-client (>= 0.44.2, < 0.51)
google-cloud-env (~> 1.2) google-cloud-env (~> 1.2)
gitlab-labkit (0.16.0) gitlab-labkit (0.16.1)
actionpack (>= 5.0.0, < 7.0.0) actionpack (>= 5.0.0, < 7.0.0)
activesupport (>= 5.0.0, < 7.0.0) activesupport (>= 5.0.0, < 7.0.0)
grpc (~> 1.19) grpc (~> 1.19)
...@@ -1416,7 +1416,7 @@ DEPENDENCIES ...@@ -1416,7 +1416,7 @@ DEPENDENCIES
gitlab-experiment (~> 0.4.12) gitlab-experiment (~> 0.4.12)
gitlab-fog-azure-rm (~> 1.0.1) gitlab-fog-azure-rm (~> 1.0.1)
gitlab-fog-google (~> 1.13) gitlab-fog-google (~> 1.13)
gitlab-labkit (~> 0.16.0) gitlab-labkit (~> 0.16.1)
gitlab-license (~> 1.3) gitlab-license (~> 1.3)
gitlab-mail_room (~> 0.0.8) gitlab-mail_room (~> 0.0.8)
gitlab-markup (~> 1.7.1) gitlab-markup (~> 1.7.1)
......
---
title: Add client_id to application context
merge_request: 55683
author:
type: added
...@@ -43,10 +43,12 @@ RSpec.describe Gitlab::ApplicationContext do ...@@ -43,10 +43,12 @@ RSpec.describe Gitlab::ApplicationContext do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
where(:provided_options, :expected_context_keys) do where(:provided_options, :expected_context_keys) do
[:user, :namespace, :project] | [:user, :project, :root_namespace, :subscription_plan] [:user, :namespace, :project] | [:user, :project, :root_namespace, :client_id, :subscription_plan]
[:user, :project] | [:user, :project, :root_namespace, :subscription_plan] [:user, :project] | [:user, :project, :root_namespace, :client_id, :subscription_plan]
[:user, :namespace] | [:user, :root_namespace, :subscription_plan] [:user, :namespace] | [:user, :root_namespace, :client_id, :subscription_plan]
[:user] | [:user] [:user] | [:user, :client_id]
[:remote_ip] | [:remote_ip, :client_id]
[:runner] | [:project, :root_namespace, :client_id, :subscription_plan]
[:caller_id] | [:caller_id] [:caller_id] | [:caller_id]
[] | [] [] | []
end end
......
...@@ -58,6 +58,7 @@ module API ...@@ -58,6 +58,7 @@ module API
user: -> { @current_user }, user: -> { @current_user },
project: -> { @project }, project: -> { @project },
namespace: -> { @group }, namespace: -> { @group },
runner: -> { @current_runner || @runner },
caller_id: route.origin, caller_id: route.origin,
remote_ip: request.ip, remote_ip: request.ip,
feature_category: feature_category feature_category: feature_category
......
...@@ -44,12 +44,12 @@ module API ...@@ -44,12 +44,12 @@ module API
forbidden! forbidden!
end end
runner = ::Ci::Runner.create(attributes) @runner = ::Ci::Runner.create(attributes)
if runner.persisted? if @runner.persisted?
present runner, with: Entities::RunnerRegistrationDetails present @runner, with: Entities::RunnerRegistrationDetails
else else
render_validation_error!(runner) render_validation_error!(@runner)
end end
end end
...@@ -62,9 +62,7 @@ module API ...@@ -62,9 +62,7 @@ module API
delete '/' do delete '/' do
authenticate_runner! authenticate_runner!
runner = ::Ci::Runner.find_by_token(params[:token]) destroy_conditionally!(current_runner)
destroy_conditionally!(runner)
end end
desc 'Validates authentication credentials' do desc 'Validates authentication credentials' do
......
...@@ -73,23 +73,12 @@ module API ...@@ -73,23 +73,12 @@ module API
end end
def set_application_context def set_application_context
if current_job return unless current_job
Gitlab::ApplicationContext.push(
user: -> { current_job.user }, Gitlab::ApplicationContext.push(
project: -> { current_job.project } user: -> { current_job.user },
) project: -> { current_job.project }
elsif current_runner&.project_type? )
Gitlab::ApplicationContext.push(
project: -> do
projects = current_runner.projects.limit(2) # rubocop: disable CodeReuse/ActiveRecord
projects.first if projects.length == 1
end
)
elsif current_runner&.group_type?
Gitlab::ApplicationContext.push(
namespace: -> { current_runner.groups.first }
)
end
end end
end end
end end
......
...@@ -4,6 +4,7 @@ module Gitlab ...@@ -4,6 +4,7 @@ module Gitlab
# A GitLab-rails specific accessor for `Labkit::Logging::ApplicationContext` # A GitLab-rails specific accessor for `Labkit::Logging::ApplicationContext`
class ApplicationContext class ApplicationContext
include Gitlab::Utils::LazyAttributes include Gitlab::Utils::LazyAttributes
include Gitlab::Utils::StrongMemoize
Attribute = Struct.new(:name, :type) Attribute = Struct.new(:name, :type)
...@@ -11,6 +12,7 @@ module Gitlab ...@@ -11,6 +12,7 @@ module Gitlab
Attribute.new(:project, Project), Attribute.new(:project, Project),
Attribute.new(:namespace, Namespace), Attribute.new(:namespace, Namespace),
Attribute.new(:user, User), Attribute.new(:user, User),
Attribute.new(:runner, ::Ci::Runner),
Attribute.new(:caller_id, String), Attribute.new(:caller_id, String),
Attribute.new(:remote_ip, String), Attribute.new(:remote_ip, String),
Attribute.new(:related_class, String), Attribute.new(:related_class, String),
...@@ -47,8 +49,9 @@ module Gitlab ...@@ -47,8 +49,9 @@ module Gitlab
def to_lazy_hash def to_lazy_hash
{}.tap do |hash| {}.tap do |hash|
hash[:user] = -> { username } if set_values.include?(:user) hash[:user] = -> { username } if set_values.include?(:user)
hash[:project] = -> { project_path } if set_values.include?(:project) hash[:project] = -> { project_path } if set_values.include?(:project) || set_values.include?(:runner)
hash[:root_namespace] = -> { root_namespace_path } if include_namespace? hash[:root_namespace] = -> { root_namespace_path } if include_namespace?
hash[:client_id] = -> { client } if include_client?
hash[:caller_id] = caller_id if set_values.include?(:caller_id) hash[:caller_id] = caller_id if set_values.include?(:caller_id)
hash[:remote_ip] = remote_ip if set_values.include?(:remote_ip) hash[:remote_ip] = remote_ip if set_values.include?(:remote_ip)
hash[:related_class] = related_class if set_values.include?(:related_class) hash[:related_class] = related_class if set_values.include?(:related_class)
...@@ -75,7 +78,8 @@ module Gitlab ...@@ -75,7 +78,8 @@ module Gitlab
end end
def project_path def project_path
project&.full_path associated_routable = project || runner_project
associated_routable&.full_path
end end
def username def username
...@@ -83,15 +87,43 @@ module Gitlab ...@@ -83,15 +87,43 @@ module Gitlab
end end
def root_namespace_path def root_namespace_path
if namespace associated_routable = namespace || project || runner_project || runner_group
namespace.full_path_components.first associated_routable&.full_path_components&.first
end
def include_namespace?
set_values.include?(:namespace) || set_values.include?(:project) || set_values.include?(:runner)
end
def include_client?
set_values.include?(:user) || set_values.include?(:runner) || set_values.include?(:remote_ip)
end
def client
if user
"user/#{user.id}"
elsif runner
"runner/#{runner.id}"
else else
project&.full_path_components&.first "ip/#{remote_ip}"
end end
end end
def include_namespace? def runner_project
set_values.include?(:namespace) || set_values.include?(:project) strong_memoize(:runner_project) do
next unless runner&.project_type?
projects = runner.projects.take(2) # rubocop: disable CodeReuse/ActiveRecord
projects.first if projects.one?
end
end
def runner_group
strong_memoize(:runner_group) do
next unless runner&.group_type?
runner.groups.first
end
end end
end end
end end
......
...@@ -30,7 +30,7 @@ RSpec.describe Gitlab::ApplicationContext do ...@@ -30,7 +30,7 @@ RSpec.describe Gitlab::ApplicationContext do
describe '.push' do describe '.push' do
it 'passes the expected context on to labkit' do it 'passes the expected context on to labkit' do
fake_proc = duck_type(:call) fake_proc = duck_type(:call)
expected_context = { user: fake_proc } expected_context = { user: fake_proc, client_id: fake_proc }
expect(Labkit::Context).to receive(:push).with(expected_context) expect(Labkit::Context).to receive(:push).with(expected_context)
...@@ -92,6 +92,34 @@ RSpec.describe Gitlab::ApplicationContext do ...@@ -92,6 +92,34 @@ RSpec.describe Gitlab::ApplicationContext do
expect(result(context)) expect(result(context))
.to include(project: project.full_path, root_namespace: project.full_path_components.first) .to include(project: project.full_path, root_namespace: project.full_path_components.first)
end end
describe 'setting the client' do
let_it_be(:remote_ip) { '127.0.0.1' }
let_it_be(:runner) { create(:ci_runner) }
let_it_be(:options) { { remote_ip: remote_ip, runner: runner, user: user } }
using RSpec::Parameterized::TableSyntax
where(:provided_options, :client) do
[:remote_ip] | :remote_ip
[:remote_ip, :runner] | :runner
[:remote_ip, :runner, :user] | :user
end
with_them do
it 'sets the client_id to the expected value' do
context = described_class.new(**options.slice(*provided_options))
client_id = case client
when :remote_ip then "ip/#{remote_ip}"
when :runner then "runner/#{runner.id}"
when :user then "user/#{user.id}"
end
expect(result(context)[:client_id]).to eq(client_id)
end
end
end
end end
describe '#use' do describe '#use' do
......
...@@ -112,6 +112,7 @@ RSpec.describe API::API do ...@@ -112,6 +112,7 @@ RSpec.describe API::API do
'meta.project' => project.full_path, 'meta.project' => project.full_path,
'meta.root_namespace' => project.namespace.full_path, 'meta.root_namespace' => project.namespace.full_path,
'meta.user' => user.username, 'meta.user' => user.username,
'meta.client_id' => an_instance_of(String),
'meta.feature_category' => 'issue_tracking') 'meta.feature_category' => 'issue_tracking')
end end
end end
...@@ -125,6 +126,7 @@ RSpec.describe API::API do ...@@ -125,6 +126,7 @@ RSpec.describe API::API do
expect(log_context).to match('correlation_id' => an_instance_of(String), expect(log_context).to match('correlation_id' => an_instance_of(String),
'meta.caller_id' => '/api/:version/users', 'meta.caller_id' => '/api/:version/users',
'meta.remote_ip' => an_instance_of(String), 'meta.remote_ip' => an_instance_of(String),
'meta.client_id' => an_instance_of(String),
'meta.feature_category' => 'users') 'meta.feature_category' => 'users')
end end
end end
......
...@@ -806,7 +806,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do ...@@ -806,7 +806,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
subject { request_job(id: job.id) } subject { request_job(id: job.id) }
it_behaves_like 'storing arguments in the application context' do it_behaves_like 'storing arguments in the application context' do
let(:expected_params) { { user: user.username, project: project.full_path } } let(:expected_params) { { user: user.username, project: project.full_path, client_id: "user/#{user.id}" } }
end end
it_behaves_like 'not executing any extra queries for the application context', 3 do it_behaves_like 'not executing any extra queries for the application context', 3 do
...@@ -817,7 +817,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do ...@@ -817,7 +817,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
context 'when the runner is of project type' do context 'when the runner is of project type' do
it_behaves_like 'storing arguments in the application context' do it_behaves_like 'storing arguments in the application context' do
let(:expected_params) { { project: project.full_path } } let(:expected_params) { { project: project.full_path, client_id: "runner/#{runner.id}" } }
end end
it_behaves_like 'not executing any extra queries for the application context', 2 do it_behaves_like 'not executing any extra queries for the application context', 2 do
...@@ -831,7 +831,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do ...@@ -831,7 +831,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
let(:runner) { create(:ci_runner, :group, groups: [group]) } let(:runner) { create(:ci_runner, :group, groups: [group]) }
it_behaves_like 'storing arguments in the application context' do it_behaves_like 'storing arguments in the application context' do
let(:expected_params) { { root_namespace: group.full_path_components.first } } let(:expected_params) { { root_namespace: group.full_path_components.first, client_id: "runner/#{runner.id}" } }
end end
it_behaves_like 'not executing any extra queries for the application context', 2 do it_behaves_like 'not executing any extra queries for the application context', 2 do
......
...@@ -37,8 +37,10 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do ...@@ -37,8 +37,10 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
context 'when valid token is provided' do context 'when valid token is provided' do
let(:runner) { create(:ci_runner) } let(:runner) { create(:ci_runner) }
subject { delete api('/runners'), params: { token: runner.token } }
it 'deletes Runner' do it 'deletes Runner' do
delete api('/runners'), params: { token: runner.token } subject
expect(response).to have_gitlab_http_status(:no_content) expect(response).to have_gitlab_http_status(:no_content)
expect(::Ci::Runner.count).to eq(0) expect(::Ci::Runner.count).to eq(0)
...@@ -48,6 +50,10 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do ...@@ -48,6 +50,10 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
let(:request) { api('/runners') } let(:request) { api('/runners') }
let(:params) { { token: runner.token } } let(:params) { { token: runner.token } }
end end
it_behaves_like 'storing arguments in the application context' do
let(:expected_params) { { client_id: "runner/#{runner.id}" } }
end
end end
end end
end end
......
...@@ -39,18 +39,32 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do ...@@ -39,18 +39,32 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
post api('/runners'), params: { token: token } post api('/runners'), params: { token: token }
end end
it 'creates runner with default values' do context 'with a registration token' do
post api('/runners'), params: { token: registration_token } let(:token) { registration_token }
runner = ::Ci::Runner.first it 'creates runner with default values' do
request
expect(response).to have_gitlab_http_status(:created) runner = ::Ci::Runner.first
expect(json_response['id']).to eq(runner.id)
expect(json_response['token']).to eq(runner.token) expect(response).to have_gitlab_http_status(:created)
expect(runner.run_untagged).to be true expect(json_response['id']).to eq(runner.id)
expect(runner.active).to be true expect(json_response['token']).to eq(runner.token)
expect(runner.token).not_to eq(registration_token) expect(runner.run_untagged).to be true
expect(runner).to be_instance_type expect(runner.active).to be true
expect(runner.token).not_to eq(registration_token)
expect(runner).to be_instance_type
end
it_behaves_like 'storing arguments in the application context' do
subject { request }
let(:expected_params) { { client_id: "runner/#{::Ci::Runner.first.id}" } }
end
it_behaves_like 'not executing any extra queries for the application context' do
let(:subject_proc) { proc { request } }
end
end end
context 'when project token is used' do context 'when project token is used' do
...@@ -71,7 +85,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do ...@@ -71,7 +85,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
it_behaves_like 'storing arguments in the application context' do it_behaves_like 'storing arguments in the application context' do
subject { request } subject { request }
let(:expected_params) { { project: project.full_path } } let(:expected_params) { { project: project.full_path, client_id: "runner/#{::Ci::Runner.first.id}" } }
end end
it_behaves_like 'not executing any extra queries for the application context' do it_behaves_like 'not executing any extra queries for the application context' do
...@@ -97,7 +111,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do ...@@ -97,7 +111,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
it_behaves_like 'storing arguments in the application context' do it_behaves_like 'storing arguments in the application context' do
subject { request } subject { request }
let(:expected_params) { { root_namespace: group.full_path_components.first } } let(:expected_params) { { root_namespace: group.full_path_components.first, client_id: "runner/#{::Ci::Runner.first.id}" } }
end end
it_behaves_like 'not executing any extra queries for the application context' do it_behaves_like 'not executing any extra queries for the application context' do
......
...@@ -37,11 +37,17 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do ...@@ -37,11 +37,17 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end end
context 'when valid token is provided' do context 'when valid token is provided' do
subject { post api('/runners/verify'), params: { token: runner.token } }
it 'verifies Runner credentials' do it 'verifies Runner credentials' do
post api('/runners/verify'), params: { token: runner.token } subject
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
end end
it_behaves_like 'storing arguments in the application context' do
let(:expected_params) { { client_id: "runner/#{runner.id}" } }
end
end 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