Commit 3df3912b authored by James Fargher's avatar James Fargher

Merge branch 'extract-ci-minutes-limit-class' into 'master'

Extract CI::Minutes::Limit class

See merge request gitlab-org/gitlab!73344
parents b378ddef 4fc752f8
# frozen_string_literal: true
# This class is responsible for dealing with the CI minutes limits set at root namespace level.
module Ci
module Minutes
class Limit
include Gitlab::Utils::StrongMemoize
def initialize(namespace)
@namespace = namespace
end
def enabled?
namespace.root? && !unlimited?
end
def total
monthly + purchased
end
def monthly
strong_memoize(:monthly) do
(namespace.shared_runners_minutes_limit || ::Gitlab::CurrentSettings.shared_runners_minutes).to_i
end
end
def purchased
strong_memoize(:purchased) do
namespace.extra_shared_runners_minutes_limit.to_i
end
end
def any_purchased?
purchased > 0
end
private
attr_reader :namespace
def unlimited?
total == 0
end
end
end
end
...@@ -9,34 +9,29 @@ module Ci ...@@ -9,34 +9,29 @@ module Ci
class Quota class Quota
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
attr_reader :namespace attr_reader :namespace, :limit
def initialize(namespace) def initialize(namespace)
@namespace = namespace @namespace = namespace
@limit = ::Ci::Minutes::Limit.new(namespace)
end end
def enabled? def enabled?
namespace_root? && !namespace_unlimited_minutes? limit.enabled?
end end
def minutes_used_up? def minutes_used_up?
enabled? && total_minutes_used >= total_minutes enabled? && total_minutes_used >= limit.total
end end
def percent_total_minutes_remaining def percent_total_minutes_remaining
return 0 if total_minutes == 0 return 0 unless limit.enabled?
100 * total_minutes_remaining.to_i / total_minutes 100 * total_minutes_remaining.to_i / limit.total
end end
def current_balance def current_balance
total_minutes.to_i - total_minutes_used limit.total - total_minutes_used
end
def total_minutes
strong_memoize(:total_minutes) do
monthly_minutes + purchased_minutes
end
end end
def total_minutes_used def total_minutes_used
...@@ -59,33 +54,11 @@ module Ci ...@@ -59,33 +54,11 @@ module Ci
end end
end end
def purchased_minutes
strong_memoize(:purchased_minutes) do
namespace.extra_shared_runners_minutes_limit.to_i
end
end
def namespace_root?
strong_memoize(:namespace_root) do
namespace.root?
end
end
def namespace_unlimited_minutes?
total_minutes.to_i == 0
end
def monthly_minutes
strong_memoize(:monthly_minutes) do
(namespace.shared_runners_minutes_limit || ::Gitlab::CurrentSettings.shared_runners_minutes).to_i
end
end
# === private to view === # === private to view ===
def monthly_minutes_used_up? def monthly_minutes_used_up?
return false unless enabled? return false unless enabled?
monthly_minutes_used >= monthly_minutes monthly_minutes_used >= limit.monthly
end end
def monthly_minutes_used def monthly_minutes_used
...@@ -95,13 +68,13 @@ module Ci ...@@ -95,13 +68,13 @@ module Ci
def purchased_minutes_used_up? def purchased_minutes_used_up?
return false unless enabled? return false unless enabled?
any_minutes_purchased? && purchased_minutes_used >= purchased_minutes limit.any_purchased? && purchased_minutes_used >= limit.purchased
end end
def purchased_minutes_used def purchased_minutes_used
return 0 if no_minutes_purchased? || monthly_minutes_available? return 0 if !limit.any_purchased? || monthly_minutes_available?
total_minutes_used - monthly_minutes total_minutes_used - limit.monthly
end end
private private
...@@ -111,15 +84,7 @@ module Ci ...@@ -111,15 +84,7 @@ module Ci
end end
def monthly_minutes_available? def monthly_minutes_available?
total_minutes_used <= monthly_minutes total_minutes_used <= limit.monthly
end
def no_minutes_purchased?
purchased_minutes == 0
end
def any_minutes_purchased?
purchased_minutes > 0
end end
def total_minutes_remaining def total_minutes_remaining
......
...@@ -16,30 +16,30 @@ module Ci ...@@ -16,30 +16,30 @@ module Ci
def monthly_percent_used def monthly_percent_used
return 0 unless quota.enabled? return 0 unless quota.enabled?
return 0 if quota.monthly_minutes == 0 return 0 if quota.limit.monthly == 0
100 * quota.monthly_minutes_used.to_i / quota.monthly_minutes 100 * quota.monthly_minutes_used.to_i / quota.limit.monthly
end end
# Status of any purchased minutes used. # Status of any purchased minutes used.
def purchased_minutes_report def purchased_minutes_report
status = quota.purchased_minutes_used_up? ? :over_quota : :under_quota status = quota.purchased_minutes_used_up? ? :over_quota : :under_quota
Report.new(quota.purchased_minutes_used, quota.purchased_minutes, status) Report.new(quota.purchased_minutes_used, quota.limit.purchased, status)
end end
def purchased_percent_used def purchased_percent_used
return 0 unless quota.enabled? return 0 unless quota.enabled?
return 0 if quota.purchased_minutes == 0 return 0 unless quota.limit.any_purchased?
100 * quota.purchased_minutes_used.to_i / quota.purchased_minutes 100 * quota.purchased_minutes_used.to_i / quota.limit.purchased
end end
def display_minutes_available_data? def display_minutes_available_data?
display_shared_runners_data? && !quota.namespace_unlimited_minutes? display_shared_runners_data? && quota.limit.enabled?
end end
def display_shared_runners_data? def display_shared_runners_data?
quota.namespace_root? && any_project_enabled? quota.namespace.root? && any_project_enabled?
end end
def any_project_enabled? def any_project_enabled?
...@@ -60,7 +60,7 @@ module Ci ...@@ -60,7 +60,7 @@ module Ci
return _('Not supported') unless display_shared_runners_data? return _('Not supported') unless display_shared_runners_data?
if display_minutes_available_data? if display_minutes_available_data?
quota.monthly_minutes quota.limit.monthly
else else
_('Unlimited') _('Unlimited')
end end
......
...@@ -12,7 +12,7 @@ module EE ...@@ -12,7 +12,7 @@ module EE
end end
expose :limit do |runner| expose :limit do |runner|
project.ci_minutes_quota.total_minutes project.ci_minutes_quota.limit.total
end end
end end
end end
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
- if project.shared_runners_enabled? - if project.shared_runners_enabled?
Enabled Enabled
- if project.shared_runners_minutes_limit_enabled? - if project.shared_runners_minutes_limit_enabled?
- limit = project.ci_minutes_quota.total_minutes - limit = project.ci_minutes_quota.limit.total
(Limited to #{limit} pipeline minutes per month) (Limited to #{limit} pipeline minutes per month)
- else - else
(Unlimited pipeline minutes) (Unlimited pipeline minutes)
......
...@@ -49,12 +49,12 @@ RSpec.describe EE::NamespacesHelper do ...@@ -49,12 +49,12 @@ RSpec.describe EE::NamespacesHelper do
context "when ci minutes quota is not enabled" do context "when ci minutes quota is not enabled" do
before do before do
allow(quota).to receive(:namespace_unlimited_minutes?).and_return(true) user_group.update!(shared_runners_minutes_limit: 0)
end end
context 'and the namespace is eligible for unlimited' do context 'and the namespace is eligible for unlimited' do
before do before do
allow(quota).to receive(:namespace_root?).and_return(true) allow(user_group).to receive(:root?).and_return(true)
allow(user_group).to receive(:any_project_with_shared_runners_enabled?).and_return(true) allow(user_group).to receive(:any_project_with_shared_runners_enabled?).and_return(true)
end end
...@@ -65,7 +65,7 @@ RSpec.describe EE::NamespacesHelper do ...@@ -65,7 +65,7 @@ RSpec.describe EE::NamespacesHelper do
context 'and the namespace is not eligible for unlimited' do context 'and the namespace is not eligible for unlimited' do
before do before do
allow(quota).to receive(:namespace_root?).and_return(false) allow(user_group).to receive(:root?).and_return(false)
end end
it 'returns Not supported for the limit section' do it 'returns Not supported for the limit section' do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::Minutes::Limit do
using RSpec::Parameterized::TableSyntax
let_it_be_with_reload(:namespace) { create(:namespace) }
let_it_be_with_reload(:another_group) { create(:group) }
let(:limit) { described_class.new(namespace) }
let(:namespace_monthly_limit) { 400 }
let(:application_monthly_limit) { 400 }
let(:purchased_minutes) { 0 }
before do
namespace.shared_runners_minutes_limit = namespace_monthly_limit
namespace.extra_shared_runners_minutes_limit = purchased_minutes
allow(::Gitlab::CurrentSettings).to receive(:shared_runners_minutes).and_return(application_monthly_limit)
end
describe '#enabled?' do
subject { limit.enabled? }
where(:namespace_monthly_limit, :application_monthly_limit, :purchased_minutes, :result) do
0 | 100 | 0 | false
0 | 100 | 10 | true
nil | 100 | 10 | true
nil | 100 | 0 | true
20 | 100 | 0 | true
nil | nil | 0 | false
nil | 0 | 0 | false
end
with_them do
it { is_expected.to eq(result) }
context 'when namespace is not root' do
before do
namespace.parent = another_group
end
it { is_expected.to eq(false) }
end
end
end
describe '#total' do
subject { limit.total }
where(:namespace_monthly_limit, :application_monthly_limit, :purchased_minutes, :result) do
20 | 100 | 30 | 50
nil | 100 | 30 | 130
20 | 100 | 0 | 20
0 | 0 | 30 | 30
nil | 0 | 30 | 30
end
with_them do
it { is_expected.to eq(result) }
end
end
describe '#monthly' do
subject { limit.monthly}
where(:namespace_monthly_limit, :application_monthly_limit, :result) do
20 | 100 | 20
nil | 100 | 100
100 | nil | 100
0 | 100 | 0
nil | nil | 0
end
with_them do
it { is_expected.to eq(result) }
end
end
describe '#purchased and #any_purchased?' do
where(:purchased_minutes, :purchased, :any_purchased) do
nil | 0 | false
0 | 0 | false
10 | 10 | true
end
with_them do
it do
expect(limit.purchased).to eq(purchased)
expect(limit.any_purchased?).to eq(any_purchased)
end
end
end
end
...@@ -91,28 +91,6 @@ RSpec.describe Ci::Minutes::Quota do ...@@ -91,28 +91,6 @@ RSpec.describe Ci::Minutes::Quota do
end end
end end
describe '#total_minutes' do
subject { quota.total_minutes }
where(:namespace_monthly_limit, :application_monthly_limit, :purchased_minutes, :result) do
20 | 100 | 30 | 50
nil | 100 | 30 | 130
20 | 100 | 0 | 20
0 | 0 | 30 | 30
nil | 0 | 30 | 30
end
with_them do
before do
namespace.shared_runners_minutes_limit = namespace_monthly_limit
allow(::Gitlab::CurrentSettings).to receive(:shared_runners_minutes).and_return(application_monthly_limit)
allow(namespace).to receive(:extra_shared_runners_minutes_limit).and_return(purchased_minutes)
end
it { is_expected.to eq(result) }
end
end
describe '#total_minutes_used' do describe '#total_minutes_used' do
subject { quota.total_minutes_used } subject { quota.total_minutes_used }
......
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