Commit 75715dc5 authored by Jan Provaznik's avatar Jan Provaznik Committed by Fabio Pitino

Exclude project namespaces on CI minutes reset

parent 7d567806
......@@ -25,9 +25,8 @@ module Ci
@failed_batches = []
end
def execute!(ids_range: nil, batch_size: BATCH_SIZE)
relation = Namespace
relation = relation.id_in(ids_range) if ids_range
def execute!(ids_range:, batch_size: BATCH_SIZE)
relation = Namespace.without_project_namespaces.id_in(ids_range)
relation.each_batch(of: batch_size) do |namespaces|
reset_ci_minutes!(namespaces)
end
......
......@@ -8,13 +8,10 @@ RSpec.describe Ci::Minutes::BatchResetService do
let(:service) { described_class.new }
describe '#execute!' do
let(:ids_range) { nil }
subject { service.execute!(ids_range: ids_range, batch_size: 3) }
def create_namespace_with_project(id, seconds_used, monthly_minutes_limit = nil)
def create_namespace_with_project(seconds_used, monthly_minutes_limit = nil)
namespace = create(:namespace,
id: id,
shared_runners_minutes_limit: monthly_minutes_limit, # when `nil` it inherits the global limit
extra_shared_runners_minutes_limit: 50,
last_ci_minutes_notification_at: Time.current,
......@@ -37,86 +34,58 @@ RSpec.describe Ci::Minutes::BatchResetService do
allow(::Gitlab::CurrentSettings).to receive(:shared_runners_minutes).and_return(2_000)
end
let!(:namespace_1) { create_namespace_with_project(1, 2_020.minutes, nil) }
let!(:namespace_2) { create_namespace_with_project(2, 2_020.minutes, 2_000) }
let!(:namespace_3) { create_namespace_with_project(3, 2_020.minutes, 2_000) }
let!(:namespace_4) { create_namespace_with_project(4, 1_000.minutes, nil) }
let!(:namespace_5) { create_namespace_with_project(5, 1_000.minutes, 2_000) }
let!(:namespace_6) { create_namespace_with_project(6, 1_000.minutes, 0) }
context 'when ID range is provided' do
let(:ids_range) { (1..5) }
let(:namespaces_exceeding_minutes) { [namespace_1, namespace_2, namespace_3] }
let(:namespaces_not_exceeding_minutes) { [namespace_4, namespace_5] }
it 'resets minutes in batches for the given range' do
expect(service).to receive(:reset_ci_minutes!).with([namespace_1, namespace_2, namespace_3])
expect(service).to receive(:reset_ci_minutes!).with([namespace_4, namespace_5])
subject
end
it 'resets CI minutes and recalculates purchased minutes for the namespace exceeding the monthly minutes' do
subject
let_it_be(:project_namespace) { create(:project_namespace) }
let_it_be_with_reload(:namespace_1) { create_namespace_with_project(2_020.minutes, nil) }
let_it_be_with_reload(:namespace_2) { create_namespace_with_project(2_020.minutes, 2_000) }
let_it_be_with_reload(:namespace_3) { create_namespace_with_project(2_020.minutes, 2_000) }
let_it_be_with_reload(:namespace_4) { create_namespace_with_project(1_000.minutes, nil) }
let_it_be_with_reload(:namespace_5) { create_namespace_with_project(1_000.minutes, 2_000) }
let_it_be_with_reload(:namespace_6) { create_namespace_with_project(1_000.minutes, 0) }
namespaces_exceeding_minutes.each do |namespace|
namespace.reset
expect(namespace.extra_shared_runners_minutes_limit).to eq 30
expect(namespace.namespace_statistics.shared_runners_seconds).to eq 0
expect(namespace.namespace_statistics.shared_runners_seconds_last_reset).to be_present
expect(ProjectStatistics.find_by(namespace: namespace).shared_runners_seconds).to eq 0
expect(ProjectStatistics.find_by(namespace: namespace).shared_runners_seconds_last_reset).to be_present
expect(namespace.last_ci_minutes_notification_at).to be_nil
expect(namespace.last_ci_minutes_usage_notification_level).to be_nil
end
end
let(:ids_range) { (project_namespace.id..namespace_5.id) }
let(:namespaces_exceeding_minutes) { [namespace_1, namespace_2, namespace_3] }
let(:namespaces_not_exceeding_minutes) { [namespace_4, namespace_5] }
it 'resets CI minutes but does not recalculate purchased minutes for the namespace not exceeding the monthly minutes' do
subject
it 'resets minutes in batches for the given range and ignores project namespaces' do
expect(service).to receive(:reset_ci_minutes!).with([namespace_1, namespace_2, namespace_3])
expect(service).to receive(:reset_ci_minutes!).with([namespace_4, namespace_5])
namespaces_not_exceeding_minutes.each do |namespace|
namespace.reset
expect(namespace.extra_shared_runners_minutes_limit).to eq 50
expect(namespace.namespace_statistics.shared_runners_seconds).to eq 0
expect(namespace.namespace_statistics.shared_runners_seconds_last_reset).to be_present
expect(ProjectStatistics.find_by(namespace: namespace).shared_runners_seconds).to eq 0
expect(ProjectStatistics.find_by(namespace: namespace).shared_runners_seconds_last_reset).to be_present
expect(namespace.last_ci_minutes_notification_at).to be_nil
expect(namespace.last_ci_minutes_usage_notification_level).to be_nil
end
end
subject
end
context 'when ID range is not provided' do
let(:ids_range) { nil }
it 'resets CI minutes and recalculates purchased minutes for the namespace exceeding the monthly minutes' do
subject
it 'resets minutes in batches for all namespaces' do
expect(service).to receive(:reset_ci_minutes!).with([namespace_1, namespace_2, namespace_3])
expect(service).to receive(:reset_ci_minutes!).with([namespace_4, namespace_5, namespace_6])
namespaces_exceeding_minutes.each do |namespace|
namespace.reset
subject
expect(namespace.extra_shared_runners_minutes_limit).to eq 30
expect(namespace.namespace_statistics.shared_runners_seconds).to eq 0
expect(namespace.namespace_statistics.shared_runners_seconds_last_reset).to be_present
expect(ProjectStatistics.find_by(namespace: namespace).shared_runners_seconds).to eq 0
expect(ProjectStatistics.find_by(namespace: namespace).shared_runners_seconds_last_reset).to be_present
expect(namespace.last_ci_minutes_notification_at).to be_nil
expect(namespace.last_ci_minutes_usage_notification_level).to be_nil
end
end
it 'resets CI minutes and does not recalculate purchased minutes for the namespace having unlimited monthly minutes' do
subject
it 'resets CI minutes but does not recalculate purchased minutes for the namespace not exceeding the monthly minutes' do
subject
namespace_6.reset
namespaces_not_exceeding_minutes.each do |namespace|
namespace.reset
expect(namespace_6.extra_shared_runners_minutes_limit).to eq 50
expect(namespace_6.namespace_statistics.shared_runners_seconds).to eq 0
expect(namespace_6.namespace_statistics.shared_runners_seconds_last_reset).to be_present
expect(ProjectStatistics.find_by(namespace: namespace_6).shared_runners_seconds).to eq 0
expect(ProjectStatistics.find_by(namespace: namespace_6).shared_runners_seconds_last_reset).to be_present
expect(namespace_6.last_ci_minutes_notification_at).to be_nil
expect(namespace_6.last_ci_minutes_usage_notification_level).to be_nil
expect(namespace.extra_shared_runners_minutes_limit).to eq 50
expect(namespace.namespace_statistics.shared_runners_seconds).to eq 0
expect(namespace.namespace_statistics.shared_runners_seconds_last_reset).to be_present
expect(ProjectStatistics.find_by(namespace: namespace).shared_runners_seconds).to eq 0
expect(ProjectStatistics.find_by(namespace: namespace).shared_runners_seconds_last_reset).to be_present
expect(namespace.last_ci_minutes_notification_at).to be_nil
expect(namespace.last_ci_minutes_usage_notification_level).to be_nil
end
end
context 'when an ActiveRecordError is raised' do
let(:ids_range) { nil }
before do
expect(Namespace).to receive(:transaction).once.ordered.and_raise(ActiveRecord::ActiveRecordError, 'something went wrong')
expect(Namespace).to receive(:transaction).once.ordered.and_call_original
......@@ -124,7 +93,7 @@ RSpec.describe Ci::Minutes::BatchResetService do
it 'continues its progress and raises exception at the end' do
expect(service).to receive(:reset_ci_minutes!).with([namespace_1, namespace_2, namespace_3]).and_call_original
expect(service).to receive(:reset_ci_minutes!).with([namespace_4, namespace_5, namespace_6]).and_call_original
expect(service).to receive(:reset_ci_minutes!).with([namespace_4, namespace_5]).and_call_original
expect { subject }
.to raise_error(described_class::BatchNotResetError) do |error|
......@@ -132,8 +101,8 @@ RSpec.describe Ci::Minutes::BatchResetService do
expect(error.sentry_extra_data[:failed_batches]).to contain_exactly(
{
count: 3,
first_namespace_id: 1,
last_namespace_id: 3,
first_namespace_id: namespace_1.id,
last_namespace_id: namespace_3.id,
error_message: 'something went wrong',
error_backtrace: kind_of(Array)
}
......@@ -154,7 +123,9 @@ RSpec.describe Ci::Minutes::BatchResetService do
end
with_them do
let!(:namespace) { create_namespace_with_project(1, 100.minutes, namespace_limit) }
let!(:namespace) { create_namespace_with_project(100.minutes, namespace_limit) }
let(:ids_range) { [namespace.id] }
before do
allow(::Gitlab::CurrentSettings).to receive(:shared_runners_minutes).and_return(global_limit)
......
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