Commit 056f1b70 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Simplify implementation of pipeline retry service

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