diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-20 11:10:13 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-20 11:10:13 +0000 |
commit | 0ea3fcec397b69815975647f5e2aa5fe944a8486 (patch) | |
tree | 7979381b89d26011bcf9bdc989a40fcc2f1ed4ff /spec/tasks | |
parent | 72123183a20411a36d607d70b12d57c484394c8e (diff) | |
download | gitlab-ce-0ea3fcec397b69815975647f5e2aa5fe944a8486.tar.gz |
Add latest changes from gitlab-org/gitlab@15-1-stable-eev15.1.0-rc42
Diffstat (limited to 'spec/tasks')
-rw-r--r-- | spec/tasks/gitlab/background_migrations_rake_spec.rb | 2 | ||||
-rw-r--r-- | spec/tasks/gitlab/backup_rake_spec.rb | 2 | ||||
-rw-r--r-- | spec/tasks/gitlab/db/decomposition/rollback/bump_ci_sequences_rake_spec.rb | 103 | ||||
-rw-r--r-- | spec/tasks/gitlab/db/lock_writes_rake_spec.rb | 177 | ||||
-rw-r--r-- | spec/tasks/gitlab/db/validate_config_rake_spec.rb | 41 | ||||
-rw-r--r-- | spec/tasks/gitlab/db_rake_spec.rb | 17 | ||||
-rw-r--r-- | spec/tasks/gitlab/pages_rake_spec.rb | 80 | ||||
-rw-r--r-- | spec/tasks/rubocop_rake_spec.rb | 11 |
8 files changed, 335 insertions, 98 deletions
diff --git a/spec/tasks/gitlab/background_migrations_rake_spec.rb b/spec/tasks/gitlab/background_migrations_rake_spec.rb index 36623e86f27..bbd33f71e60 100644 --- a/spec/tasks/gitlab/background_migrations_rake_spec.rb +++ b/spec/tasks/gitlab/background_migrations_rake_spec.rb @@ -112,7 +112,7 @@ RSpec.describe 'gitlab:background_migrations namespace rake tasks' do let(:main_database_name) { Gitlab::Database::MAIN_DATABASE_NAME } let(:model) { Gitlab::Database.database_base_models[main_database_name] } let(:connection) { double(:connection) } - let(:base_models) { { 'main' => model } } + let(:base_models) { { 'main' => model }.with_indifferent_access } around do |example| Gitlab::Database::SharedModel.using_connection(model.connection) do diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 52a0a9a7385..4a3b81a072f 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -465,7 +465,7 @@ RSpec.describe 'gitlab:app namespace rake task', :delete do stub_env('GITLAB_BACKUP_MAX_STORAGE_CONCURRENCY', 2) expect(::Backup::Repositories).to receive(:new) - .with(anything, strategy: anything, storages: []) + .with(anything, strategy: anything, storages: [], paths: []) .and_call_original expect(::Backup::GitalyBackup).to receive(:new).with(anything, max_parallelism: 5, storage_parallelism: 2, incremental: false).and_call_original diff --git a/spec/tasks/gitlab/db/decomposition/rollback/bump_ci_sequences_rake_spec.rb b/spec/tasks/gitlab/db/decomposition/rollback/bump_ci_sequences_rake_spec.rb new file mode 100644 index 00000000000..29b80176ef8 --- /dev/null +++ b/spec/tasks/gitlab/db/decomposition/rollback/bump_ci_sequences_rake_spec.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +require 'rake_helper' + +RSpec.describe 'gitlab:db:decomposition:rollback:bump_ci_sequences', :silence_stdout do + before :all do + Rake.application.rake_require 'tasks/gitlab/db/decomposition/rollback/bump_ci_sequences' + + # empty task as env is already loaded + Rake::Task.define_task :environment + end + + let(:expected_error_message) do + <<-EOS.strip_heredoc + Please specify a positive integer `increase_by` value + Example: rake gitlab:db:decomposition:rollback:bump_ci_sequences[100000] + EOS + end + + let(:main_sequence_name) { 'issues_id_seq' } + let(:ci_sequence_name) { 'ci_build_needs_id_seq' } + + # This is just to make sure that all of the sequences start with `is_called=True` + # which means that the next call to nextval() is going to increment the sequence. + # To give predictable test results. + before do + ApplicationRecord.connection.select_value("select nextval($1)", nil, [ci_sequence_name]) + end + + context 'when passing wrong argument' do + it 'will print an error message and exit when passing no argument' do + expect do + run_rake_task('gitlab:db:decomposition:rollback:bump_ci_sequences') + end.to raise_error(SystemExit) { |error| expect(error.status).to eq(1) } + .and output(expected_error_message).to_stdout + end + + it 'will print an error message and exit when passing a non positive integer value' do + expect do + run_rake_task('gitlab:db:decomposition:rollback:bump_ci_sequences', '-5') + end.to raise_error(SystemExit) { |error| expect(error.status).to eq(1) } + .and output(expected_error_message).to_stdout + end + end + + context 'when bumping the ci sequences' do + it 'changes ci sequences by the passed argument `increase_by` value on the main database' do + expect do + run_rake_task('gitlab:db:decomposition:rollback:bump_ci_sequences', '15') + end.to change { + last_value_of_sequence(ApplicationRecord.connection, ci_sequence_name) + }.by(16) # the +1 is because the sequence has is_called = true + end + + it 'will still increase the value of sequences that have is_called = False' do + # see `is_called`: https://www.postgresql.org/docs/12/functions-sequence.html + # choosing a new arbitrary value for the sequence + new_value = last_value_of_sequence(ApplicationRecord.connection, ci_sequence_name) + 1000 + ApplicationRecord.connection.select_value("select setval($1, $2, false)", nil, [ci_sequence_name, new_value]) + expect do + run_rake_task('gitlab:db:decomposition:rollback:bump_ci_sequences', '15') + end.to change { + last_value_of_sequence(ApplicationRecord.connection, ci_sequence_name) + }.by(15) + end + + it 'resets the INCREMENT value of the sequences back to 1 for the following calls to nextval()' do + run_rake_task('gitlab:db:decomposition:rollback:bump_ci_sequences', '15') + value_1 = ApplicationRecord.connection.select_value("select nextval($1)", nil, [ci_sequence_name]) + value_2 = ApplicationRecord.connection.select_value("select nextval($1)", nil, [ci_sequence_name]) + expect(value_2 - value_1).to eq(1) + end + + it 'does not change the sequences on the gitlab_main tables' do + expect do + run_rake_task('gitlab:db:decomposition:rollback:bump_ci_sequences', '10') + end.to change { + last_value_of_sequence(ApplicationRecord.connection, main_sequence_name) + }.by(0) + .and change { + last_value_of_sequence(ApplicationRecord.connection, ci_sequence_name) + }.by(11) # the +1 is because the sequence has is_called = true + end + end + + context 'when multiple databases' do + before do + skip_if_multiple_databases_not_setup + end + + it 'does not change ci sequences on the ci database' do + expect do + run_rake_task('gitlab:db:decomposition:rollback:bump_ci_sequences', '10') + end.to change { + last_value_of_sequence(Ci::ApplicationRecord.connection, ci_sequence_name) + }.by(0) + end + end +end + +def last_value_of_sequence(connection, sequence_name) + connection.select_value("select last_value from #{sequence_name}") +end diff --git a/spec/tasks/gitlab/db/lock_writes_rake_spec.rb b/spec/tasks/gitlab/db/lock_writes_rake_spec.rb new file mode 100644 index 00000000000..034c520887e --- /dev/null +++ b/spec/tasks/gitlab/db/lock_writes_rake_spec.rb @@ -0,0 +1,177 @@ +# frozen_string_literal: true + +require 'rake_helper' + +RSpec.describe 'gitlab:db:lock_writes', :silence_stdout, :reestablished_active_record_base do + before :all do + Rake.application.rake_require 'active_record/railties/databases' + Rake.application.rake_require 'tasks/seed_fu' + Rake.application.rake_require 'tasks/gitlab/db/validate_config' + Rake.application.rake_require 'tasks/gitlab/db/lock_writes' + + # empty task as env is already loaded + Rake::Task.define_task :environment + end + + let!(:project) { create(:project) } + let!(:ci_build) { create(:ci_build) } + let(:main_connection) { ApplicationRecord.connection } + let(:ci_connection) { Ci::ApplicationRecord.connection } + + context 'single database' do + before do + skip_if_multiple_databases_are_setup + end + + context 'when locking writes' do + it 'does not add any triggers to the main schema tables' do + expect do + run_rake_task('gitlab:db:lock_writes') + end.to change { + number_of_triggers(main_connection) + }.by(0) + end + + it 'will be still able to modify tables that belong to the main two schemas' do + run_rake_task('gitlab:db:lock_writes') + expect do + Project.last.touch + Ci::Build.last.touch + end.not_to raise_error + end + end + end + + context 'multiple databases' do + before do + skip_if_multiple_databases_not_setup + end + + context 'when locking writes' do + it 'adds 3 triggers to the ci schema tables on the main database' do + expect do + run_rake_task('gitlab:db:lock_writes') + end.to change { + number_of_triggers_on(main_connection, Ci::Build.table_name) + }.by(3) # Triggers to block INSERT / UPDATE / DELETE + # Triggers on TRUNCATE are not added to the information_schema.triggers + # See https://www.postgresql.org/message-id/16934.1568989957%40sss.pgh.pa.us + end + + it 'adds 3 triggers to the main schema tables on the ci database' do + expect do + run_rake_task('gitlab:db:lock_writes') + end.to change { + number_of_triggers_on(ci_connection, Project.table_name) + }.by(3) # Triggers to block INSERT / UPDATE / DELETE + # Triggers on TRUNCATE are not added to the information_schema.triggers + # See https://www.postgresql.org/message-id/16934.1568989957%40sss.pgh.pa.us + end + + it 'still allows writes on the tables with the correct connections' do + Project.update_all(updated_at: Time.now) + Ci::Build.update_all(updated_at: Time.now) + end + + it 'still allows writing to gitlab_shared schema on any connection' do + connections = [main_connection, ci_connection] + connections.each do |connection| + Gitlab::Database::SharedModel.using_connection(connection) do + LooseForeignKeys::DeletedRecord.create!( + fully_qualified_table_name: "public.projects", + primary_key_value: 1, + cleanup_attempts: 0 + ) + end + end + end + + it 'prevents writes on the main tables on the ci database' do + run_rake_task('gitlab:db:lock_writes') + expect do + ci_connection.execute("delete from projects") + end.to raise_error(ActiveRecord::StatementInvalid, /Table: "projects" is write protected/) + end + + it 'prevents writes on the ci tables on the main database' do + run_rake_task('gitlab:db:lock_writes') + expect do + main_connection.execute("delete from ci_builds") + end.to raise_error(ActiveRecord::StatementInvalid, /Table: "ci_builds" is write protected/) + end + + it 'prevents truncating a ci table on the main database' do + run_rake_task('gitlab:db:lock_writes') + expect do + main_connection.execute("truncate ci_build_needs") + end.to raise_error(ActiveRecord::StatementInvalid, /Table: "ci_build_needs" is write protected/) + end + + it 'retries again if it receives a statement_timeout a few number of times' do + error_message = "PG::QueryCanceled: ERROR: canceling statement due to statement timeout" + call_count = 0 + allow(main_connection).to receive(:execute) do |statement| + if statement.include?("CREATE TRIGGER") + call_count += 1 + raise(ActiveRecord::QueryCanceled, error_message) if call_count.even? + end + end + run_rake_task('gitlab:db:lock_writes') + end + + it 'raises the exception if it happened many times' do + error_message = "PG::QueryCanceled: ERROR: canceling statement due to statement timeout" + allow(main_connection).to receive(:execute) do |statement| + if statement.include?("CREATE TRIGGER") + raise(ActiveRecord::QueryCanceled, error_message) + end + end + + expect do + run_rake_task('gitlab:db:lock_writes') + end.to raise_error(ActiveRecord::QueryCanceled) + end + end + + context 'when unlocking writes' do + before do + run_rake_task('gitlab:db:lock_writes') + end + + it 'removes the write protection triggers from the gitlab_main tables on the ci database' do + expect do + run_rake_task('gitlab:db:unlock_writes') + end.to change { + number_of_triggers_on(ci_connection, Project.table_name) + }.by(-3) # Triggers to block INSERT / UPDATE / DELETE + # Triggers on TRUNCATE are not added to the information_schema.triggers + # See https://www.postgresql.org/message-id/16934.1568989957%40sss.pgh.pa.us + + expect do + ci_connection.execute("delete from projects") + end.not_to raise_error + end + + it 'removes the write protection triggers from the gitlab_ci tables on the main database' do + expect do + run_rake_task('gitlab:db:unlock_writes') + end.to change { + number_of_triggers_on(main_connection, Ci::Build.table_name) + }.by(-3) + + expect do + main_connection.execute("delete from ci_builds") + end.not_to raise_error + end + end + end + + def number_of_triggers(connection) + connection.select_value("SELECT count(*) FROM information_schema.triggers") + end + + def number_of_triggers_on(connection, table_name) + connection + .select_value("SELECT count(*) FROM information_schema.triggers WHERE event_object_table=$1", nil, [table_name]) + end +end diff --git a/spec/tasks/gitlab/db/validate_config_rake_spec.rb b/spec/tasks/gitlab/db/validate_config_rake_spec.rb index 0b2c844a91f..03d7504e8b1 100644 --- a/spec/tasks/gitlab/db/validate_config_rake_spec.rb +++ b/spec/tasks/gitlab/db/validate_config_rake_spec.rb @@ -3,6 +3,10 @@ require 'rake_helper' RSpec.describe 'gitlab:db:validate_config', :silence_stdout do + # We don't need to delete this data since it only modifies `ar_internal_metadata` + # which would not be cleaned either by `DbCleaner` + self.use_transactional_tests = false + before :all do Rake.application.rake_require 'active_record/railties/databases' Rake.application.rake_require 'tasks/seed_fu' @@ -111,6 +115,26 @@ RSpec.describe 'gitlab:db:validate_config', :silence_stdout do end it_behaves_like 'validates successfully' + + context 'when config is pointing to incorrect server' do + let(:test_config) do + { + main: main_database_config.merge(port: 11235) + } + end + + it_behaves_like 'validates successfully' + end + + context 'when config is pointing to non-existent database' do + let(:test_config) do + { + main: main_database_config.merge(database: 'non_existent_database') + } + end + + it_behaves_like 'validates successfully' + end end context 'when main: uses database_tasks=false' do @@ -181,6 +205,23 @@ RSpec.describe 'gitlab:db:validate_config', :silence_stdout do it_behaves_like 'raises an error', /The 'ci' since it is using 'database_tasks: false' should share database with 'main:'/ end end + + context 'one of the databases is in read-only mode' do + let(:test_config) do + { + main: main_database_config + } + end + + let(:exception) { ActiveRecord::StatementInvalid.new("READONLY") } + + before do + allow(exception).to receive(:cause).and_return(PG::ReadOnlySqlTransaction.new("cannot execute INSERT in a read-only transaction")) + allow(ActiveRecord::InternalMetadata).to receive(:upsert).at_least(:once).and_raise(exception) + end + + it_behaves_like 'validates successfully' + end end %w[db:migrate db:schema:load db:schema:dump].each do |task| diff --git a/spec/tasks/gitlab/db_rake_spec.rb b/spec/tasks/gitlab/db_rake_spec.rb index e340d568269..d8199c09ca1 100644 --- a/spec/tasks/gitlab/db_rake_spec.rb +++ b/spec/tasks/gitlab/db_rake_spec.rb @@ -822,18 +822,20 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout do let(:connection_pool) { instance_double(ActiveRecord::ConnectionAdapters::ConnectionPool ) } let(:connection) { instance_double(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) } let(:configurations) { double(ActiveRecord::DatabaseConfigurations) } - let(:configuration) { instance_double(ActiveRecord::DatabaseConfigurations::HashConfig) } + let(:configuration) { instance_double(ActiveRecord::DatabaseConfigurations::HashConfig, env_name: 'test', name: 'main') } let(:config_hash) { { username: 'foo' } } - it 'migrate as nonsuperuser check with default username' do + before do allow(Rake::Task['db:drop']).to receive(:invoke) allow(Rake::Task['db:create']).to receive(:invoke) allow(ActiveRecord::Base).to receive(:configurations).and_return(configurations) allow(configurations).to receive(:configs_for).and_return([configuration]) allow(configuration).to receive(:configuration_hash).and_return(config_hash) allow(ActiveRecord::Base).to receive(:establish_connection).and_return(connection_pool) + end - expect(config_hash).to receive(:merge).with({ username: 'gitlab' }) + it 'migrate as nonsuperuser check with default username' do + expect(config_hash).to receive(:merge).with({ username: 'gitlab' }).and_call_original expect(Gitlab::Database).to receive(:check_for_non_superuser) expect(Rake::Task['db:migrate']).to receive(:invoke) @@ -841,14 +843,7 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout do end it 'migrate as nonsuperuser check with specified username' do - allow(Rake::Task['db:drop']).to receive(:invoke) - allow(Rake::Task['db:create']).to receive(:invoke) - allow(ActiveRecord::Base).to receive(:configurations).and_return(configurations) - allow(configurations).to receive(:configs_for).and_return([configuration]) - allow(configuration).to receive(:configuration_hash).and_return(config_hash) - allow(ActiveRecord::Base).to receive(:establish_connection).and_return(connection_pool) - - expect(config_hash).to receive(:merge).with({ username: 'foo' }) + expect(config_hash).to receive(:merge).with({ username: 'foo' }).and_call_original expect(Gitlab::Database).to receive(:check_for_non_superuser) expect(Rake::Task['db:migrate']).to receive(:invoke) diff --git a/spec/tasks/gitlab/pages_rake_spec.rb b/spec/tasks/gitlab/pages_rake_spec.rb index d4bfcafa7b4..9e3d5c3ccf0 100644 --- a/spec/tasks/gitlab/pages_rake_spec.rb +++ b/spec/tasks/gitlab/pages_rake_spec.rb @@ -7,86 +7,6 @@ RSpec.describe 'gitlab:pages', :silence_stdout do Rake.application.rake_require 'tasks/gitlab/pages' end - describe 'migrate_legacy_storage task' do - subject { run_rake_task('gitlab:pages:migrate_legacy_storage') } - - it 'calls migration service' do - expect_next_instance_of(::Pages::MigrateFromLegacyStorageService, anything, - ignore_invalid_entries: false, - mark_projects_as_not_deployed: false) do |service| - expect(service).to receive(:execute_with_threads).with(threads: 3, batch_size: 10).and_call_original - end - - subject - end - - it 'uses PAGES_MIGRATION_THREADS environment variable' do - stub_env('PAGES_MIGRATION_THREADS', '5') - - expect_next_instance_of(::Pages::MigrateFromLegacyStorageService, anything, - ignore_invalid_entries: false, - mark_projects_as_not_deployed: false) do |service| - expect(service).to receive(:execute_with_threads).with(threads: 5, batch_size: 10).and_call_original - end - - subject - end - - it 'uses PAGES_MIGRATION_BATCH_SIZE environment variable' do - stub_env('PAGES_MIGRATION_BATCH_SIZE', '100') - - expect_next_instance_of(::Pages::MigrateFromLegacyStorageService, anything, - ignore_invalid_entries: false, - mark_projects_as_not_deployed: false) do |service| - expect(service).to receive(:execute_with_threads).with(threads: 3, batch_size: 100).and_call_original - end - - subject - end - - it 'uses PAGES_MIGRATION_IGNORE_INVALID_ENTRIES environment variable' do - stub_env('PAGES_MIGRATION_IGNORE_INVALID_ENTRIES', 'true') - - expect_next_instance_of(::Pages::MigrateFromLegacyStorageService, anything, - ignore_invalid_entries: true, - mark_projects_as_not_deployed: false) do |service| - expect(service).to receive(:execute_with_threads).with(threads: 3, batch_size: 10).and_call_original - end - - subject - end - - it 'uses PAGES_MIGRATION_MARK_PROJECTS_AS_NOT_DEPLOYED environment variable' do - stub_env('PAGES_MIGRATION_MARK_PROJECTS_AS_NOT_DEPLOYED', 'true') - - expect_next_instance_of(::Pages::MigrateFromLegacyStorageService, anything, - ignore_invalid_entries: false, - mark_projects_as_not_deployed: true) do |service| - expect(service).to receive(:execute_with_threads).with(threads: 3, batch_size: 10).and_call_original - end - - subject - end - end - - describe 'clean_migrated_zip_storage task' do - it 'removes only migrated deployments' do - regular_deployment = create(:pages_deployment) - migrated_deployment = create(:pages_deployment, :migrated) - - regular_deployment.project.update_pages_deployment!(regular_deployment) - migrated_deployment.project.update_pages_deployment!(migrated_deployment) - - expect(PagesDeployment.all).to contain_exactly(regular_deployment, migrated_deployment) - - run_rake_task('gitlab:pages:clean_migrated_zip_storage') - - expect(PagesDeployment.all).to contain_exactly(regular_deployment) - expect(PagesDeployment.find_by_id(regular_deployment.id)).not_to be_nil - expect(PagesDeployment.find_by_id(migrated_deployment.id)).to be_nil - end - end - describe 'gitlab:pages:deployments:migrate_to_object_storage' do subject { run_rake_task('gitlab:pages:deployments:migrate_to_object_storage') } diff --git a/spec/tasks/rubocop_rake_spec.rb b/spec/tasks/rubocop_rake_spec.rb index cf7e45aae28..a92d7dc2e52 100644 --- a/spec/tasks/rubocop_rake_spec.rb +++ b/spec/tasks/rubocop_rake_spec.rb @@ -8,6 +8,7 @@ require 'fileutils' require_relative '../support/silence_stdout' require_relative '../support/helpers/next_instance_of' require_relative '../support/helpers/rake_helpers' +require_relative '../../rubocop/formatter/todo_formatter' require_relative '../../rubocop/todo_dir' RSpec.describe 'rubocop rake tasks', :silence_stdout do @@ -29,22 +30,22 @@ RSpec.describe 'rubocop rake tasks', :silence_stdout do around do |example| Dir.chdir(tmp_dir) do - with_inflections do - example.run + ::RuboCop::Formatter::TodoFormatter.with_base_directory(rubocop_todo_dir) do + with_inflections do + example.run + end end end end before do - allow(RuboCop::TodoDir).to receive(:new).and_return(todo_dir) - # This Ruby file will trigger the following 3 offenses. File.write('a.rb', <<~RUBY) a+b RUBY - # Mimic GitLab's .rubocop_todo.yml avoids relying on RuboCop's + # Mimicking GitLab's .rubocop_todo.yml avoids relying on RuboCop's # default.yml configuration. File.write('.rubocop.yml', <<~YAML) <% unless ENV['REVEAL_RUBOCOP_TODO'] == '1' %> |