summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/ci/cache-repo.gitlab-ci.yml1
-rw-r--r--app/models/ci/pipeline.rb34
-rw-r--r--app/models/ci/sources/pipeline.rb3
-rw-r--r--app/services/ci/pipeline_bridge_status_service.rb13
-rw-r--r--app/workers/all_queues.yml6
-rw-r--r--app/workers/ci/pipeline_bridge_status_worker.rb19
-rw-r--r--doc/ci/junit_test_reports.md5
-rw-r--r--spec/models/ci/pipeline_spec.rb104
-rw-r--r--spec/models/ci/sources/pipeline_spec.rb1
-rw-r--r--spec/services/ci/pipeline_bridge_status_service_spec.rb27
-rw-r--r--spec/workers/ci/pipeline_bridge_status_worker_spec.rb38
11 files changed, 248 insertions, 3 deletions
diff --git a/.gitlab/ci/cache-repo.gitlab-ci.yml b/.gitlab/ci/cache-repo.gitlab-ci.yml
index 9dcb6f40589..670341eab65 100644
--- a/.gitlab/ci/cache-repo.gitlab-ci.yml
+++ b/.gitlab/ci/cache-repo.gitlab-ci.yml
@@ -34,6 +34,7 @@ cache-repo:
- git clone --progress $CI_REPOSITORY_URL $CI_PROJECT_NAME
- cd $CI_PROJECT_NAME
- gcloud auth activate-service-account --key-file=$CI_REPO_CACHE_CREDENTIALS
+ - git remote rm origin
- tar cf $TAR_FILENAME .
- gzip $TAR_FILENAME
- gsutil cp $TAR_FILENAME.gz gs://gitlab-ci-git-repo-cache/project-$CI_PROJECT_ID/gitlab-master.tar.gz
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 2bf0ee57d39..addc8a7a2fc 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -16,6 +16,8 @@ module Ci
include FromUnion
include UpdatedAtFilterable
+ BridgeStatusError = Class.new(StandardError)
+
sha_attribute :source_sha
sha_attribute :target_sha
@@ -64,6 +66,7 @@ module Ci
has_one :triggered_by_pipeline, through: :source_pipeline, source: :source_pipeline
has_one :parent_pipeline, -> { merge(Ci::Sources::Pipeline.same_project) }, through: :source_pipeline, source: :source_pipeline
has_one :source_job, through: :source_pipeline, source: :source_job
+ has_one :source_bridge, through: :source_pipeline, source: :source_bridge
has_one :pipeline_config, class_name: 'Ci::PipelineConfig', inverse_of: :pipeline
@@ -204,6 +207,22 @@ module Ci
end
end
+ after_transition any => ::Ci::Pipeline.completed_statuses do |pipeline|
+ next unless pipeline.bridge_triggered?
+ next unless pipeline.bridge_waiting?
+
+ pipeline.run_after_commit do
+ ::Ci::PipelineBridgeStatusWorker.perform_async(pipeline.id)
+ end
+ end
+
+ after_transition created: :pending do |pipeline|
+ next unless pipeline.bridge_triggered?
+ next if pipeline.bridge_waiting?
+
+ pipeline.update_bridge_status!
+ end
+
after_transition any => [:success, :failed] do |pipeline|
pipeline.run_after_commit do
PipelineNotificationWorker.perform_async(pipeline.id)
@@ -722,6 +741,21 @@ module Ci
end
end
+ def update_bridge_status!
+ raise ArgumentError unless bridge_triggered?
+ raise BridgeStatusError unless source_bridge.active?
+
+ source_bridge.success!
+ end
+
+ def bridge_triggered?
+ source_bridge.present?
+ end
+
+ def bridge_waiting?
+ source_bridge&.dependent?
+ end
+
def child?
parent_pipeline.present?
end
diff --git a/app/models/ci/sources/pipeline.rb b/app/models/ci/sources/pipeline.rb
index d71e3b55b9a..f19aac213be 100644
--- a/app/models/ci/sources/pipeline.rb
+++ b/app/models/ci/sources/pipeline.rb
@@ -10,6 +10,7 @@ module Ci
belongs_to :source_project, class_name: "Project", foreign_key: :source_project_id
belongs_to :source_job, class_name: "CommitStatus", foreign_key: :source_job_id
+ belongs_to :source_bridge, class_name: "Ci::Bridge", foreign_key: :source_job_id
belongs_to :source_pipeline, class_name: "Ci::Pipeline", foreign_key: :source_pipeline_id
validates :project, presence: true
@@ -23,5 +24,3 @@ module Ci
end
end
end
-
-::Ci::Sources::Pipeline.prepend_if_ee('::EE::Ci::Sources::Pipeline')
diff --git a/app/services/ci/pipeline_bridge_status_service.rb b/app/services/ci/pipeline_bridge_status_service.rb
new file mode 100644
index 00000000000..19ed5026a3a
--- /dev/null
+++ b/app/services/ci/pipeline_bridge_status_service.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Ci
+ class PipelineBridgeStatusService < ::BaseService
+ def execute(pipeline)
+ return unless pipeline.bridge_triggered?
+
+ pipeline.source_bridge.inherit_status_from_downstream!(pipeline)
+ end
+ end
+end
+
+Ci::PipelineBridgeStatusService.prepend_if_ee('EE::Ci::PipelineBridgeStatusService')
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index b344e1e36b8..51b91e8e8be 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -531,6 +531,12 @@
:latency_sensitive:
:resource_boundary: :unknown
:weight: 3
+- :name: pipeline_default:ci_pipeline_bridge_status
+ :feature_category: :continuous_integration
+ :has_external_dependencies:
+ :latency_sensitive: true
+ :resource_boundary: :cpu
+ :weight: 3
- :name: pipeline_default:pipeline_metrics
:feature_category: :continuous_integration
:has_external_dependencies:
diff --git a/app/workers/ci/pipeline_bridge_status_worker.rb b/app/workers/ci/pipeline_bridge_status_worker.rb
new file mode 100644
index 00000000000..f196573deaa
--- /dev/null
+++ b/app/workers/ci/pipeline_bridge_status_worker.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Ci
+ class PipelineBridgeStatusWorker
+ include ::ApplicationWorker
+ include ::PipelineQueue
+
+ latency_sensitive_worker!
+ worker_resource_boundary :cpu
+
+ def perform(pipeline_id)
+ ::Ci::Pipeline.find_by_id(pipeline_id).try do |pipeline|
+ ::Ci::PipelineBridgeStatusService
+ .new(pipeline.project, pipeline.user)
+ .execute(pipeline)
+ end
+ end
+ end
+end
diff --git a/doc/ci/junit_test_reports.md b/doc/ci/junit_test_reports.md
index 8773f712110..c82f0ab3a42 100644
--- a/doc/ci/junit_test_reports.md
+++ b/doc/ci/junit_test_reports.md
@@ -221,7 +221,10 @@ with failed showing at the top, skipped next and successful cases last.
### Enabling the feature
-This feature comes with the `:junit_pipeline_view` feature flag disabled by default.
+This feature comes with the `:junit_pipeline_view` feature flag disabled by default. This
+feature is disabled due to some performance issues with very large data sets.
+When [the performance issue](https://gitlab.com/gitlab-org/gitlab/issues/37725) is resolved, the feature will be enabled by default.
+
To enable this feature, ask a GitLab administrator with Rails console access to run the
following command:
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 09d6d661d81..18f3c4af08c 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -1210,6 +1210,32 @@ describe Ci::Pipeline, :mailer do
end
end
+ context 'when pipeline is bridge triggered' do
+ before do
+ pipeline.source_bridge = create(:ci_bridge)
+ end
+
+ context 'when source bridge is dependent on pipeline status' do
+ before do
+ allow(pipeline.source_bridge).to receive(:dependent?).and_return(true)
+ end
+
+ it 'schedules the pipeline bridge worker' do
+ expect(::Ci::PipelineBridgeStatusWorker).to receive(:perform_async)
+
+ pipeline.succeed!
+ end
+ end
+
+ context 'when source bridge is not dependent on pipeline status' do
+ it 'does not schedule the pipeline bridge worker' do
+ expect(::Ci::PipelineBridgeStatusWorker).not_to receive(:perform_async)
+
+ pipeline.succeed!
+ end
+ end
+ end
+
def auto_devops_pipelines_completed_total(status)
Gitlab::Metrics.counter(:auto_devops_pipelines_completed_total, 'Number of completed auto devops pipelines').get(status: status)
end
@@ -2883,4 +2909,82 @@ describe Ci::Pipeline, :mailer do
end
end
end
+
+ describe 'upstream status interactions' do
+ context 'when a pipeline has an upstream status' do
+ context 'when an upstream status is a bridge' do
+ let(:bridge) { create(:ci_bridge, status: :pending) }
+
+ before do
+ create(:ci_sources_pipeline, pipeline: pipeline, source_job: bridge)
+ end
+
+ describe '#bridge_triggered?' do
+ it 'is a pipeline triggered by a bridge' do
+ expect(pipeline).to be_bridge_triggered
+ end
+ end
+
+ describe '#source_job' do
+ it 'has a correct source job' do
+ expect(pipeline.source_job).to eq bridge
+ end
+ end
+
+ describe '#source_bridge' do
+ it 'has a correct bridge source' do
+ expect(pipeline.source_bridge).to eq bridge
+ end
+ end
+
+ describe '#update_bridge_status!' do
+ it 'can update bridge status if it is running' do
+ pipeline.update_bridge_status!
+
+ expect(bridge.reload).to be_success
+ end
+
+ it 'can not update bridge status if is not active' do
+ bridge.success!
+
+ expect { pipeline.update_bridge_status! }
+ .to raise_error Ci::Pipeline::BridgeStatusError
+ end
+ end
+ end
+
+ context 'when an upstream status is a build' do
+ let(:build) { create(:ci_build) }
+
+ before do
+ create(:ci_sources_pipeline, pipeline: pipeline, source_job: build)
+ end
+
+ describe '#bridge_triggered?' do
+ it 'is a pipeline that has not been triggered by a bridge' do
+ expect(pipeline).not_to be_bridge_triggered
+ end
+ end
+
+ describe '#source_job' do
+ it 'has a correct source job' do
+ expect(pipeline.source_job).to eq build
+ end
+ end
+
+ describe '#source_bridge' do
+ it 'does not have a bridge source' do
+ expect(pipeline.source_bridge).to be_nil
+ end
+ end
+
+ describe '#update_bridge_status!' do
+ it 'can not update upstream job status' do
+ expect { pipeline.update_bridge_status! }
+ .to raise_error ArgumentError
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/ci/sources/pipeline_spec.rb b/spec/models/ci/sources/pipeline_spec.rb
index 63bee5bfb55..5023747b487 100644
--- a/spec/models/ci/sources/pipeline_spec.rb
+++ b/spec/models/ci/sources/pipeline_spec.rb
@@ -8,6 +8,7 @@ describe Ci::Sources::Pipeline do
it { is_expected.to belong_to(:source_project) }
it { is_expected.to belong_to(:source_job) }
+ it { is_expected.to belong_to(:source_bridge) }
it { is_expected.to belong_to(:source_pipeline) }
it { is_expected.to validate_presence_of(:project) }
diff --git a/spec/services/ci/pipeline_bridge_status_service_spec.rb b/spec/services/ci/pipeline_bridge_status_service_spec.rb
new file mode 100644
index 00000000000..95f16af3af9
--- /dev/null
+++ b/spec/services/ci/pipeline_bridge_status_service_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Ci::PipelineBridgeStatusService do
+ let(:user) { build(:user) }
+ let(:project) { build(:project) }
+ let(:pipeline) { build(:ci_pipeline, project: project) }
+
+ describe '#execute' do
+ subject { described_class.new(project, user).execute(pipeline) }
+
+ context 'when pipeline has upstream bridge' do
+ let(:bridge) { build(:ci_bridge) }
+
+ before do
+ pipeline.source_bridge = bridge
+ end
+
+ it 'calls inherit_status_from_downstream on upstream bridge' do
+ expect(bridge).to receive(:inherit_status_from_downstream!).with(pipeline)
+
+ subject
+ end
+ end
+ end
+end
diff --git a/spec/workers/ci/pipeline_bridge_status_worker_spec.rb b/spec/workers/ci/pipeline_bridge_status_worker_spec.rb
new file mode 100644
index 00000000000..d5f95a035fd
--- /dev/null
+++ b/spec/workers/ci/pipeline_bridge_status_worker_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Ci::PipelineBridgeStatusWorker do
+ describe '#perform' do
+ subject { described_class.new.perform(pipeline_id) }
+
+ context 'when pipeline exists' do
+ let(:pipeline) { create(:ci_pipeline) }
+ let(:pipeline_id) { pipeline.id }
+
+ it 'calls the service' do
+ service = double('bridge status service')
+
+ expect(Ci::PipelineBridgeStatusService)
+ .to receive(:new)
+ .with(pipeline.project, pipeline.user)
+ .and_return(service)
+
+ expect(service).to receive(:execute).with(pipeline)
+
+ subject
+ end
+ end
+
+ context 'when pipeline does not exist' do
+ let(:pipeline_id) { 1234 }
+
+ it 'does not call the service' do
+ expect(Ci::PipelineBridgeStatusService)
+ .not_to receive(:new)
+
+ subject
+ end
+ end
+ end
+end