Commit d473995d authored by Kamil Trzciński's avatar Kamil Trzciński Committed by Heinrich Lee Yu

Create `default` and `free` plans

This introduces a persistent `default`
and `free` plans into database.

The `default` is used by all on-premise
installations. The `free` is created for
GitLab.com.
parent 8d6a4c1f
---
title: Create explicit Default and Free plans
merge_request: 19033
author:
type: other
# frozen_string_literal: true
class AddDefaultAndFreePlans < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
class Plan < ApplicationRecord
end
def up
plan_names.each do |plan_name|
Plan.create_with(title: plan_name.titleize).find_or_create_by(name: plan_name)
end
end
def down
Plan.where(name: plan_names).delete_all
end
private
def plan_names
[
('free' if Gitlab.com?),
'default'
].compact
end
end
......@@ -224,7 +224,7 @@ module EE
# for billing purposes.
override :billable_members_count
def billable_members_count(requested_hosted_plan = nil)
if [actual_plan_name, requested_hosted_plan].include?(Namespace::GOLD_PLAN)
if [actual_plan_name, requested_hosted_plan].include?(Plan::GOLD)
users_with_descendants.excluding_guests.count
else
users_with_descendants.count
......
......@@ -10,22 +10,15 @@ module EE
extend ::Gitlab::Utils::Override
include ::Gitlab::Utils::StrongMemoize
FREE_PLAN = 'free'.freeze
BRONZE_PLAN = 'bronze'.freeze
SILVER_PLAN = 'silver'.freeze
GOLD_PLAN = 'gold'.freeze
EARLY_ADOPTER_PLAN = 'early_adopter'.freeze
NAMESPACE_PLANS_TO_LICENSE_PLANS = {
BRONZE_PLAN => License::STARTER_PLAN,
SILVER_PLAN => License::PREMIUM_PLAN,
GOLD_PLAN => License::ULTIMATE_PLAN,
EARLY_ADOPTER_PLAN => License::EARLY_ADOPTER_PLAN
Plan::BRONZE => License::STARTER_PLAN,
Plan::SILVER => License::PREMIUM_PLAN,
Plan::GOLD => License::ULTIMATE_PLAN,
Plan::EARLY_ADOPTER => License::EARLY_ADOPTER_PLAN
}.freeze
LICENSE_PLANS_TO_NAMESPACE_PLANS = NAMESPACE_PLANS_TO_LICENSE_PLANS.invert.freeze
PLANS = NAMESPACE_PLANS_TO_LICENSE_PLANS.keys.freeze
PLANS = (NAMESPACE_PLANS_TO_LICENSE_PLANS.keys + [Plan::FREE]).freeze
CI_USAGE_ALERT_LEVELS = [30, 5].freeze
......@@ -148,13 +141,15 @@ module EE
end
def actual_plan
subscription = find_or_create_subscription
strong_memoize(:actual_plan) do
subscription = find_or_create_subscription
subscription&.hosted_plan
subscription&.hosted_plan || Plan.free || Plan.default
end
end
def actual_plan_name
actual_plan&.name || FREE_PLAN
actual_plan&.name || Plan::FREE
end
def actual_size_limit
......@@ -252,7 +247,7 @@ module EE
::Gitlab.com? &&
parent_id.nil? &&
trial_ends_on.blank? &&
[EARLY_ADOPTER_PLAN, FREE_PLAN].include?(actual_plan_name)
[Plan::EARLY_ADOPTER, Plan::FREE].include?(actual_plan_name)
end
def trial_active?
......@@ -266,7 +261,7 @@ module EE
def trial_expired?
trial_ends_on.present? &&
trial_ends_on < Date.today &&
actual_plan_name == FREE_PLAN
actual_plan_name == Plan::FREE
end
# A namespace may not have a file template project
......@@ -286,23 +281,23 @@ module EE
end
def free_plan?
actual_plan_name == FREE_PLAN
actual_plan_name == Plan::FREE
end
def early_adopter_plan?
actual_plan_name == EARLY_ADOPTER_PLAN
actual_plan_name == Plan::EARLY_ADOPTER
end
def bronze_plan?
actual_plan_name == BRONZE_PLAN
actual_plan_name == Plan::BRONZE
end
def silver_plan?
actual_plan_name == SILVER_PLAN
actual_plan_name == Plan::SILVER
end
def gold_plan?
actual_plan_name == GOLD_PLAN
actual_plan_name == Plan::GOLD
end
def use_elasticsearch?
......
......@@ -49,7 +49,7 @@ class GitlabSubscription < ApplicationRecord
end
def plan_code=(code)
code ||= Namespace::FREE_PLAN
code ||= Plan::FREE
self.hosted_plan = Plan.find_by(name: code)
end
......
# frozen_string_literal: true
class Plan < ApplicationRecord
DEFAULT = 'default'.freeze
FREE = 'free'.freeze
BRONZE = 'bronze'.freeze
SILVER = 'silver'.freeze
GOLD = 'gold'.freeze
EARLY_ADOPTER = 'early_adopter'.freeze
# This constant must keep ordered by tier.
PAID_HOSTED_PLANS = %w[bronze silver gold].freeze
ALL_HOSTED_PLANS = (PAID_HOSTED_PLANS + ['early_adopter']).freeze
PAID_HOSTED_PLANS = [BRONZE, SILVER, GOLD].freeze
DEFAULT_PLANS = [DEFAULT, FREE].freeze
ALL_HOSTED_PLANS = (PAID_HOSTED_PLANS + [EARLY_ADOPTER]).freeze
has_many :namespaces
has_many :hosted_subscriptions, class_name: 'GitlabSubscription', foreign_key: 'hosted_plan_id'
def self.default
Gitlab::SafeRequestStore[:plan_default] ||= find_by(name: DEFAULT)
end
def self.free
return unless Gitlab.com?
Gitlab::SafeRequestStore[:plan_free] ||= find_by(name: FREE)
end
def self.hosted_plans_for_namespaces(namespaces)
namespaces = Array(namespaces)
......@@ -17,4 +35,12 @@ class Plan < ApplicationRecord
.where(gitlab_subscriptions: { namespace_id: namespaces })
.distinct
end
def default?
DEFAULT_PLANS.include?(name)
end
def paid?
!default?
end
end
- namespace = local_assigns.fetch(:namespace)
- return unless Gitlab::CurrentSettings.should_check_namespace_plan? && namespace.actual_plan.present?
- return unless Gitlab::CurrentSettings.should_check_namespace_plan? && namespace.actual_plan&.paid?
%span.plan-badge.has-tooltip{ data: { plan: namespace.actual_plan.name }, title: "#{namespace.actual_plan.title} Plan" }
= custom_icon('icon_premium')
......@@ -3,7 +3,7 @@
%li
%span.light Plan:
- if namespace.actual_plan.present?
- if namespace.actual_plan&.paid?
%strong.plan-badge.inline{ data: { plan: namespace.actual_plan.name } }
= custom_icon('icon_premium')
= namespace.actual_plan.title
......
......@@ -10,6 +10,6 @@
- plans_data.each do |plan|
= render 'shared/billings/billing_plan', namespace: namespace, plan: plan, current_plan: current_plan
- if namespace.actual_plan
- if namespace.actual_plan&.paid?
.center
&= s_('BillingPlans|If you would like to downgrade your plan please contact %{support_link_start}Customer Support%{support_link_end}.').html_safe % { support_link_start: support_link_start, support_link_end: support_link_end }
# frozen_string_literal: true
Gitlab::Seeder.quiet do
Plan.create!(name: Plan::DEFAULT, title: Plan::DEFAULT.titleize)
Plan.create!(name: Plan::FREE, title: Plan::FREE.titleize) if Gitlab.com?
end
......@@ -3,8 +3,8 @@
# EE-only
FactoryBot.define do
factory :plan do
factory :free_plan do
name { EE::Namespace::FREE_PLAN }
factory :default_plan do
name { Plan::DEFAULT }
title { name.titleize }
end
......
......@@ -219,7 +219,7 @@ describe EE::UserCalloutsHelper do
describe '#render_billings_gold_trial' do
using RSpec::Parameterized::TableSyntax
set(:namespace) { create(:namespace) }
let(:namespace) { create(:namespace) }
set(:free_plan) { create(:free_plan) }
set(:silver_plan) { create(:silver_plan) }
set(:gold_plan) { create(:gold_plan) }
......
......@@ -3,10 +3,8 @@
require 'spec_helper'
describe EE::Gitlab::Ci::Pipeline::Quota::JobActivity do
set(:namespace) { create(:namespace) }
set(:project) { create(:project, namespace: namespace) }
let(:active_jobs_limit) { 0 }
let_it_be(:namespace, refind: true) { create(:namespace) }
let_it_be(:project, refind: true) { create(:project, namespace: namespace) }
let(:gold_plan) { create(:gold_plan, active_jobs_limit: active_jobs_limit) }
let(:limit) { described_class.new(namespace, project) }
......@@ -25,6 +23,8 @@ describe EE::Gitlab::Ci::Pipeline::Quota::JobActivity do
end
context 'when limit is not enabled' do
let(:active_jobs_limit) { 0 }
it 'is not enabled' do
expect(limit).not_to be_enabled
end
......
......@@ -6,7 +6,8 @@ describe Namespace do
include EE::GeoHelpers
let!(:namespace) { create(:namespace) }
let!(:free_plan) { create(:free_plan) }
let(:default_plan) { create(:default_plan) }
let(:free_plan) { create(:free_plan) }
let!(:bronze_plan) { create(:bronze_plan) }
let!(:silver_plan) { create(:silver_plan) }
let!(:gold_plan) { create(:gold_plan) }
......@@ -676,10 +677,32 @@ describe Namespace do
end
context 'when namespace does not have a subscription associated' do
it 'generates a subscription with the Free plan' do
expect(namespace.actual_plan).to eq(free_plan)
it 'generates a subscription without a plan' do
expect(namespace.actual_plan).to be_nil
expect(namespace.gitlab_subscription).to be_present
end
context 'when free plan does exist' do
before do
free_plan
end
it 'generates a subscription' do
expect(namespace.actual_plan).to eq(free_plan)
expect(namespace.gitlab_subscription).to be_present
end
end
context 'when default plan does exist' do
before do
default_plan
end
it 'generates a subscription' do
expect(namespace.actual_plan).to eq(default_plan)
expect(namespace.gitlab_subscription).to be_present
end
end
end
end
......
......@@ -6,4 +6,44 @@ describe Plan do
describe 'associations' do
it { is_expected.to have_many(:namespaces) }
end
describe '#default?' do
subject { plan.default? }
Plan::DEFAULT_PLANS.each do |plan|
context "when '#{plan}'" do
let(:plan) { build("#{plan}_plan".to_sym) }
it { is_expected.to be_truthy }
end
end
Plan::PAID_HOSTED_PLANS.each do |plan|
context "when '#{plan}'" do
let(:plan) { build("#{plan}_plan".to_sym) }
it { is_expected.to be_falsey }
end
end
end
describe '#paid?' do
subject { plan.paid? }
Plan::DEFAULT_PLANS.each do |plan|
context "when '#{plan}'" do
let(:plan) { build("#{plan}_plan".to_sym) }
it { is_expected.to be_falsey }
end
end
Plan::PAID_HOSTED_PLANS.each do |plan|
context "when '#{plan}'" do
let(:plan) { build("#{plan}_plan".to_sym) }
it { is_expected.to be_truthy }
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'migrate', '20191023152913_add_default_and_free_plans.rb')
describe AddDefaultAndFreePlans, :migration do
describe 'migrate' do
let(:plans) { table(:plans) }
context 'when on Gitlab.com' do
before do
expect(Gitlab).to receive(:com?) { true }
end
it 'creates free and default plans' do
expect { migrate! }.to change { plans.count }.by 2
expect(plans.last(2).pluck(:name)).to eq %w[free default]
end
end
context 'when on self-hosted' do
before do
expect(Gitlab).to receive(:com?) { false }
end
it 'creates only a default plan' do
expect { migrate! }.to change { plans.count }.by 1
expect(plans.last.name).to eq 'default'
end
end
end
end
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