181 lines
6.1 KiB
Ruby
181 lines
6.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'spec_helper'
|
|
|
|
RSpec.describe Gitlab::Database::Decomposition::Migrate, :delete, query_analyzers: false, feature_category: :cell do
|
|
let(:ci_database_name) do
|
|
config = ActiveRecord::Base.configurations.find_db_config(Rails.env).configuration_hash
|
|
|
|
"#{config[:database]}_ci"
|
|
end
|
|
|
|
let(:ci_connection) do
|
|
database_model = self.class.const_set(:TestCiApplicationRecord, Class.new(ApplicationRecord))
|
|
|
|
database_model.establish_connection(
|
|
ActiveRecord::DatabaseConfigurations::HashConfig.new(
|
|
ActiveRecord::Base.connection_db_config.env_name,
|
|
'ci',
|
|
ActiveRecord::Base.connection_db_config.configuration_hash.dup.merge(database: ci_database_name)
|
|
)
|
|
)
|
|
|
|
Gitlab::Database::LoadBalancing::Setup.new(database_model).setup
|
|
|
|
database_model.connection
|
|
end
|
|
|
|
let(:backup_location_postfix) { SecureRandom.alphanumeric(10) }
|
|
|
|
before do
|
|
skip_if_database_exists(:ci)
|
|
|
|
allow(SecureRandom).to receive(:alphanumeric).with(10).and_return(backup_location_postfix)
|
|
end
|
|
|
|
after do
|
|
Milestone.delete_all
|
|
Ci::Pipeline.delete_all
|
|
end
|
|
|
|
describe '#new' do
|
|
context 'when backup_location is not specified' do
|
|
subject(:instance) { described_class.new }
|
|
|
|
it 'defaults to subdirectory of configured backup location' do
|
|
expect(instance.instance_variable_get(:@backup_location)).to eq(
|
|
File.join(Gitlab.config.backup.path, "migration_#{backup_location_postfix}")
|
|
)
|
|
end
|
|
end
|
|
|
|
context 'when backup_location is specified' do
|
|
let(:backup_base_location) { Rails.root.join('tmp') }
|
|
|
|
subject(:instance) { described_class.new(backup_base_location: backup_base_location) }
|
|
|
|
it 'uses subdirectory of specified backup_location' do
|
|
expect(instance.instance_variable_get(:@backup_location)).to eq(
|
|
File.join(backup_base_location, "migration_#{backup_location_postfix}")
|
|
)
|
|
end
|
|
|
|
context 'when specified_backup_location does not exist' do
|
|
let(:backup_base_location) { Rails.root.join('tmp', SecureRandom.alphanumeric(10)) }
|
|
|
|
context 'and creation of the directory succeeds' do
|
|
it 'uses subdirectory of specified backup_location' do
|
|
expect(instance.instance_variable_get(:@backup_location)).to eq(
|
|
File.join(backup_base_location, "migration_#{backup_location_postfix}")
|
|
)
|
|
end
|
|
end
|
|
|
|
context 'and creation of the directory fails' do
|
|
before do
|
|
allow(FileUtils).to receive(:mkdir_p).with(backup_base_location).and_raise(Errno::EROFS.new)
|
|
end
|
|
|
|
it 'raises error' do
|
|
expect { instance.process! }.to raise_error(
|
|
Gitlab::Database::Decomposition::MigrateError,
|
|
"Failed to create directory #{backup_base_location}: Read-only file system"
|
|
)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '#process!' do
|
|
subject(:process) { described_class.new.process! }
|
|
|
|
before do
|
|
# Database `ci` is not configured. But it can still exist. So drop and create it
|
|
ActiveRecord::Base.connection.execute("DROP DATABASE IF EXISTS #{ci_database_name} WITH (FORCE)")
|
|
ActiveRecord::Base.connection.execute("CREATE DATABASE #{ci_database_name}")
|
|
end
|
|
|
|
context 'when the checks pass' do
|
|
let!(:milestone) { create(:milestone) }
|
|
let!(:ci_pipeline) { create(:ci_pipeline) }
|
|
|
|
it 'copies main database to ci database' do
|
|
process
|
|
|
|
ci_milestones = ci_connection.execute("SELECT COUNT(*) FROM milestones").getvalue(0, 0)
|
|
ci_pipelines = ci_connection.execute("SELECT COUNT(*) FROM ci_pipelines").getvalue(0, 0)
|
|
|
|
expect(ci_milestones).to be(Milestone.count)
|
|
expect(ci_pipelines).to be(Ci::Pipeline.count)
|
|
end
|
|
end
|
|
|
|
context 'when local diskspace is not enough' do
|
|
let(:backup_location) { described_class.new.backup_location }
|
|
let(:fake_stats) { instance_double(Sys::Filesystem::Stat, bytes_free: 1000) }
|
|
|
|
before do
|
|
allow(Sys::Filesystem).to receive(:stat).with(File.expand_path("#{backup_location}/../")).and_return(fake_stats)
|
|
end
|
|
|
|
it 'raises error' do
|
|
expect { process }.to raise_error(
|
|
Gitlab::Database::Decomposition::MigrateError,
|
|
/Not enough diskspace available on #{backup_location}: Available: (.+?), Needed: (.+?)/
|
|
)
|
|
end
|
|
end
|
|
|
|
context 'when connection to ci database fails' do
|
|
before do
|
|
ActiveRecord::Base.connection.execute("DROP DATABASE IF EXISTS #{ci_database_name} WITH (FORCE)")
|
|
end
|
|
|
|
it 'raises error' do
|
|
host = ActiveRecord::Base.configurations.find_db_config(Rails.env).configuration_hash[:host]
|
|
expect { process }.to raise_error(
|
|
Gitlab::Database::Decomposition::MigrateError,
|
|
"Can't connect to database '#{ci_database_name} on host '#{host}'. Ensure the database has been created.")
|
|
end
|
|
end
|
|
|
|
context 'when ci database is not empty' do
|
|
before do
|
|
ci_connection.execute("CREATE TABLE IF NOT EXISTS _test_table (id integer, primary key (id))")
|
|
end
|
|
|
|
it 'raises error' do
|
|
expect { process }.to raise_error(
|
|
Gitlab::Database::Decomposition::MigrateError,
|
|
"Database '#{ci_database_name}' is not empty"
|
|
)
|
|
end
|
|
end
|
|
|
|
context 'when already on decomposed setup' do
|
|
before do
|
|
allow(Gitlab::Database).to receive(:database_mode).and_return(Gitlab::Database::MODE_MULTIPLE_DATABASES)
|
|
end
|
|
|
|
it 'raises error' do
|
|
expect { process }.to raise_error(
|
|
Gitlab::Database::Decomposition::MigrateError,
|
|
"GitLab is already configured to run on multiple databases"
|
|
)
|
|
end
|
|
end
|
|
|
|
context 'when not all background migrations are finished' do
|
|
let!(:batched_migration) { create(:batched_background_migration, :active) }
|
|
|
|
it 'raises error' do
|
|
expect { process }.to raise_error(
|
|
Gitlab::Database::Decomposition::MigrateError,
|
|
"Found 1 unfinished Background Migration(s). Please wait until they are finished."
|
|
)
|
|
end
|
|
end
|
|
end
|
|
end
|