summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrzegorz Bizon <grzesiek.bizon@gmail.com>2017-09-25 16:22:00 +0200
committerGrzegorz Bizon <grzesiek.bizon@gmail.com>2017-09-25 16:22:00 +0200
commit8f47d484dab12df982655c3c05305bce7624914d (patch)
treebd4eef75a1714a22377962092f6f0103df49feed
parent1209f4f671c290ce6c577c0ff16ad7f9ea8b6271 (diff)
downloadgitlab-ce-8f47d484dab12df982655c3c05305bce7624914d.tar.gz
Extract pipeline chain builder classes from service
-rw-r--r--app/services/ci/create_pipeline_service.rb118
-rw-r--r--lib/gitlab/ci/pipeline/chain/base.rb27
-rw-r--r--lib/gitlab/ci/pipeline/chain/skip.rb33
-rw-r--r--lib/gitlab/ci/pipeline/chain/validate.rb107
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate_spec.rb110
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb99
6 files changed, 301 insertions, 193 deletions
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index d20de9b16a4..1a5bf7142e0 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -2,6 +2,9 @@ module Ci
class CreatePipelineService < BaseService
attr_reader :pipeline
+ SEQUENCE = [Gitlab::Ci::Pipeline::Chain::Validate,
+ Gitlab::Ci::Pipeline::Chain::Skip]
+
def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil)
@pipeline = Ci::Pipeline.new(
source: source,
@@ -16,11 +19,23 @@ module Ci
protected: project.protected_for?(ref)
)
- result = validate_project_and_git_items ||
- validate_pipeline(ignore_skip_ci: ignore_skip_ci,
- save_on_errors: save_on_errors)
+ command = OpenStruct.new(ignore_skip_ci: ignore_skip_ci,
+ save_incompleted: save_on_errors,
+ trigger_request: trigger_request,
+ schedule: schedule,
+ project: project,
+ current_user: current_user)
+
+ sequence = SEQUENCE.map { |chain| chain.new(pipeline, command) }
+
+ done = sequence.any? do |chain|
+ chain.perform!
+ chain.break?
+ end
- return result if result
+ update_merge_requests_head_pipeline if pipeline.persisted?
+
+ return pipeline if done
begin
Ci::Pipeline.transaction do
@@ -36,10 +51,8 @@ module Ci
return error("Failed to persist the pipeline: #{e}")
end
- update_merge_requests_head_pipeline
-
+ update_merge_requests_head_pipeline if pipeline.persisted?
cancel_pending_pipelines if project.auto_cancel_pending_pipelines?
-
pipeline_created_counter.increment(source: source)
pipeline.tap(&:process!)
@@ -47,65 +60,12 @@ module Ci
private
- def validate_project_and_git_items
- unless project.builds_enabled?
- return error('Pipeline is disabled')
- end
-
- unless allowed_to_trigger_pipeline?
- if can?(current_user, :create_pipeline, project)
- return error("Insufficient permissions for protected ref '#{ref}'")
- else
- return error('Insufficient permissions to create a new pipeline')
- end
- end
-
- unless branch? || tag?
- return error('Reference not found')
- end
-
- unless commit
- return error('Commit not found')
- end
- end
-
- def validate_pipeline(ignore_skip_ci:, save_on_errors:)
- unless pipeline.config_processor
- unless pipeline.ci_yaml_file
- return error("Missing #{pipeline.ci_yaml_file_path} file")
- end
- return error(pipeline.yaml_errors, save: save_on_errors)
- end
-
- if !ignore_skip_ci && skip_ci?
- pipeline.skip if save_on_errors
- return pipeline
- end
-
- unless pipeline.has_stage_seeds?
- return error('No stages / jobs for this pipeline.')
- end
- end
-
- def allowed_to_trigger_pipeline?
- if current_user
- allowed_to_create?
- else # legacy triggers don't have a corresponding user
- !project.protected_for?(ref)
- end
+ def commit
+ @commit ||= project.commit(origin_sha || origin_ref)
end
- def allowed_to_create?
- return unless can?(current_user, :create_pipeline, project)
-
- access = Gitlab::UserAccess.new(current_user, project: project)
- if branch?
- access.can_update_branch?(ref)
- elsif tag?
- access.can_create_tag?(ref)
- else
- true # Allow it for now and we'll reject when we check ref existence
- end
+ def sha
+ commit.try(:id)
end
def update_merge_requests_head_pipeline
@@ -115,11 +75,6 @@ module Ci
.update_all(head_pipeline_id: @pipeline.id)
end
- def skip_ci?
- return false unless pipeline.git_commit_message
- pipeline.git_commit_message =~ /\[(ci[ _-]skip|skip[ _-]ci)\]/i
- end
-
def cancel_pending_pipelines
Gitlab::OptimisticLocking.retry_lock(auto_cancelable_pipelines) do |cancelables|
cancelables.find_each do |cancelable|
@@ -136,13 +91,6 @@ module Ci
.created_or_pending
end
- def commit
- @commit ||= project.commit(origin_sha || origin_ref)
- end
-
- def sha
- commit.try(:id)
- end
def before_sha
params[:checkout_sha] || params[:before] || Gitlab::Git::BLANK_SHA
@@ -156,13 +104,6 @@ module Ci
params[:ref]
end
- def branch?
- return @is_branch if defined?(@is_branch)
-
- @is_branch =
- project.repository.ref_exists?(Gitlab::Git::BRANCH_REF_PREFIX + ref)
- end
-
def tag?
return @is_tag if defined?(@is_tag)
@@ -178,17 +119,6 @@ module Ci
origin_sha && origin_sha != Gitlab::Git::BLANK_SHA
end
- def error(message, save: false)
- pipeline.tap do
- pipeline.errors.add(:base, message)
-
- if save
- pipeline.drop
- update_merge_requests_head_pipeline
- end
- end
- end
-
def pipeline_created_counter
@pipeline_created_counter ||= Gitlab::Metrics.counter(:pipelines_created_total, "Counter of pipelines created")
end
diff --git a/lib/gitlab/ci/pipeline/chain/base.rb b/lib/gitlab/ci/pipeline/chain/base.rb
new file mode 100644
index 00000000000..8d82e1b288d
--- /dev/null
+++ b/lib/gitlab/ci/pipeline/chain/base.rb
@@ -0,0 +1,27 @@
+module Gitlab
+ module Ci
+ module Pipeline
+ module Chain
+ class Base
+ attr_reader :pipeline, :project, :current_user
+
+ def initialize(pipeline, command)
+ @pipeline = pipeline
+ @command = command
+
+ @project = command.project
+ @current_user = command.current_user
+ end
+
+ def perform!
+ raise NotImplementedError
+ end
+
+ def break?
+ raise NotImplementedError
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/pipeline/chain/skip.rb b/lib/gitlab/ci/pipeline/chain/skip.rb
new file mode 100644
index 00000000000..3f86275ae15
--- /dev/null
+++ b/lib/gitlab/ci/pipeline/chain/skip.rb
@@ -0,0 +1,33 @@
+module Gitlab
+ module Ci
+ module Pipeline
+ module Chain
+ class Skip < Chain::Base
+ SKIP_PATTERN = /\[(ci[ _-]skip|skip[ _-]ci)\]/i
+
+ def perform!
+ if skipped?
+ @pipeline.skip if @command.save_incompleted
+ end
+ end
+
+ def skipped?
+ !@command.ignore_skip_ci && commit_message_skips_ci?
+ end
+
+ def break?
+ skipped?
+ end
+
+ private
+
+ def commit_message_skips_ci?
+ return false unless @pipeline.git_commit_message
+
+ @skipped ||= @pipeline.git_commit_message =~ SKIP_PATTERN
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/pipeline/chain/validate.rb b/lib/gitlab/ci/pipeline/chain/validate.rb
new file mode 100644
index 00000000000..e7109425d6c
--- /dev/null
+++ b/lib/gitlab/ci/pipeline/chain/validate.rb
@@ -0,0 +1,107 @@
+module Gitlab
+ module Ci
+ module Pipeline
+ module Chain
+ class Validate < Chain::Base
+ include Gitlab::Allowable
+
+ def perform!
+ validate_project! || validate_repository! || validate_pipeline!
+ end
+
+ def break?
+ @pipeline.errors.any? || @pipeline.persisted?
+ end
+
+ def allowed_to_trigger_pipeline?
+ if current_user
+ allowed_to_create?
+ else # legacy triggers don't have a corresponding user
+ !project.protected_for?(@pipeline.ref)
+ end
+ end
+
+ def allowed_to_create?
+ return unless can?(current_user, :create_pipeline, project)
+
+ access = Gitlab::UserAccess.new(current_user, project: project)
+
+ if branch?
+ access.can_update_branch?(@pipeline.ref)
+ elsif tag?
+ access.can_create_tag?(@pipeline.ref)
+ else
+ true # Allow it for now and we'll reject when we check ref existence
+ end
+ end
+
+ private
+
+ def validate_project!
+ unless project.builds_enabled?
+ return error('Pipeline is disabled')
+ end
+
+ unless allowed_to_trigger_pipeline?
+ if can?(current_user, :create_pipeline, project)
+ return error("Insufficient permissions for protected ref '#{pipeline.ref}'")
+ else
+ return error('Insufficient permissions to create a new pipeline')
+ end
+ end
+ end
+
+ def validate_repository!
+ unless branch? || tag?
+ return error('Reference not found')
+ end
+
+ unless pipeline.sha
+ return error('Commit not found')
+ end
+ end
+
+ def validate_pipeline!
+ unless @pipeline.config_processor
+ unless @pipeline.ci_yaml_file
+ return error("Missing #{@pipeline.ci_yaml_file_path} file")
+ end
+
+ if @command.save_incompleted && @pipeline.has_yaml_errors?
+ @pipeline.drop
+ end
+
+ return error(@pipeline.yaml_errors)
+ end
+
+ unless @pipeline.has_stage_seeds?
+ return error('No stages / jobs for this pipeline.')
+ end
+ end
+
+ ## TODO, move to Pipeline as `branch_exists?`
+ #
+ def branch?
+ return @is_branch if defined?(@is_branch)
+
+ @is_branch = project.repository
+ .ref_exists?(Gitlab::Git::BRANCH_REF_PREFIX + pipeline.ref)
+ end
+
+ ## TODO, move to pipeline as `tag_exists?`
+ #
+ def tag?
+ return @is_tag if defined?(@is_tag)
+
+ @is_tag = project.repository
+ .ref_exists?(Gitlab::Git::TAG_REF_PREFIX + pipeline.ref)
+ end
+
+ def error(message)
+ pipeline.errors.add(:base, message)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate_spec.rb
new file mode 100644
index 00000000000..117f55fd8a7
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/chain/validate_spec.rb
@@ -0,0 +1,110 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Pipeline::Chain::Validate do
+ describe '#allowed_to_create?' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
+ let(:ref) { 'master' }
+
+ let(:pipeline) do
+ build_stubbed(:ci_pipeline, ref: ref, project: project)
+ end
+
+ let(:command) do
+ double('command', project: project, current_user: user)
+ end
+
+ subject do
+ described_class.new(pipeline, command).allowed_to_create?
+ end
+
+ context 'when user is a developer' do
+ before do
+ project.add_developer(user)
+ end
+
+ it { is_expected.to be_truthy }
+
+ context 'when the branch is protected' do
+ let!(:protected_branch) do
+ create(:protected_branch, project: project, name: ref)
+ end
+
+ it { is_expected.to be_falsey }
+
+ context 'when developers are allowed to merge' do
+ let!(:protected_branch) do
+ create(:protected_branch,
+ :developers_can_merge,
+ project: project,
+ name: ref)
+ end
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ context 'when the tag is protected' do
+ let(:ref) { 'v1.0.0' }
+
+ let!(:protected_tag) do
+ create(:protected_tag, project: project, name: ref)
+ end
+
+ it { is_expected.to be_falsey }
+
+ context 'when developers are allowed to create the tag' do
+ let!(:protected_tag) do
+ create(:protected_tag,
+ :developers_can_create,
+ project: project,
+ name: ref)
+ end
+
+ it { is_expected.to be_truthy }
+ end
+ end
+ end
+
+ context 'when user is a master' do
+ before do
+ project.add_master(user)
+ end
+
+ it { is_expected.to be_truthy }
+
+ context 'when the branch is protected' do
+ let!(:protected_branch) do
+ create(:protected_branch, project: project, name: ref)
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when the tag is protected' do
+ let(:ref) { 'v1.0.0' }
+
+ let!(:protected_tag) do
+ create(:protected_tag, project: project, name: ref)
+ end
+
+ it { is_expected.to be_truthy }
+
+ context 'when no one can create the tag' do
+ let!(:protected_tag) do
+ create(:protected_tag,
+ :no_one_can_create,
+ project: project,
+ name: ref)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+ end
+
+ context 'when owner cannot create pipeline' do
+ it { is_expected.to be_falsey }
+ end
+ end
+end
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index 4c2ff08039c..5b959b9a142 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -482,103 +482,4 @@ describe Ci::CreatePipelineService do
end
end
- describe '#allowed_to_create?' do
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
- let(:ref) { 'master' }
-
- subject do
- described_class.new(project, user, ref: ref)
- .send(:allowed_to_create?)
- end
-
- context 'when user is a developer' do
- before do
- project.add_developer(user)
- end
-
- it { is_expected.to be_truthy }
-
- context 'when the branch is protected' do
- let!(:protected_branch) do
- create(:protected_branch, project: project, name: ref)
- end
-
- it { is_expected.to be_falsey }
-
- context 'when developers are allowed to merge' do
- let!(:protected_branch) do
- create(:protected_branch,
- :developers_can_merge,
- project: project,
- name: ref)
- end
-
- it { is_expected.to be_truthy }
- end
- end
-
- context 'when the tag is protected' do
- let(:ref) { 'v1.0.0' }
-
- let!(:protected_tag) do
- create(:protected_tag, project: project, name: ref)
- end
-
- it { is_expected.to be_falsey }
-
- context 'when developers are allowed to create the tag' do
- let!(:protected_tag) do
- create(:protected_tag,
- :developers_can_create,
- project: project,
- name: ref)
- end
-
- it { is_expected.to be_truthy }
- end
- end
- end
-
- context 'when user is a master' do
- before do
- project.add_master(user)
- end
-
- it { is_expected.to be_truthy }
-
- context 'when the branch is protected' do
- let!(:protected_branch) do
- create(:protected_branch, project: project, name: ref)
- end
-
- it { is_expected.to be_truthy }
- end
-
- context 'when the tag is protected' do
- let(:ref) { 'v1.0.0' }
-
- let!(:protected_tag) do
- create(:protected_tag, project: project, name: ref)
- end
-
- it { is_expected.to be_truthy }
-
- context 'when no one can create the tag' do
- let!(:protected_tag) do
- create(:protected_tag,
- :no_one_can_create,
- project: project,
- name: ref)
- end
-
- it { is_expected.to be_falsey }
- end
- end
- end
-
- context 'when owner cannot create pipeline' do
- it { is_expected.to be_falsey }
- end
- end
end