Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/services/ci/register_job_service_spec.rb')
-rw-r--r--spec/services/ci/register_job_service_spec.rb1165
1 files changed, 600 insertions, 565 deletions
diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb
index f40f5cc5a62..9183df359b4 100644
--- a/spec/services/ci/register_job_service_spec.rb
+++ b/spec/services/ci/register_job_service_spec.rb
@@ -3,795 +3,830 @@
require 'spec_helper'
module Ci
- RSpec.describe RegisterJobService do
+ RSpec.describe RegisterJobService, feature_category: :continuous_integration do
let_it_be(:group) { create(:group) }
let_it_be_with_reload(:project) { create(:project, group: group, shared_runners_enabled: false, group_runners_enabled: false) }
let_it_be_with_reload(:pipeline) { create(:ci_pipeline, project: project) }
- let!(:shared_runner) { create(:ci_runner, :instance) }
- let!(:specific_runner) { create(:ci_runner, :project, projects: [project]) }
+ let_it_be(:shared_runner) { create(:ci_runner, :instance) }
+ let!(:project_runner) { create(:ci_runner, :project, projects: [project]) }
let!(:group_runner) { create(:ci_runner, :group, groups: [group]) }
let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
describe '#execute' do
- subject { described_class.new(shared_runner).execute }
+ subject(:execute) { described_class.new(runner, runner_machine).execute }
+
+ context 'with runner_machine specified' do
+ let(:runner) { project_runner }
+ let!(:runner_machine) { create(:ci_runner_machine, runner: project_runner) }
- context 'checks database loadbalancing stickiness' do
before do
- project.update!(shared_runners_enabled: false)
+ pending_job.update!(tag_list: ["linux"])
+ pending_job.reload
+ pending_job.create_queuing_entry!
+ project_runner.update!(tag_list: ["linux"])
end
- it 'result is valid if replica did caught-up', :aggregate_failures do
- expect(ApplicationRecord.sticking).to receive(:all_caught_up?)
- .with(:runner, shared_runner.id) { true }
+ it 'sets runner_machine on job' do
+ expect { execute }.to change { pending_job.reload.runner_machine }.from(nil).to(runner_machine)
- expect(subject).to be_valid
- expect(subject.build).to be_nil
- expect(subject.build_json).to be_nil
+ expect(execute.build).to eq(pending_job)
end
+ end
- it 'result is invalid if replica did not caught-up', :aggregate_failures do
- expect(ApplicationRecord.sticking).to receive(:all_caught_up?)
- .with(:runner, shared_runner.id) { false }
+ context 'with no runner machine' do
+ let(:runner_machine) { nil }
- expect(subject).not_to be_valid
- expect(subject.build).to be_nil
- expect(subject.build_json).to be_nil
- end
- end
+ context 'checks database loadbalancing stickiness' do
+ let(:runner) { shared_runner }
- shared_examples 'handles runner assignment' do
- context 'runner follow tag list' do
- it "picks build with the same tag" do
- pending_job.update!(tag_list: ["linux"])
- pending_job.reload
- pending_job.create_queuing_entry!
- specific_runner.update!(tag_list: ["linux"])
- expect(execute(specific_runner)).to eq(pending_job)
+ before do
+ project.update!(shared_runners_enabled: false)
end
- it "does not pick build with different tag" do
- pending_job.update!(tag_list: ["linux"])
- pending_job.reload
- pending_job.create_queuing_entry!
- specific_runner.update!(tag_list: ["win32"])
- expect(execute(specific_runner)).to be_falsey
- end
+ it 'result is valid if replica did caught-up', :aggregate_failures do
+ expect(ApplicationRecord.sticking).to receive(:all_caught_up?).with(:runner, runner.id) { true }
- it "picks build without tag" do
- expect(execute(specific_runner)).to eq(pending_job)
+ expect(execute).to be_valid
+ expect(execute.build).to be_nil
+ expect(execute.build_json).to be_nil
end
- it "does not pick build with tag" do
- pending_job.update!(tag_list: ["linux"])
- pending_job.reload
- pending_job.create_queuing_entry!
- expect(execute(specific_runner)).to be_falsey
- end
+ it 'result is invalid if replica did not caught-up', :aggregate_failures do
+ expect(ApplicationRecord.sticking).to receive(:all_caught_up?)
+ .with(:runner, shared_runner.id) { false }
- it "pick build without tag" do
- specific_runner.update!(tag_list: ["win32"])
- expect(execute(specific_runner)).to eq(pending_job)
+ expect(subject).not_to be_valid
+ expect(subject.build).to be_nil
+ expect(subject.build_json).to be_nil
end
end
- context 'deleted projects' do
- before do
- project.update!(pending_delete: true)
- end
+ shared_examples 'handles runner assignment' do
+ context 'runner follow tag list' do
+ it "picks build with the same tag" do
+ pending_job.update!(tag_list: ["linux"])
+ pending_job.reload
+ pending_job.create_queuing_entry!
+ project_runner.update!(tag_list: ["linux"])
+ expect(build_on(project_runner)).to eq(pending_job)
+ end
- context 'for shared runners' do
- before do
- project.update!(shared_runners_enabled: true)
+ it "does not pick build with different tag" do
+ pending_job.update!(tag_list: ["linux"])
+ pending_job.reload
+ pending_job.create_queuing_entry!
+ project_runner.update!(tag_list: ["win32"])
+ expect(build_on(project_runner)).to be_falsey
end
- it 'does not pick a build' do
- expect(execute(shared_runner)).to be_nil
+ it "picks build without tag" do
+ expect(build_on(project_runner)).to eq(pending_job)
end
- end
- context 'for specific runner' do
- it 'does not pick a build' do
- expect(execute(specific_runner)).to be_nil
- expect(pending_job.reload).to be_failed
- expect(pending_job.queuing_entry).to be_nil
+ it "does not pick build with tag" do
+ pending_job.update!(tag_list: ["linux"])
+ pending_job.reload
+ pending_job.create_queuing_entry!
+ expect(build_on(project_runner)).to be_falsey
end
- end
- end
- context 'allow shared runners' do
- before do
- project.update!(shared_runners_enabled: true)
- pipeline.reload
- pending_job.reload
- pending_job.create_queuing_entry!
+ it "pick build without tag" do
+ project_runner.update!(tag_list: ["win32"])
+ expect(build_on(project_runner)).to eq(pending_job)
+ end
end
- context 'when build owner has been blocked' do
- let(:user) { create(:user, :blocked) }
-
+ context 'deleted projects' do
before do
- pending_job.update!(user: user)
+ project.update!(pending_delete: true)
end
- it 'does not pick the build and drops the build' do
- expect(execute(shared_runner)).to be_falsey
+ context 'for shared runners' do
+ before do
+ project.update!(shared_runners_enabled: true)
+ end
- expect(pending_job.reload).to be_user_blocked
+ it 'does not pick a build' do
+ expect(build_on(shared_runner)).to be_nil
+ end
+ end
+
+ context 'for project runner' do
+ it 'does not pick a build' do
+ expect(build_on(project_runner)).to be_nil
+ expect(pending_job.reload).to be_failed
+ expect(pending_job.queuing_entry).to be_nil
+ end
end
end
- context 'for multiple builds' do
- let!(:project2) { create :project, shared_runners_enabled: true }
- let!(:pipeline2) { create :ci_pipeline, project: project2 }
- let!(:project3) { create :project, shared_runners_enabled: true }
- let!(:pipeline3) { create :ci_pipeline, project: project3 }
- let!(:build1_project1) { pending_job }
- let!(:build2_project1) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
- let!(:build3_project1) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
- let!(:build1_project2) { create(:ci_build, :pending, :queued, pipeline: pipeline2) }
- let!(:build2_project2) { create(:ci_build, :pending, :queued, pipeline: pipeline2) }
- let!(:build1_project3) { create(:ci_build, :pending, :queued, pipeline: pipeline3) }
-
- it 'picks builds one-by-one' do
- expect(Ci::Build).to receive(:find).with(pending_job.id).and_call_original
-
- expect(execute(shared_runner)).to eq(build1_project1)
- end
-
- context 'when using fair scheduling' do
- context 'when all builds are pending' do
- it 'prefers projects without builds first' do
- # it gets for one build from each of the projects
- expect(execute(shared_runner)).to eq(build1_project1)
- expect(execute(shared_runner)).to eq(build1_project2)
- expect(execute(shared_runner)).to eq(build1_project3)
-
- # then it gets a second build from each of the projects
- expect(execute(shared_runner)).to eq(build2_project1)
- expect(execute(shared_runner)).to eq(build2_project2)
-
- # in the end the third build
- expect(execute(shared_runner)).to eq(build3_project1)
- end
+ context 'allow shared runners' do
+ before do
+ project.update!(shared_runners_enabled: true)
+ pipeline.reload
+ pending_job.reload
+ pending_job.create_queuing_entry!
+ end
+
+ context 'when build owner has been blocked' do
+ let(:user) { create(:user, :blocked) }
+
+ before do
+ pending_job.update!(user: user)
end
- context 'when some builds transition to success' do
- it 'equalises number of running builds' do
- # after finishing the first build for project 1, get a second build from the same project
- expect(execute(shared_runner)).to eq(build1_project1)
- build1_project1.reload.success
- expect(execute(shared_runner)).to eq(build2_project1)
+ it 'does not pick the build and drops the build' do
+ expect(build_on(shared_runner)).to be_falsey
- expect(execute(shared_runner)).to eq(build1_project2)
- build1_project2.reload.success
- expect(execute(shared_runner)).to eq(build2_project2)
- expect(execute(shared_runner)).to eq(build1_project3)
- expect(execute(shared_runner)).to eq(build3_project1)
- end
+ expect(pending_job.reload).to be_user_blocked
end
end
- context 'when using DEFCON mode that disables fair scheduling' do
- before do
- stub_feature_flags(ci_queueing_disaster_recovery_disable_fair_scheduling: true)
- end
-
- context 'when all builds are pending' do
- it 'returns builds in order of creation (FIFO)' do
- # it gets for one build from each of the projects
- expect(execute(shared_runner)).to eq(build1_project1)
- expect(execute(shared_runner)).to eq(build2_project1)
- expect(execute(shared_runner)).to eq(build3_project1)
- expect(execute(shared_runner)).to eq(build1_project2)
- expect(execute(shared_runner)).to eq(build2_project2)
- expect(execute(shared_runner)).to eq(build1_project3)
+ context 'for multiple builds' do
+ let!(:project2) { create :project, shared_runners_enabled: true }
+ let!(:pipeline2) { create :ci_pipeline, project: project2 }
+ let!(:project3) { create :project, shared_runners_enabled: true }
+ let!(:pipeline3) { create :ci_pipeline, project: project3 }
+ let!(:build1_project1) { pending_job }
+ let!(:build2_project1) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
+ let!(:build3_project1) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
+ let!(:build1_project2) { create(:ci_build, :pending, :queued, pipeline: pipeline2) }
+ let!(:build2_project2) { create(:ci_build, :pending, :queued, pipeline: pipeline2) }
+ let!(:build1_project3) { create(:ci_build, :pending, :queued, pipeline: pipeline3) }
+
+ it 'picks builds one-by-one' do
+ expect(Ci::Build).to receive(:find).with(pending_job.id).and_call_original
+
+ expect(build_on(shared_runner)).to eq(build1_project1)
+ end
+
+ context 'when using fair scheduling' do
+ context 'when all builds are pending' do
+ it 'prefers projects without builds first' do
+ # it gets for one build from each of the projects
+ expect(build_on(shared_runner)).to eq(build1_project1)
+ expect(build_on(shared_runner)).to eq(build1_project2)
+ expect(build_on(shared_runner)).to eq(build1_project3)
+
+ # then it gets a second build from each of the projects
+ expect(build_on(shared_runner)).to eq(build2_project1)
+ expect(build_on(shared_runner)).to eq(build2_project2)
+
+ # in the end the third build
+ expect(build_on(shared_runner)).to eq(build3_project1)
+ end
+ end
+
+ context 'when some builds transition to success' do
+ it 'equalises number of running builds' do
+ # after finishing the first build for project 1, get a second build from the same project
+ expect(build_on(shared_runner)).to eq(build1_project1)
+ build1_project1.reload.success
+ expect(build_on(shared_runner)).to eq(build2_project1)
+
+ expect(build_on(shared_runner)).to eq(build1_project2)
+ build1_project2.reload.success
+ expect(build_on(shared_runner)).to eq(build2_project2)
+ expect(build_on(shared_runner)).to eq(build1_project3)
+ expect(build_on(shared_runner)).to eq(build3_project1)
+ end
end
end
- context 'when some builds transition to success' do
- it 'returns builds in order of creation (FIFO)' do
- expect(execute(shared_runner)).to eq(build1_project1)
- build1_project1.reload.success
- expect(execute(shared_runner)).to eq(build2_project1)
+ context 'when using DEFCON mode that disables fair scheduling' do
+ before do
+ stub_feature_flags(ci_queueing_disaster_recovery_disable_fair_scheduling: true)
+ end
+
+ context 'when all builds are pending' do
+ it 'returns builds in order of creation (FIFO)' do
+ # it gets for one build from each of the projects
+ expect(build_on(shared_runner)).to eq(build1_project1)
+ expect(build_on(shared_runner)).to eq(build2_project1)
+ expect(build_on(shared_runner)).to eq(build3_project1)
+ expect(build_on(shared_runner)).to eq(build1_project2)
+ expect(build_on(shared_runner)).to eq(build2_project2)
+ expect(build_on(shared_runner)).to eq(build1_project3)
+ end
+ end
- expect(execute(shared_runner)).to eq(build3_project1)
- build2_project1.reload.success
- expect(execute(shared_runner)).to eq(build1_project2)
- expect(execute(shared_runner)).to eq(build2_project2)
- expect(execute(shared_runner)).to eq(build1_project3)
+ context 'when some builds transition to success' do
+ it 'returns builds in order of creation (FIFO)' do
+ expect(build_on(shared_runner)).to eq(build1_project1)
+ build1_project1.reload.success
+ expect(build_on(shared_runner)).to eq(build2_project1)
+
+ expect(build_on(shared_runner)).to eq(build3_project1)
+ build2_project1.reload.success
+ expect(build_on(shared_runner)).to eq(build1_project2)
+ expect(build_on(shared_runner)).to eq(build2_project2)
+ expect(build_on(shared_runner)).to eq(build1_project3)
+ end
end
end
end
- end
- context 'shared runner' do
- let(:response) { described_class.new(shared_runner).execute }
- let(:build) { response.build }
+ context 'shared runner' do
+ let(:response) { described_class.new(shared_runner, nil).execute }
+ let(:build) { response.build }
- it { expect(build).to be_kind_of(Build) }
- it { expect(build).to be_valid }
- it { expect(build).to be_running }
- it { expect(build.runner).to eq(shared_runner) }
- it { expect(Gitlab::Json.parse(response.build_json)['id']).to eq(build.id) }
- end
+ it { expect(build).to be_kind_of(Build) }
+ it { expect(build).to be_valid }
+ it { expect(build).to be_running }
+ it { expect(build.runner).to eq(shared_runner) }
+ it { expect(Gitlab::Json.parse(response.build_json)['id']).to eq(build.id) }
+ end
- context 'specific runner' do
- let(:build) { execute(specific_runner) }
+ context 'project runner' do
+ let(:build) { build_on(project_runner) }
- it { expect(build).to be_kind_of(Build) }
- it { expect(build).to be_valid }
- it { expect(build).to be_running }
- it { expect(build.runner).to eq(specific_runner) }
+ it { expect(build).to be_kind_of(Build) }
+ it { expect(build).to be_valid }
+ it { expect(build).to be_running }
+ it { expect(build.runner).to eq(project_runner) }
+ end
end
- end
- context 'disallow shared runners' do
- before do
- project.update!(shared_runners_enabled: false)
- end
+ context 'disallow shared runners' do
+ before do
+ project.update!(shared_runners_enabled: false)
+ end
- context 'shared runner' do
- let(:build) { execute(shared_runner) }
+ context 'shared runner' do
+ let(:build) { build_on(shared_runner) }
- it { expect(build).to be_nil }
- end
+ it { expect(build).to be_nil }
+ end
- context 'specific runner' do
- let(:build) { execute(specific_runner) }
+ context 'project runner' do
+ let(:build) { build_on(project_runner) }
- it { expect(build).to be_kind_of(Build) }
- it { expect(build).to be_valid }
- it { expect(build).to be_running }
- it { expect(build.runner).to eq(specific_runner) }
+ it { expect(build).to be_kind_of(Build) }
+ it { expect(build).to be_valid }
+ it { expect(build).to be_running }
+ it { expect(build.runner).to eq(project_runner) }
+ end
end
- end
- context 'disallow when builds are disabled' do
- before do
- project.update!(shared_runners_enabled: true, group_runners_enabled: true)
- project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED)
+ context 'disallow when builds are disabled' do
+ before do
+ project.update!(shared_runners_enabled: true, group_runners_enabled: true)
+ project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED)
- pending_job.reload.create_queuing_entry!
- end
+ pending_job.reload.create_queuing_entry!
+ end
- context 'and uses shared runner' do
- let(:build) { execute(shared_runner) }
+ context 'and uses shared runner' do
+ let(:build) { build_on(shared_runner) }
- it { expect(build).to be_nil }
- end
+ it { expect(build).to be_nil }
+ end
- context 'and uses group runner' do
- let(:build) { execute(group_runner) }
+ context 'and uses group runner' do
+ let(:build) { build_on(group_runner) }
- it { expect(build).to be_nil }
- end
+ it { expect(build).to be_nil }
+ end
- context 'and uses project runner' do
- let(:build) { execute(specific_runner) }
+ context 'and uses project runner' do
+ let(:build) { build_on(project_runner) }
- it 'does not pick a build' do
- expect(build).to be_nil
- expect(pending_job.reload).to be_failed
- expect(pending_job.queuing_entry).to be_nil
+ it 'does not pick a build' do
+ expect(build).to be_nil
+ expect(pending_job.reload).to be_failed
+ expect(pending_job.queuing_entry).to be_nil
+ end
end
end
- end
- context 'allow group runners' do
- before do
- project.update!(group_runners_enabled: true)
- end
+ context 'allow group runners' do
+ before do
+ project.update!(group_runners_enabled: true)
+ end
- context 'for multiple builds' do
- let!(:project2) { create(:project, group_runners_enabled: true, group: group) }
- let!(:pipeline2) { create(:ci_pipeline, project: project2) }
- let!(:project3) { create(:project, group_runners_enabled: true, group: group) }
- let!(:pipeline3) { create(:ci_pipeline, project: project3) }
+ context 'for multiple builds' do
+ let!(:project2) { create(:project, group_runners_enabled: true, group: group) }
+ let!(:pipeline2) { create(:ci_pipeline, project: project2) }
+ let!(:project3) { create(:project, group_runners_enabled: true, group: group) }
+ let!(:pipeline3) { create(:ci_pipeline, project: project3) }
- let!(:build1_project1) { pending_job }
- let!(:build2_project1) { create(:ci_build, :queued, pipeline: pipeline) }
- let!(:build3_project1) { create(:ci_build, :queued, pipeline: pipeline) }
- let!(:build1_project2) { create(:ci_build, :queued, pipeline: pipeline2) }
- let!(:build2_project2) { create(:ci_build, :queued, pipeline: pipeline2) }
- let!(:build1_project3) { create(:ci_build, :queued, pipeline: pipeline3) }
+ let!(:build1_project1) { pending_job }
+ let!(:build2_project1) { create(:ci_build, :queued, pipeline: pipeline) }
+ let!(:build3_project1) { create(:ci_build, :queued, pipeline: pipeline) }
+ let!(:build1_project2) { create(:ci_build, :queued, pipeline: pipeline2) }
+ let!(:build2_project2) { create(:ci_build, :queued, pipeline: pipeline2) }
+ let!(:build1_project3) { create(:ci_build, :queued, pipeline: pipeline3) }
- # these shouldn't influence the scheduling
- let!(:unrelated_group) { create(:group) }
- let!(:unrelated_project) { create(:project, group_runners_enabled: true, group: unrelated_group) }
- let!(:unrelated_pipeline) { create(:ci_pipeline, project: unrelated_project) }
- let!(:build1_unrelated_project) { create(:ci_build, :pending, :queued, pipeline: unrelated_pipeline) }
- let!(:unrelated_group_runner) { create(:ci_runner, :group, groups: [unrelated_group]) }
+ # these shouldn't influence the scheduling
+ let!(:unrelated_group) { create(:group) }
+ let!(:unrelated_project) { create(:project, group_runners_enabled: true, group: unrelated_group) }
+ let!(:unrelated_pipeline) { create(:ci_pipeline, project: unrelated_project) }
+ let!(:build1_unrelated_project) { create(:ci_build, :pending, :queued, pipeline: unrelated_pipeline) }
+ let!(:unrelated_group_runner) { create(:ci_runner, :group, groups: [unrelated_group]) }
- it 'does not consider builds from other group runners' do
- queue = ::Ci::Queue::BuildQueueService.new(group_runner)
+ it 'does not consider builds from other group runners' do
+ queue = ::Ci::Queue::BuildQueueService.new(group_runner)
- expect(queue.builds_for_group_runner.size).to eq 6
- execute(group_runner)
+ expect(queue.builds_for_group_runner.size).to eq 6
+ build_on(group_runner)
- expect(queue.builds_for_group_runner.size).to eq 5
- execute(group_runner)
+ expect(queue.builds_for_group_runner.size).to eq 5
+ build_on(group_runner)
- expect(queue.builds_for_group_runner.size).to eq 4
- execute(group_runner)
+ expect(queue.builds_for_group_runner.size).to eq 4
+ build_on(group_runner)
- expect(queue.builds_for_group_runner.size).to eq 3
- execute(group_runner)
+ expect(queue.builds_for_group_runner.size).to eq 3
+ build_on(group_runner)
- expect(queue.builds_for_group_runner.size).to eq 2
- execute(group_runner)
+ expect(queue.builds_for_group_runner.size).to eq 2
+ build_on(group_runner)
- expect(queue.builds_for_group_runner.size).to eq 1
- execute(group_runner)
+ expect(queue.builds_for_group_runner.size).to eq 1
+ build_on(group_runner)
- expect(queue.builds_for_group_runner.size).to eq 0
- expect(execute(group_runner)).to be_nil
+ expect(queue.builds_for_group_runner.size).to eq 0
+ expect(build_on(group_runner)).to be_nil
+ end
end
- end
- context 'group runner' do
- let(:build) { execute(group_runner) }
+ context 'group runner' do
+ let(:build) { build_on(group_runner) }
- it { expect(build).to be_kind_of(Build) }
- it { expect(build).to be_valid }
- it { expect(build).to be_running }
- it { expect(build.runner).to eq(group_runner) }
+ it { expect(build).to be_kind_of(Build) }
+ it { expect(build).to be_valid }
+ it { expect(build).to be_running }
+ it { expect(build.runner).to eq(group_runner) }
+ end
end
- end
- context 'disallow group runners' do
- before do
- project.update!(group_runners_enabled: false)
+ context 'disallow group runners' do
+ before do
+ project.update!(group_runners_enabled: false)
- pending_job.reload.create_queuing_entry!
- end
+ pending_job.reload.create_queuing_entry!
+ end
- context 'group runner' do
- let(:build) { execute(group_runner) }
+ context 'group runner' do
+ let(:build) { build_on(group_runner) }
- it { expect(build).to be_nil }
+ it { expect(build).to be_nil }
+ end
end
- end
- context 'when first build is stalled' do
- before do
- allow_any_instance_of(Ci::RegisterJobService).to receive(:assign_runner!).and_call_original
- allow_any_instance_of(Ci::RegisterJobService).to receive(:assign_runner!)
- .with(pending_job, anything).and_raise(ActiveRecord::StaleObjectError)
- end
+ context 'when first build is stalled' do
+ before do
+ allow_any_instance_of(Ci::RegisterJobService).to receive(:assign_runner!).and_call_original
+ allow_any_instance_of(Ci::RegisterJobService).to receive(:assign_runner!)
+ .with(pending_job, anything).and_raise(ActiveRecord::StaleObjectError)
+ end
- subject { described_class.new(specific_runner).execute }
+ subject { described_class.new(project_runner, nil).execute }
- context 'with multiple builds are in queue' do
- let!(:other_build) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
+ context 'with multiple builds are in queue' do
+ let!(:other_build) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
- before do
- allow_any_instance_of(::Ci::Queue::BuildQueueService)
- .to receive(:execute)
- .and_return(Ci::Build.where(id: [pending_job, other_build]).pluck(:id))
- end
+ before do
+ allow_any_instance_of(::Ci::Queue::BuildQueueService)
+ .to receive(:execute)
+ .and_return(Ci::Build.where(id: [pending_job, other_build]).pluck(:id))
+ end
- it "receives second build from the queue" do
- expect(subject).to be_valid
- expect(subject.build).to eq(other_build)
+ it "receives second build from the queue" do
+ expect(subject).to be_valid
+ expect(subject.build).to eq(other_build)
+ end
end
- end
- context 'when single build is in queue' do
- before do
- allow_any_instance_of(::Ci::Queue::BuildQueueService)
- .to receive(:execute)
- .and_return(Ci::Build.where(id: pending_job).pluck(:id))
- end
+ context 'when single build is in queue' do
+ before do
+ allow_any_instance_of(::Ci::Queue::BuildQueueService)
+ .to receive(:execute)
+ .and_return(Ci::Build.where(id: pending_job).pluck(:id))
+ end
- it "does not receive any valid result" do
- expect(subject).not_to be_valid
+ it "does not receive any valid result" do
+ expect(subject).not_to be_valid
+ end
end
- end
- context 'when there is no build in queue' do
- before do
- allow_any_instance_of(::Ci::Queue::BuildQueueService)
- .to receive(:execute)
- .and_return([])
- end
+ context 'when there is no build in queue' do
+ before do
+ allow_any_instance_of(::Ci::Queue::BuildQueueService)
+ .to receive(:execute)
+ .and_return([])
+ end
- it "does not receive builds but result is valid" do
- expect(subject).to be_valid
- expect(subject.build).to be_nil
+ it "does not receive builds but result is valid" do
+ expect(subject).to be_valid
+ expect(subject.build).to be_nil
+ end
end
end
- end
- context 'when access_level of runner is not_protected' do
- let!(:specific_runner) { create(:ci_runner, :project, projects: [project]) }
+ context 'when access_level of runner is not_protected' do
+ let!(:project_runner) { create(:ci_runner, :project, projects: [project]) }
- context 'when a job is protected' do
- let!(:pending_job) { create(:ci_build, :pending, :queued, :protected, pipeline: pipeline) }
+ context 'when a job is protected' do
+ let!(:pending_job) { create(:ci_build, :pending, :queued, :protected, pipeline: pipeline) }
- it 'picks the job' do
- expect(execute(specific_runner)).to eq(pending_job)
+ it 'picks the job' do
+ expect(build_on(project_runner)).to eq(pending_job)
+ end
end
- end
- context 'when a job is unprotected' do
- let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
+ context 'when a job is unprotected' do
+ let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
- it 'picks the job' do
- expect(execute(specific_runner)).to eq(pending_job)
+ it 'picks the job' do
+ expect(build_on(project_runner)).to eq(pending_job)
+ end
end
- end
- context 'when protected attribute of a job is nil' do
- let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
+ context 'when protected attribute of a job is nil' do
+ let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
- before do
- pending_job.update_attribute(:protected, nil)
- end
+ before do
+ pending_job.update_attribute(:protected, nil)
+ end
- it 'picks the job' do
- expect(execute(specific_runner)).to eq(pending_job)
+ it 'picks the job' do
+ expect(build_on(project_runner)).to eq(pending_job)
+ end
end
end
- end
- context 'when access_level of runner is ref_protected' do
- let!(:specific_runner) { create(:ci_runner, :project, :ref_protected, projects: [project]) }
+ context 'when access_level of runner is ref_protected' do
+ let!(:project_runner) { create(:ci_runner, :project, :ref_protected, projects: [project]) }
- context 'when a job is protected' do
- let!(:pending_job) { create(:ci_build, :pending, :queued, :protected, pipeline: pipeline) }
+ context 'when a job is protected' do
+ let!(:pending_job) { create(:ci_build, :pending, :queued, :protected, pipeline: pipeline) }
- it 'picks the job' do
- expect(execute(specific_runner)).to eq(pending_job)
+ it 'picks the job' do
+ expect(build_on(project_runner)).to eq(pending_job)
+ end
end
- end
- context 'when a job is unprotected' do
- let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
+ context 'when a job is unprotected' do
+ let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
- it 'does not pick the job' do
- expect(execute(specific_runner)).to be_nil
+ it 'does not pick the job' do
+ expect(build_on(project_runner)).to be_nil
+ end
end
- end
- context 'when protected attribute of a job is nil' do
- let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
+ context 'when protected attribute of a job is nil' do
+ let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
- before do
- pending_job.update_attribute(:protected, nil)
- end
+ before do
+ pending_job.update_attribute(:protected, nil)
+ end
- it 'does not pick the job' do
- expect(execute(specific_runner)).to be_nil
+ it 'does not pick the job' do
+ expect(build_on(project_runner)).to be_nil
+ end
end
end
- end
- context 'runner feature set is verified' do
- let(:options) { { artifacts: { reports: { junit: "junit.xml" } } } }
- let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline, options: options) }
+ context 'runner feature set is verified' do
+ let(:options) { { artifacts: { reports: { junit: "junit.xml" } } } }
+ let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline, options: options) }
- subject { execute(specific_runner, params) }
+ subject { build_on(project_runner, params: params) }
- context 'when feature is missing by runner' do
- let(:params) { {} }
+ context 'when feature is missing by runner' do
+ let(:params) { {} }
- it 'does not pick the build and drops the build' do
- expect(subject).to be_nil
- expect(pending_job.reload).to be_failed
- expect(pending_job).to be_runner_unsupported
+ it 'does not pick the build and drops the build' do
+ expect(subject).to be_nil
+ expect(pending_job.reload).to be_failed
+ expect(pending_job).to be_runner_unsupported
+ end
end
- end
- context 'when feature is supported by runner' do
- let(:params) do
- { info: { features: { upload_multiple_artifacts: true } } }
- end
+ context 'when feature is supported by runner' do
+ let(:params) do
+ { info: { features: { upload_multiple_artifacts: true } } }
+ end
- it 'does pick job' do
- expect(subject).not_to be_nil
+ it 'does pick job' do
+ expect(subject).not_to be_nil
+ end
end
end
- end
-
- context 'when "dependencies" keyword is specified' do
- let!(:pre_stage_job) do
- create(:ci_build, :success, :artifacts, pipeline: pipeline, name: 'test', stage_idx: 0)
- end
- let!(:pending_job) do
- create(:ci_build, :pending, :queued,
- pipeline: pipeline, stage_idx: 1,
- options: { script: ["bash"], dependencies: dependencies })
- end
+ context 'when "dependencies" keyword is specified' do
+ let!(:pre_stage_job) do
+ create(:ci_build, :success, :artifacts, pipeline: pipeline, name: 'test', stage_idx: 0)
+ end
- let(:dependencies) { %w[test] }
+ let!(:pending_job) do
+ create(:ci_build, :pending, :queued,
+ pipeline: pipeline, stage_idx: 1,
+ options: { script: ["bash"], dependencies: dependencies })
+ end
- subject { execute(specific_runner) }
+ let(:dependencies) { %w[test] }
- it 'picks a build with a dependency' do
- picked_build = execute(specific_runner)
+ subject { build_on(project_runner) }
- expect(picked_build).to be_present
- end
+ it 'picks a build with a dependency' do
+ picked_build = build_on(project_runner)
- context 'when there are multiple dependencies with artifacts' do
- let!(:pre_stage_job_second) do
- create(:ci_build, :success, :artifacts, pipeline: pipeline, name: 'deploy', stage_idx: 0)
+ expect(picked_build).to be_present
end
- let(:dependencies) { %w[test deploy] }
-
- it 'logs build artifacts size' do
- execute(specific_runner)
-
- artifacts_size = [pre_stage_job, pre_stage_job_second].sum do |job|
- job.job_artifacts_archive.size
+ context 'when there are multiple dependencies with artifacts' do
+ let!(:pre_stage_job_second) do
+ create(:ci_build, :success, :artifacts, pipeline: pipeline, name: 'deploy', stage_idx: 0)
end
- expect(artifacts_size).to eq 107464 * 2
- expect(Gitlab::ApplicationContext.current).to include({
- 'meta.artifacts_dependencies_size' => artifacts_size,
- 'meta.artifacts_dependencies_count' => 2
- })
- end
- end
+ let(:dependencies) { %w[test deploy] }
- shared_examples 'not pick' do
- it 'does not pick the build and drops the build' do
- expect(subject).to be_nil
- expect(pending_job.reload).to be_failed
- expect(pending_job).to be_missing_dependency_failure
- end
- end
+ it 'logs build artifacts size' do
+ build_on(project_runner)
- shared_examples 'validation is active' do
- context 'when depended job has not been completed yet' do
- let!(:pre_stage_job) { create(:ci_build, :pending, :queued, :manual, pipeline: pipeline, name: 'test', stage_idx: 0) }
+ artifacts_size = [pre_stage_job, pre_stage_job_second].sum do |job|
+ job.job_artifacts_archive.size
+ end
- it { is_expected.to eq(pending_job) }
+ expect(artifacts_size).to eq 107464 * 2
+ expect(Gitlab::ApplicationContext.current).to include({
+ 'meta.artifacts_dependencies_size' => artifacts_size,
+ 'meta.artifacts_dependencies_count' => 2
+ })
+ end
end
- context 'when artifacts of depended job has been expired' do
- let!(:pre_stage_job) { create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0) }
+ shared_examples 'not pick' do
+ it 'does not pick the build and drops the build' do
+ expect(subject).to be_nil
+ expect(pending_job.reload).to be_failed
+ expect(pending_job).to be_missing_dependency_failure
+ end
+ end
- context 'when the pipeline is locked' do
- before do
- pipeline.artifacts_locked!
+ shared_examples 'validation is active' do
+ context 'when depended job has not been completed yet' do
+ let!(:pre_stage_job) do
+ create(:ci_build, :pending, :queued, :manual, pipeline: pipeline, name: 'test', stage_idx: 0)
end
it { is_expected.to eq(pending_job) }
end
- context 'when the pipeline is unlocked' do
- before do
- pipeline.unlocked!
+ context 'when artifacts of depended job has been expired' do
+ let!(:pre_stage_job) do
+ create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0)
end
- it_behaves_like 'not pick'
+ context 'when the pipeline is locked' do
+ before do
+ pipeline.artifacts_locked!
+ end
+
+ it { is_expected.to eq(pending_job) }
+ end
+
+ context 'when the pipeline is unlocked' do
+ before do
+ pipeline.unlocked!
+ end
+
+ it_behaves_like 'not pick'
+ end
end
- end
- context 'when artifacts of depended job has been erased' do
- let!(:pre_stage_job) { create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0, erased_at: 1.minute.ago) }
+ context 'when artifacts of depended job has been erased' do
+ let!(:pre_stage_job) do
+ create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0, erased_at: 1.minute.ago)
+ end
- it_behaves_like 'not pick'
- end
+ it_behaves_like 'not pick'
+ end
- context 'when job object is staled' do
- let!(:pre_stage_job) { create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0) }
+ context 'when job object is staled' do
+ let!(:pre_stage_job) do
+ create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0)
+ end
- before do
- pipeline.unlocked!
+ before do
+ pipeline.unlocked!
- allow_next_instance_of(Ci::Build) do |build|
- expect(build).to receive(:drop!)
- .and_raise(ActiveRecord::StaleObjectError.new(pending_job, :drop!))
+ allow_next_instance_of(Ci::Build) do |build|
+ expect(build).to receive(:drop!)
+ .and_raise(ActiveRecord::StaleObjectError.new(pending_job, :drop!))
+ end
end
- end
- it 'does not drop nor pick' do
- expect(subject).to be_nil
+ it 'does not drop nor pick' do
+ expect(subject).to be_nil
+ end
end
end
- end
- shared_examples 'validation is not active' do
- context 'when depended job has not been completed yet' do
- let!(:pre_stage_job) { create(:ci_build, :pending, :queued, :manual, pipeline: pipeline, name: 'test', stage_idx: 0) }
+ shared_examples 'validation is not active' do
+ context 'when depended job has not been completed yet' do
+ let!(:pre_stage_job) do
+ create(:ci_build, :pending, :queued, :manual, pipeline: pipeline, name: 'test', stage_idx: 0)
+ end
- it { expect(subject).to eq(pending_job) }
- end
+ it { expect(subject).to eq(pending_job) }
+ end
- context 'when artifacts of depended job has been expired' do
- let!(:pre_stage_job) { create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0) }
+ context 'when artifacts of depended job has been expired' do
+ let!(:pre_stage_job) do
+ create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0)
+ end
- it { expect(subject).to eq(pending_job) }
- end
+ it { expect(subject).to eq(pending_job) }
+ end
- context 'when artifacts of depended job has been erased' do
- let!(:pre_stage_job) { create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0, erased_at: 1.minute.ago) }
+ context 'when artifacts of depended job has been erased' do
+ let!(:pre_stage_job) do
+ create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0, erased_at: 1.minute.ago)
+ end
- it { expect(subject).to eq(pending_job) }
+ it { expect(subject).to eq(pending_job) }
+ end
end
- end
- it_behaves_like 'validation is active'
- end
+ it_behaves_like 'validation is active'
+ end
- context 'when build is degenerated' do
- let!(:pending_job) { create(:ci_build, :pending, :queued, :degenerated, pipeline: pipeline) }
+ context 'when build is degenerated' do
+ let!(:pending_job) { create(:ci_build, :pending, :queued, :degenerated, pipeline: pipeline) }
- subject { execute(specific_runner, {}) }
+ subject { build_on(project_runner) }
- it 'does not pick the build and drops the build' do
- expect(subject).to be_nil
+ it 'does not pick the build and drops the build' do
+ expect(subject).to be_nil
- pending_job.reload
- expect(pending_job).to be_failed
- expect(pending_job).to be_archived_failure
+ pending_job.reload
+ expect(pending_job).to be_failed
+ expect(pending_job).to be_archived_failure
+ end
end
- end
- context 'when build has data integrity problem' do
- let!(:pending_job) do
- create(:ci_build, :pending, :queued, pipeline: pipeline)
- end
+ context 'when build has data integrity problem' do
+ let!(:pending_job) do
+ create(:ci_build, :pending, :queued, pipeline: pipeline)
+ end
- before do
- pending_job.update_columns(options: "string")
- end
+ before do
+ pending_job.update_columns(options: "string")
+ end
- subject { execute(specific_runner, {}) }
+ subject { build_on(project_runner) }
- it 'does drop the build and logs both failures' do
- expect(Gitlab::ErrorTracking).to receive(:track_exception)
- .with(anything, a_hash_including(build_id: pending_job.id))
- .twice
- .and_call_original
+ it 'does drop the build and logs both failures' do
+ expect(Gitlab::ErrorTracking).to receive(:track_exception)
+ .with(anything, a_hash_including(build_id: pending_job.id))
+ .twice
+ .and_call_original
- expect(subject).to be_nil
+ expect(subject).to be_nil
- pending_job.reload
- expect(pending_job).to be_failed
- expect(pending_job).to be_data_integrity_failure
+ pending_job.reload
+ expect(pending_job).to be_failed
+ expect(pending_job).to be_data_integrity_failure
+ end
end
- end
- context 'when build fails to be run!' do
- let!(:pending_job) do
- create(:ci_build, :pending, :queued, pipeline: pipeline)
- end
+ context 'when build fails to be run!' do
+ let!(:pending_job) do
+ create(:ci_build, :pending, :queued, pipeline: pipeline)
+ end
- before do
- expect_any_instance_of(Ci::Build).to receive(:run!)
- .and_raise(RuntimeError, 'scheduler error')
- end
+ before do
+ expect_any_instance_of(Ci::Build).to receive(:run!)
+ .and_raise(RuntimeError, 'scheduler error')
+ end
- subject { execute(specific_runner, {}) }
+ subject { build_on(project_runner) }
- it 'does drop the build and logs failure' do
- expect(Gitlab::ErrorTracking).to receive(:track_exception)
- .with(anything, a_hash_including(build_id: pending_job.id))
- .once
- .and_call_original
+ it 'does drop the build and logs failure' do
+ expect(Gitlab::ErrorTracking).to receive(:track_exception)
+ .with(anything, a_hash_including(build_id: pending_job.id))
+ .once
+ .and_call_original
- expect(subject).to be_nil
+ expect(subject).to be_nil
- pending_job.reload
- expect(pending_job).to be_failed
- expect(pending_job).to be_scheduler_failure
+ pending_job.reload
+ expect(pending_job).to be_failed
+ expect(pending_job).to be_scheduler_failure
+ end
end
- end
- context 'when an exception is raised during a persistent ref creation' do
- before do
- allow_any_instance_of(Ci::PersistentRef).to receive(:exist?) { false }
- allow_any_instance_of(Ci::PersistentRef).to receive(:create_ref) { raise ArgumentError }
- end
+ context 'when an exception is raised during a persistent ref creation' do
+ before do
+ allow_any_instance_of(Ci::PersistentRef).to receive(:exist?) { false }
+ allow_any_instance_of(Ci::PersistentRef).to receive(:create_ref) { raise ArgumentError }
+ end
- subject { execute(specific_runner, {}) }
+ subject { build_on(project_runner) }
- it 'picks the build' do
- expect(subject).to eq(pending_job)
+ it 'picks the build' do
+ expect(subject).to eq(pending_job)
- pending_job.reload
- expect(pending_job).to be_running
- end
- end
-
- context 'when only some builds can be matched by runner' do
- let!(:specific_runner) { create(:ci_runner, :project, projects: [project], tag_list: %w[matching]) }
- let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline, tag_list: %w[matching]) }
-
- before do
- # create additional matching and non-matching jobs
- create_list(:ci_build, 2, :pending, :queued, pipeline: pipeline, tag_list: %w[matching])
- create(:ci_build, :pending, :queued, pipeline: pipeline, tag_list: %w[non-matching])
+ pending_job.reload
+ expect(pending_job).to be_running
+ end
end
- it 'observes queue size of only matching jobs' do
- # pending_job + 2 x matching ones
- expect(Gitlab::Ci::Queue::Metrics.queue_size_total).to receive(:observe)
- .with({ runner_type: specific_runner.runner_type }, 3)
+ context 'when only some builds can be matched by runner' do
+ let!(:project_runner) { create(:ci_runner, :project, projects: [project], tag_list: %w[matching]) }
+ let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline, tag_list: %w[matching]) }
- expect(execute(specific_runner)).to eq(pending_job)
- end
+ before do
+ # create additional matching and non-matching jobs
+ create_list(:ci_build, 2, :pending, :queued, pipeline: pipeline, tag_list: %w[matching])
+ create(:ci_build, :pending, :queued, pipeline: pipeline, tag_list: %w[non-matching])
+ end
- it 'observes queue processing time by the runner type' do
- expect(Gitlab::Ci::Queue::Metrics.queue_iteration_duration_seconds)
- .to receive(:observe)
- .with({ runner_type: specific_runner.runner_type }, anything)
+ it 'observes queue size of only matching jobs' do
+ # pending_job + 2 x matching ones
+ expect(Gitlab::Ci::Queue::Metrics.queue_size_total).to receive(:observe)
+ .with({ runner_type: project_runner.runner_type }, 3)
- expect(Gitlab::Ci::Queue::Metrics.queue_retrieval_duration_seconds)
- .to receive(:observe)
- .with({ runner_type: specific_runner.runner_type }, anything)
+ expect(build_on(project_runner)).to eq(pending_job)
+ end
- expect(execute(specific_runner)).to eq(pending_job)
- end
- end
+ it 'observes queue processing time by the runner type' do
+ expect(Gitlab::Ci::Queue::Metrics.queue_iteration_duration_seconds)
+ .to receive(:observe)
+ .with({ runner_type: project_runner.runner_type }, anything)
- context 'when ci_register_job_temporary_lock is enabled' do
- before do
- stub_feature_flags(ci_register_job_temporary_lock: true)
+ expect(Gitlab::Ci::Queue::Metrics.queue_retrieval_duration_seconds)
+ .to receive(:observe)
+ .with({ runner_type: project_runner.runner_type }, anything)
- allow(Gitlab::Ci::Queue::Metrics.queue_operations_total).to receive(:increment)
+ expect(build_on(project_runner)).to eq(pending_job)
+ end
end
- context 'when a build is temporarily locked' do
- let(:service) { described_class.new(specific_runner) }
-
+ context 'when ci_register_job_temporary_lock is enabled' do
before do
- service.send(:acquire_temporary_lock, pending_job.id)
- end
-
- it 'skips this build and marks queue as invalid' do
- expect(Gitlab::Ci::Queue::Metrics.queue_operations_total).to receive(:increment)
- .with(operation: :queue_iteration)
- expect(Gitlab::Ci::Queue::Metrics.queue_operations_total).to receive(:increment)
- .with(operation: :build_temporary_locked)
+ stub_feature_flags(ci_register_job_temporary_lock: true)
- expect(service.execute).not_to be_valid
+ allow(Gitlab::Ci::Queue::Metrics.queue_operations_total).to receive(:increment)
end
- context 'when there is another build in queue' do
- let!(:next_pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
+ context 'when a build is temporarily locked' do
+ let(:service) { described_class.new(project_runner, nil) }
- it 'skips this build and picks another build' do
+ before do
+ service.send(:acquire_temporary_lock, pending_job.id)
+ end
+
+ it 'skips this build and marks queue as invalid' do
expect(Gitlab::Ci::Queue::Metrics.queue_operations_total).to receive(:increment)
- .with(operation: :queue_iteration).twice
+ .with(operation: :queue_iteration)
expect(Gitlab::Ci::Queue::Metrics.queue_operations_total).to receive(:increment)
.with(operation: :build_temporary_locked)
- result = service.execute
+ expect(service.execute).not_to be_valid
+ end
+
+ context 'when there is another build in queue' do
+ let!(:next_pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) }
+
+ it 'skips this build and picks another build' do
+ expect(Gitlab::Ci::Queue::Metrics.queue_operations_total).to receive(:increment)
+ .with(operation: :queue_iteration).twice
+ expect(Gitlab::Ci::Queue::Metrics.queue_operations_total).to receive(:increment)
+ .with(operation: :build_temporary_locked)
- expect(result.build).to eq(next_pending_job)
- expect(result).to be_valid
+ result = service.execute
+
+ expect(result.build).to eq(next_pending_job)
+ expect(result).to be_valid
+ end
end
end
end
end
- end
-
- context 'when using pending builds table' do
- include_examples 'handles runner assignment'
- context 'when a conflicting data is stored in denormalized table' do
- let!(:specific_runner) { create(:ci_runner, :project, projects: [project], tag_list: %w[conflict]) }
- let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline, tag_list: %w[conflict]) }
+ context 'when using pending builds table' do
+ include_examples 'handles runner assignment'
- before do
- pending_job.update_column(:status, :running)
- end
+ context 'when a conflicting data is stored in denormalized table' do
+ let!(:runner) { create(:ci_runner, :project, projects: [project], tag_list: %w[conflict]) }
+ let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline, tag_list: %w[conflict]) }
- it 'removes queuing entry upon build assignment attempt' do
- expect(pending_job.reload).to be_running
- expect(pending_job.queuing_entry).to be_present
+ before do
+ pending_job.update_column(:status, :running)
+ end
- result = described_class.new(specific_runner).execute
+ it 'removes queuing entry upon build assignment attempt' do
+ expect(pending_job.reload).to be_running
+ expect(pending_job.queuing_entry).to be_present
- expect(result).not_to be_valid
- expect(pending_job.reload.queuing_entry).not_to be_present
+ expect(execute).not_to be_valid
+ expect(pending_job.reload.queuing_entry).not_to be_present
+ end
end
end
end
@@ -807,11 +842,11 @@ module Ci
# Stub tested metrics
allow(Gitlab::Ci::Queue::Metrics)
.to receive(:attempt_counter)
- .and_return(attempt_counter)
+ .and_return(attempt_counter)
allow(Gitlab::Ci::Queue::Metrics)
.to receive(:job_queue_duration_seconds)
- .and_return(job_queue_duration_seconds)
+ .and_return(job_queue_duration_seconds)
project.update!(shared_runners_enabled: true)
pending_job.update!(created_at: current_time - 3600, queued_at: current_time - 1800)
@@ -822,7 +857,7 @@ module Ci
allow(job_queue_duration_seconds).to receive(:observe)
expect(attempt_counter).to receive(:increment)
- execute(runner)
+ build_on(runner)
end
end
@@ -834,7 +869,7 @@ module Ci
jobs_running_for_project: expected_jobs_running_for_project_first_job,
shard: expected_shard }, 1800)
- execute(runner)
+ build_on(runner)
end
context 'when project already has running jobs' do
@@ -854,7 +889,7 @@ module Ci
jobs_running_for_project: expected_jobs_running_for_project_third_job,
shard: expected_shard }, 1800)
- execute(runner)
+ build_on(runner)
end
end
end
@@ -913,12 +948,12 @@ module Ci
allow(attempt_counter).to receive(:increment)
expect(job_queue_duration_seconds).not_to receive(:observe)
- execute(runner)
+ build_on(runner)
end
end
end
- context 'when specific runner is used' do
+ context 'when project runner is used' do
let(:runner) { create(:ci_runner, :project, projects: [project], tag_list: %w(tag1 metrics_shard::shard_tag tag2)) }
let(:expected_shared_runner) { false }
let(:expected_shard) { ::Gitlab::Ci::Queue::Metrics::DEFAULT_METRICS_SHARD }
@@ -933,12 +968,12 @@ module Ci
it 'present sets runner session configuration in the build' do
runner_session_params = { session: { 'url' => 'https://example.com' } }
- expect(execute(specific_runner, runner_session_params).runner_session.attributes)
+ expect(build_on(project_runner, params: runner_session_params).runner_session.attributes)
.to include(runner_session_params[:session])
end
it 'not present it does not configure the runner session' do
- expect(execute(specific_runner).runner_session).to be_nil
+ expect(build_on(project_runner).runner_session).to be_nil
end
end
@@ -954,7 +989,7 @@ module Ci
it 'returns 409 conflict' do
expect(Ci::Build.pending.unstarted.count).to eq 3
- result = described_class.new(specific_runner).execute
+ result = described_class.new(project_runner, nil).execute
expect(result).not_to be_valid
expect(result.build).to be_nil
@@ -962,8 +997,8 @@ module Ci
end
end
- def execute(runner, params = {})
- described_class.new(runner).execute(params).build
+ def build_on(runner, runner_machine: nil, params: {})
+ described_class.new(runner, runner_machine).execute(params).build
end
end
end