diff options
Diffstat (limited to 'spec/models/container_repository_spec.rb')
-rw-r--r-- | spec/models/container_repository_spec.rb | 327 |
1 files changed, 226 insertions, 101 deletions
diff --git a/spec/models/container_repository_spec.rb b/spec/models/container_repository_spec.rb index c8d86edc55f..2ea042fb767 100644 --- a/spec/models/container_repository_spec.rb +++ b/spec/models/container_repository_spec.rb @@ -122,6 +122,27 @@ RSpec.describe ContainerRepository, :aggregate_failures do expect(repository).to be_import_aborted end end + + context 'already imported' do + it 'finishes the import' do + expect(repository).to receive(:migration_pre_import).and_return(:already_imported) + + expect { subject } + .to change { repository.reload.migration_state }.to('import_done') + .and change { repository.reload.migration_skipped_reason }.to('native_import') + end + end + + context 'non-existing repository' do + it 'finishes the import' do + expect(repository).to receive(:migration_pre_import).and_return(:not_found) + + expect { subject } + .to change { repository.reload.migration_state }.to('import_done') + .and change { repository.migration_skipped_reason }.to('not_found') + .and change { repository.migration_import_done_at }.from(nil) + end + end end shared_examples 'transitioning to importing', skip_import_success: true do @@ -151,6 +172,16 @@ RSpec.describe ContainerRepository, :aggregate_failures do expect(repository).to be_import_aborted end end + + context 'already imported' do + it 'finishes the import' do + expect(repository).to receive(:migration_import).and_return(:already_imported) + + expect { subject } + .to change { repository.reload.migration_state }.to('import_done') + .and change { repository.reload.migration_skipped_reason }.to('native_import') + end + end end shared_examples 'transitioning out of import_aborted' do @@ -193,7 +224,7 @@ RSpec.describe ContainerRepository, :aggregate_failures do end end - it_behaves_like 'transitioning from allowed states', %w[default] + it_behaves_like 'transitioning from allowed states', %w[default pre_importing importing import_aborted] it_behaves_like 'transitioning to pre_importing' end @@ -208,7 +239,7 @@ RSpec.describe ContainerRepository, :aggregate_failures do end end - it_behaves_like 'transitioning from allowed states', %w[import_aborted] + it_behaves_like 'transitioning from allowed states', %w[pre_importing importing import_aborted] it_behaves_like 'transitioning to pre_importing' it_behaves_like 'transitioning out of import_aborted' end @@ -218,7 +249,7 @@ RSpec.describe ContainerRepository, :aggregate_failures do subject { repository.finish_pre_import } - it_behaves_like 'transitioning from allowed states', %w[pre_importing import_aborted] + it_behaves_like 'transitioning from allowed states', %w[pre_importing importing import_aborted] it 'sets migration_pre_import_done_at' do expect { subject }.to change { repository.reload.migration_pre_import_done_at } @@ -238,7 +269,7 @@ RSpec.describe ContainerRepository, :aggregate_failures do end end - it_behaves_like 'transitioning from allowed states', %w[pre_import_done] + it_behaves_like 'transitioning from allowed states', %w[pre_import_done pre_importing importing import_aborted] it_behaves_like 'transitioning to importing' end @@ -253,7 +284,7 @@ RSpec.describe ContainerRepository, :aggregate_failures do end end - it_behaves_like 'transitioning from allowed states', %w[import_aborted] + it_behaves_like 'transitioning from allowed states', %w[pre_importing importing import_aborted] it_behaves_like 'transitioning to importing' it_behaves_like 'no action when feature flag is disabled' end @@ -263,7 +294,7 @@ RSpec.describe ContainerRepository, :aggregate_failures do subject { repository.finish_import } - it_behaves_like 'transitioning from allowed states', %w[importing import_aborted] + it_behaves_like 'transitioning from allowed states', %w[default pre_importing importing import_aborted] it_behaves_like 'queueing the next import' it 'sets migration_import_done_at and queues the next import' do @@ -302,6 +333,19 @@ RSpec.describe ContainerRepository, :aggregate_failures do expect(repository.migration_aborted_in_state).to eq('importing') expect(repository).to be_import_aborted end + + context 'above the max retry limit' do + before do + stub_application_setting(container_registry_import_max_retries: 1) + end + + it 'skips the migration' do + expect { subject }.to change { repository.migration_skipped_at } + + expect(repository.reload).to be_import_skipped + expect(repository.migration_skipped_reason).to eq('too_many_retries') + end + end end describe '#skip_import' do @@ -309,7 +353,7 @@ RSpec.describe ContainerRepository, :aggregate_failures do subject { repository.skip_import(reason: :too_many_retries) } - it_behaves_like 'transitioning from allowed states', ContainerRepository::ABORTABLE_MIGRATION_STATES + it_behaves_like 'transitioning from allowed states', ContainerRepository::SKIPPABLE_MIGRATION_STATES it 'sets migration_skipped_at and migration_skipped_reason' do expect { subject }.to change { repository.reload.migration_skipped_at } @@ -334,7 +378,7 @@ RSpec.describe ContainerRepository, :aggregate_failures do end end - it_behaves_like 'transitioning from allowed states', %w[pre_importing import_aborted] + it_behaves_like 'transitioning from allowed states', %w[pre_importing importing import_aborted] it_behaves_like 'transitioning to importing' end end @@ -391,7 +435,7 @@ RSpec.describe ContainerRepository, :aggregate_failures do describe '#retry_aborted_migration' do subject { repository.retry_aborted_migration } - shared_examples 'no action' do + context 'when migration_state is not aborted' do it 'does nothing' do expect { subject }.not_to change { repository.reload.migration_state } @@ -399,104 +443,45 @@ RSpec.describe ContainerRepository, :aggregate_failures do end end - shared_examples 'retrying the pre_import' do - it 'retries the pre_import' do - expect(repository).to receive(:migration_pre_import).and_return(:ok) - - expect { subject }.to change { repository.reload.migration_state }.to('pre_importing') - end - end - - shared_examples 'retrying the import' do - it 'retries the import' do - expect(repository).to receive(:migration_import).and_return(:ok) - - expect { subject }.to change { repository.reload.migration_state }.to('importing') - end - end - - context 'when migration_state is not aborted' do - it_behaves_like 'no action' - end - context 'when migration_state is aborted' do before do repository.abort_import allow(repository.gitlab_api_client) - .to receive(:import_status).with(repository.path).and_return(client_response) + .to receive(:import_status).with(repository.path).and_return(status) end - context 'native response' do - let(:client_response) { 'native' } - - it 'raises an error' do - expect { subject }.to raise_error(described_class::NativeImportError) - end - end + it_behaves_like 'reconciling migration_state' do + context 'error response' do + let(:status) { 'error' } - context 'import_in_progress response' do - let(:client_response) { 'import_in_progress' } - - it_behaves_like 'no action' - end - - context 'import_complete response' do - let(:client_response) { 'import_complete' } - - it 'finishes the import' do - expect { subject }.to change { repository.reload.migration_state }.to('import_done') - end - end - - context 'import_failed response' do - let(:client_response) { 'import_failed' } - - it_behaves_like 'retrying the import' - end - - context 'pre_import_in_progress response' do - let(:client_response) { 'pre_import_in_progress' } - - it_behaves_like 'no action' - end + context 'migration_pre_import_done_at is NULL' do + it_behaves_like 'retrying the pre_import' + end - context 'pre_import_complete response' do - let(:client_response) { 'pre_import_complete' } + context 'migration_pre_import_done_at is not NULL' do + before do + repository.update_columns( + migration_pre_import_started_at: 5.minutes.ago, + migration_pre_import_done_at: Time.zone.now + ) + end - it 'finishes the pre_import and starts the import' do - expect(repository).to receive(:finish_pre_import).and_call_original - expect(repository).to receive(:migration_import).and_return(:ok) - - expect { subject }.to change { repository.reload.migration_state }.to('importing') + it_behaves_like 'retrying the import' + end end end + end + end - context 'pre_import_failed response' do - let(:client_response) { 'pre_import_failed' } - - it_behaves_like 'retrying the pre_import' - end - - context 'error response' do - let(:client_response) { 'error' } - - context 'migration_pre_import_done_at is NULL' do - it_behaves_like 'retrying the pre_import' - end - - context 'migration_pre_import_done_at is not NULL' do - before do - repository.update_columns( - migration_pre_import_started_at: 5.minutes.ago, - migration_pre_import_done_at: Time.zone.now - ) - end + describe '#reconcile_import_status' do + subject { repository.reconcile_import_status(status) } - it_behaves_like 'retrying the import' - end - end + before do + repository.abort_import end + + it_behaves_like 'reconciling migration_state' end describe '#tag' do @@ -667,7 +652,7 @@ RSpec.describe ContainerRepository, :aggregate_failures do context 'supports gitlab api on .com with a recent repository' do before do expect(repository.gitlab_api_client).to receive(:supports_gitlab_api?).and_return(true) - expect(repository.gitlab_api_client).to receive(:repository_details).with(repository.path, with_size: true).and_return(response) + expect(repository.gitlab_api_client).to receive(:repository_details).with(repository.path, sizing: :self).and_return(response) end context 'with a size_bytes field' do @@ -722,12 +707,12 @@ RSpec.describe ContainerRepository, :aggregate_failures do end context 'registry migration' do - shared_examples 'handling the migration step' do |step| - let(:client_response) { :foobar } + before do + allow(repository.gitlab_api_client).to receive(:supports_gitlab_api?).and_return(true) + end - before do - allow(repository.gitlab_api_client).to receive(:supports_gitlab_api?).and_return(true) - end + shared_examples 'gitlab migration client request' do |step| + let(:client_response) { :foobar } it 'returns the same response as the client' do expect(repository.gitlab_api_client) @@ -746,6 +731,10 @@ RSpec.describe ContainerRepository, :aggregate_failures do expect(subject).to eq(:error) end end + end + + shared_examples 'handling the migration step' do |step| + it_behaves_like 'gitlab migration client request', step context 'too many imports' do it 'raises an error when it receives too_many_imports as a response' do @@ -767,6 +756,67 @@ RSpec.describe ContainerRepository, :aggregate_failures do it_behaves_like 'handling the migration step', :import_repository end + + describe '#migration_cancel' do + subject { repository.migration_cancel } + + it_behaves_like 'gitlab migration client request', :cancel_repository_import + end + + describe '#force_migration_cancel' do + subject { repository.force_migration_cancel } + + shared_examples 'returning the same response as the client' do + it 'returns the same response' do + expect(repository.gitlab_api_client) + .to receive(:cancel_repository_import).with(repository.path, force: true).and_return(client_response) + + expect(subject).to eq(client_response) + end + end + + context 'successful cancellation' do + let(:client_response) { { status: :ok } } + + it_behaves_like 'returning the same response as the client' + + it 'skips the migration' do + expect(repository.gitlab_api_client) + .to receive(:cancel_repository_import).with(repository.path, force: true).and_return(client_response) + + expect { subject }.to change { repository.reload.migration_state }.to('import_skipped') + .and change { repository.migration_skipped_reason }.to('migration_forced_canceled') + .and change { repository.migration_skipped_at } + end + end + + context 'failed cancellation' do + let(:client_response) { { status: :error } } + + it_behaves_like 'returning the same response as the client' + + it 'does not skip the migration' do + expect(repository.gitlab_api_client) + .to receive(:cancel_repository_import).with(repository.path, force: true).and_return(client_response) + + expect { subject }.to not_change { repository.reload.migration_state } + .and not_change { repository.migration_skipped_reason } + .and not_change { repository.migration_skipped_at } + end + end + + context 'when the gitlab_api feature is not supported' do + before do + allow(repository.gitlab_api_client).to receive(:supports_gitlab_api?).and_return(false) + end + + it 'returns :error' do + expect(repository.gitlab_api_client).not_to receive(:cancel_repository_import) + + expect(subject).to eq(:error) + end + end + end end describe '.build_from_path' do @@ -1081,6 +1131,43 @@ RSpec.describe ContainerRepository, :aggregate_failures do end end + describe '.all_migrated?' do + let_it_be(:project) { create(:project) } + + subject { project.container_repositories.all_migrated? } + + context 'with no repositories' do + it { is_expected.to be_truthy } + end + + context 'with only recent repositories' do + let_it_be(:container_repository1) { create(:container_repository, project: project) } + let_it_be_with_reload(:container_repository2) { create(:container_repository, project: project) } + + it { is_expected.to be_truthy } + + context 'with one old non migrated repository' do + before do + container_repository2.update!(created_at: described_class::MIGRATION_PHASE_1_ENDED_AT - 3.months) + end + + it { is_expected.to be_falsey } + end + + context 'with one old migrated repository' do + before do + container_repository2.update!( + created_at: described_class::MIGRATION_PHASE_1_ENDED_AT - 3.months, + migration_state: 'import_done', + migration_import_done_at: Time.zone.now + ) + end + + it { is_expected.to be_truthy } + end + end + end + describe '.with_enabled_policy' do let_it_be(:repository) { create(:container_repository) } let_it_be(:repository2) { create(:container_repository) } @@ -1168,6 +1255,17 @@ RSpec.describe ContainerRepository, :aggregate_failures do end end + context 'not found response' do + let(:response) { :not_found } + + it 'completes the migration' do + expect(subject).to eq(false) + + expect(container_repository).to be_import_done + expect(container_repository.reload.migration_skipped_reason).to eq('not_found') + end + end + context 'other response' do let(:response) { :error } @@ -1185,6 +1283,30 @@ RSpec.describe ContainerRepository, :aggregate_failures do end end + describe '#retried_too_many_times?' do + subject { repository.retried_too_many_times? } + + before do + stub_application_setting(container_registry_import_max_retries: 3) + end + + context 'migration_retries_count is equal or greater than max_retries' do + before do + repository.update_column(:migration_retries_count, 3) + end + + it { is_expected.to eq(true) } + end + + context 'migration_retries_count is lower than max_retries' do + before do + repository.update_column(:migration_retries_count, 2) + end + + it { is_expected.to eq(false) } + end + end + context 'with repositories' do let_it_be_with_reload(:repository) { create(:container_repository, :cleanup_unscheduled) } let_it_be(:other_repository) { create(:container_repository, :cleanup_unscheduled) } @@ -1241,11 +1363,12 @@ RSpec.describe ContainerRepository, :aggregate_failures do let_it_be(:import_done_repository) { create(:container_repository, :import_done, migration_pre_import_done_at: 3.days.ago, migration_import_done_at: 2.days.ago) } let_it_be(:import_aborted_repository) { create(:container_repository, :import_aborted, migration_pre_import_done_at: 5.days.ago, migration_aborted_at: 1.day.ago) } let_it_be(:pre_import_done_repository) { create(:container_repository, :pre_import_done, migration_pre_import_done_at: 1.hour.ago) } + let_it_be(:import_skipped_repository) { create(:container_repository, :import_skipped, migration_skipped_at: 90.minutes.ago) } subject { described_class.recently_done_migration_step } it 'returns completed imports by done_at date' do - expect(subject.to_a).to eq([pre_import_done_repository, import_aborted_repository, import_done_repository]) + expect(subject.to_a).to eq([pre_import_done_repository, import_skipped_repository, import_aborted_repository, import_done_repository]) end end @@ -1255,7 +1378,7 @@ RSpec.describe ContainerRepository, :aggregate_failures do subject { described_class.ready_for_import } before do - stub_application_setting(container_registry_import_target_plan: root_group.actual_plan_name) + stub_application_setting(container_registry_import_target_plan: valid_container_repository.migration_plan) end it 'works' do @@ -1266,13 +1389,15 @@ RSpec.describe ContainerRepository, :aggregate_failures do describe '#last_import_step_done_at' do let_it_be(:aborted_at) { Time.zone.now - 1.hour } let_it_be(:pre_import_done_at) { Time.zone.now - 2.hours } + let_it_be(:skipped_at) { Time.zone.now - 3.hours } subject { repository.last_import_step_done_at } before do repository.update_columns( migration_pre_import_done_at: pre_import_done_at, - migration_aborted_at: aborted_at + migration_aborted_at: aborted_at, + migration_skipped_at: skipped_at ) end |