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/retry_job_service_spec.rb')
-rw-r--r--spec/services/ci/retry_job_service_spec.rb284
1 files changed, 217 insertions, 67 deletions
diff --git a/spec/services/ci/retry_job_service_spec.rb b/spec/services/ci/retry_job_service_spec.rb
index 69f19c5acf2..540e700efa6 100644
--- a/spec/services/ci/retry_job_service_spec.rb
+++ b/spec/services/ci/retry_job_service_spec.rb
@@ -3,6 +3,7 @@
require 'spec_helper'
RSpec.describe Ci::RetryJobService do
+ using RSpec::Parameterized::TableSyntax
let_it_be(:reporter) { create(:user) }
let_it_be(:developer) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
@@ -11,11 +12,11 @@ RSpec.describe Ci::RetryJobService do
end
let_it_be(:stage) do
- create(:ci_stage, project: project,
- pipeline: pipeline,
- name: 'test')
+ create(:ci_stage, pipeline: pipeline, name: 'test')
end
+ let_it_be(:deploy_stage) { create(:ci_stage, pipeline: pipeline, name: 'deploy', position: stage.position + 1) }
+
let(:job_variables_attributes) { [{ key: 'MANUAL_VAR', value: 'manual test var' }] }
let(:user) { developer }
@@ -26,24 +27,11 @@ RSpec.describe Ci::RetryJobService do
project.add_reporter(reporter)
end
- shared_context 'retryable bridge' do
- let_it_be(:downstream_project) { create(:project, :repository) }
-
- let_it_be_with_refind(:job) do
- create(:ci_bridge, :success,
- pipeline: pipeline, downstream: downstream_project, description: 'a trigger job', ci_stage: stage
- )
- end
-
- let_it_be(:job_to_clone) { job }
-
- before do
- job.update!(retried: false)
+ shared_context 'retryable build' do
+ let_it_be_with_reload(:job) do
+ create(:ci_build, :success, pipeline: pipeline, ci_stage: stage)
end
- end
- shared_context 'retryable build' do
- let_it_be_with_refind(:job) { create(:ci_build, :success, pipeline: pipeline, ci_stage: stage) }
let_it_be(:another_pipeline) { create(:ci_empty_pipeline, project: project) }
let_it_be(:job_to_clone) do
@@ -60,6 +48,12 @@ RSpec.describe Ci::RetryJobService do
end
end
+ shared_context 'with ci_retry_job_fix disabled' do
+ before do
+ stub_feature_flags(ci_retry_job_fix: false)
+ end
+ end
+
shared_examples_for 'clones the job' do
let(:job) { job_to_clone }
@@ -87,8 +81,7 @@ RSpec.describe Ci::RetryJobService do
context 'when the job has needs' do
before do
- create(:ci_build_need, build: job, name: 'build1')
- create(:ci_build_need, build: job, name: 'build2')
+ create_list(:ci_build_need, 2, build: job)
end
it 'bulk inserts all the needs' do
@@ -123,16 +116,12 @@ RSpec.describe Ci::RetryJobService do
end
context 'when there are subsequent processables that are skipped' do
- let_it_be(:stage) { create(:ci_stage, pipeline: pipeline, name: 'deploy') }
-
let!(:subsequent_build) do
- create(:ci_build, :skipped, stage_idx: 2,
- pipeline: pipeline,
- ci_stage: stage)
+ create(:ci_build, :skipped, pipeline: pipeline, ci_stage: deploy_stage)
end
let!(:subsequent_bridge) do
- create(:ci_bridge, :skipped, stage_idx: 2, pipeline: pipeline, ci_stage: stage)
+ create(:ci_bridge, :skipped, pipeline: pipeline, ci_stage: deploy_stage)
end
it 'resumes pipeline processing in the subsequent stage' do
@@ -152,10 +141,9 @@ RSpec.describe Ci::RetryJobService do
end
context 'when the pipeline has other jobs' do
- let!(:stage2) { create(:ci_stage, project: project, pipeline: pipeline, name: 'deploy') }
- let!(:build2) { create(:ci_build, pipeline: pipeline, ci_stage: stage ) }
- let!(:deploy) { create(:ci_build, pipeline: pipeline, ci_stage: stage2) }
- let!(:deploy_needs_build2) { create(:ci_build_need, build: deploy, name: build2.name) }
+ let!(:other_test_build) { create(:ci_build, pipeline: pipeline, ci_stage: stage) }
+ let!(:deploy) { create(:ci_build, pipeline: pipeline, ci_stage: deploy_stage) }
+ let!(:deploy_needs_build2) { create(:ci_build_need, build: deploy, name: other_test_build.name) }
context 'when job has a nil scheduling_type' do
before do
@@ -166,7 +154,7 @@ RSpec.describe Ci::RetryJobService do
it 'populates scheduling_type of processables' do
expect(new_job.scheduling_type).to eq('stage')
expect(job.reload.scheduling_type).to eq('stage')
- expect(build2.reload.scheduling_type).to eq('stage')
+ expect(other_test_build.reload.scheduling_type).to eq('stage')
expect(deploy.reload.scheduling_type).to eq('dag')
end
end
@@ -193,6 +181,13 @@ RSpec.describe Ci::RetryJobService do
end
end
+ shared_examples_for 'checks enqueue_immediately?' do
+ it "returns enqueue_immediately" do
+ subject
+ expect(new_job.enqueue_immediately?).to eq enqueue_immediately
+ end
+ end
+
describe '#clone!' do
let(:new_job) { service.clone!(job) }
@@ -200,20 +195,6 @@ RSpec.describe Ci::RetryJobService do
expect { service.clone!(create(:ci_build).present) }.to raise_error(TypeError)
end
- context 'when the job to be cloned is a bridge' do
- include_context 'retryable bridge'
-
- it_behaves_like 'clones the job'
-
- context 'when given variables' do
- let(:new_job) { service.clone!(job, variables: job_variables_attributes) }
-
- it 'does not give variables to the new bridge' do
- expect { new_job }.not_to raise_error
- end
- end
- end
-
context 'when the job to be cloned is a build' do
include_context 'retryable build'
@@ -224,7 +205,7 @@ RSpec.describe Ci::RetryJobService do
context 'when a build with a deployment is retried' do
let!(:job) do
create(:ci_build, :with_deployment, :deploy_to_production,
- pipeline: pipeline, ci_stage: stage, project: project)
+ pipeline: pipeline, ci_stage: stage)
end
it 'creates a new deployment' do
@@ -247,7 +228,6 @@ RSpec.describe Ci::RetryJobService do
options: { environment: { name: environment_name } },
pipeline: pipeline,
ci_stage: stage,
- project: project,
user: other_developer)
end
@@ -282,24 +262,44 @@ RSpec.describe Ci::RetryJobService do
end
end
end
- end
- describe '#execute' do
- let(:new_job) { service.execute(job)[:job] }
+ context 'when enqueue_if_actionable is provided' do
+ let!(:job) do
+ create(:ci_build, *[trait].compact, :failed, pipeline: pipeline, ci_stage: stage)
+ end
- context 'when the job to be retried is a bridge' do
- include_context 'retryable bridge'
+ let(:new_job) { subject }
- it_behaves_like 'retries the job'
+ subject { service.clone!(job, enqueue_if_actionable: enqueue_if_actionable) }
- context 'when given variables' do
- let(:new_job) { service.clone!(job, variables: job_variables_attributes) }
+ where(:enqueue_if_actionable, :trait, :enqueue_immediately) do
+ true | nil | false
+ true | :manual | true
+ true | :expired_scheduled | true
+
+ false | nil | false
+ false | :manual | false
+ false | :expired_scheduled | false
+ end
+
+ with_them do
+ it_behaves_like 'checks enqueue_immediately?'
- it 'does not give variables to the new bridge' do
- expect { new_job }.not_to raise_error
+ context 'when feature flag is disabled' do
+ include_context 'with ci_retry_job_fix disabled'
+
+ it_behaves_like 'checks enqueue_immediately?' do
+ let(:enqueue_immediately) { false }
+ end
end
end
end
+ end
+
+ describe '#execute' do
+ let(:new_job) { subject[:job] }
+
+ subject { service.execute(job) }
context 'when the job to be retried is a build' do
include_context 'retryable build'
@@ -307,24 +307,18 @@ RSpec.describe Ci::RetryJobService do
it_behaves_like 'retries the job'
context 'when there are subsequent jobs that are skipped' do
- let_it_be(:stage) { create(:ci_stage, pipeline: pipeline, name: 'deploy') }
-
let!(:subsequent_build) do
- create(:ci_build, :skipped, stage_idx: 2,
- pipeline: pipeline,
- stage_id: stage.id)
+ create(:ci_build, :skipped, pipeline: pipeline, ci_stage: deploy_stage)
end
let!(:subsequent_bridge) do
- create(:ci_bridge, :skipped, stage_idx: 2,
- pipeline: pipeline,
- stage_id: stage.id)
+ create(:ci_bridge, :skipped, pipeline: pipeline, ci_stage: deploy_stage)
end
it 'does not cause an N+1 when updating the job ownership' do
control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) { service.execute(job) }.count
- create_list(:ci_build, 2, :skipped, stage_idx: job.stage_idx + 1, pipeline: pipeline, stage_id: stage.id)
+ create_list(:ci_build, 2, :skipped, pipeline: pipeline, ci_stage: deploy_stage)
expect { service.execute(job) }.not_to exceed_all_query_limit(control_count)
end
@@ -352,5 +346,161 @@ RSpec.describe Ci::RetryJobService do
end
end
end
+
+ context 'when job being retried has jobs in previous stages' do
+ let!(:job) do
+ create(
+ :ci_build,
+ :failed,
+ name: 'deploy_a',
+ pipeline: pipeline,
+ ci_stage: deploy_stage
+ )
+ end
+
+ before do
+ create(
+ :ci_build,
+ previous_stage_job_status,
+ name: 'test_a',
+ pipeline: pipeline,
+ ci_stage: stage
+ )
+ end
+
+ where(:previous_stage_job_status, :after_status) do
+ :created | 'created'
+ :pending | 'created'
+ :running | 'created'
+ :manual | 'created'
+ :scheduled | 'created'
+ :success | 'pending'
+ :failed | 'skipped'
+ :skipped | 'pending'
+ end
+
+ with_them do
+ it 'updates the new job status to after_status' do
+ expect(subject).to be_success
+ expect(new_job.status).to eq after_status
+ end
+
+ context 'when feature flag is disabled' do
+ include_context 'with ci_retry_job_fix disabled'
+
+ it 'enqueues the new job' do
+ expect(subject).to be_success
+ expect(new_job).to be_pending
+ end
+ end
+ end
+ end
+
+ context 'when job being retried has DAG dependencies' do
+ let!(:job) do
+ create(
+ :ci_build,
+ :failed,
+ :dependent,
+ name: 'deploy_a',
+ pipeline: pipeline,
+ ci_stage: deploy_stage,
+ needed: dependency
+ )
+ end
+
+ let(:dependency) do
+ create(
+ :ci_build,
+ dag_dependency_status,
+ name: 'test_a',
+ pipeline: pipeline,
+ ci_stage: stage
+ )
+ end
+
+ where(:dag_dependency_status, :after_status) do
+ :created | 'created'
+ :pending | 'created'
+ :running | 'created'
+ :manual | 'created'
+ :scheduled | 'created'
+ :success | 'pending'
+ :failed | 'skipped'
+ :skipped | 'skipped'
+ end
+
+ with_them do
+ it 'updates the new job status to after_status' do
+ expect(subject).to be_success
+ expect(new_job.status).to eq after_status
+ end
+
+ context 'when feature flag is disabled' do
+ include_context 'with ci_retry_job_fix disabled'
+
+ it 'enqueues the new job' do
+ expect(subject).to be_success
+ expect(new_job).to be_pending
+ end
+ end
+ end
+ end
+
+ context 'when there are other manual/scheduled jobs' do
+ let_it_be(:test_manual_build) do
+ create(:ci_build, :manual, pipeline: pipeline, ci_stage: stage)
+ end
+
+ let_it_be(:subsequent_manual_build) do
+ create(:ci_build, :manual, pipeline: pipeline, ci_stage: deploy_stage)
+ end
+
+ let_it_be(:test_scheduled_build) do
+ create(:ci_build, :scheduled, pipeline: pipeline, ci_stage: stage)
+ end
+
+ let_it_be(:subsequent_scheduled_build) do
+ create(:ci_build, :scheduled, pipeline: pipeline, ci_stage: deploy_stage)
+ end
+
+ let!(:job) do
+ create(:ci_build, *[trait].compact, :failed, pipeline: pipeline, ci_stage: stage)
+ end
+
+ where(:trait, :enqueue_immediately) do
+ nil | false
+ :manual | true
+ :expired_scheduled | true
+ end
+
+ with_them do
+ it 'retries the given job but not the other manual/scheduled jobs' do
+ expect { subject }
+ .to change { Ci::Build.count }.by(1)
+ .and not_change { test_manual_build.reload.status }
+ .and not_change { subsequent_manual_build.reload.status }
+ .and not_change { test_scheduled_build.reload.status }
+ .and not_change { subsequent_scheduled_build.reload.status }
+
+ expect(new_job).to be_pending
+ end
+
+ it_behaves_like 'checks enqueue_immediately?'
+
+ context 'when feature flag is disabled' do
+ include_context 'with ci_retry_job_fix disabled'
+
+ it 'enqueues the new job' do
+ expect(subject).to be_success
+ expect(new_job).to be_pending
+ end
+
+ it_behaves_like 'checks enqueue_immediately?' do
+ let(:enqueue_immediately) { false }
+ end
+ end
+ end
+ end
end
end