spec/chef/cookbooks/postgresql/recipes/postgresql_spec.rb (802 lines of code) (raw):
require 'chef_helper'
RSpec.describe 'postgresql' do
let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service postgresql_config database_objects)).converge('gitlab::default') }
let(:postgresql_data_dir) { '/var/opt/gitlab/postgresql/data' }
let(:postgresql_ssl_cert) { File.join(postgresql_data_dir, 'server.crt') }
let(:postgresql_ssl_key) { File.join(postgresql_data_dir, 'server.key') }
let(:postgresql_conf) { File.join(postgresql_data_dir, 'postgresql.conf') }
let(:runtime_conf) { '/var/opt/gitlab/postgresql/data/runtime.conf' }
let(:pg_hba_conf) { '/var/opt/gitlab/postgresql/data/pg_hba.conf' }
before do
allow(Gitlab).to receive(:[]).and_call_original
allow_any_instance_of(PgHelper).to receive(:version).and_return(PGVersion.new('best_version'))
allow_any_instance_of(PgHelper).to receive(:database_version).and_return(PGVersion.new('best_version'))
allow_any_instance_of(PgHelper).to receive(:running_version).and_return(PGVersion.new('best_version'))
end
it 'includes the postgresql::bin recipe' do
expect(chef_run).to include_recipe('postgresql::bin')
end
it 'includes the postgresql::user recipe' do
expect(chef_run).to include_recipe('postgresql::user')
end
it 'includes postgresql::sysctl recipe' do
expect(chef_run).to include_recipe('postgresql::sysctl')
end
it 'does not warn the user that a restart is needed by default' do
allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true)
expect(chef_run).not_to run_ruby_block('warn pending postgresql restart')
end
it 'includes runtime.conf in postgresql.conf' do
expect(chef_run).to render_file(postgresql_conf)
.with_content(/include 'runtime.conf'/)
end
context 'with default settings' do
it_behaves_like 'enabled runit service', 'postgresql', 'root', 'root', 'gitlab-psql', 'gitlab-psql', true
context 'when rendering postgresql.conf' do
it 'correctly sets the shared_preload_libraries default setting' do
expect(chef_run.node['postgresql']['shared_preload_libraries'])
.to be_nil
expect(chef_run).to render_file(postgresql_conf)
.with_content(/shared_preload_libraries = ''/)
end
it 'disables archive mode' do
expect(chef_run).to render_file(
postgresql_conf
).with_content(/archive_mode = off/)
end
it 'enables SSL by default' do
expect(chef_run.node['postgresql']['ssl'])
.to eq('on')
expect(chef_run).to render_file(
postgresql_conf
).with_content(/ssl = on/)
end
it 'sets the default SSL cipher list' do
expect(chef_run).to render_file(
postgresql_conf
).with_content(%r{ssl_ciphers = 'HIGH:MEDIUM:\+3DES:!aNULL:!SSLv3:!TLSv1'})
end
it 'sets the default locations of SSL certificates' do
expect(chef_run).to render_file(
postgresql_conf
).with_content(/ssl_cert_file = 'server.crt'/)
expect(chef_run).to render_file(
postgresql_conf
).with_content(/ssl_key_file = 'server.key'/)
expect(chef_run).to render_file(
postgresql_conf
).with_content(%r{ssl_ca_file = '/opt/gitlab/embedded/ssl/certs/cacert.pem'})
end
it 'leaves synchronous_standby_names empty' do
expect(chef_run.node['postgresql']['synchronous_standby_names'])
.to eq('')
expect(chef_run).to render_file(
postgresql_conf
).with_content(/synchronous_standby_names = ''/)
end
it 'disables wal_log_hints setting' do
expect(chef_run.node['postgresql']['wal_log_hints']).to eq('off')
expect(chef_run).to render_file(
postgresql_conf
).with_content(/wal_log_hints = off/)
end
it 'does not set dynamic_shared_memory_type by default' do
expect(chef_run).not_to render_file(
postgresql_conf
).with_content(/^dynamic_shared_memory_type = /)
end
it 'sets the max_locks_per_transaction setting' do
expect(chef_run.node['postgresql']['max_locks_per_transaction'])
.to eq(128)
expect(chef_run).to render_file(
postgresql_conf
).with_content(/max_locks_per_transaction = 128/)
end
it 'does not include gitlab-geo.conf' do
expect(chef_run).to render_file(postgresql_conf)
.with_content { |content|
expect(content).not_to match(/include_if_exists 'gitlab-geo.conf'/)
}
end
end
it 'generates a self-signed SSL certificate and key' do
stub_gitlab_rb(postgresql: { ssl_cert_file: 'certfile', ssl_key_file: 'keyfile' })
absolute_cert_path = File.join(postgresql_data_dir, 'certfile')
absolute_key_path = File.join(postgresql_data_dir, 'keyfile')
expect(chef_run).to create_file(absolute_cert_path).with(
user: 'gitlab-psql',
group: 'gitlab-psql',
mode: 0400
)
expect(chef_run).to create_file(absolute_key_path).with(
user: 'gitlab-psql',
group: 'gitlab-psql',
mode: 0400
)
expect(chef_run).to render_file(absolute_cert_path)
.with_content(/-----BEGIN CERTIFICATE-----/)
expect(chef_run).to render_file(absolute_key_path)
.with_content(/-----BEGIN RSA PRIVATE KEY-----/)
end
context 'when rendering runtime.conf' do
it 'correctly sets the log_line_prefix default setting' do
expect(chef_run.node['postgresql']['log_line_prefix'])
.to be_nil
expect(chef_run).to render_file(runtime_conf)
.with_content(/log_line_prefix = ''/)
end
it 'does not include log_statement by default' do
expect(chef_run).not_to render_file(runtime_conf)
.with_content(/log_statement = /)
end
it 'sets max_standby settings' do
expect(chef_run).to render_file(
runtime_conf
).with_content(/max_standby_archive_delay = 30s/)
expect(chef_run).to render_file(
runtime_conf
).with_content(/max_standby_streaming_delay = 30s/)
end
it 'sets archive settings' do
expect(chef_run).to render_file(
runtime_conf
).with_content(/archive_command = ''/)
expect(chef_run).to render_file(
runtime_conf
).with_content(/archive_timeout = 0/)
end
it 'sets logging directory' do
expect(chef_run.node['postgresql']['log_directory'])
.to eq('/var/log/gitlab/postgresql')
expect(chef_run).to render_file(
runtime_conf
).with_content(%r(^log_directory = '/var/log/gitlab/postgresql'))
end
it 'disables hot_standby_feedback' do
expect(chef_run.node['postgresql']['hot_standby_feedback'])
.to eq('off')
expect(chef_run).to render_file(
runtime_conf
).with_content(/hot_standby_feedback = off/)
end
it 'sets the random_page_cost setting' do
expect(chef_run.node['postgresql']['random_page_cost'])
.to eq(2.0)
expect(chef_run).to render_file(
runtime_conf
).with_content(/random_page_cost = 2\.0/)
end
it 'sets the log_temp_files setting' do
expect(chef_run.node['postgresql']['log_temp_files'])
.to eq(-1)
expect(chef_run).to render_file(
runtime_conf
).with_content(/log_temp_files = -1/)
end
it 'disables the log_checkpoints setting' do
expect(chef_run.node['postgresql']['log_checkpoints'])
.to eq('off')
expect(chef_run).to render_file(
runtime_conf
).with_content(/log_checkpoints = off/)
end
it 'sets idle_in_transaction_session_timeout' do
expect(chef_run.node['postgresql']['idle_in_transaction_session_timeout'])
.to eq('60000')
expect(chef_run).to render_file(runtime_conf)
.with_content(/idle_in_transaction_session_timeout = 60000/)
end
it 'sets effective_io_concurrency' do
expect(chef_run.node['postgresql']['effective_io_concurrency'])
.to eq(1)
expect(chef_run).to render_file(runtime_conf)
.with_content(/effective_io_concurrency = 1/)
end
it 'sets max_worker_processes' do
expect(chef_run.node['postgresql']['max_worker_processes'])
.to eq(8)
expect(chef_run).to render_file(runtime_conf)
.with_content(/max_worker_processes = 8/)
end
it 'sets max_parallel_workers_per_gather' do
expect(chef_run.node['postgresql']['max_parallel_workers_per_gather'])
.to eq(0)
expect(chef_run).to render_file(runtime_conf)
.with_content(/max_parallel_workers_per_gather = 0/)
end
it 'sets log_lock_waits' do
expect(chef_run.node['postgresql']['log_lock_waits'])
.to eq(1)
expect(chef_run).to render_file(runtime_conf)
.with_content(/log_lock_waits = 1/)
end
it 'sets log_min_duration_statement' do
expect(chef_run.node['postgresql']['log_min_duration_statement'])
.to eq(1000)
expect(chef_run).to render_file(runtime_conf)
.with_content(/log_min_duration_statement = 1000/)
end
it 'sets deadlock_timeout' do
expect(chef_run.node['postgresql']['deadlock_timeout'])
.to eq('5s')
expect(chef_run).to render_file(runtime_conf)
.with_content(/deadlock_timeout = '5s'/)
end
it 'disables track_io_timing' do
expect(chef_run.node['postgresql']['track_io_timing'])
.to eq('off')
expect(chef_run).to render_file(runtime_conf)
.with_content(/track_io_timing = 'off'/)
end
it 'sets default_statistics_target' do
expect(chef_run.node['postgresql']['default_statistics_target'])
.to eq(1000)
expect(chef_run).to render_file(runtime_conf)
.with_content(/default_statistics_target = 1000/)
end
it 'enables the synchronous_commit setting' do
expect(chef_run.node['postgresql']['synchronous_commit'])
.to eq('on')
expect(chef_run).to render_file(
runtime_conf
).with_content(/synchronous_commit = on/)
end
it 'sets log_connections setting' do
expect(chef_run.node['postgresql']['log_connections'])
.to eq('off')
expect(chef_run).to render_file(
runtime_conf
).with_content(/log_connections = off/)
end
it 'sets log_disconnections setting' do
expect(chef_run.node['postgresql']['log_disconnections'])
.to eq('off')
expect(chef_run).to render_file(
runtime_conf
).with_content(/log_disconnections = off/)
end
end
context 'when rendering pg_hba.conf' do
it 'creates a standard pg_hba.conf' do
expect(chef_run).to render_file(pg_hba_conf)
.with_content('local all all peer map=gitlab')
end
it 'cert authentication is disabled by default' do
expect(chef_run).to render_file(pg_hba_conf).with_content { |content|
expect(content).to_not match(/cert$/)
}
end
end
end
context 'with user specified settings' do
before do
stub_gitlab_rb(postgresql: {
shared_preload_libraries: 'pg_stat_statements',
archive_mode: 'on',
username: 'foo',
group: 'bar',
ssl: 'off',
ssl_crl_file: 'revoke.crl',
ssl_ciphers: 'ALL',
log_destination: 'csvlog',
logging_collector: 'on',
log_filename: 'test.log',
log_file_mode: '0600',
log_truncate_on_rotation: 'on',
log_rotation_age: '1d',
log_rotation_size: '10MB',
dynamic_shared_memory_type: 'none',
wal_log_hints: 'on'
},
geo_secondary_role: {
enable: true
})
end
it_behaves_like 'enabled runit service', 'postgresql', 'root', 'root', 'foo', 'bar', true
context 'when rendering postgresql.conf' do
it 'correctly sets the shared_preload_libraries setting' do
expect(chef_run.node['postgresql']['shared_preload_libraries'])
.to eql('pg_stat_statements')
expect(chef_run).to render_file(postgresql_conf)
.with_content(/shared_preload_libraries = 'pg_stat_statements'/)
end
it 'enables archive mode' do
expect(chef_run).to render_file(
postgresql_conf
).with_content(/archive_mode = on/)
end
it 'disables SSL' do
expect(chef_run).to render_file(
postgresql_conf
).with_content(/ssl = off/)
expect(chef_run).not_to render_file(postgresql_ssl_cert)
expect(chef_run).not_to render_file(postgresql_ssl_key)
end
it 'sets the certificate revocation list' do
expect(chef_run).to render_file(
postgresql_conf
).with_content(/ssl_crl_file = 'revoke.crl'/)
end
it 'sets the SSL cipher list' do
expect(chef_run).to render_file(
postgresql_conf
).with_content(/ssl_ciphers = 'ALL'/)
end
it 'includes gitlab-geo.conf in postgresql.conf' do
expect(chef_run).to render_file(postgresql_conf)
.with_content(/include_if_exists 'gitlab-geo.conf'/)
end
it 'sets user specified logging parameters' do
expect(chef_run).to render_file(postgresql_conf).with_content { |content|
expect(content).to match(/logging_collector = on/)
}
expect(chef_run).to render_file(runtime_conf).with_content { |content|
expect(content).to match(/log_destination = 'csvlog'/)
expect(content).to match(/log_filename = 'test.log'/)
expect(content).to match(/log_file_mode = 0600/)
expect(content).to match(/log_truncate_on_rotation = on/)
expect(content).to match(/log_rotation_age = 1d/)
expect(content).to match(/log_rotation_size = 10MB/)
}
end
it 'sets the dynamic_shared_memory_type' do
expect(chef_run).to render_file(
postgresql_conf
).with_content(/^dynamic_shared_memory_type = none/)
end
it 'enables wal_log_hints' do
expect(chef_run).to render_file(
postgresql_conf
).with_content(/^wal_log_hints = on/)
end
end
context 'when rendering runtime.conf' do
before do
stub_gitlab_rb(postgresql: {
log_line_prefix: '%a',
log_statement: 'all',
max_standby_archive_delay: '60s',
max_standby_streaming_delay: '120s',
archive_command: 'command',
archive_timeout: '120',
log_connections: 'on',
log_disconnections: 'on'
})
end
it 'correctly sets the log_line_prefix setting' do
expect(chef_run.node['postgresql']['log_line_prefix'])
.to eql('%a')
expect(chef_run).to render_file(runtime_conf)
.with_content(/log_line_prefix = '%a'/)
end
it 'correctly sets the log_statement setting' do
expect(chef_run.node['postgresql']['log_statement'])
.to eql('all')
expect(chef_run).to render_file(runtime_conf)
.with_content(/log_statement = 'all'/)
end
it 'sets max_standby settings' do
expect(chef_run).to render_file(
runtime_conf
).with_content(/max_standby_archive_delay = 60s/)
expect(chef_run).to render_file(
runtime_conf
).with_content(/max_standby_streaming_delay = 120s/)
end
it 'sets archive settings' do
expect(chef_run).to render_file(
runtime_conf
).with_content(/archive_command = 'command'/)
expect(chef_run).to render_file(
runtime_conf
).with_content(/archive_timeout = 120/)
end
it 'sets log_connections setting' do
expect(chef_run.node['postgresql']['log_connections'])
.to eq('on')
expect(chef_run).to render_file(
runtime_conf
).with_content(/log_connections = on/)
end
it 'sets log_disconnections setting' do
expect(chef_run.node['postgresql']['log_disconnections'])
.to eq('on')
expect(chef_run).to render_file(
runtime_conf
).with_content(/log_disconnections = on/)
end
end
context 'when rendering pg_hba.conf' do
before do
stub_gitlab_rb(
postgresql: {
hostssl: true,
trust_auth_cidr_addresses: ['127.0.0.1/32'],
custom_pg_hba_entries: {
foo: [
{
type: 'host',
database: 'foo',
user: 'bar',
cidr: '127.0.0.1/32',
method: 'trust'
}
]
},
cert_auth_addresses: {
'1.2.3.4/32' => {
database: 'fakedatabase',
user: 'fakeuser'
},
'fakehostname' => {
database: 'anotherfakedatabase',
user: 'anotherfakeuser'
},
}
}
)
end
it 'prefers hostssl when configured in pg_hba.conf' do
expect(chef_run).to render_file(pg_hba_conf)
.with_content('hostssl all all 127.0.0.1/32 trust')
end
it 'adds users custom entries to pg_hba.conf' do
expect(chef_run).to render_file(pg_hba_conf)
.with_content('host foo bar 127.0.0.1/32 trust')
end
it 'allows cert authentication to be enabled' do
expect(chef_run).to render_file(pg_hba_conf).with_content('hostssl fakedatabase fakeuser 1.2.3.4/32 cert')
expect(chef_run).to render_file(pg_hba_conf).with_content('hostssl anotherfakedatabase anotherfakeuser fakehostname cert')
end
end
end
context 'when postgresql.conf changes' do
before do
allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).and_call_original
allow_any_instance_of(OmnibusHelper).to receive(:should_notify?).with('postgresql').and_return(true)
allow_any_instance_of(OmnibusHelper).to receive(:service_dir_enabled?).and_call_original
allow_any_instance_of(OmnibusHelper).to receive(:service_dir_enabled?).with('postgresql').and_return(true)
end
it 'notifies reload postgresql task' do
expect(chef_run).to create_postgresql_config('gitlab')
postgresql_config = chef_run.postgresql_config('gitlab')
expect(postgresql_config).to notify('execute[reload postgresql]').to(:run).immediately
expect(postgresql_config).to notify('execute[start postgresql]').to(:run).immediately
end
end
context 'when enabling extensions' do
it 'creates the pg_trgm extension when it is possible' do
allow_any_instance_of(PgHelper).to receive(:extension_can_be_enabled?).with('pg_trgm', 'gitlabhq_production').and_return(true)
expect(chef_run).to enable_postgresql_extension('pg_trgm')
end
it 'does not create the pg_trgm extension if it is not possible' do
allow_any_instance_of(PgHelper).to receive(:extension_can_be_enabled?).with('pg_trgm', 'gitlabhq_production').and_return(false)
expect(chef_run).not_to run_execute('enable pg_trgm extension')
end
context 'when on a secondary database node' do
before do
allow_any_instance_of(PgHelper).to receive(:is_standby?).and_return(true)
allow_any_instance_of(PgHelper).to receive(:replica?).and_return(true)
end
it 'should not activate pg_trgm' do
expect(chef_run).not_to run_execute('enable pg_trgm extension')
end
end
it 'creates the btree_gist extension when it is possible' do
allow_any_instance_of(PgHelper).to receive(:extension_can_be_enabled?).with('btree_gist', 'gitlabhq_production').and_return(true)
expect(chef_run).to enable_postgresql_extension('btree_gist')
end
it 'does not create the btree_gist extension if it is not possible' do
allow_any_instance_of(PgHelper).to receive(:extension_can_be_enabled?).with('btree_gist', 'gitlabhq_production').and_return(false)
expect(chef_run).not_to run_execute('enable btree_gist extension')
end
end
context 'when configuring postgresql_user resources' do
before do
stub_gitlab_rb(
{
postgresql: {
sql_user_password: 'fakepassword',
sql_replication_password: 'fakepassword'
}
}
)
end
context 'when on the primary database node' do
before do
allow_any_instance_of(PgHelper).to receive(:is_standby?).and_return(false)
end
it 'should set a password for sql_user when sql_user_password is set' do
expect(chef_run).to create_postgresql_user('gitlab').with(password: 'md5fakepassword')
end
it 'should create the gitlab_replicator user with replication permissions' do
expect(chef_run).to create_postgresql_user('gitlab_replicator').with(
options: %w(replication),
password: 'md5fakepassword'
)
end
end
context 'when on a secondary database node' do
before do
allow_any_instance_of(PgHelper).to receive(:is_standby?).and_return(true)
allow_any_instance_of(PgHelper).to receive(:replica?).and_return(true)
end
it 'should not create users' do
expect(chef_run).not_to create_postgresql_user('gitlab')
expect(chef_run).not_to create_postgresql_user('gitlab_replicator')
end
end
end
context 'log directory and runit group' do
context 'default values' do
it_behaves_like 'enabled logged service', 'postgresql', true, { log_directory_owner: 'gitlab-psql' }
end
context 'custom values' do
before do
stub_gitlab_rb(
postgresql: {
log_group: 'fugee'
}
)
end
it_behaves_like 'enabled logged service', 'postgresql', true, { log_directory_owner: 'gitlab-psql', log_group: 'fugee' }
end
end
end
RSpec.describe 'postgresql 16' do
let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service postgresql_config)).converge('gitlab::default') }
let(:postgresql_conf) { File.join(postgresql_data_dir, 'postgresql.conf') }
let(:runtime_conf) { '/var/opt/gitlab/postgresql/data/runtime.conf' }
before do
allow_any_instance_of(PgHelper).to receive(:version).and_return(PGVersion.new('16.0'))
allow_any_instance_of(PgHelper).to receive(:database_version).and_return(PGVersion.new('16.0'))
end
it 'configures wal_keep_size instead of wal_keep_segments' do
expect(chef_run).to render_file(runtime_conf).with_content { |content|
expect(content).to include("wal_keep_size")
expect(content).not_to include("wal_keep_segments")
}
end
end
RSpec.describe 'postgres when version mismatches occur' do
let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service postgresql_config)).converge('gitlab::default') }
let(:postgresql_conf) { File.join(postgresql_data_dir, 'postgresql.conf') }
let(:runtime_conf) { '/var/opt/gitlab/postgresql/data/runtime.conf' }
context 'when data and binary versions differ' do
before do
allow_any_instance_of(PgHelper).to receive(:version).and_return(PGVersion.new('expectation'))
allow_any_instance_of(PgHelper).to receive(:running_version).and_return(PGVersion.new('expectation'))
allow_any_instance_of(PgHelper).to receive(:database_version).and_return(PGVersion.new('reality'))
allow(File).to receive(:exists?).and_call_original
allow(File).to receive(:exists?).with("/var/opt/gitlab/postgresql/data/PG_VERSION").and_return(true)
allow(Dir).to receive(:glob).and_call_original
allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/reality*").and_return(
['/opt/gitlab/embedded/postgresql/reality']
)
allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/reality/bin/*").and_return(
%w(
/opt/gitlab/embedded/postgresql/reality/bin/foo_one
/opt/gitlab/embedded/postgresql/reality/bin/foo_two
/opt/gitlab/embedded/postgresql/reality/bin/foo_three
)
)
end
it 'corrects symlinks to the correct location' do
allow(FileUtils).to receive(:ln_sf).and_return(true)
%w(foo_one foo_two foo_three).each do |pg_bin|
expect(FileUtils).to receive(:ln_sf).with(
"/opt/gitlab/embedded/postgresql/reality/bin/#{pg_bin}",
"/opt/gitlab/embedded/bin/#{pg_bin}"
)
end
chef_run.ruby_block('Link postgresql bin files to the correct version').block.call
end
it 'does not warn the user that a restart is needed by default' do
allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true)
expect(chef_run).not_to run_ruby_block('warn pending postgresql restart')
end
end
context 'when running version and installed version differ' do
before do
allow(Gitlab).to receive(:[]).and_call_original
allow_any_instance_of(PgHelper).to receive(:version).and_return(PGVersion.new('expectation'))
allow_any_instance_of(PgHelper).to receive(:running_version).and_return(PGVersion.new('reality'))
end
context 'by defaul' do
it 'does not warns the user that a restart is needed' do
expect(chef_run).not_to run_ruby_block('warn pending postgresql restart')
end
end
context 'when auto_restart_on_version_change is set to false' do
before do
stub_gitlab_rb(
postgresql: {
auto_restart_on_version_change: false
}
)
end
it 'warns the user that a restart is needed' do
allow_any_instance_of(PgHelper).to receive(:is_running?).and_return(true)
expect(chef_run).to run_ruby_block('warn pending postgresql restart')
end
it 'does not warns the user that a restart is needed when postgres is stopped' do
expect(chef_run).not_to run_ruby_block('warn pending postgresql restart')
end
end
end
context 'when an older data version is present and no longer used' do
before do
allow(Gitlab).to receive(:[]).and_call_original
allow_any_instance_of(PgHelper).to receive(:version).and_return(PGVersion.new('new_shiny'))
allow_any_instance_of(PGVersion).to receive(:major).and_return('new_shiny')
allow_any_instance_of(PgHelper).to receive(:running_version).and_return(PGVersion.new('new_shiny'))
allow_any_instance_of(PgHelper).to receive(:database_version).and_return(PGVersion.new('ancient_history'))
allow(File).to receive(:exists?).and_call_original
allow(File).to receive(:exists?).with("/var/opt/gitlab/postgresql/data/PG_VERSION").and_return(true)
allow(Dir).to receive(:glob).and_call_original
allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/ancient_history*").and_return([])
allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/new_shiny*").and_return(
['/opt/gitlab/embedded/postgresql/new_shiny']
)
allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/new_shiny/bin/*").and_return(
%w(
/opt/gitlab/embedded/postgresql/new_shiny/bin/foo_one
/opt/gitlab/embedded/postgresql/new_shiny/bin/foo_two
/opt/gitlab/embedded/postgresql/new_shiny/bin/foo_three
)
)
end
it 'corrects symlinks to the correct location' do
allow(FileUtils).to receive(:ln_sf).and_return(true)
%w(foo_one foo_two foo_three).each do |pg_bin|
expect(FileUtils).to receive(:ln_sf).with(
"/opt/gitlab/embedded/postgresql/new_shiny/bin/#{pg_bin}",
"/opt/gitlab/embedded/bin/#{pg_bin}"
)
end
chef_run.ruby_block('Link postgresql bin files to the correct version').block.call
end
end
context 'when the expected postgres version is missing' do
before do
allow_any_instance_of(PgHelper).to receive(:database_version).and_return(PGVersion.new('how_it_started'))
allow(File).to receive(:exists?).and_call_original
allow(File).to receive(:exists?).with("/var/opt/gitlab/postgresql/data/PG_VERSION").and_return(true)
allow(Dir).to receive(:glob).and_call_original
allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/how_it_started*").and_return([])
allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/how_it_is_going*").and_return([])
end
it 'throws an error' do
expect do
chef_run.ruby_block('Link postgresql bin files to the correct version').block.call
end.to raise_error(RuntimeError, /Could not find PostgreSQL binaries/)
end
end
end
RSpec.describe 'postgresql::bin' do
let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') }
let(:gitlab_psql_rc) do
<<-EOF
psql_user='gitlab-psql'
psql_group='gitlab-psql'
psql_host='/var/opt/gitlab/postgresql'
psql_port='5432'
EOF
end
before do
allow(Gitlab). to receive(:[]).and_call_original
end
context 'when bundled postgresql is disabled' do
before do
stub_gitlab_rb(
postgresql: {
enable: false
}
)
allow(File).to receive(:exist?).and_call_original
allow(File).to receive(:exist?).with('/var/opt/gitlab/postgresql/data/PG_VERSION').and_return(false)
allow_any_instance_of(PgHelper).to receive(:database_version).and_return(nil)
version = double("PgHelper", major: 10, minor: 9)
allow_any_instance_of(PgHelper).to receive(:version).and_return(version)
end
it 'still includes the postgresql::bin recipe' do
expect(chef_run).to include_recipe('postgresql::bin')
end
it 'includes postgresql::directory_locations' do
expect(chef_run).to include_recipe('postgresql::directory_locations')
end
it 'creates gitlab-psql-rc' do
expect(chef_run).to render_file('/opt/gitlab/etc/gitlab-psql-rc')
.with_content(gitlab_psql_rc)
end
# We do expect the ruby block to run, but nothing to be found
it "doesn't link any files by default" do
expect(FileUtils).to_not receive(:ln_sf)
end
context "with postgresql['version'] set" do
before do
stub_gitlab_rb(
postgresql: {
enable: false,
version: '999'
}
)
allow(Dir).to receive(:glob).and_call_original
allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/999*").and_return(
%w(
/opt/gitlab/embedded/postgresql/999
)
)
allow(Dir).to receive(:glob).with("/opt/gitlab/embedded/postgresql/999/bin/*").and_return(
%w(
/opt/gitlab/embedded/postgresql/999/bin/foo_one
/opt/gitlab/embedded/postgresql/999/bin/foo_two
/opt/gitlab/embedded/postgresql/999/bin/foo_three
)
)
end
it "doesn't print a warning with a valid postgresql version" do
expect(chef_run).to_not run_ruby_block('check_postgresql_version')
end
it 'links the specified version' do
allow(FileUtils).to receive(:ln_sf).and_return(true)
%w(foo_one foo_two foo_three).each do |pg_bin|
expect(FileUtils).to receive(:ln_sf).with(
"/opt/gitlab/embedded/postgresql/999/bin/#{pg_bin}",
"/opt/gitlab/embedded/bin/#{pg_bin}"
)
end
chef_run.ruby_block('Link postgresql bin files to the correct version').block.call
end
end
context "with an invalid version in postgresql['version']" do
before do
stub_gitlab_rb(
postgresql: {
enable: false,
version: '888'
}
)
allow(Dir).to receive(:glob).and_call_original
allow(Dir).to receive(:glob).with('/opt/gitlab/embedded/postgresql/888*').and_return([])
end
it 'should print a warning' do
expect(chef_run).to run_ruby_block('check_postgresql_version')
end
end
end
end
RSpec.describe 'default directories' do
let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') }
before do
allow(Gitlab).to receive(:[]).and_call_original
end
context 'postgresql directory' do
context 'with default settings' do
it 'creates postgresql directory' do
expect(chef_run).to create_directory('/var/opt/gitlab/postgresql').with(
owner: 'gitlab-psql',
group: 'gitlab-psql',
mode: '2775',
recursive: true
)
end
end
context 'with custom settings' do
before do
stub_gitlab_rb(
postgresql: {
dir: '/mypgdir',
home: '/mypghomedir'
})
end
it 'creates postgresql directory with custom path' do
expect(chef_run).to create_directory('/mypgdir').with(
owner: 'gitlab-psql',
group: 'gitlab-psql',
mode: '2775',
recursive: true
)
end
end
end
end