Commit cf5e9f0f authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch 'pedropombeiro/321368/limit-number-of-runners' into 'master'

Introduce limit to number of registered runners [RUN ALL RSPEC] [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!60157
parents 517d78ff 25d4af5f
...@@ -3,6 +3,11 @@ ...@@ -3,6 +3,11 @@
module Ci module Ci
class RunnerNamespace < ApplicationRecord class RunnerNamespace < ApplicationRecord
extend Gitlab::Ci::Model extend Gitlab::Ci::Model
include Limitable
self.limit_name = 'ci_registered_group_runners'
self.limit_scope = :group
self.limit_feature_flag = :ci_runner_limits
belongs_to :runner, inverse_of: :runner_namespaces belongs_to :runner, inverse_of: :runner_namespaces
belongs_to :namespace, inverse_of: :runner_namespaces, class_name: '::Namespace' belongs_to :namespace, inverse_of: :runner_namespaces, class_name: '::Namespace'
......
...@@ -3,6 +3,11 @@ ...@@ -3,6 +3,11 @@
module Ci module Ci
class RunnerProject < ApplicationRecord class RunnerProject < ApplicationRecord
extend Gitlab::Ci::Model extend Gitlab::Ci::Model
include Limitable
self.limit_name = 'ci_registered_project_runners'
self.limit_scope = :project
self.limit_feature_flag = :ci_runner_limits
belongs_to :runner, inverse_of: :runner_projects belongs_to :runner, inverse_of: :runner_projects
belongs_to :project, inverse_of: :runner_projects belongs_to :project, inverse_of: :runner_projects
......
...@@ -7,6 +7,7 @@ module Limitable ...@@ -7,6 +7,7 @@ module Limitable
included do included do
class_attribute :limit_scope class_attribute :limit_scope
class_attribute :limit_name class_attribute :limit_name
class_attribute :limit_feature_flag
self.limit_name = self.name.demodulize.tableize self.limit_name = self.name.demodulize.tableize
validate :validate_plan_limit_not_exceeded, on: :create validate :validate_plan_limit_not_exceeded, on: :create
...@@ -25,6 +26,7 @@ module Limitable ...@@ -25,6 +26,7 @@ module Limitable
def validate_scoped_plan_limit_not_exceeded def validate_scoped_plan_limit_not_exceeded
scope_relation = self.public_send(limit_scope) # rubocop:disable GitlabSecurity/PublicSend scope_relation = self.public_send(limit_scope) # rubocop:disable GitlabSecurity/PublicSend
return unless scope_relation return unless scope_relation
return if limit_feature_flag && ::Feature.disabled?(limit_feature_flag, scope_relation, default_enabled: :yaml)
relation = self.class.where(limit_scope => scope_relation) relation = self.class.where(limit_scope => scope_relation)
limits = scope_relation.actual_limits limits = scope_relation.actual_limits
......
---
title: Introduce limit to number of registered runners
merge_request: 60157
author:
type: added
---
name: ci_runner_limits
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60157
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/329438
milestone: '13.12'
type: development
group: group::runner
default_enabled: false
# frozen_string_literal: true
class AddRunnerRegistrationToPlanLimits < ActiveRecord::Migration[6.0]
def change
add_column(:plan_limits, :ci_registered_group_runners, :integer, default: 1000, null: false)
add_column(:plan_limits, :ci_registered_project_runners, :integer, default: 1000, null: false)
end
end
# frozen_string_literal: true
class InsertRunnerRegistrationPlanLimits < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
def up
create_or_update_plan_limit('ci_registered_group_runners', 'free', 50)
create_or_update_plan_limit('ci_registered_group_runners', 'bronze', 1000)
create_or_update_plan_limit('ci_registered_group_runners', 'silver', 1000)
create_or_update_plan_limit('ci_registered_group_runners', 'gold', 1000)
create_or_update_plan_limit('ci_registered_project_runners', 'free', 50)
create_or_update_plan_limit('ci_registered_project_runners', 'bronze', 1000)
create_or_update_plan_limit('ci_registered_project_runners', 'silver', 1000)
create_or_update_plan_limit('ci_registered_project_runners', 'gold', 1000)
end
def down
%w[group project].each do |scope|
create_or_update_plan_limit("ci_registered_#{scope}_runners", 'free', 1000)
create_or_update_plan_limit("ci_registered_#{scope}_runners", 'bronze', 1000)
create_or_update_plan_limit("ci_registered_#{scope}_runners", 'silver', 1000)
create_or_update_plan_limit("ci_registered_#{scope}_runners", 'gold', 1000)
end
end
end
c582b7dda33492e122725001200aeb470cbf4458f247728a3b2102e325c97193
\ No newline at end of file
b5e34f7827edcdf81c66250353cddc3481e39c693f983df610b8ed20c58cb65b
\ No newline at end of file
...@@ -16025,7 +16025,9 @@ CREATE TABLE plan_limits ( ...@@ -16025,7 +16025,9 @@ CREATE TABLE plan_limits (
daily_invites integer DEFAULT 0 NOT NULL, daily_invites integer DEFAULT 0 NOT NULL,
rubygems_max_file_size bigint DEFAULT '3221225472'::bigint NOT NULL, rubygems_max_file_size bigint DEFAULT '3221225472'::bigint NOT NULL,
terraform_module_max_file_size bigint DEFAULT 1073741824 NOT NULL, terraform_module_max_file_size bigint DEFAULT 1073741824 NOT NULL,
helm_max_file_size bigint DEFAULT 5242880 NOT NULL helm_max_file_size bigint DEFAULT 5242880 NOT NULL,
ci_registered_group_runners integer DEFAULT 1000 NOT NULL,
ci_registered_project_runners integer DEFAULT 1000 NOT NULL
); );
CREATE SEQUENCE plan_limits_id_seq CREATE SEQUENCE plan_limits_id_seq
...@@ -435,6 +435,32 @@ installation, run the following in the [GitLab Rails console](operations/rails_c ...@@ -435,6 +435,32 @@ installation, run the following in the [GitLab Rails console](operations/rails_c
Plan.default.actual_limits.update!(ci_max_artifact_size_junit: 10) Plan.default.actual_limits.update!(ci_max_artifact_size_junit: 10)
``` ```
### Number of registered runners per scope
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/321368) in GitLab 13.12.
The total number of registered runners is limited at the group and project
levels. Each time a new runner is registered, GitLab checks these limits. A
runner's registration fails if it exceeds the limit for the scope determined by
the runner registration token.
- GitLab SaaS subscribers have different limits defined per plan, affecting all projects using that plan.
- Self-managed GitLab Premium and Ultimate limits are defined by a default plan that affects all projects:
| Runner scope | Default value |
|---------------------------------------------|---------------|
| `ci_registered_group_runners` | 1000 |
| `ci_registered_project_runners` | 1000 |
To update these limits, run the following in the
[GitLab Rails console](operations/rails_console.md#starting-a-rails-console-session):
```ruby
# Use ci_registered_group_runners or ci_registered_project_runners
# depending on desired scope
Plan.default.actual_limits.update!(ci_registered_project_runners: 100)
```
## Instance monitoring and metrics ## Instance monitoring and metrics
### Incident Management inbound alert limits ### Incident Management inbound alert limits
......
...@@ -98,11 +98,12 @@ Any settings or feature limits not listed here are using the defaults listed in ...@@ -98,11 +98,12 @@ Any settings or feature limits not listed here are using the defaults listed in
| Artifacts maximum size (compressed) | 1G | 100M | | Artifacts maximum size (compressed) | 1G | 100M |
| Artifacts [expiry time](../../ci/yaml/README.md#artifactsexpire_in) | From June 22, 2020, deleted after 30 days unless otherwise specified (artifacts created before that date have no expiry). | deleted after 30 days unless otherwise specified | | Artifacts [expiry time](../../ci/yaml/README.md#artifactsexpire_in) | From June 22, 2020, deleted after 30 days unless otherwise specified (artifacts created before that date have no expiry). | deleted after 30 days unless otherwise specified |
| Scheduled Pipeline Cron | `*/5 * * * *` | `3-59/10 * * * *` | | Scheduled Pipeline Cron | `*/5 * * * *` | `3-59/10 * * * *` |
| [Max jobs in active pipelines](../../administration/instance_limits.md#number-of-jobs-in-active-pipelines) | `500` for Free tier, unlimited otherwise | Unlimited | [Max jobs in active pipelines](../../administration/instance_limits.md#number-of-jobs-in-active-pipelines) | `500` for Free tier, unlimited otherwise | Unlimited |
| [Max CI/CD subscriptions to a project](../../administration/instance_limits.md#number-of-cicd-subscriptions-to-a-project) | `2` | Unlimited | | [Max CI/CD subscriptions to a project](../../administration/instance_limits.md#number-of-cicd-subscriptions-to-a-project) | `2` | Unlimited |
| [Max pipeline schedules in projects](../../administration/instance_limits.md#number-of-pipeline-schedules) | `10` for Free tier, `50` for all paid tiers | Unlimited | | [Max pipeline schedules in projects](../../administration/instance_limits.md#number-of-pipeline-schedules) | `10` for Free tier, `50` for all paid tiers | Unlimited |
| [Scheduled Job Archival](../../user/admin_area/settings/continuous_integration.md#archive-jobs) | 3 months | Never | | [Scheduled Job Archival](../../user/admin_area/settings/continuous_integration.md#archive-jobs) | 3 months | Never |
| Max test cases per [unit test report](../../ci/unit_test_reports.md) | `500_000` | Unlimited | | Max test cases per [unit test report](../../ci/unit_test_reports.md) | `500_000` | Unlimited |
| [Max registered runners](../../administration/instance_limits.md#number-of-registered-runners-per-scope) | `50` per-project and per-group for Free tier,<br/>`1_000` per-group for all paid tiers / `1_000` per-project for all paid tiers | `1_000` per-group / `1_000` per-project |
## Account and limit settings ## Account and limit settings
......
...@@ -302,6 +302,9 @@ RSpec.describe Groups::RunnersController do ...@@ -302,6 +302,9 @@ RSpec.describe Groups::RunnersController do
context 'when user is not an owner' do context 'when user is not an owner' do
before do before do
# Disable limit checking
allow(runner).to receive(:runner_scope).and_return(nil)
group.add_maintainer(user) group.add_maintainer(user)
end end
......
# frozen_string_literal: true
FactoryBot.define do
factory :ci_runner_namespace, class: 'Ci::RunnerNamespace' do
runner factory: [:ci_runner, :group]
group
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::RunnerNamespace do
it_behaves_like 'includes Limitable concern' do
subject { build(:ci_runner_namespace, group: create(:group, :nested), runner: create(:ci_runner, :group)) }
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::RunnerProject do
it_behaves_like 'includes Limitable concern' do
subject { build(:ci_runner_project, project: create(:project), runner: create(:ci_runner, :project)) }
end
end
...@@ -91,6 +91,21 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do ...@@ -91,6 +91,21 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
it_behaves_like 'not executing any extra queries for the application context' do it_behaves_like 'not executing any extra queries for the application context' do
let(:subject_proc) { proc { request } } let(:subject_proc) { proc { request } }
end end
context 'when it exceeds the application limits' do
before do
create(:ci_runner, runner_type: :project_type, projects: [project])
create(:plan_limits, :default_plan, ci_registered_project_runners: 1)
end
it 'does not create runner' do
request
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']).to include('runner_projects' => ['is invalid'])
expect(project.runners.reload.size).to eq(1)
end
end
end end
context 'when group token is used' do context 'when group token is used' do
...@@ -117,6 +132,21 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do ...@@ -117,6 +132,21 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
it_behaves_like 'not executing any extra queries for the application context' do it_behaves_like 'not executing any extra queries for the application context' do
let(:subject_proc) { proc { request } } let(:subject_proc) { proc { request } }
end end
context 'when it exceeds the application limits' do
before do
create(:ci_runner, runner_type: :group_type, groups: [group])
create(:plan_limits, :default_plan, ci_registered_group_runners: 1)
end
it 'does not create runner' do
request
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']).to include('runner_namespaces' => ['is invalid'])
expect(group.runners.reload.size).to eq(1)
end
end
end end
end end
......
...@@ -999,6 +999,19 @@ RSpec.describe API::Ci::Runners do ...@@ -999,6 +999,19 @@ RSpec.describe API::Ci::Runners do
end.to change { project.runners.count }.by(+1) end.to change { project.runners.count }.by(+1)
expect(response).to have_gitlab_http_status(:created) expect(response).to have_gitlab_http_status(:created)
end end
context 'when it exceeds the application limits' do
before do
create(:plan_limits, :default_plan, ci_registered_project_runners: 1)
end
it 'does not enable specific runner' do
expect do
post api("/projects/#{project.id}/runners", admin), params: { runner_id: new_project_runner.id }
end.not_to change { project.runners.count }
expect(response).to have_gitlab_http_status(:bad_request)
end
end
end end
it 'enables a instance type runner' do it 'enables a instance type runner' do
......
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