Commit c30fdacd authored by Will Meek's avatar Will Meek Committed by Lin Jen-Shin

CI check that non-superuser can migrate

This adds a CI check and a rake task
to check that a non-superuser can
run a DB migrate
parent fd10ab10
......@@ -429,6 +429,13 @@ db:check-migrations-decomposed:
- .decomposed-database
- .rails:rules:decomposed-databases
db:migrate-non-superuser:
extends:
- .db-job-base
- .rails:rules:ee-and-foss-mr-with-migration
script:
- bundle exec rake gitlab:db:reset_as_non_superuser
db:gitlabcom-database-testing:
extends: .rails:rules:db:gitlabcom-database-testing
stage: test
......
......@@ -109,6 +109,26 @@ module Gitlab
name.to_s == CI_DATABASE_NAME
end
class PgUser < ApplicationRecord
self.table_name = 'pg_user'
self.primary_key = :usename
end
# rubocop: disable CodeReuse/ActiveRecord
def self.check_for_non_superuser
user = PgUser.find_by('usename = CURRENT_USER')
am_i_superuser = user.usesuper
Gitlab::AppLogger.info(
"Account details: User: \"#{user.usename}\", UseSuper: (#{am_i_superuser})"
)
raise 'Error: detected superuser' if am_i_superuser
rescue ActiveRecord::StatementInvalid
raise 'User CURRENT_USER not found'
end
# rubocop: enable CodeReuse/ActiveRecord
def self.check_postgres_version_and_print_warning
return if Gitlab::Runtime.rails_runner?
......
......@@ -270,6 +270,19 @@ namespace :gitlab do
end
end
desc 'Run migration as gitlab non-superuser'
task :reset_as_non_superuser, [:username] => :environment do |_, args|
username = args.fetch(:username, 'gitlab')
puts "Migrate using username #{username}"
Rake::Task['db:drop'].invoke
Rake::Task['db:create'].invoke
ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
ActiveRecord::Base.establish_connection(db_config.configuration_hash.merge(username: username)) # rubocop: disable Database/EstablishConnection
Gitlab::Database.check_for_non_superuser
Rake::Task['db:migrate'].invoke
end
end
# Only for development environments,
# we execute pending data migrations inline for convenience.
Rake::Task['db:migrate'].enhance do
......
......@@ -104,6 +104,34 @@ RSpec.describe Gitlab::Database do
end
end
describe '.check_for_non_superuser' do
subject { described_class.check_for_non_superuser }
let(:non_superuser) { Gitlab::Database::PgUser.new(usename: 'foo', usesuper: false ) }
let(:superuser) { Gitlab::Database::PgUser.new(usename: 'bar', usesuper: true) }
it 'prints user details if not superuser' do
allow(Gitlab::Database::PgUser).to receive(:find_by).with('usename = CURRENT_USER').and_return(non_superuser)
expect(Gitlab::AppLogger).to receive(:info).with("Account details: User: \"foo\", UseSuper: (false)")
subject
end
it 'raises an exception if superuser' do
allow(Gitlab::Database::PgUser).to receive(:find_by).with('usename = CURRENT_USER').and_return(superuser)
expect(Gitlab::AppLogger).to receive(:info).with("Account details: User: \"bar\", UseSuper: (true)")
expect { subject }.to raise_error('Error: detected superuser')
end
it 'catches exception if find_by fails' do
allow(Gitlab::Database::PgUser).to receive(:find_by).with('usename = CURRENT_USER').and_raise(ActiveRecord::StatementInvalid)
expect { subject }.to raise_error('User CURRENT_USER not found')
end
end
describe '.check_postgres_version_and_print_warning' do
let(:reflect) { instance_spy(Gitlab::Database::Reflection) }
......
......@@ -446,6 +446,44 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout do
end
end
describe 'gitlab:db:reset_as_non_superuser' 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(:config_hash) { { username: 'foo' } }
it 'migrate as nonsuperuser check with default 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: 'gitlab' })
expect(Gitlab::Database).to receive(:check_for_non_superuser)
expect(Rake::Task['db:migrate']).to receive(:invoke)
run_rake_task('gitlab:db:reset_as_non_superuser')
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(Gitlab::Database).to receive(:check_for_non_superuser)
expect(Rake::Task['db:migrate']).to receive(:invoke)
run_rake_task('gitlab:db:reset_as_non_superuser', '[foo]')
end
end
def run_rake_task(task_name, arguments = '')
Rake::Task[task_name].reenable
Rake.application.invoke_task("#{task_name}#{arguments}")
......
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