Commit 883e5e0d authored by Matija Čupić's avatar Matija Čupić

Add subscription count to application limits

Moves the subscription count to the model with the Limitable concern.
parent 1b1cf299
# frozen_string_literal: true
class AddProjectSubscriptionsToPlanLimits < ActiveRecord::Migration[6.0]
DOWNTIME = false
def change
add_column(:plan_limits, :ci_project_subscriptions, :integer, default: 0, null: false)
end
end
# frozen_string_literal: true
class InsertProjectSubscriptionsPlanLimits < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
return if Rails.env.test?
if Gitlab.com?
create_or_update_plan_limit('ci_project_subscriptions', 'free', 2)
create_or_update_plan_limit('ci_project_subscriptions', 'bronze', 2)
create_or_update_plan_limit('ci_project_subscriptions', 'silver', 2)
create_or_update_plan_limit('ci_project_subscriptions', 'gold', 2)
else
create_or_update_plan_limit('ci_project_subscriptions', 'default', 2)
end
end
def down
return if Rails.env.test?
if Gitlab.com?
create_or_update_plan_limit('ci_project_subscriptions', 'free', 0)
create_or_update_plan_limit('ci_project_subscriptions', 'bronze', 0)
create_or_update_plan_limit('ci_project_subscriptions', 'silver', 0)
create_or_update_plan_limit('ci_project_subscriptions', 'gold', 0)
else
create_or_update_plan_limit('ci_project_subscriptions', 'default', 0)
end
end
end
......@@ -3131,6 +3131,7 @@ ActiveRecord::Schema.define(version: 2020_03_04_160823) do
t.integer "ci_active_jobs", default: 0, null: false
t.integer "project_hooks", default: 0, null: false
t.integer "group_hooks", default: 0, null: false
t.integer "ci_project_subscriptions", default: 0, null: false
t.index ["plan_id"], name: "index_plan_limits_on_plan_id", unique: true
end
......
......@@ -87,6 +87,28 @@ Plan.default.limits.update!(ci_active_jobs: 500)
NOTE: **Note:** Set the limit to `0` to disable it.
### Number of CI/CD subscriptions to a project
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/9045) in GitLab 12.9.
The total number of subscriptions can be limited per project. This limit is
checked each time a new subscription is created.
If a new subscription would cause the total number of subscription to exceed the
limit, the subscription will be considered invalid.
- On GitLab.com different [limits are defined per plan](../user/gitlab_com/index.md#gitlab-cicd) and they affect all projects under that plan.
- On [GitLab Starter](https://about.gitlab.com/pricing/#self-managed) tier or higher self-hosted installations, this limit is defined for the `default` plan that affects all projects.
To set this limit on a self-hosted installation, run the following in the
[GitLab Rails console](https://docs.gitlab.com/omnibus/maintenance/#starting-a-rails-console-session):
```ruby
Plan.default.limits.update!(ci_project_subscriptions: 500)
```
NOTE: **Note:** Set the limit to `0` to disable it.
## Environment data on Deploy Boards
[Deploy Boards](../user/project/deploy_boards.md) load information from Kubernetes about
......
......@@ -39,6 +39,12 @@ limit values. It's recommended to create separate migration script files.
create_or_update_plan_limit('project_hooks', 'gold', 100)
```
NOTE: **Note:** Some plans exist only on GitLab.com. You can check if the
migration is running on GitLab.com with `Gitlab.com?`.
NOTE: **Note:** The test environment doesn't have any plans. You can check if a
migration is running in a test environment with `Rails.env.test?`
### Plan limits validation
#### Get current limit
......@@ -93,3 +99,20 @@ it_behaves_like 'includes Limitable concern' do
subject { build(:project_hook, project: create(:project)) }
end
```
### Subscription Plans
Self-hosted:
- `default` - Everyone
Hosted:
- `free` - Everyone
- `bronze`- Namespaces with a Bronze subscription
- `silver` - Namespaces with a Silver subscription
- `gold` - Namespaces with a Gold subscription
NOTE: **Note:** Hosted plans exist only on GitLab.com.
NOTE: **Note:** The test environment doesn't have any plans.
......@@ -6,7 +6,6 @@ class Projects::SubscriptionsController < Projects::ApplicationController
before_action :authorize_admin_project!
before_action :feature_ci_project_subscriptions!
before_action :authorize_upstream_project!, only: [:create]
before_action :check_subscription_count!, only: [:create]
def create
subscription = project.upstream_project_subscriptions.create(upstream_project: upstream_project)
......@@ -14,7 +13,7 @@ class Projects::SubscriptionsController < Projects::ApplicationController
if subscription.persisted?
flash[:notice] = _('Subscription successfully created.')
else
flash[:alert] = _('Subscription creation failed because the specified project is not public.')
flash[:alert] = subscription.errors.full_messages
end
redirect_to project_settings_ci_cd_path(project)
......@@ -52,11 +51,4 @@ class Projects::SubscriptionsController < Projects::ApplicationController
flash[:warning] = _('This project path either does not exist or you do not have access.')
redirect_to project_settings_ci_cd_path(project)
end
def check_subscription_count!
return if project.upstream_project_subscriptions.count < 2
flash[:warning] = _('Subscription limit reached.')
redirect_to project_settings_ci_cd_path(project)
end
end
......@@ -3,8 +3,13 @@
module Ci
module Subscriptions
class Project < ApplicationRecord
include ::Limitable
self.table_name = "ci_subscriptions_projects"
self.limit_name = 'ci_project_subscriptions'
self.limit_scope = :upstream_project
belongs_to :downstream_project, class_name: '::Project', optional: false
belongs_to :upstream_project, class_name: '::Project', optional: false
......
......@@ -12,7 +12,7 @@
- default_branch_docs = link_to(_("default branch"), help_page_path('user/project/repository/branches', anchor: 'default-branch'))
= _("A subscription will trigger a new pipeline on the default branch of this project when a pipeline successfully completes for a new tag on the %{default_branch_docs} of the subscribed project.").html_safe % { default_branch_docs: default_branch_docs }
%p
= _("There is a limit of 2 subscriptions from or to a project.")
= _("There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project.").html_safe % { ci_project_subscriptions_limit: @project.actual_limits.ci_project_subscriptions }
.settings-content
= render 'projects/settings/subscriptions/index'
......@@ -16,6 +16,11 @@ describe Projects::SubscriptionsController do
let(:upstream_project) { create(:project, :public) }
before do
plan_limits = create(:plan_limits, :default_plan)
plan_limits.update(ci_project_subscriptions: 2)
end
context 'when user is authorized' do
before do
project.add_maintainer(user)
......@@ -50,19 +55,19 @@ describe Projects::SubscriptionsController do
end
end
context 'when subscription count is on the limit' do
context 'when subscription count is above the limit' do
before do
create_list(:ci_subscriptions_project, 2, downstream_project: project)
create_list(:ci_subscriptions_project, 2, upstream_project: upstream_project)
end
it 'does not create a new subscription' do
expect { post_create }.not_to change { project.upstream_project_subscriptions.count }.from(2)
expect { post_create }.not_to change { project.upstream_project_subscriptions.count }.from(0)
end
it 'sets the flash' do
post_create
expect(response).to set_flash[:warning].to('Subscription limit reached.')
expect(response).to set_flash[:alert].to(['Maximum number of ci project subscriptions (2) exceeded'])
end
it 'redirects to ci_cd settings' do
......@@ -85,7 +90,7 @@ describe Projects::SubscriptionsController do
it 'sets the flash' do
post_create
expect(response).to set_flash[:alert].to('Subscription creation failed because the specified project is not public.')
expect(response).to set_flash[:alert].to(['Upstream project needs to be public'])
end
it 'redirects to ci_cd settings' do
......
......@@ -3,18 +3,24 @@
require 'spec_helper'
describe Ci::Subscriptions::Project do
let!(:subscription) { create(:ci_subscriptions_project) }
let(:upstream_project) { create(:project, :public) }
describe 'Relations' do
it { is_expected.to belong_to(:downstream_project).required }
it { is_expected.to belong_to(:upstream_project).required }
end
it_behaves_like 'includes Limitable concern' do
subject { build(:ci_subscriptions_project, upstream_project: upstream_project) }
end
describe 'Validations' do
let!(:subscription) { create(:ci_subscriptions_project, upstream_project: upstream_project) }
it { is_expected.to validate_uniqueness_of(:upstream_project_id).scoped_to(:downstream_project_id) }
it 'validates that upstream project is public' do
subscription.upstream_project.update(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
upstream_project.update(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
expect(subscription).not_to be_valid
end
......
......@@ -7,7 +7,7 @@ RSpec.shared_examples 'includes Limitable concern' do
it { is_expected.to be_a(Limitable) }
context 'without plan limits configured' do
it 'can create new group hooks' do
it 'can create new models' do
expect { subject.save }.to change { described_class.count }
end
end
......
......@@ -18810,15 +18810,9 @@ msgstr ""
msgid "Subscription"
msgstr ""
msgid "Subscription creation failed because the specified project is not public."
msgstr ""
msgid "Subscription deletion failed."
msgstr ""
msgid "Subscription limit reached."
msgstr ""
msgid "Subscription successfully applied to \"%{group_name}\""
msgstr ""
......@@ -19738,7 +19732,7 @@ msgstr ""
msgid "There are no unstaged changes"
msgstr ""
msgid "There is a limit of 2 subscriptions from or to a project."
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr ""
msgid "There is already a repository with that name on disk"
......
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