Skip to content
Snippets Groups Projects
Unverified Commit 436ba944 authored by Tomasz Maczukin's avatar Tomasz Maczukin
Browse files

Add Runner separation by plan PoC

Changelog: added
parent 01e5fb65
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -45,6 +45,7 @@ class Build < Ci::Processable
has_one :runtime_metadata, class_name: 'Ci::RunningBuild', foreign_key: :build_id
has_many :trace_chunks, class_name: 'Ci::BuildTraceChunk', foreign_key: :build_id, inverse_of: :build
has_many :report_results, class_name: 'Ci::BuildReportResult', inverse_of: :build
has_one :namespace, through: :project
 
# Projects::DestroyService destroys Ci::Pipelines, which use_fast_destroy on :job_artifacts
# before we delete builds. By doing this, the relation should be empty and not fire any
Loading
Loading
---
name: ci_runner_separation_by_plan
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83780
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/361639
milestone: '15.0'
type: ops
group: group::runner
default_enabled: false
# frozen_string_literal: true
class AddAllowedPlansToCiRunners < Gitlab::Database::Migration[1.0]
def change
# rubocop:disable Migration/AddLimitToTextColumns
add_column :ci_runners, :allowed_plans, :text, array: true, null: false, default: []
# rubocop:enable Migration/AddLimitToTextColumns
end
end
cd332bdb33335750855cd0d6e49bed12a841defa24bc5ffb14ad49a39bd663aa
\ No newline at end of file
Loading
Loading
@@ -13035,6 +13035,7 @@ CREATE TABLE ci_runners (
executor_type smallint,
maintainer_note text,
token_expires_at timestamp with time zone,
allowed_plans text[] DEFAULT '{}'::text[] NOT NULL,
CONSTRAINT check_ce275cee06 CHECK ((char_length(maintainer_note) <= 1024))
);
 
Loading
Loading
@@ -33,6 +33,22 @@ def visibility_levels_without_minutes_quota
end
end
 
def matches_build?(build)
return false unless super(build)
allowed_for_plans?(build)
end
def allowed_for_plans?(build)
return true unless ::Feature.enabled?(:ci_runner_separation_by_plan, self, type: :ops)
return true if allowed_plans.empty?
plans = build.namespace&.plans || []
common = allowed_plans & plans.map(&:name)
common.any?
end
private
 
def cost_factor
Loading
Loading
Loading
Loading
@@ -241,4 +241,47 @@
end
end
end
describe '#allowed_for_plans?', :saas do
let(:namespace) { create(:namespace_with_plan, plan: plan) }
let(:project) { create(:project, namespace: namespace) }
let(:pipeline) { create(:ci_pipeline, project: project) }
let(:build) { create(:ci_build, pipeline: pipeline) }
subject { create(:ci_runner, :instance, allowed_plans: allowed_plans).allowed_for_plans?(build) }
context 'when allowed plans are not defined' do
let(:allowed_plans) { [] }
let(:plan) { :premium_plan }
it { is_expected.to be_truthy }
end
context 'when allowed_plans are defined' do
let(:allowed_plans) { %w(silver premium) }
context 'when plans match allowed plans' do
let(:plan) { :premium_plan }
it { is_expected.to be_truthy }
end
context 'when plans do not match allowed plans' do
let(:plan) { :ultimate_plan }
it { is_expected.to be_falsey }
end
end
context 'when ci_runner_separation_by_plan feature flag is disabled' do
let(:allowed_plans) { %w(silver premium) }
let(:plan) { :ultimate_plan }
before do
stub_feature_flags(ci_runner_separation_by_plan: false)
end
it { is_expected.to be_truthy }
end
end
end
Loading
Loading
@@ -7,7 +7,8 @@
 
let_it_be_with_refind(:shared_runner) { create(:ci_runner, :instance) }
 
let!(:project) { create(:project, shared_runners_enabled: true) }
let!(:namespace) { create(:namespace) }
let!(:project) { create(:project, shared_runners_enabled: true, namespace: namespace) }
let!(:pipeline) { create(:ci_empty_pipeline, project: project) }
let!(:pending_build) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
 
Loading
Loading
@@ -362,4 +363,43 @@
include_examples 'namespace minutes quota'
end
end
describe 'ensure plan limitation', :saas do
let(:allowed_plans) { [] }
let(:plan_check_runner) { create(:ci_runner, :instance, allowed_plans: allowed_plans) }
subject { described_class.new(plan_check_runner).execute.build }
context 'when namespace has no plan attached' do
context 'runner does not define allowed plans' do
it { is_expected.to be_kind_of(Ci::Build) }
end
context 'runner defines allowed plans' do
let(:allowed_plans) { ['free'] }
it { is_expected.to be_nil }
end
end
context 'when namespace has plan attached' do
let(:namespace) { create(:namespace_with_plan, plan: :premium_plan) }
context 'runner does not define allowed plans' do
it { is_expected.to be_kind_of(Ci::Build) }
end
context 'runner defines allowed plans' do
let(:allowed_plans) { ['premium'] }
it { is_expected.to be_kind_of(Ci::Build) }
context 'allowed plans do not match namespace plan' do
let(:allowed_plans) { ['ultimate'] }
it { is_expected.to be_nil }
end
end
end
end
end
Loading
Loading
@@ -743,7 +743,7 @@
it { is_expected.to be_falsey }
end
 
context 'when there are runners' do
context 'when there is a runner' do
let(:runner) { create(:ci_runner, :project, projects: [build.project]) }
 
before do
Loading
Loading
@@ -752,19 +752,28 @@
 
it { is_expected.to be_truthy }
 
it 'that is inactive' do
runner.update!(active: false)
is_expected.to be_falsey
context 'that is inactive' do
before do
runner.update!(active: false)
end
it { is_expected.to be_falsey }
end
 
it 'that is not online' do
runner.update!(contacted_at: nil)
is_expected.to be_falsey
context 'that is not online' do
before do
runner.update!(contacted_at: nil)
end
it { is_expected.to be_falsey }
end
 
it 'that cannot handle build' do
expect_any_instance_of(Ci::Runner).to receive(:matches_build?).with(build).and_return(false)
is_expected.to be_falsey
context 'that cannot handle build' do
before do
expect_any_instance_of(Gitlab::Ci::Matching::RunnerMatcher).to receive(:matches?).with(build.build_matcher).and_return(false)
end
it { is_expected.to be_falsey }
end
end
 
Loading
Loading
Loading
Loading
@@ -76,7 +76,7 @@
end
 
let(:ignore_accessors) do
%i[type lock_version target_url base_tags trace_sections
%i[type namespace lock_version target_url base_tags trace_sections
commit_id deployment erased_by_id project_id
runner_id tag_taggings taggings tags trigger_request_id
user_id auto_canceled_by_id retried failure_reason
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment