Skip to content
Snippets Groups Projects
Commit 056f1b70 authored by Grzegorz Bizon's avatar Grzegorz Bizon
Browse files

Simplify implementation of pipeline retry service

parent 94495f98
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -214,13 +214,7 @@ module Ci
def cancel_running
Gitlab::OptimisticLocking.retry_lock(
statuses.cancelable) do |cancelable|
cancelable.each do |status|
if status.created?
status.skip
elsif status.active?
status.cancel
end
end
cancelable.find_each(&:cancel)
end
end
 
Loading
Loading
module Ci
class RetryPipelineService < ::BaseService
def execute(pipeline)
@pipeline = pipeline
unless can?(current_user, :update_pipeline, pipeline)
raise Gitlab::Access::AccessDeniedError
end
 
pipeline.mark_as_processable_after_stage(resume_stage.index)
each_build(pipeline.builds.failed_or_canceled) do |build|
next unless build.retryable?
 
retryable_builds_in_subsequent_stages do |build|
Ci::RetryBuildService.new(project, current_user)
.reprocess(build)
end
 
retryable_builds_in_first_unsuccessful_stage do |build|
Ci::RetryBuildService.new(project, current_user)
.retry(build)
end
pipeline.process!
end
 
private
 
def retryable_builds_in_subsequent_stages
relation = @pipeline.builds
.after_stage(resume_stage.index)
.failed_or_canceled
each_retryable_build_with_locking(relation) do |build|
yield build
end
end
def retryable_builds_in_first_unsuccessful_stage
relation = resume_stage.builds.failed_or_canceled
each_retryable_build_with_locking(relation) do |build|
yield build
end
end
def each_retryable_build_with_locking(relation)
def each_build(relation)
Gitlab::OptimisticLocking.retry_lock(relation) do |builds|
builds.find_each do |build|
next unless build.retryable?
yield build
end
end
end
def resume_stage
@resume_stage ||= @pipeline.stages.find do |stage|
stage.failed? || stage.canceled?
builds.find_each { |build| yield build }
end
end
end
Loading
Loading
Loading
Loading
@@ -766,8 +766,8 @@ describe Ci::Pipeline, models: true do
pipeline.cancel_running
end
 
it 'skips created builds' do
expect(latest_status).to eq ['canceled', 'skipped']
it 'cancels created builds' do
expect(latest_status).to eq ['canceled', 'canceled']
end
end
end
Loading
Loading
@@ -801,7 +801,7 @@ describe Ci::Pipeline, models: true do
end
 
it 'retries both builds' do
expect(latest_status).to contain_exactly('pending', 'pending')
expect(latest_status).to contain_exactly('pending', 'created')
end
end
 
Loading
Loading
@@ -814,7 +814,7 @@ describe Ci::Pipeline, models: true do
end
 
it 'retries both builds' do
expect(latest_status).to contain_exactly('pending', 'pending')
expect(latest_status).to contain_exactly('pending', 'created')
end
end
end
Loading
Loading
Loading
Loading
@@ -11,9 +11,9 @@ describe Ci::RetryPipelineService, '#execute', :services do
 
context 'when there are failed builds in the last stage' do
before do
create_build(name: 'rspec 1', status: :success, stage_num: 0)
create_build(name: 'rspec 2', status: :failed, stage_num: 1)
create_build(name: 'rspec 3', status: :canceled, stage_num: 1)
create_build('rspec 1', :success, 0)
create_build('rspec 2', :failed, 1)
create_build('rspec 3', :canceled, 1)
end
 
it 'enqueues all builds in the last stage' do
Loading
Loading
@@ -27,10 +27,10 @@ describe Ci::RetryPipelineService, '#execute', :services do
 
context 'when there are failed or canceled builds in the first stage' do
before do
create_build(name: 'rspec 1', status: :failed, stage_num: 0)
create_build(name: 'rspec 2', status: :canceled, stage_num: 0)
create_build(name: 'rspec 3', status: :skipped, stage_num: 1)
create_build(name: 'deploy 1', status: :skipped, stage_num: 2)
create_build('rspec 1', :failed, 0)
create_build('rspec 2', :canceled, 0)
create_build('rspec 3', :canceled, 1)
create_build('deploy 1', :canceled, 2)
end
 
it 'retries builds failed builds and marks subsequent for processing' do
Loading
Loading
@@ -46,10 +46,10 @@ describe Ci::RetryPipelineService, '#execute', :services do
 
context 'when there is failed build present which was run on failure' do
before do
create_build(name: 'rspec 1', status: :failed, stage_num: 0)
create_build(name: 'rspec 2', status: :canceled, stage_num: 0)
create_build(name: 'rspec 3', status: :skipped, stage_num: 1)
create_build(name: 'report 1', status: :failed, stage_num: 2)
create_build('rspec 1', :failed, 0)
create_build('rspec 2', :canceled, 0)
create_build('rspec 3', :canceled, 1)
create_build('report 1', :failed, 2)
end
 
it 'retries builds failed builds and marks subsequent for processing' do
Loading
Loading
@@ -65,9 +65,27 @@ describe Ci::RetryPipelineService, '#execute', :services do
it 'creates a new job for report job in this case' do
service.execute(pipeline)
 
# TODO, expect to be_retried
expect(statuses.where(name: 'report 1').count).to eq 2
end
end
context 'when there is canceled manual build in first stage' do
before do
create_build('rspec 1', :failed, 0)
create_build('staging', :canceled, 0, :manual)
create_build('rspec 2', :canceled, 1)
end
it 'retries builds failed builds and marks subsequent for processing' do
service.execute(pipeline)
expect(build('rspec 1')).to be_pending
expect(build('staging')).to be_skipped
expect(build('rspec 2')).to be_created
expect(pipeline.reload).to be_running
end
end
end
 
context 'when user is not allowed to retry pipeline' do
Loading
Loading
@@ -85,11 +103,12 @@ describe Ci::RetryPipelineService, '#execute', :services do
statuses.latest.find_by(name: name)
end
 
def create_build(name:, status:, stage_num:)
def create_build(name, status, stage_num, on = 'on_success')
create(:ci_build, name: name,
status: status,
stage: "stage_#{stage_num}",
stage_idx: stage_num,
when: on,
pipeline: pipeline) do |build|
pipeline.update_status
end
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