Commit 6f09c2fa authored by Marius Bobin's avatar Marius Bobin

Merge branch 'add-open-source-cost-factor' into 'master'

Add subsidized cost factor for Open Source plan

See merge request gitlab-org/gitlab!77882
parents 19a7edd1 f71cc952
...@@ -454,7 +454,7 @@ class Project < ApplicationRecord ...@@ -454,7 +454,7 @@ class Project < ApplicationRecord
delegate :job_token_scope_enabled, :job_token_scope_enabled=, to: :ci_cd_settings, prefix: :ci, allow_nil: true delegate :job_token_scope_enabled, :job_token_scope_enabled=, to: :ci_cd_settings, prefix: :ci, allow_nil: true
delegate :keep_latest_artifact, :keep_latest_artifact=, to: :ci_cd_settings, allow_nil: true delegate :keep_latest_artifact, :keep_latest_artifact=, to: :ci_cd_settings, allow_nil: true
delegate :restrict_user_defined_variables, :restrict_user_defined_variables=, to: :ci_cd_settings, allow_nil: true delegate :restrict_user_defined_variables, :restrict_user_defined_variables=, to: :ci_cd_settings, allow_nil: true
delegate :actual_limits, :actual_plan_name, to: :namespace, allow_nil: true delegate :actual_limits, :actual_plan_name, :actual_plan, to: :namespace, allow_nil: true
delegate :allow_merge_on_skipped_pipeline, :allow_merge_on_skipped_pipeline?, delegate :allow_merge_on_skipped_pipeline, :allow_merge_on_skipped_pipeline?,
:allow_merge_on_skipped_pipeline=, :has_confluence?, :has_shimo?, :allow_merge_on_skipped_pipeline=, :has_confluence?, :has_shimo?,
to: :project_setting to: :project_setting
......
...@@ -180,6 +180,8 @@ The cost factor for a job running on a shared runner is: ...@@ -180,6 +180,8 @@ The cost factor for a job running on a shared runner is:
- `0.008` for public projects on GitLab SaaS, if [created 2021-07-17 or later](https://gitlab.com/gitlab-org/gitlab/-/issues/332708). - `0.008` for public projects on GitLab SaaS, if [created 2021-07-17 or later](https://gitlab.com/gitlab-org/gitlab/-/issues/332708).
(For every 125 minutes of job time, you accrue 1 CD/CD minute.) (For every 125 minutes of job time, you accrue 1 CD/CD minute.)
- `0.008` for projects members of GitLab [Open Source program](../../subscriptions/index.md#gitlab-for-open-source).
(For every 125 minutes of job time, you accrue 1 CD/CD minute.)
- `0` for public projects on GitLab self-managed instances, and for GitLab SaaS public projects created before 2021-07-17. - `0` for public projects on GitLab self-managed instances, and for GitLab SaaS public projects created before 2021-07-17.
- `1` for internal and private projects. - `1` for internal and private projects.
......
...@@ -14,9 +14,10 @@ module EE ...@@ -14,9 +14,10 @@ module EE
ULTIMATE = 'ultimate' ULTIMATE = 'ultimate'
ULTIMATE_TRIAL = 'ultimate_trial' ULTIMATE_TRIAL = 'ultimate_trial'
PREMIUM_TRIAL = 'premium_trial' PREMIUM_TRIAL = 'premium_trial'
OPEN_SOURCE = 'opensource'
EE_DEFAULT_PLANS = (const_get(:DEFAULT_PLANS, false) + [FREE]).freeze EE_DEFAULT_PLANS = (const_get(:DEFAULT_PLANS, false) + [FREE]).freeze
PAID_HOSTED_PLANS = [BRONZE, SILVER, PREMIUM, GOLD, ULTIMATE, ULTIMATE_TRIAL, PREMIUM_TRIAL].freeze PAID_HOSTED_PLANS = [BRONZE, SILVER, PREMIUM, GOLD, ULTIMATE, ULTIMATE_TRIAL, PREMIUM_TRIAL, OPEN_SOURCE].freeze
EE_ALL_PLANS = (EE_DEFAULT_PLANS + PAID_HOSTED_PLANS).freeze EE_ALL_PLANS = (EE_DEFAULT_PLANS + PAID_HOSTED_PLANS).freeze
PLANS_ELIGIBLE_FOR_TRIAL = EE_DEFAULT_PLANS PLANS_ELIGIBLE_FOR_TRIAL = EE_DEFAULT_PLANS
...@@ -64,5 +65,9 @@ module EE ...@@ -64,5 +65,9 @@ module EE
def paid? def paid?
PAID_HOSTED_PLANS.include?(name) PAID_HOSTED_PLANS.include?(name)
end end
def open_source?
name == OPEN_SOURCE
end
end end
end end
...@@ -4,7 +4,10 @@ module Gitlab ...@@ -4,7 +4,10 @@ module Gitlab
module Ci module Ci
module Minutes module Minutes
class CostFactor class CostFactor
NEW_NAMESPACE_PUBLIC_PROJECT_COST_FACTOR = 0.008 DISABLED = 0.0
STANDARD = 1.0
OPEN_SOURCE = 0.008
NEW_NAMESPACE_PUBLIC_PROJECT = 0.008
def initialize(runner_matcher) def initialize(runner_matcher)
ensure_runner_matcher_instance(runner_matcher) ensure_runner_matcher_instance(runner_matcher)
...@@ -21,15 +24,20 @@ module Gitlab ...@@ -21,15 +24,20 @@ module Gitlab
end end
def for_project(project) def for_project(project)
return 0.0 unless @runner_matcher.instance_type? return DISABLED unless @runner_matcher.instance_type?
return 0.0 unless project.ci_minutes_quota.enabled? return DISABLED unless project.ci_minutes_quota.enabled?
cost_factor = for_visibility(project.visibility_level) runner_cost_factor = for_visibility(project.visibility_level)
if cost_factor == 0 && project.force_cost_factor? if runner_cost_factor == DISABLED && project.force_cost_factor?
NEW_NAMESPACE_PUBLIC_PROJECT_COST_FACTOR # Once visibility level cost factors are consolidated into a single
# cost factor, this condition can be removed.
# https://gitlab.com/gitlab-org/gitlab/-/issues/243722
NEW_NAMESPACE_PUBLIC_PROJECT
elsif runner_cost_factor == STANDARD && project.actual_plan.open_source?
OPEN_SOURCE
else else
cost_factor runner_cost_factor
end end
end end
......
...@@ -75,5 +75,9 @@ FactoryBot.define do ...@@ -75,5 +75,9 @@ FactoryBot.define do
trait :ultimate_trial do trait :ultimate_trial do
association :hosted_plan, factory: :ultimate_trial_plan association :hosted_plan, factory: :ultimate_trial_plan
end end
trait :opensource do
association :hosted_plan, factory: :opensource_plan
end
end end
end end
...@@ -8,6 +8,7 @@ RSpec.describe Gitlab::Ci::Minutes::CostFactor do ...@@ -8,6 +8,7 @@ RSpec.describe Gitlab::Ci::Minutes::CostFactor do
let(:runner_type) {} let(:runner_type) {}
let(:public_cost_factor) {} let(:public_cost_factor) {}
let(:private_cost_factor) {} let(:private_cost_factor) {}
let(:cost_factor) { described_class.new(runner.runner_matcher) }
let(:runner) do let(:runner) do
build_stubbed(:ci_runner, build_stubbed(:ci_runner,
...@@ -27,9 +28,8 @@ RSpec.describe Gitlab::Ci::Minutes::CostFactor do ...@@ -27,9 +28,8 @@ RSpec.describe Gitlab::Ci::Minutes::CostFactor do
describe '#enabled?' do describe '#enabled?' do
let(:project) { build_stubbed(:project) } let(:project) { build_stubbed(:project) }
let(:cost_factor) { described_class.new(runner.runner_matcher) }
subject { cost_factor.enabled?(project) } subject(:is_enabled) { cost_factor.enabled?(project) }
context 'when the cost factor is zero' do context 'when the cost factor is zero' do
before do before do
...@@ -50,9 +50,8 @@ RSpec.describe Gitlab::Ci::Minutes::CostFactor do ...@@ -50,9 +50,8 @@ RSpec.describe Gitlab::Ci::Minutes::CostFactor do
describe '#disabled?' do describe '#disabled?' do
let(:project) { build_stubbed(:project) } let(:project) { build_stubbed(:project) }
let(:cost_factor) { described_class.new(runner.runner_matcher) }
subject { cost_factor.disabled?(project) } subject(:is_disabled) { cost_factor.disabled?(project) }
context 'when the cost factor is zero' do context 'when the cost factor is zero' do
before do before do
...@@ -72,6 +71,8 @@ RSpec.describe Gitlab::Ci::Minutes::CostFactor do ...@@ -72,6 +71,8 @@ RSpec.describe Gitlab::Ci::Minutes::CostFactor do
end end
describe '#for_project' do describe '#for_project' do
subject(:for_project) { cost_factor.for_project(project) }
context 'before the public project cost factor release date' do context 'before the public project cost factor release date' do
where(:runner_type, :visibility_level, :public_cost_factor, :private_cost_factor, :namespace_limit, :instance_limit, :result) do where(:runner_type, :visibility_level, :public_cost_factor, :private_cost_factor, :namespace_limit, :instance_limit, :result) do
:project | Gitlab::VisibilityLevel::PRIVATE | 1 | 1 | nil | 400 | 0 :project | Gitlab::VisibilityLevel::PRIVATE | 1 | 1 | nil | 400 | 0
...@@ -123,13 +124,11 @@ RSpec.describe Gitlab::Ci::Minutes::CostFactor do ...@@ -123,13 +124,11 @@ RSpec.describe Gitlab::Ci::Minutes::CostFactor do
allow(Gitlab::CurrentSettings).to receive(:shared_runners_minutes) { instance_limit } allow(Gitlab::CurrentSettings).to receive(:shared_runners_minutes) { instance_limit }
end end
subject { described_class.new(runner.runner_matcher).for_project(project) }
it { is_expected.to eq(result) } it { is_expected.to eq(result) }
end end
end end
context 'after the public project cost factor release date' do context 'after the public project cost factor release date', :saas do
where(:runner_type, :visibility_level, :public_cost_factor, :private_cost_factor, :namespace_limit, :instance_limit, :result) do where(:runner_type, :visibility_level, :public_cost_factor, :private_cost_factor, :namespace_limit, :instance_limit, :result) do
:project | Gitlab::VisibilityLevel::PRIVATE | 1 | 1 | nil | 400 | 0 :project | Gitlab::VisibilityLevel::PRIVATE | 1 | 1 | nil | 400 | 0
:project | Gitlab::VisibilityLevel::INTERNAL | 1 | 1 | nil | 400 | 0 :project | Gitlab::VisibilityLevel::INTERNAL | 1 | 1 | nil | 400 | 0
...@@ -178,30 +177,68 @@ RSpec.describe Gitlab::Ci::Minutes::CostFactor do ...@@ -178,30 +177,68 @@ RSpec.describe Gitlab::Ci::Minutes::CostFactor do
before do before do
allow(Gitlab::CurrentSettings).to receive(:shared_runners_minutes) { instance_limit } allow(Gitlab::CurrentSettings).to receive(:shared_runners_minutes) { instance_limit }
allow(Gitlab).to receive(:com?).and_return(true)
end end
subject { described_class.new(runner.runner_matcher).for_project(project) }
it { is_expected.to eq(result) } it { is_expected.to eq(result) }
end end
end end
context 'when the project has an invalid visibility level' do context 'plan based cost factor', :saas do
let(:namespace) { nil }
let(:private_cost_factor) { 1 }
let(:public_cost_factor) { 1 }
let(:runner_type) { :instance } let(:runner_type) { :instance }
let(:visibility_level) { 123 } let(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
it 'raises an error' do before do
expect { subject }.to raise_error(ArgumentError) create(:gitlab_subscription, namespace: project.namespace, hosted_plan: plan)
allow(Gitlab::CurrentSettings).to receive(:shared_runners_minutes) { 100 }
end
context 'when project has an Open Source plan' do
let(:plan) { create(:opensource_plan) }
context 'when runner cost factor is standard' do
let(:private_cost_factor) { described_class::STANDARD }
it 'returns a lower cost factor' do
expect(subject).to eq(described_class::OPEN_SOURCE)
expect(subject).to be < private_cost_factor
expect(subject).to be > described_class::DISABLED
end
end
context 'when runner cost factor is custom' do
let(:private_cost_factor) { 2.0 }
it 'returns the runner cost factor' do
expect(subject).to eq(private_cost_factor)
end
end
end
context 'when project does not have an Open Source plan' do
let(:plan) { create(:free_plan) }
context 'when runner cost factor is standard' do
let(:private_cost_factor) { described_class::STANDARD }
it 'returns the runner cost factor' do
expect(subject).to eq(private_cost_factor)
end
end
context 'when runner cost factor is custom' do
let(:private_cost_factor) { 2.0 }
it 'returns the runner cost factor' do
expect(subject).to eq(private_cost_factor)
end
end
end end
end end
end end
describe '#for_visibility' do describe '#for_visibility' do
subject { described_class.new(runner.runner_matcher).for_visibility(visibility_level) } subject(:for_visibility) { described_class.new(runner.runner_matcher).for_visibility(visibility_level) }
where(:runner_type, :visibility_level, :public_cost_factor, :private_cost_factor, :result) do where(:runner_type, :visibility_level, :public_cost_factor, :private_cost_factor, :result) do
:project | Gitlab::VisibilityLevel::PRIVATE | 1 | 1 | 0 :project | Gitlab::VisibilityLevel::PRIVATE | 1 | 1 | 0
......
...@@ -28,4 +28,20 @@ RSpec.describe Plan do ...@@ -28,4 +28,20 @@ RSpec.describe Plan do
it { is_expected.to eq(%w[default free]) } it { is_expected.to eq(%w[default free]) }
end end
describe '#open_source?' do
subject { plan.open_source? }
context 'when is opensource' do
let(:plan) { build(:opensource_plan) }
it { is_expected.to be_truthy }
end
context 'when is not opensource' do
let(:plan) { build(:free_plan) }
it { is_expected.to be_falsey }
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