diff options
author | Shinya Maeda <shinya@gitlab.com> | 2019-08-28 11:38:57 +0700 |
---|---|---|
committer | Shinya Maeda <shinya@gitlab.com> | 2019-08-28 11:42:53 +0700 |
commit | f7f7d61d365859c49e07a3f1caa3d9e2f28c69a6 (patch) | |
tree | 7b7223389158b9686b1baca1ded4b23da199d585 | |
parent | ef0f1509dd2a2a3ba5798362e2be21108b705a85 (diff) | |
download | gitlab-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.rb | 3 | ||||
-rw-r--r-- | app/presenters/ci/pipeline_presenter.rb | 5 | ||||
-rw-r--r-- | app/services/ci/create_pipeline_service.rb | 1 | ||||
-rw-r--r-- | lib/gitlab/ci/pipeline/chain/rate_limit.rb | 57 |
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 |