summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShinya Maeda <shinya@gitlab.com>2019-08-28 11:38:57 +0700
committerShinya Maeda <shinya@gitlab.com>2019-08-28 11:42:53 +0700
commitf7f7d61d365859c49e07a3f1caa3d9e2f28c69a6 (patch)
tree7b7223389158b9686b1baca1ded4b23da199d585
parentef0f1509dd2a2a3ba5798362e2be21108b705a85 (diff)
downloadgitlab-ce-pipeline-rate-limit-per-source.tar.gz
Pipeline rate limit per source and userpipeline-rate-limit-per-source
This commit adds a rate limit to pipeline creation process. At this moment, users cannot create merge request pipelines more than 10 per a minute.
-rw-r--r--app/models/ci/pipeline_enums.rb3
-rw-r--r--app/presenters/ci/pipeline_presenter.rb5
-rw-r--r--app/services/ci/create_pipeline_service.rb1
-rw-r--r--lib/gitlab/ci/pipeline/chain/rate_limit.rb57
4 files changed, 64 insertions, 2 deletions
diff --git a/app/models/ci/pipeline_enums.rb b/app/models/ci/pipeline_enums.rb
index 571c4271475..56d18bbe8f9 100644
--- a/app/models/ci/pipeline_enums.rb
+++ b/app/models/ci/pipeline_enums.rb
@@ -7,7 +7,8 @@ module Ci
def self.failure_reasons
{
unknown_failure: 0,
- config_error: 1
+ config_error: 1,
+ rate_limit_exceeded: 2
}
end
diff --git a/app/presenters/ci/pipeline_presenter.rb b/app/presenters/ci/pipeline_presenter.rb
index 358473d0a74..ebfd6254bd2 100644
--- a/app/presenters/ci/pipeline_presenter.rb
+++ b/app/presenters/ci/pipeline_presenter.rb
@@ -8,7 +8,10 @@ module Ci
# We use a class method here instead of a constant, allowing EE to redefine
# the returned `Hash` more easily.
def self.failure_reasons
- { config_error: 'CI/CD YAML configuration error!' }
+ {
+ config_error: 'CI/CD YAML configuration error!',
+ rate_limit_exceeded: 'Pipeline rate limit exceeded!'
+ }
end
presents :pipeline
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index cdcc4b15bea..52436f9ce8a 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -12,6 +12,7 @@ module Ci
Gitlab::Ci::Pipeline::Chain::Validate::Repository,
Gitlab::Ci::Pipeline::Chain::Validate::Config,
Gitlab::Ci::Pipeline::Chain::Skip,
+ Gitlab::Ci::Pipeline::Chain::Limit::RateLimit,
Gitlab::Ci::Pipeline::Chain::Limit::Size,
Gitlab::Ci::Pipeline::Chain::Populate,
Gitlab::Ci::Pipeline::Chain::Create,
diff --git a/lib/gitlab/ci/pipeline/chain/rate_limit.rb b/lib/gitlab/ci/pipeline/chain/rate_limit.rb
new file mode 100644
index 00000000000..0a1dff52552
--- /dev/null
+++ b/lib/gitlab/ci/pipeline/chain/rate_limit.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Pipeline
+ module Chain
+ module Limit
+ class RateLimit < Chain::Base
+ include Gitlab::Utils::StrongMemoize
+
+ RATE_LIMIT_PER_SOURCE = {
+ push: 10,
+ web: 10,
+ merge_request_event: 10
+ }.freeze
+
+ def initialize
+ super
+
+ limiter = ::Gitlab::ActionRateLimiter.new(action: "pipeline_creation:#{command.source}")
+ end
+
+ def perform!
+ return unless Feature.enable?(:ci_pipeline_rate_limit, pipeline.project)
+ return unless rate_limit_for_source
+ return unless throttled?
+
+ if command.save_incompleted
+ pipeline.drop!(:rate_limit_exceeded)
+ end
+
+ error('Pipeline rate limit exceeded. Please wait a minute and retry again.')
+ end
+
+ def break?
+ throttled?
+ end
+
+ private
+
+ def rate_limit_for_source
+ strong_memoize(:rate_limit_for_source) do
+ RATE_LIMIT_PER_SOURCE[command.source]
+ end
+ end
+
+ def throttled?
+ strong_memoize(:throttled) do
+ limiter.throttled?([pipeline.user], rate_limit_for_source)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end