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

Simplify implementation of pipeline retry service

parent 94495f98
......@@ -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
......
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
......
......@@ -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
......@@ -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
......@@ -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
......
......@@ -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
......@@ -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
......@@ -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
......@@ -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
......@@ -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
......
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