Commit 60efde82 authored by Fabio Pitino's avatar Fabio Pitino

Merge branch 'jprovazn-projectns-exclude-ci' into 'master'

Exclude project namespaces on CI minutes reset

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