Commit ffa2a3a8 authored by Fabio Pitino's avatar Fabio Pitino

Merge branch '208411-auto-cancel-child-pipeline-jobs' into 'master'

Allow canceling all pipelines with auto-cancel

See merge request gitlab-org/gitlab!46686
parents 8507ac1d 5471ec10
...@@ -277,6 +277,7 @@ module Ci ...@@ -277,6 +277,7 @@ module Ci
scope :internal, -> { where(source: internal_sources) } scope :internal, -> { where(source: internal_sources) }
scope :no_child, -> { where.not(source: :parent_pipeline) } scope :no_child, -> { where.not(source: :parent_pipeline) }
scope :ci_sources, -> { where(source: Enums::Ci::Pipeline.ci_sources.values) } scope :ci_sources, -> { where(source: Enums::Ci::Pipeline.ci_sources.values) }
scope :ci_and_parent_sources, -> { where(source: Enums::Ci::Pipeline.ci_and_parent_sources.values) }
scope :for_user, -> (user) { where(user: user) } scope :for_user, -> (user) { where(user: user) }
scope :for_sha, -> (sha) { where(sha: sha) } scope :for_sha, -> (sha) { where(sha: sha) }
scope :for_source_sha, -> (source_sha) { where(source_sha: source_sha) } scope :for_source_sha, -> (source_sha) { where(source_sha: source_sha) }
......
...@@ -53,6 +53,10 @@ module Enums ...@@ -53,6 +53,10 @@ module Enums
sources.except(*dangling_sources.keys) sources.except(*dangling_sources.keys)
end end
def self.ci_and_parent_sources
ci_sources.merge(sources.slice(:parent_pipeline))
end
# Returns the `Hash` to use for creating the `config_sources` enum for # Returns the `Hash` to use for creating the `config_sources` enum for
# `Ci::Pipeline`. # `Ci::Pipeline`.
def self.config_sources def self.config_sources
......
---
name: ci_auto_cancel_all_pipelines
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46686
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/275997
milestone: '13.6'
type: development
group: group::pipeline authoring
default_enabled: false
...@@ -25,7 +25,7 @@ module Gitlab ...@@ -25,7 +25,7 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def auto_cancelable_pipelines def auto_cancelable_pipelines
project.ci_pipelines pipelines
.where(ref: pipeline.ref) .where(ref: pipeline.ref)
.where.not(id: pipeline.same_family_pipeline_ids) .where.not(id: pipeline.same_family_pipeline_ids)
.where.not(sha: project.commit(pipeline.ref).try(:id)) .where.not(sha: project.commit(pipeline.ref).try(:id))
...@@ -33,6 +33,14 @@ module Gitlab ...@@ -33,6 +33,14 @@ module Gitlab
.with_only_interruptible_builds .with_only_interruptible_builds
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def pipelines
if ::Feature.enabled?(:ci_auto_cancel_all_pipelines, project, default_enabled: false)
project.all_pipelines.ci_and_parent_sources
else
project.ci_pipelines
end
end
end end
end end
end end
......
...@@ -514,5 +514,11 @@ FactoryBot.define do ...@@ -514,5 +514,11 @@ FactoryBot.define do
build.build_runner_session(url: 'https://localhost') build.build_runner_session(url: 'https://localhost')
end end
end end
trait :interruptible do
after(:build) do |build|
build.metadata.interruptible = true
end
end
end end
end end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Chain::CancelPendingPipelines do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let(:prev_pipeline) { create(:ci_pipeline, project: project) }
let(:new_commit) { create(:commit, project: project) }
let(:pipeline) { create(:ci_pipeline, project: project, sha: new_commit.sha) }
let(:command) do
Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user)
end
let(:step) { described_class.new(pipeline, command) }
before do
create(:ci_build, :interruptible, :running, pipeline: prev_pipeline)
create(:ci_build, :interruptible, :success, pipeline: prev_pipeline)
create(:ci_build, :created, pipeline: prev_pipeline)
create(:ci_build, :interruptible, pipeline: pipeline)
end
describe '#perform!' do
subject(:perform) { step.perform! }
before do
expect(build_statuses(prev_pipeline)).to contain_exactly('running', 'success', 'created')
expect(build_statuses(pipeline)).to contain_exactly('pending')
end
context 'when auto-cancel is enabled' do
before do
project.update!(auto_cancel_pending_pipelines: 'enabled')
end
it 'cancels only previous interruptible builds' do
perform
expect(build_statuses(prev_pipeline)).to contain_exactly('canceled', 'success', 'canceled')
expect(build_statuses(pipeline)).to contain_exactly('pending')
end
context 'when the previous pipeline has a child pipeline' do
let(:child_pipeline) { create(:ci_pipeline, child_of: prev_pipeline) }
context 'when the child pipeline has an interruptible job' do
before do
create(:ci_build, :interruptible, :running, pipeline: child_pipeline)
end
it 'cancels interruptible builds of child pipeline' do
expect(build_statuses(child_pipeline)).to contain_exactly('running')
perform
expect(build_statuses(child_pipeline)).to contain_exactly('canceled')
end
context 'when FF ci_auto_cancel_all_pipelines is disabled' do
before do
stub_feature_flags(ci_auto_cancel_all_pipelines: false)
end
it 'does not cancel interruptible builds of child pipeline' do
expect(build_statuses(child_pipeline)).to contain_exactly('running')
perform
expect(build_statuses(child_pipeline)).to contain_exactly('running')
end
end
end
context 'when the child pipeline has not an interruptible job' do
before do
create(:ci_build, :running, pipeline: child_pipeline)
end
it 'does not cancel the build of child pipeline' do
expect(build_statuses(child_pipeline)).to contain_exactly('running')
perform
expect(build_statuses(child_pipeline)).to contain_exactly('running')
end
end
end
context 'when the prev pipeline source is webide' do
let(:prev_pipeline) { create(:ci_pipeline, :webide, project: project) }
it 'does not cancel builds of the previous pipeline' do
perform
expect(build_statuses(prev_pipeline)).to contain_exactly('created', 'running', 'success')
expect(build_statuses(pipeline)).to contain_exactly('pending')
end
end
end
context 'when auto-cancel is disabled' do
before do
project.update!(auto_cancel_pending_pipelines: 'disabled')
end
it 'does not cancel any build' do
subject
expect(build_statuses(prev_pipeline)).to contain_exactly('running', 'success', 'created')
expect(build_statuses(pipeline)).to contain_exactly('pending')
end
end
end
private
def build_statuses(pipeline)
pipeline.builds.pluck(: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