Commit e772d976 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge branch 'define-ci-dangling-pipelines' into 'master'

Define dangling pipelines to be excluded from Project#ci_pipelines

See merge request gitlab-org/gitlab!40512
parents 7bc572bf 785d505f
...@@ -262,7 +262,7 @@ module Ci ...@@ -262,7 +262,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(config_source: Enums::Ci::Pipeline.ci_config_sources_values) } scope :ci_sources, -> { where(source: Enums::Ci::Pipeline.ci_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) }
...@@ -1033,7 +1033,11 @@ module Ci ...@@ -1033,7 +1033,11 @@ module Ci
end end
def cacheable? def cacheable?
Enums::Ci::Pipeline.ci_config_sources.key?(config_source.to_sym) !dangling?
end
def dangling?
Enums::Ci::Pipeline.dangling_sources.key?(source.to_sym)
end end
def source_ref_path def source_ref_path
......
...@@ -36,6 +36,23 @@ module Enums ...@@ -36,6 +36,23 @@ module Enums
} }
end end
# Dangling sources are those events that generate pipelines for which
# we don't want to directly affect the ref CI status.
# - when a webide pipeline fails it does not change the ref CI status to failed
# - when a child pipeline (from parent_pipeline source) fails it affects its
# parent pipeline. It's up to the parent to affect the ref CI status
# - when an ondemand_dast_scan pipeline runs it is for testing purpose and should
# not affect the ref CI status.
def self.dangling_sources
sources.slice(:webide, :parent_pipeline, :ondemand_dast_scan)
end
# CI sources are those pipeline events that affect the CI status of the ref
# they run for. By definition it excludes dangling pipelines.
def self.ci_sources
sources.except(*dangling_sources.keys)
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
...@@ -50,24 +67,6 @@ module Enums ...@@ -50,24 +67,6 @@ module Enums
parameter_source: 7 parameter_source: 7
} }
end end
def self.ci_config_sources
config_sources.slice(
:unknown_source,
:repository_source,
:auto_devops_source,
:remote_source,
:external_project_source
)
end
def self.ci_config_sources_values
ci_config_sources.values
end
def self.non_ci_config_source_values
config_sources.values - ci_config_sources.values
end
end end
end end
end end
......
...@@ -280,10 +280,9 @@ class Project < ApplicationRecord ...@@ -280,10 +280,9 @@ class Project < ApplicationRecord
# The relation :all_pipelines is intended to be used when we want to get the # The relation :all_pipelines is intended to be used when we want to get the
# whole list of pipelines associated to the project # whole list of pipelines associated to the project
has_many :all_pipelines, class_name: 'Ci::Pipeline', inverse_of: :project has_many :all_pipelines, class_name: 'Ci::Pipeline', inverse_of: :project
# The relation :ci_pipelines is intended to be used when we want to get only # The relation :ci_pipelines includes all those that directly contribute to the
# those pipeline which are directly related to CI. There are # latest status of a ref. This does not include dangling pipelines such as those
# other pipelines, like webide ones, that we won't retrieve # from webide, child pipelines, etc.
# if we use this relation.
has_many :ci_pipelines, has_many :ci_pipelines,
-> { ci_sources }, -> { ci_sources },
class_name: 'Ci::Pipeline', class_name: 'Ci::Pipeline',
......
...@@ -24,29 +24,6 @@ RSpec.describe Ci::Pipeline do ...@@ -24,29 +24,6 @@ RSpec.describe Ci::Pipeline do
end end
end end
describe '.ci_sources' do
subject { described_class.ci_sources }
let(:all_config_sources) { described_class.config_sources }
before do
all_config_sources.each do |source, _value|
create(:ci_pipeline, config_source: source)
end
end
it 'contains pipelines having CI only config sources' do
expect(subject.map(&:config_source)).to contain_exactly(
'auto_devops_source',
'external_project_source',
'remote_source',
'repository_source',
'unknown_source'
)
expect(subject.size).to be < all_config_sources.size
end
end
describe '#with_vulnerabilities scope' do describe '#with_vulnerabilities scope' do
let!(:pipeline_1) { create(:ci_pipeline, project: project) } let!(:pipeline_1) { create(:ci_pipeline, project: project) }
let!(:pipeline_2) { create(:ci_pipeline, project: project) } let!(:pipeline_2) { create(:ci_pipeline, project: project) }
...@@ -376,7 +353,7 @@ RSpec.describe Ci::Pipeline do ...@@ -376,7 +353,7 @@ RSpec.describe Ci::Pipeline do
context 'when pipeline is web terminal triggered' do context 'when pipeline is web terminal triggered' do
before do before do
pipeline.config_source = 'webide_source' pipeline.source = 'webide'
end end
it 'does not schedule the pipeline cache worker' do it 'does not schedule the pipeline cache worker' do
......
...@@ -34,12 +34,10 @@ RSpec.describe Resolvers::ProjectPipelineResolver do ...@@ -34,12 +34,10 @@ RSpec.describe Resolvers::ProjectPipelineResolver do
expect { resolve_pipeline(project, {}) }.to raise_error(ArgumentError) expect { resolve_pipeline(project, {}) }.to raise_error(ArgumentError)
end end
context 'when the pipeline is not a ci_config_source' do context 'when the pipeline is a dangling pipeline' do
let(:pipeline) do let(:pipeline) do
config_source_value = ::Enums::Ci::Pipeline.non_ci_config_source_values.first dangling_source = ::Enums::Ci::Pipeline.dangling_sources.each_value.first
config_source = ::Enums::Ci::Pipeline.config_sources.key(config_source_value) create(:ci_pipeline, source: dangling_source, project: project)
create(:ci_pipeline, config_source: config_source, project: project)
end end
it 'resolves pipeline for the passed iid' do it 'resolves pipeline for the passed iid' do
......
...@@ -221,6 +221,26 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do ...@@ -221,6 +221,26 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end end
end end
describe '.ci_sources' do
subject { described_class.ci_sources }
let!(:push_pipeline) { create(:ci_pipeline, source: :push) }
let!(:web_pipeline) { create(:ci_pipeline, source: :web) }
let!(:api_pipeline) { create(:ci_pipeline, source: :api) }
let!(:webide_pipeline) { create(:ci_pipeline, source: :webide) }
let!(:child_pipeline) { create(:ci_pipeline, source: :parent_pipeline) }
it 'contains pipelines having CI only sources' do
expect(subject).to contain_exactly(push_pipeline, web_pipeline, api_pipeline)
end
it 'filters on expected sources' do
expect(::Enums::Ci::Pipeline.ci_sources.keys).to contain_exactly(
*%i[unknown push web trigger schedule api external pipeline chat
merge_request_event external_pull_request_event])
end
end
describe '.outside_pipeline_family' do describe '.outside_pipeline_family' do
subject(:outside_pipeline_family) { described_class.outside_pipeline_family(upstream_pipeline) } subject(:outside_pipeline_family) { described_class.outside_pipeline_family(upstream_pipeline) }
...@@ -1239,14 +1259,42 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do ...@@ -1239,14 +1259,42 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end end
describe 'pipeline caching' do describe 'pipeline caching' do
before do context 'if pipeline is cacheable' do
pipeline.config_source = 'repository_source' before do
pipeline.source = 'push'
end
it 'performs ExpirePipelinesCacheWorker' do
expect(ExpirePipelineCacheWorker).to receive(:perform_async).with(pipeline.id)
pipeline.cancel
end
end end
it 'performs ExpirePipelinesCacheWorker' do context 'if pipeline is not cacheable' do
expect(ExpirePipelineCacheWorker).to receive(:perform_async).with(pipeline.id) before do
pipeline.source = 'webide'
end
pipeline.cancel it 'deos not perform ExpirePipelinesCacheWorker' do
expect(ExpirePipelineCacheWorker).not_to receive(:perform_async)
pipeline.cancel
end
end
end
describe '#dangling?' do
it 'returns true if pipeline comes from any dangling sources' do
pipeline.source = Enums::Ci::Pipeline.dangling_sources.each_key.first
expect(pipeline).to be_dangling
end
it 'returns true if pipeline comes from any CI sources' do
pipeline.source = Enums::Ci::Pipeline.ci_sources.each_key.first
expect(pipeline).not_to be_dangling
end end
end end
...@@ -1959,12 +2007,12 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do ...@@ -1959,12 +2007,12 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '.last_finished_for_ref_id' do describe '.last_finished_for_ref_id' do
let(:branch) { project.default_branch } let(:branch) { project.default_branch }
let(:ref) { project.ci_refs.take } let(:ref) { project.ci_refs.take }
let(:config_source) { Enums::Ci::Pipeline.config_sources[:parameter_source] } let(:dangling_source) { Enums::Ci::Pipeline.sources[:ondemand_dast_scan] }
let!(:pipeline1) { create(:ci_pipeline, :success, project: project, ref: branch) } let!(:pipeline1) { create(:ci_pipeline, :success, project: project, ref: branch) }
let!(:pipeline2) { create(:ci_pipeline, :success, project: project, ref: branch) } let!(:pipeline2) { create(:ci_pipeline, :success, project: project, ref: branch) }
let!(:pipeline3) { create(:ci_pipeline, :failed, project: project, ref: branch) } let!(:pipeline3) { create(:ci_pipeline, :failed, project: project, ref: branch) }
let!(:pipeline4) { create(:ci_pipeline, :success, project: project, ref: branch) } let!(:pipeline4) { create(:ci_pipeline, :success, project: project, ref: branch) }
let!(:pipeline5) { create(:ci_pipeline, :success, project: project, ref: branch, config_source: config_source) } let!(:pipeline5) { create(:ci_pipeline, :success, project: project, ref: branch, source: dangling_source) }
it 'returns the expected pipeline' do it 'returns the expected pipeline' do
result = described_class.last_finished_for_ref_id(ref.id) result = described_class.last_finished_for_ref_id(ref.id)
......
...@@ -107,8 +107,8 @@ RSpec.describe Ci::Ref do ...@@ -107,8 +107,8 @@ RSpec.describe Ci::Ref do
describe '#last_finished_pipeline_id' do describe '#last_finished_pipeline_id' do
let(:pipeline_status) { :running } let(:pipeline_status) { :running }
let(:config_source) { Enums::Ci::Pipeline.config_sources[:repository_source] } let(:pipeline_source) { Enums::Ci::Pipeline.sources[:push] }
let(:pipeline) { create(:ci_pipeline, pipeline_status, config_source: config_source) } let(:pipeline) { create(:ci_pipeline, pipeline_status, source: pipeline_source) }
let(:ci_ref) { pipeline.ci_ref } let(:ci_ref) { pipeline.ci_ref }
context 'when there are no finished pipelines' do context 'when there are no finished pipelines' do
...@@ -124,8 +124,8 @@ RSpec.describe Ci::Ref do ...@@ -124,8 +124,8 @@ RSpec.describe Ci::Ref do
expect(ci_ref.last_finished_pipeline_id).to eq(pipeline.id) expect(ci_ref.last_finished_pipeline_id).to eq(pipeline.id)
end end
context 'when the pipeline is not a ci_source' do context 'when the pipeline a dangling pipeline' do
let(:config_source) { Enums::Ci::Pipeline.config_sources[:parameter_source] } let(:pipeline_source) { Enums::Ci::Pipeline.sources[:ondemand_dast_scan] }
it 'returns nil' do it 'returns nil' do
expect(ci_ref.last_finished_pipeline_id).to be_nil expect(ci_ref.last_finished_pipeline_id).to be_nil
......
...@@ -522,9 +522,10 @@ RSpec.describe Project do ...@@ -522,9 +522,10 @@ RSpec.describe Project do
before do before do
create(:ci_pipeline, project: project, ref: 'master', source: :web) create(:ci_pipeline, project: project, ref: 'master', source: :web)
create(:ci_pipeline, project: project, ref: 'master', source: :external) create(:ci_pipeline, project: project, ref: 'master', source: :external)
create(:ci_pipeline, project: project, ref: 'master', source: :webide)
end end
it 'has ci pipelines' do it 'excludes dangling pipelines such as :webide' do
expect(project.ci_pipelines.size).to eq(2) expect(project.ci_pipelines.size).to eq(2)
end end
......
...@@ -476,17 +476,18 @@ RSpec.describe API::Ci::Pipelines do ...@@ -476,17 +476,18 @@ RSpec.describe API::Ci::Pipelines do
end end
end end
context 'when config source is not ci' do context 'when pipeline is a dangling pipeline' do
let(:non_ci_config_source) { Enums::Ci::Pipeline.non_ci_config_source_values.first } let(:dangling_source) { Enums::Ci::Pipeline.dangling_sources.each_value.first }
let(:pipeline_not_ci) do
create(:ci_pipeline, config_source: non_ci_config_source, project: project) let(:dangling_pipeline) do
create(:ci_pipeline, source: dangling_source, project: project)
end end
it 'returns the specified pipeline' do it 'returns the specified pipeline' do
get api("/projects/#{project.id}/pipelines/#{pipeline_not_ci.id}", user) get api("/projects/#{project.id}/pipelines/#{dangling_pipeline.id}", user)
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(json_response['sha']).to eq(pipeline_not_ci.sha) expect(json_response['sha']).to eq(dangling_pipeline.sha)
end end
end end
end end
......
...@@ -239,13 +239,13 @@ RSpec.describe API::Jobs do ...@@ -239,13 +239,13 @@ RSpec.describe API::Jobs do
end end
end end
context 'when config source not ci' do context 'when jobs belong to a dangling pipeline' do
let(:non_ci_config_source) { Enums::Ci::Pipeline.non_ci_config_source_values.first } let(:dangling_source) { Enums::Ci::Pipeline.dangling_sources.each_value.first }
let(:pipeline) do let(:pipeline) do
create(:ci_pipeline, config_source: non_ci_config_source, project: project) create(:ci_pipeline, source: dangling_source, project: project)
end end
it 'returns the specified pipeline' do it 'returns pipeline jobs' do
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(json_response[0]['pipeline']['sha']).to eq(pipeline.sha.to_s) expect(json_response[0]['pipeline']['sha']).to eq(pipeline.sha.to_s)
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