Simplify implementation of pipeline retry service
This commit is contained in:
parent
94495f984c
commit
056f1b7020
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -1,57 +1,25 @@
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
|
|
Loading…
Reference in New Issue