Commit d9b99c31 authored by Bob Van Landuyt's avatar Bob Van Landuyt

Merge branch '325273-remove-feature-flag' into 'master'

Remove the container expiration loopless feature flag [RUN ALL RSPEC] [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!63873
parents 8c7c7a7d 5aca511a
...@@ -49,7 +49,6 @@ module ContainerExpirationPolicies ...@@ -49,7 +49,6 @@ module ContainerExpirationPolicies
private private
def schedule_next_run_if_needed def schedule_next_run_if_needed
return unless Feature.enabled?(:container_registry_expiration_policies_loopless)
return if policy.next_run_at.future? return if policy.next_run_at.future?
repos_before_next_run = ::ContainerRepository.for_project_id(policy.project_id) repos_before_next_run = ::ContainerRepository.for_project_id(policy.project_id)
......
...@@ -65,19 +65,9 @@ module ContainerExpirationPolicies ...@@ -65,19 +65,9 @@ module ContainerExpirationPolicies
def container_repository def container_repository
strong_memoize(:container_repository) do strong_memoize(:container_repository) do
ContainerRepository.transaction do ContainerRepository.transaction do
# rubocop: disable CodeReuse/ActiveRecord
# We need a lock to prevent two workers from picking up the same row # We need a lock to prevent two workers from picking up the same row
container_repository = if loopless_enabled? container_repository = next_container_repository
next_container_repository
else
ContainerRepository.waiting_for_cleanup
.order(:expiration_policy_cleanup_status, :expiration_policy_started_at)
.limit(1)
.lock('FOR UPDATE SKIP LOCKED')
.first
end
# rubocop: enable CodeReuse/ActiveRecord
container_repository&.tap(&:cleanup_ongoing!) container_repository&.tap(&:cleanup_ongoing!)
end end
end end
...@@ -102,28 +92,20 @@ module ContainerExpirationPolicies ...@@ -102,28 +92,20 @@ module ContainerExpirationPolicies
def cleanup_scheduled_count def cleanup_scheduled_count
strong_memoize(:cleanup_scheduled_count) do strong_memoize(:cleanup_scheduled_count) do
if loopless_enabled? limit = max_running_jobs + 1
limit = max_running_jobs + 1 ContainerExpirationPolicy.with_container_repositories
ContainerExpirationPolicy.with_container_repositories .runnable_schedules
.runnable_schedules .limit(limit)
.limit(limit) .count
.count
else
ContainerRepository.cleanup_scheduled.count
end
end end
end end
def cleanup_unfinished_count def cleanup_unfinished_count
strong_memoize(:cleanup_unfinished_count) do strong_memoize(:cleanup_unfinished_count) do
if loopless_enabled? limit = max_running_jobs + 1
limit = max_running_jobs + 1 ContainerRepository.with_unfinished_cleanup
ContainerRepository.with_unfinished_cleanup .limit(limit)
.limit(limit) .count
.count
else
ContainerRepository.cleanup_unfinished.count
end
end end
end end
...@@ -132,21 +114,13 @@ module ContainerExpirationPolicies ...@@ -132,21 +114,13 @@ module ContainerExpirationPolicies
now = Time.zone.now now = Time.zone.now
if loopless_enabled? policy.next_run_at < now || (now + max_cleanup_execution_time.seconds < policy.next_run_at)
policy.next_run_at < now || (now + max_cleanup_execution_time.seconds < policy.next_run_at)
else
now + max_cleanup_execution_time.seconds < policy.next_run_at
end
end end
def throttling_enabled? def throttling_enabled?
Feature.enabled?(:container_registry_expiration_policies_throttling) Feature.enabled?(:container_registry_expiration_policies_throttling)
end end
def loopless_enabled?
Feature.enabled?(:container_registry_expiration_policies_loopless)
end
def max_cleanup_execution_time def max_cleanup_execution_time
::Gitlab::CurrentSettings.container_registry_delete_tags_service_timeout ::Gitlab::CurrentSettings.container_registry_delete_tags_service_timeout
end end
......
...@@ -38,18 +38,6 @@ class ContainerExpirationPolicyWorker # rubocop:disable Scalability/IdempotentWo ...@@ -38,18 +38,6 @@ class ContainerExpirationPolicyWorker # rubocop:disable Scalability/IdempotentWo
def perform_throttled def perform_throttled
try_obtain_lease do try_obtain_lease do
unless loopless_enabled?
with_runnable_policy do |policy|
ContainerExpirationPolicy.transaction do
policy.schedule_next_run!
ContainerRepository.for_project_id(policy.id)
.each_batch do |relation|
relation.update_all(expiration_policy_cleanup_status: :cleanup_scheduled)
end
end
end
end
ContainerExpirationPolicies::CleanupContainerRepositoryWorker.perform_with_capacity ContainerExpirationPolicies::CleanupContainerRepositoryWorker.perform_with_capacity
end end
end end
...@@ -86,10 +74,6 @@ class ContainerExpirationPolicyWorker # rubocop:disable Scalability/IdempotentWo ...@@ -86,10 +74,6 @@ class ContainerExpirationPolicyWorker # rubocop:disable Scalability/IdempotentWo
Feature.enabled?(:container_registry_expiration_policies_throttling) Feature.enabled?(:container_registry_expiration_policies_throttling)
end end
def loopless_enabled?
Feature.enabled?(:container_registry_expiration_policies_loopless)
end
def lease_timeout def lease_timeout
5.hours 5.hours
end end
......
---
name: container_registry_expiration_policies_loopless
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56962
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/325273
milestone: '13.11'
type: development
group: group::package
default_enabled: false
...@@ -9,37 +9,68 @@ RSpec.describe ContainerExpirationPolicies::CleanupService do ...@@ -9,37 +9,68 @@ RSpec.describe ContainerExpirationPolicies::CleanupService do
let(:service) { described_class.new(repository) } let(:service) { described_class.new(repository) }
describe '#execute' do describe '#execute' do
let(:policy) { repository.project.container_expiration_policy }
subject { service.execute } subject { service.execute }
shared_examples 'cleaning up a container repository' do before do
context 'with a successful cleanup tags service execution' do policy.update!(enabled: true)
let(:cleanup_tags_service_params) { project.container_expiration_policy.policy_params.merge('container_expiration_policy' => true) } policy.update_column(:next_run_at, 5.minutes.ago)
let(:cleanup_tags_service) { instance_double(Projects::ContainerRepository::CleanupTagsService) } end
it 'completely clean up the repository' do context 'with a successful cleanup tags service execution' do
expect(Projects::ContainerRepository::CleanupTagsService) let(:cleanup_tags_service_params) { project.container_expiration_policy.policy_params.merge('container_expiration_policy' => true) }
.to receive(:new).with(project, nil, cleanup_tags_service_params).and_return(cleanup_tags_service) let(:cleanup_tags_service) { instance_double(Projects::ContainerRepository::CleanupTagsService) }
expect(cleanup_tags_service).to receive(:execute).with(repository).and_return(status: :success)
response = subject it 'completely clean up the repository' do
expect(Projects::ContainerRepository::CleanupTagsService)
.to receive(:new).with(project, nil, cleanup_tags_service_params).and_return(cleanup_tags_service)
expect(cleanup_tags_service).to receive(:execute).with(repository).and_return(status: :success)
aggregate_failures "checking the response and container repositories" do response = subject
expect(response.success?).to eq(true)
expect(response.payload).to include(cleanup_status: :finished, container_repository_id: repository.id) aggregate_failures "checking the response and container repositories" do
expect(ContainerRepository.waiting_for_cleanup.count).to eq(0) expect(response.success?).to eq(true)
expect(repository.reload.cleanup_unscheduled?).to be_truthy expect(response.payload).to include(cleanup_status: :finished, container_repository_id: repository.id)
expect(repository.expiration_policy_completed_at).not_to eq(nil) expect(ContainerRepository.waiting_for_cleanup.count).to eq(0)
expect(repository.expiration_policy_started_at).not_to eq(nil) expect(repository.reload.cleanup_unscheduled?).to be_truthy
end expect(repository.expiration_policy_completed_at).not_to eq(nil)
expect(repository.expiration_policy_started_at).not_to eq(nil)
end end
end end
end
context 'without a successful cleanup tags service execution' do context 'without a successful cleanup tags service execution' do
let(:cleanup_tags_service_response) { { status: :error, message: 'timeout' } } let(:cleanup_tags_service_response) { { status: :error, message: 'timeout' } }
before do before do
expect(Projects::ContainerRepository::CleanupTagsService) expect(Projects::ContainerRepository::CleanupTagsService)
.to receive(:new).and_return(double(execute: cleanup_tags_service_response)) .to receive(:new).and_return(double(execute: cleanup_tags_service_response))
end
it 'partially clean up the repository' do
response = subject
aggregate_failures "checking the response and container repositories" do
expect(response.success?).to eq(true)
expect(response.payload).to include(cleanup_status: :unfinished, container_repository_id: repository.id)
expect(ContainerRepository.waiting_for_cleanup.count).to eq(1)
expect(repository.reload.cleanup_unfinished?).to be_truthy
expect(repository.expiration_policy_started_at).not_to eq(nil)
expect(repository.expiration_policy_completed_at).to eq(nil)
end
end
context 'with a truncated cleanup tags service response' do
let(:cleanup_tags_service_response) do
{
status: :error,
original_size: 1000,
before_truncate_size: 800,
after_truncate_size: 200,
before_delete_size: 100,
deleted_size: 100
}
end end
it 'partially clean up the repository' do it 'partially clean up the repository' do
...@@ -47,179 +78,134 @@ RSpec.describe ContainerExpirationPolicies::CleanupService do ...@@ -47,179 +78,134 @@ RSpec.describe ContainerExpirationPolicies::CleanupService do
aggregate_failures "checking the response and container repositories" do aggregate_failures "checking the response and container repositories" do
expect(response.success?).to eq(true) expect(response.success?).to eq(true)
expect(response.payload).to include(cleanup_status: :unfinished, container_repository_id: repository.id) expect(response.payload)
.to include(
cleanup_status: :unfinished,
container_repository_id: repository.id,
cleanup_tags_service_original_size: 1000,
cleanup_tags_service_before_truncate_size: 800,
cleanup_tags_service_after_truncate_size: 200,
cleanup_tags_service_before_delete_size: 100,
cleanup_tags_service_deleted_size: 100
)
expect(ContainerRepository.waiting_for_cleanup.count).to eq(1) expect(ContainerRepository.waiting_for_cleanup.count).to eq(1)
expect(repository.reload.cleanup_unfinished?).to be_truthy expect(repository.reload.cleanup_unfinished?).to be_truthy
expect(repository.expiration_policy_started_at).not_to eq(nil) expect(repository.expiration_policy_started_at).not_to eq(nil)
expect(repository.expiration_policy_completed_at).to eq(nil) expect(repository.expiration_policy_completed_at).to eq(nil)
end end
end end
context 'with a truncated cleanup tags service response' do
let(:cleanup_tags_service_response) do
{
status: :error,
original_size: 1000,
before_truncate_size: 800,
after_truncate_size: 200,
before_delete_size: 100,
deleted_size: 100
}
end
it 'partially clean up the repository' do
response = subject
aggregate_failures "checking the response and container repositories" do
expect(response.success?).to eq(true)
expect(response.payload)
.to include(
cleanup_status: :unfinished,
container_repository_id: repository.id,
cleanup_tags_service_original_size: 1000,
cleanup_tags_service_before_truncate_size: 800,
cleanup_tags_service_after_truncate_size: 200,
cleanup_tags_service_before_delete_size: 100,
cleanup_tags_service_deleted_size: 100
)
expect(ContainerRepository.waiting_for_cleanup.count).to eq(1)
expect(repository.reload.cleanup_unfinished?).to be_truthy
expect(repository.expiration_policy_started_at).not_to eq(nil)
expect(repository.expiration_policy_completed_at).to eq(nil)
end
end
end
end end
end
context 'with no repository' do context 'with no repository' do
let(:service) { described_class.new(nil) } let(:service) { described_class.new(nil) }
it 'returns an error response' do it 'returns an error response' do
expect(subject.success?).to eq(false) expect(subject.success?).to eq(false)
expect(subject.message).to eq('no repository') expect(subject.message).to eq('no repository')
end
end end
end
context 'with an invalid policy' do context 'with an invalid policy' do
let(:policy) { repository.project.container_expiration_policy } let(:policy) { repository.project.container_expiration_policy }
before do before do
policy.name_regex = nil policy.name_regex = nil
policy.enabled = true policy.enabled = true
repository.expiration_policy_cleanup_status = :cleanup_ongoing repository.expiration_policy_cleanup_status = :cleanup_ongoing
end end
it 'returns an error response' do it 'returns an error response' do
expect { subject }.to change { repository.expiration_policy_cleanup_status }.from('cleanup_ongoing').to('cleanup_unscheduled') expect { subject }.to change { repository.expiration_policy_cleanup_status }.from('cleanup_ongoing').to('cleanup_unscheduled')
expect(subject.success?).to eq(false) expect(subject.success?).to eq(false)
expect(subject.message).to eq('invalid policy') expect(subject.message).to eq('invalid policy')
expect(policy).not_to be_enabled expect(policy).not_to be_enabled
end
end end
end
context 'with a network error' do context 'with a network error' do
before do before do
expect(Projects::ContainerRepository::CleanupTagsService) expect(Projects::ContainerRepository::CleanupTagsService)
.to receive(:new).and_raise(Faraday::TimeoutError) .to receive(:new).and_raise(Faraday::TimeoutError)
end end
it 'raises an error' do it 'raises an error' do
expect { subject }.to raise_error(Faraday::TimeoutError) expect { subject }.to raise_error(Faraday::TimeoutError)
expect(ContainerRepository.waiting_for_cleanup.count).to eq(1) expect(ContainerRepository.waiting_for_cleanup.count).to eq(1)
expect(repository.reload.cleanup_unfinished?).to be_truthy expect(repository.reload.cleanup_unfinished?).to be_truthy
expect(repository.expiration_policy_started_at).not_to eq(nil) expect(repository.expiration_policy_started_at).not_to eq(nil)
expect(repository.expiration_policy_completed_at).to eq(nil) expect(repository.expiration_policy_completed_at).to eq(nil)
end
end end
end end
context 'with loopless enabled' do context 'next run scheduling' do
let(:policy) { repository.project.container_expiration_policy } let_it_be_with_reload(:repository2) { create(:container_repository, project: project) }
let_it_be_with_reload(:repository3) { create(:container_repository, project: project) }
before do before do
policy.update!(enabled: true) cleanup_tags_service = instance_double(Projects::ContainerRepository::CleanupTagsService)
policy.update_column(:next_run_at, 5.minutes.ago) allow(Projects::ContainerRepository::CleanupTagsService)
.to receive(:new).and_return(cleanup_tags_service)
allow(cleanup_tags_service).to receive(:execute).and_return(status: :success)
end end
it_behaves_like 'cleaning up a container repository' shared_examples 'not scheduling the next run' do
it 'does not scheduled the next run' do
context 'next run scheduling' do expect(policy).not_to receive(:schedule_next_run!)
let_it_be_with_reload(:repository2) { create(:container_repository, project: project) }
let_it_be_with_reload(:repository3) { create(:container_repository, project: project) }
before do expect { subject }.not_to change { policy.reload.next_run_at }
cleanup_tags_service = instance_double(Projects::ContainerRepository::CleanupTagsService)
allow(Projects::ContainerRepository::CleanupTagsService)
.to receive(:new).and_return(cleanup_tags_service)
allow(cleanup_tags_service).to receive(:execute).and_return(status: :success)
end
shared_examples 'not scheduling the next run' do
it 'does not scheduled the next run' do
expect(policy).not_to receive(:schedule_next_run!)
expect { subject }.not_to change { policy.reload.next_run_at }
end
end end
end
shared_examples 'scheduling the next run' do shared_examples 'scheduling the next run' do
it 'schedules the next run' do it 'schedules the next run' do
expect(policy).to receive(:schedule_next_run!).and_call_original expect(policy).to receive(:schedule_next_run!).and_call_original
expect { subject }.to change { policy.reload.next_run_at } expect { subject }.to change { policy.reload.next_run_at }
end
end end
end
context 'with cleanups started_at before policy next_run_at' do context 'with cleanups started_at before policy next_run_at' do
before do before do
ContainerRepository.update_all(expiration_policy_started_at: 10.minutes.ago) ContainerRepository.update_all(expiration_policy_started_at: 10.minutes.ago)
end
it_behaves_like 'not scheduling the next run'
end end
context 'with cleanups started_at around policy next_run_at' do it_behaves_like 'not scheduling the next run'
before do end
repository3.update!(expiration_policy_started_at: policy.next_run_at + 10.minutes.ago)
end
it_behaves_like 'not scheduling the next run' context 'with cleanups started_at around policy next_run_at' do
before do
repository3.update!(expiration_policy_started_at: policy.next_run_at + 10.minutes.ago)
end end
context 'with only the current repository started_at before the policy next_run_at' do it_behaves_like 'not scheduling the next run'
before do end
repository2.update!(expiration_policy_started_at: policy.next_run_at + 10.minutes)
repository3.update!(expiration_policy_started_at: policy.next_run_at + 12.minutes)
end
it_behaves_like 'scheduling the next run' context 'with only the current repository started_at before the policy next_run_at' do
before do
repository2.update!(expiration_policy_started_at: policy.next_run_at + 10.minutes)
repository3.update!(expiration_policy_started_at: policy.next_run_at + 12.minutes)
end end
context 'with cleanups started_at after policy next_run_at' do it_behaves_like 'scheduling the next run'
before do end
ContainerRepository.update_all(expiration_policy_started_at: policy.next_run_at + 10.minutes)
end
it_behaves_like 'scheduling the next run' context 'with cleanups started_at after policy next_run_at' do
before do
ContainerRepository.update_all(expiration_policy_started_at: policy.next_run_at + 10.minutes)
end end
context 'with a future policy next_run_at' do it_behaves_like 'scheduling the next run'
before do end
policy.update_column(:next_run_at, 5.minutes.from_now)
end
it_behaves_like 'not scheduling the next run' context 'with a future policy next_run_at' do
before do
policy.update_column(:next_run_at, 5.minutes.from_now)
end end
end
end
context 'with loopless disabled' do it_behaves_like 'not scheduling the next run'
before do
stub_feature_flags(container_registry_expiration_policies_loopless: false)
end end
it_behaves_like 'cleaning up a container repository'
end end
end end
end end
...@@ -85,7 +85,7 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do ...@@ -85,7 +85,7 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do
context 'with policy running shortly' do context 'with policy running shortly' do
before do before do
repository.cleanup_unfinished! if loopless_enabled? repository.cleanup_unfinished!
policy.update_column(:next_run_at, 1.minute.from_now) policy.update_column(:next_run_at, 1.minute.from_now)
end end
...@@ -108,371 +108,261 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do ...@@ -108,371 +108,261 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do
it 'skips the repository' do it 'skips the repository' do
expect(ContainerExpirationPolicies::CleanupService).not_to receive(:new) expect(ContainerExpirationPolicies::CleanupService).not_to receive(:new)
if loopless_enabled? expect { subject }
expect { subject } .to not_change { ContainerRepository.waiting_for_cleanup.count }
.to not_change { ContainerRepository.waiting_for_cleanup.count } .and not_change { repository.reload.expiration_policy_cleanup_status }
.and not_change { repository.reload.expiration_policy_cleanup_status }
else
expect { subject }.to change { ContainerRepository.waiting_for_cleanup.count }.from(1).to(0)
expect(repository.reload.cleanup_unscheduled?).to be_truthy
end
end end
end end
end end
context 'with loopless enabled' do context 'with repository in cleanup unscheduled state' do
before do before do
stub_feature_flags(container_registry_expiration_policies_loopless: true) policy.update_column(:next_run_at, 5.minutes.ago)
end end
context 'with repository in cleanup unscheduled state' do it_behaves_like 'handling all repository conditions'
before do end
policy.update_column(:next_run_at, 5.minutes.ago)
end
it_behaves_like 'handling all repository conditions' context 'with repository in cleanup unfinished state' do
before do
repository.cleanup_unfinished!
end end
context 'with repository in cleanup unfinished state' do it_behaves_like 'handling all repository conditions'
before do end
repository.cleanup_unfinished!
end
it_behaves_like 'handling all repository conditions' context 'container repository selection' do
end where(:repository_cleanup_status, :repository_policy_status, :other_repository_cleanup_status, :other_repository_policy_status, :expected_selected_repository) do
:unscheduled | :disabled | :unscheduled | :disabled | :none
:unscheduled | :disabled | :unscheduled | :runnable | :other_repository
:unscheduled | :disabled | :unscheduled | :not_runnable | :none
context 'container repository selection' do :unscheduled | :disabled | :scheduled | :disabled | :none
where(:repository_cleanup_status, :repository_policy_status, :other_repository_cleanup_status, :other_repository_policy_status, :expected_selected_repository) do :unscheduled | :disabled | :scheduled | :runnable | :other_repository
:unscheduled | :disabled | :unscheduled | :disabled | :none :unscheduled | :disabled | :scheduled | :not_runnable | :none
:unscheduled | :disabled | :unscheduled | :runnable | :other_repository
:unscheduled | :disabled | :unscheduled | :not_runnable | :none
:unscheduled | :disabled | :scheduled | :disabled | :none :unscheduled | :disabled | :unfinished | :disabled | :none
:unscheduled | :disabled | :scheduled | :runnable | :other_repository :unscheduled | :disabled | :unfinished | :runnable | :other_repository
:unscheduled | :disabled | :scheduled | :not_runnable | :none :unscheduled | :disabled | :unfinished | :not_runnable | :other_repository
:unscheduled | :disabled | :unfinished | :disabled | :none :unscheduled | :disabled | :ongoing | :disabled | :none
:unscheduled | :disabled | :unfinished | :runnable | :other_repository :unscheduled | :disabled | :ongoing | :runnable | :none
:unscheduled | :disabled | :unfinished | :not_runnable | :other_repository :unscheduled | :disabled | :ongoing | :not_runnable | :none
:unscheduled | :disabled | :ongoing | :disabled | :none :unscheduled | :runnable | :unscheduled | :disabled | :repository
:unscheduled | :disabled | :ongoing | :runnable | :none :unscheduled | :runnable | :unscheduled | :runnable | :repository
:unscheduled | :disabled | :ongoing | :not_runnable | :none :unscheduled | :runnable | :unscheduled | :not_runnable | :repository
:unscheduled | :runnable | :unscheduled | :disabled | :repository :unscheduled | :runnable | :scheduled | :disabled | :repository
:unscheduled | :runnable | :unscheduled | :runnable | :repository :unscheduled | :runnable | :scheduled | :runnable | :repository
:unscheduled | :runnable | :unscheduled | :not_runnable | :repository :unscheduled | :runnable | :scheduled | :not_runnable | :repository
:unscheduled | :runnable | :scheduled | :disabled | :repository :unscheduled | :runnable | :unfinished | :disabled | :repository
:unscheduled | :runnable | :scheduled | :runnable | :repository :unscheduled | :runnable | :unfinished | :runnable | :repository
:unscheduled | :runnable | :scheduled | :not_runnable | :repository :unscheduled | :runnable | :unfinished | :not_runnable | :repository
:unscheduled | :runnable | :unfinished | :disabled | :repository :unscheduled | :runnable | :ongoing | :disabled | :repository
:unscheduled | :runnable | :unfinished | :runnable | :repository :unscheduled | :runnable | :ongoing | :runnable | :repository
:unscheduled | :runnable | :unfinished | :not_runnable | :repository :unscheduled | :runnable | :ongoing | :not_runnable | :repository
:unscheduled | :runnable | :ongoing | :disabled | :repository :scheduled | :disabled | :unscheduled | :disabled | :none
:unscheduled | :runnable | :ongoing | :runnable | :repository :scheduled | :disabled | :unscheduled | :runnable | :other_repository
:unscheduled | :runnable | :ongoing | :not_runnable | :repository :scheduled | :disabled | :unscheduled | :not_runnable | :none
:scheduled | :disabled | :unscheduled | :disabled | :none :scheduled | :disabled | :scheduled | :disabled | :none
:scheduled | :disabled | :unscheduled | :runnable | :other_repository :scheduled | :disabled | :scheduled | :runnable | :other_repository
:scheduled | :disabled | :unscheduled | :not_runnable | :none :scheduled | :disabled | :scheduled | :not_runnable | :none
:scheduled | :disabled | :scheduled | :disabled | :none :scheduled | :disabled | :unfinished | :disabled | :none
:scheduled | :disabled | :scheduled | :runnable | :other_repository :scheduled | :disabled | :unfinished | :runnable | :other_repository
:scheduled | :disabled | :scheduled | :not_runnable | :none :scheduled | :disabled | :unfinished | :not_runnable | :other_repository
:scheduled | :disabled | :unfinished | :disabled | :none :scheduled | :disabled | :ongoing | :disabled | :none
:scheduled | :disabled | :unfinished | :runnable | :other_repository :scheduled | :disabled | :ongoing | :runnable | :none
:scheduled | :disabled | :unfinished | :not_runnable | :other_repository :scheduled | :disabled | :ongoing | :not_runnable | :none
:scheduled | :disabled | :ongoing | :disabled | :none :scheduled | :runnable | :unscheduled | :disabled | :repository
:scheduled | :disabled | :ongoing | :runnable | :none :scheduled | :runnable | :unscheduled | :runnable | :other_repository
:scheduled | :disabled | :ongoing | :not_runnable | :none :scheduled | :runnable | :unscheduled | :not_runnable | :repository
:scheduled | :runnable | :unscheduled | :disabled | :repository :scheduled | :runnable | :scheduled | :disabled | :repository
:scheduled | :runnable | :unscheduled | :runnable | :other_repository :scheduled | :runnable | :scheduled | :runnable | :repository
:scheduled | :runnable | :unscheduled | :not_runnable | :repository :scheduled | :runnable | :scheduled | :not_runnable | :repository
:scheduled | :runnable | :scheduled | :disabled | :repository :scheduled | :runnable | :unfinished | :disabled | :repository
:scheduled | :runnable | :scheduled | :runnable | :repository :scheduled | :runnable | :unfinished | :runnable | :repository
:scheduled | :runnable | :scheduled | :not_runnable | :repository :scheduled | :runnable | :unfinished | :not_runnable | :repository
:scheduled | :runnable | :unfinished | :disabled | :repository :scheduled | :runnable | :ongoing | :disabled | :repository
:scheduled | :runnable | :unfinished | :runnable | :repository :scheduled | :runnable | :ongoing | :runnable | :repository
:scheduled | :runnable | :unfinished | :not_runnable | :repository :scheduled | :runnable | :ongoing | :not_runnable | :repository
:scheduled | :runnable | :ongoing | :disabled | :repository :scheduled | :not_runnable | :unscheduled | :disabled | :none
:scheduled | :runnable | :ongoing | :runnable | :repository :scheduled | :not_runnable | :unscheduled | :runnable | :other_repository
:scheduled | :runnable | :ongoing | :not_runnable | :repository :scheduled | :not_runnable | :unscheduled | :not_runnable | :none
:scheduled | :not_runnable | :unscheduled | :disabled | :none :scheduled | :not_runnable | :scheduled | :disabled | :none
:scheduled | :not_runnable | :unscheduled | :runnable | :other_repository :scheduled | :not_runnable | :scheduled | :runnable | :other_repository
:scheduled | :not_runnable | :unscheduled | :not_runnable | :none :scheduled | :not_runnable | :scheduled | :not_runnable | :none
:scheduled | :not_runnable | :scheduled | :disabled | :none :scheduled | :not_runnable | :unfinished | :disabled | :none
:scheduled | :not_runnable | :scheduled | :runnable | :other_repository :scheduled | :not_runnable | :unfinished | :runnable | :other_repository
:scheduled | :not_runnable | :scheduled | :not_runnable | :none :scheduled | :not_runnable | :unfinished | :not_runnable | :other_repository
:scheduled | :not_runnable | :unfinished | :disabled | :none :scheduled | :not_runnable | :ongoing | :disabled | :none
:scheduled | :not_runnable | :unfinished | :runnable | :other_repository :scheduled | :not_runnable | :ongoing | :runnable | :none
:scheduled | :not_runnable | :unfinished | :not_runnable | :other_repository :scheduled | :not_runnable | :ongoing | :not_runnable | :none
:scheduled | :not_runnable | :ongoing | :disabled | :none :unfinished | :disabled | :unscheduled | :disabled | :none
:scheduled | :not_runnable | :ongoing | :runnable | :none :unfinished | :disabled | :unscheduled | :runnable | :other_repository
:scheduled | :not_runnable | :ongoing | :not_runnable | :none :unfinished | :disabled | :unscheduled | :not_runnable | :none
:unfinished | :disabled | :unscheduled | :disabled | :none :unfinished | :disabled | :scheduled | :disabled | :none
:unfinished | :disabled | :unscheduled | :runnable | :other_repository :unfinished | :disabled | :scheduled | :runnable | :other_repository
:unfinished | :disabled | :unscheduled | :not_runnable | :none :unfinished | :disabled | :scheduled | :not_runnable | :none
:unfinished | :disabled | :scheduled | :disabled | :none :unfinished | :disabled | :unfinished | :disabled | :none
:unfinished | :disabled | :scheduled | :runnable | :other_repository :unfinished | :disabled | :unfinished | :runnable | :other_repository
:unfinished | :disabled | :scheduled | :not_runnable | :none :unfinished | :disabled | :unfinished | :not_runnable | :other_repository
:unfinished | :disabled | :unfinished | :disabled | :none :unfinished | :disabled | :ongoing | :disabled | :none
:unfinished | :disabled | :unfinished | :runnable | :other_repository :unfinished | :disabled | :ongoing | :runnable | :none
:unfinished | :disabled | :unfinished | :not_runnable | :other_repository :unfinished | :disabled | :ongoing | :not_runnable | :none
:unfinished | :disabled | :ongoing | :disabled | :none :unfinished | :runnable | :unscheduled | :disabled | :repository
:unfinished | :disabled | :ongoing | :runnable | :none :unfinished | :runnable | :unscheduled | :runnable | :other_repository
:unfinished | :disabled | :ongoing | :not_runnable | :none :unfinished | :runnable | :unscheduled | :not_runnable | :repository
:unfinished | :runnable | :scheduled | :disabled | :repository
:unfinished | :runnable | :scheduled | :runnable | :other_repository
:unfinished | :runnable | :scheduled | :not_runnable | :repository
:unfinished | :runnable | :unscheduled | :disabled | :repository :unfinished | :runnable | :unfinished | :disabled | :repository
:unfinished | :runnable | :unscheduled | :runnable | :other_repository :unfinished | :runnable | :unfinished | :runnable | :repository
:unfinished | :runnable | :unscheduled | :not_runnable | :repository :unfinished | :runnable | :unfinished | :not_runnable | :repository
:unfinished | :runnable | :scheduled | :disabled | :repository
:unfinished | :runnable | :scheduled | :runnable | :other_repository
:unfinished | :runnable | :scheduled | :not_runnable | :repository
:unfinished | :runnable | :unfinished | :disabled | :repository :unfinished | :runnable | :ongoing | :disabled | :repository
:unfinished | :runnable | :unfinished | :runnable | :repository :unfinished | :runnable | :ongoing | :runnable | :repository
:unfinished | :runnable | :unfinished | :not_runnable | :repository :unfinished | :runnable | :ongoing | :not_runnable | :repository
:unfinished | :runnable | :ongoing | :disabled | :repository :unfinished | :not_runnable | :unscheduled | :disabled | :repository
:unfinished | :runnable | :ongoing | :runnable | :repository :unfinished | :not_runnable | :unscheduled | :runnable | :other_repository
:unfinished | :runnable | :ongoing | :not_runnable | :repository :unfinished | :not_runnable | :unscheduled | :not_runnable | :repository
:unfinished | :not_runnable | :unscheduled | :disabled | :repository :unfinished | :not_runnable | :scheduled | :disabled | :repository
:unfinished | :not_runnable | :unscheduled | :runnable | :other_repository :unfinished | :not_runnable | :scheduled | :runnable | :other_repository
:unfinished | :not_runnable | :unscheduled | :not_runnable | :repository :unfinished | :not_runnable | :scheduled | :not_runnable | :repository
:unfinished | :not_runnable | :scheduled | :disabled | :repository :unfinished | :not_runnable | :unfinished | :disabled | :repository
:unfinished | :not_runnable | :scheduled | :runnable | :other_repository :unfinished | :not_runnable | :unfinished | :runnable | :repository
:unfinished | :not_runnable | :scheduled | :not_runnable | :repository :unfinished | :not_runnable | :unfinished | :not_runnable | :repository
:unfinished | :not_runnable | :unfinished | :disabled | :repository :unfinished | :not_runnable | :ongoing | :disabled | :repository
:unfinished | :not_runnable | :unfinished | :runnable | :repository :unfinished | :not_runnable | :ongoing | :runnable | :repository
:unfinished | :not_runnable | :unfinished | :not_runnable | :repository :unfinished | :not_runnable | :ongoing | :not_runnable | :repository
:unfinished | :not_runnable | :ongoing | :disabled | :repository :ongoing | :disabled | :unscheduled | :disabled | :none
:unfinished | :not_runnable | :ongoing | :runnable | :repository :ongoing | :disabled | :unscheduled | :runnable | :other_repository
:unfinished | :not_runnable | :ongoing | :not_runnable | :repository :ongoing | :disabled | :unscheduled | :not_runnable | :none
:ongoing | :disabled | :unscheduled | :disabled | :none :ongoing | :disabled | :scheduled | :disabled | :none
:ongoing | :disabled | :unscheduled | :runnable | :other_repository :ongoing | :disabled | :scheduled | :runnable | :other_repository
:ongoing | :disabled | :unscheduled | :not_runnable | :none :ongoing | :disabled | :scheduled | :not_runnable | :none
:ongoing | :disabled | :scheduled | :disabled | :none :ongoing | :disabled | :unfinished | :disabled | :none
:ongoing | :disabled | :scheduled | :runnable | :other_repository :ongoing | :disabled | :unfinished | :runnable | :other_repository
:ongoing | :disabled | :scheduled | :not_runnable | :none :ongoing | :disabled | :unfinished | :not_runnable | :other_repository
:ongoing | :disabled | :unfinished | :disabled | :none :ongoing | :disabled | :ongoing | :disabled | :none
:ongoing | :disabled | :unfinished | :runnable | :other_repository :ongoing | :disabled | :ongoing | :runnable | :none
:ongoing | :disabled | :unfinished | :not_runnable | :other_repository :ongoing | :disabled | :ongoing | :not_runnable | :none
:ongoing | :disabled | :ongoing | :disabled | :none :ongoing | :runnable | :unscheduled | :disabled | :none
:ongoing | :disabled | :ongoing | :runnable | :none :ongoing | :runnable | :unscheduled | :runnable | :other_repository
:ongoing | :disabled | :ongoing | :not_runnable | :none :ongoing | :runnable | :unscheduled | :not_runnable | :none
:ongoing | :runnable | :unscheduled | :disabled | :none :ongoing | :runnable | :scheduled | :disabled | :none
:ongoing | :runnable | :unscheduled | :runnable | :other_repository :ongoing | :runnable | :scheduled | :runnable | :other_repository
:ongoing | :runnable | :unscheduled | :not_runnable | :none :ongoing | :runnable | :scheduled | :not_runnable | :none
:ongoing | :runnable | :scheduled | :disabled | :none :ongoing | :runnable | :unfinished | :disabled | :none
:ongoing | :runnable | :scheduled | :runnable | :other_repository :ongoing | :runnable | :unfinished | :runnable | :other_repository
:ongoing | :runnable | :scheduled | :not_runnable | :none :ongoing | :runnable | :unfinished | :not_runnable | :other_repository
:ongoing | :runnable | :unfinished | :disabled | :none :ongoing | :runnable | :ongoing | :disabled | :none
:ongoing | :runnable | :unfinished | :runnable | :other_repository :ongoing | :runnable | :ongoing | :runnable | :none
:ongoing | :runnable | :unfinished | :not_runnable | :other_repository :ongoing | :runnable | :ongoing | :not_runnable | :none
:ongoing | :runnable | :ongoing | :disabled | :none :ongoing | :not_runnable | :unscheduled | :disabled | :none
:ongoing | :runnable | :ongoing | :runnable | :none :ongoing | :not_runnable | :unscheduled | :runnable | :other_repository
:ongoing | :runnable | :ongoing | :not_runnable | :none :ongoing | :not_runnable | :unscheduled | :not_runnable | :none
:ongoing | :not_runnable | :unscheduled | :disabled | :none :ongoing | :not_runnable | :scheduled | :disabled | :none
:ongoing | :not_runnable | :unscheduled | :runnable | :other_repository :ongoing | :not_runnable | :scheduled | :runnable | :other_repository
:ongoing | :not_runnable | :unscheduled | :not_runnable | :none :ongoing | :not_runnable | :scheduled | :not_runnable | :none
:ongoing | :not_runnable | :scheduled | :disabled | :none :ongoing | :not_runnable | :unfinished | :disabled | :none
:ongoing | :not_runnable | :scheduled | :runnable | :other_repository :ongoing | :not_runnable | :unfinished | :runnable | :other_repository
:ongoing | :not_runnable | :scheduled | :not_runnable | :none :ongoing | :not_runnable | :unfinished | :not_runnable | :other_repository
:ongoing | :not_runnable | :unfinished | :disabled | :none :ongoing | :not_runnable | :ongoing | :disabled | :none
:ongoing | :not_runnable | :unfinished | :runnable | :other_repository :ongoing | :not_runnable | :ongoing | :runnable | :none
:ongoing | :not_runnable | :unfinished | :not_runnable | :other_repository :ongoing | :not_runnable | :ongoing | :not_runnable | :none
end
:ongoing | :not_runnable | :ongoing | :disabled | :none with_them do
:ongoing | :not_runnable | :ongoing | :runnable | :none before do
:ongoing | :not_runnable | :ongoing | :not_runnable | :none update_container_repository(repository, repository_cleanup_status, repository_policy_status)
update_container_repository(other_repository, other_repository_cleanup_status, other_repository_policy_status)
end end
with_them do subject { worker.send(:container_repository) }
before do
update_container_repository(repository, repository_cleanup_status, repository_policy_status)
update_container_repository(other_repository, other_repository_cleanup_status, other_repository_policy_status)
end
subject { worker.send(:container_repository) }
if params[:expected_selected_repository] == :none
it 'does not select any repository' do
expect(subject).to eq(nil)
end
else
it 'does select a repository' do
selected_repository = expected_selected_repository == :repository ? repository : other_repository
expect(subject).to eq(selected_repository) if params[:expected_selected_repository] == :none
end it 'does not select any repository' do
expect(subject).to eq(nil)
end end
else
it 'does select a repository' do
selected_repository = expected_selected_repository == :repository ? repository : other_repository
def update_container_repository(container_repository, cleanup_status, policy_status) expect(subject).to eq(selected_repository)
container_repository.update_column(:expiration_policy_cleanup_status, "cleanup_#{cleanup_status}")
policy = container_repository.project.container_expiration_policy
case policy_status
when :disabled
policy.update!(enabled: false)
when :runnable
policy.update!(enabled: true)
policy.update_column(:next_run_at, 5.minutes.ago)
when :not_runnable
policy.update!(enabled: true)
policy.update_column(:next_run_at, 5.minutes.from_now)
end
end end
end end
end
context 'with another repository in cleanup unfinished state' do def update_container_repository(container_repository, cleanup_status, policy_status)
let_it_be(:another_repository) { create(:container_repository, :cleanup_unfinished) } container_repository.update_column(:expiration_policy_cleanup_status, "cleanup_#{cleanup_status}")
before do policy = container_repository.project.container_expiration_policy
policy.update_column(:next_run_at, 5.minutes.ago)
end
it 'process the cleanup scheduled repository first' do case policy_status
service_response = cleanup_service_response(repository: repository) when :disabled
expect(ContainerExpirationPolicies::CleanupService) policy.update!(enabled: false)
.to receive(:new).with(repository).and_return(double(execute: service_response)) when :runnable
expect_log_extra_metadata(service_response: service_response) policy.update!(enabled: true)
policy.update_column(:next_run_at, 5.minutes.ago)
subject when :not_runnable
policy.update!(enabled: true)
policy.update_column(:next_run_at, 5.minutes.from_now)
end
end end
end end
end end
context 'with loopless disabled' do context 'with another repository in cleanup unfinished state' do
before do let_it_be(:another_repository) { create(:container_repository, :cleanup_unfinished) }
stub_feature_flags(container_registry_expiration_policies_loopless: false)
end
context 'with repository in cleanup scheduled state' do
it_behaves_like 'handling all repository conditions'
end
context 'with repository in cleanup unfinished state' do
before do
repository.cleanup_unfinished!
end
it_behaves_like 'handling all repository conditions'
end
context 'with another repository in cleanup unfinished state' do
let_it_be(:another_repository) { create(:container_repository, :cleanup_unfinished) }
it 'process the cleanup scheduled repository first' do
service_response = cleanup_service_response(repository: repository)
expect(ContainerExpirationPolicies::CleanupService)
.to receive(:new).with(repository).and_return(double(execute: service_response))
expect_log_extra_metadata(service_response: service_response)
subject
end
end
context 'with multiple repositories in cleanup unfinished state' do
let_it_be(:repository2) { create(:container_repository, :cleanup_unfinished, expiration_policy_started_at: 20.minutes.ago) }
let_it_be(:repository3) { create(:container_repository, :cleanup_unfinished, expiration_policy_started_at: 10.minutes.ago) }
before do
repository.update!(expiration_policy_cleanup_status: :cleanup_unfinished, expiration_policy_started_at: 30.minutes.ago)
end
it 'process the repository with the oldest expiration_policy_started_at' do before do
service_response = cleanup_service_response(repository: repository) policy.update_column(:next_run_at, 5.minutes.ago)
expect(ContainerExpirationPolicies::CleanupService)
.to receive(:new).with(repository).and_return(double(execute: service_response))
expect_log_extra_metadata(service_response: service_response)
subject
end
end
context 'with repository in cleanup ongoing state' do
before do
repository.cleanup_ongoing!
end
it 'does not process it' do
expect(Projects::ContainerRepository::CleanupTagsService).not_to receive(:new)
expect { subject }.not_to change { ContainerRepository.waiting_for_cleanup.count }
expect(repository.cleanup_ongoing?).to be_truthy
end
end
context 'with no repository in any cleanup state' do
before do
repository.cleanup_unscheduled!
end
it 'does not process it' do
expect(Projects::ContainerRepository::CleanupTagsService).not_to receive(:new)
expect { subject }.not_to change { ContainerRepository.waiting_for_cleanup.count }
expect(repository.cleanup_unscheduled?).to be_truthy
end
end
context 'with no container repository waiting' do
before do
repository.destroy!
end
it 'does not execute the cleanup tags service' do
expect(Projects::ContainerRepository::CleanupTagsService).not_to receive(:new)
expect { subject }.not_to change { ContainerRepository.waiting_for_cleanup.count }
end
end end
context 'with feature flag disabled' do it 'process the cleanup scheduled repository first' do
before do service_response = cleanup_service_response(repository: repository)
stub_feature_flags(container_registry_expiration_policies_throttling: false) expect(ContainerExpirationPolicies::CleanupService)
end .to receive(:new).with(repository).and_return(double(execute: service_response))
expect_log_extra_metadata(service_response: service_response)
it 'is a no-op' do
expect(Projects::ContainerRepository::CleanupTagsService).not_to receive(:new)
expect { subject }.not_to change { ContainerRepository.waiting_for_cleanup.count } subject
end
end end
end end
...@@ -509,69 +399,53 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do ...@@ -509,69 +399,53 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do
end end
describe '#remaining_work_count' do describe '#remaining_work_count' do
subject { worker.remaining_work_count } let_it_be(:disabled_repository) { create(:container_repository, :cleanup_scheduled) }
shared_examples 'handling all conditions' do let(:capacity) { 10 }
context 'with container repositories waiting for cleanup' do
let_it_be(:unfinished_repositories) { create_list(:container_repository, 2, :cleanup_unfinished) }
it { is_expected.to eq(3) } subject { worker.remaining_work_count }
it 'logs the work count' do before do
expect_log_info( stub_application_setting(container_registry_expiration_policies_worker_capacity: capacity)
cleanup_scheduled_count: 1,
cleanup_unfinished_count: 2,
cleanup_total_count: 3
)
subject ContainerExpirationPolicy.update_all(enabled: true)
end repository.project.container_expiration_policy.update_column(:next_run_at, 5.minutes.ago)
end disabled_repository.project.container_expiration_policy.update_column(:enabled, false)
end
context 'with no container repositories waiting for cleanup' do context 'with container repositories waiting for cleanup' do
before do let_it_be(:unfinished_repositories) { create_list(:container_repository, 2, :cleanup_unfinished) }
repository.cleanup_ongoing!
policy.update_column(:next_run_at, 5.minutes.from_now)
end
it { is_expected.to eq(0) } it { is_expected.to eq(3) }
it 'logs 0 work count' do it 'logs the work count' do
expect_log_info( expect_log_info(
cleanup_scheduled_count: 0, cleanup_scheduled_count: 1,
cleanup_unfinished_count: 0, cleanup_unfinished_count: 2,
cleanup_total_count: 0 cleanup_total_count: 3
) )
subject subject
end
end end
end end
context 'with loopless enabled' do context 'with no container repositories waiting for cleanup' do
let_it_be(:disabled_repository) { create(:container_repository, :cleanup_scheduled) }
let(:capacity) { 10 }
before do before do
stub_feature_flags(container_registry_expiration_policies_loopless: true) repository.cleanup_ongoing!
stub_application_setting(container_registry_expiration_policies_worker_capacity: capacity) policy.update_column(:next_run_at, 5.minutes.from_now)
# loopless mode is more accurate that non loopless: policies need to be enabled
ContainerExpirationPolicy.update_all(enabled: true)
repository.project.container_expiration_policy.update_column(:next_run_at, 5.minutes.ago)
disabled_repository.project.container_expiration_policy.update_column(:enabled, false)
end end
it_behaves_like 'handling all conditions' it { is_expected.to eq(0) }
end
context 'with loopless disabled' do it 'logs 0 work count' do
before do expect_log_info(
stub_feature_flags(container_registry_expiration_policies_loopless: false) cleanup_scheduled_count: 0,
end cleanup_unfinished_count: 0,
cleanup_total_count: 0
)
it_behaves_like 'handling all conditions' subject
end
end end
end end
...@@ -599,8 +473,4 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do ...@@ -599,8 +473,4 @@ RSpec.describe ContainerExpirationPolicies::CleanupContainerRepositoryWorker do
expect(worker.logger) expect(worker.logger)
.to receive(:info).with(worker.structured_payload(structure)) .to receive(:info).with(worker.structured_payload(structure))
end end
def loopless_enabled?
Feature.enabled?(:container_registry_expiration_policies_loopless)
end
end end
...@@ -34,101 +34,18 @@ RSpec.describe ContainerExpirationPolicyWorker do ...@@ -34,101 +34,18 @@ RSpec.describe ContainerExpirationPolicyWorker do
end end
end end
context 'With no container expiration policies' do
context 'with loopless disabled' do
before do
stub_feature_flags(container_registry_expiration_policies_loopless: false)
end
it 'does not execute any policies' do
expect(ContainerRepository).not_to receive(:for_project_id)
expect { subject }.not_to change { ContainerRepository.cleanup_scheduled.count }
end
end
end
context 'with throttling enabled' do context 'with throttling enabled' do
before do before do
stub_feature_flags(container_registry_expiration_policies_throttling: true) stub_feature_flags(container_registry_expiration_policies_throttling: true)
end end
context 'with loopless disabled' do it 'calls the limited capacity worker' do
before do expect(ContainerExpirationPolicies::CleanupContainerRepositoryWorker).to receive(:perform_with_capacity)
stub_feature_flags(container_registry_expiration_policies_loopless: false)
end
context 'with container expiration policies' do
let_it_be(:container_expiration_policy) { create(:container_expiration_policy, :runnable) }
let_it_be(:container_repository) { create(:container_repository, project: container_expiration_policy.project) }
before do
expect(worker).to receive(:with_runnable_policy).and_call_original
end
context 'with a valid container expiration policy' do
it 'schedules the next run' do
expect { subject }.to change { container_expiration_policy.reload.next_run_at }
end
it 'marks the container repository as scheduled for cleanup' do
expect { subject }.to change { container_repository.reload.cleanup_scheduled? }.from(false).to(true)
expect(ContainerRepository.cleanup_scheduled.count).to eq(1)
end
it 'calls the limited capacity worker' do
expect(ContainerExpirationPolicies::CleanupContainerRepositoryWorker).to receive(:perform_with_capacity)
subject
end
end
context 'with a disabled container expiration policy' do
before do
container_expiration_policy.disable!
end
it 'does not run the policy' do
expect(ContainerRepository).not_to receive(:for_project_id)
expect { subject }.not_to change { ContainerRepository.cleanup_scheduled.count }
end
end
context 'with an invalid container expiration policy' do subject
let(:user) { container_expiration_policy.project.owner }
before do
container_expiration_policy.update_column(:name_regex, '*production')
end
it 'disables the policy and tracks an error' do
expect(ContainerRepository).not_to receive(:for_project_id)
expect(Gitlab::ErrorTracking).to receive(:log_exception).with(instance_of(described_class::InvalidPolicyError), container_expiration_policy_id: container_expiration_policy.id)
expect { subject }.to change { container_expiration_policy.reload.enabled }.from(true).to(false)
expect(ContainerRepository.cleanup_scheduled).to be_empty
end
end
end
it_behaves_like 'handling a taken exclusive lease'
end end
context 'with loopless enabled' do it_behaves_like 'handling a taken exclusive lease'
before do
stub_feature_flags(container_registry_expiration_policies_loopless: true)
expect(worker).not_to receive(:with_runnable_policy)
end
it 'calls the limited capacity worker' do
expect(ContainerExpirationPolicies::CleanupContainerRepositoryWorker).to receive(:perform_with_capacity)
subject
end
it_behaves_like 'handling a taken exclusive lease'
end
end end
context 'with throttling disabled' do context 'with throttling disabled' do
......
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