spec/chef/cookbooks/gitlab-ee/recipes/geo-secondary_spec.rb (435 lines of code) (raw):
require 'chef_helper'
RSpec.describe 'gitlab-ee::geo-secondary' do
let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab-ee::default') }
let(:database_yml_template) { chef_run.template('/var/opt/gitlab/gitlab-rails/etc/database.yml') }
let(:database_yml_file_content) { ChefSpec::Renderer.new(chef_run, database_yml_template).content }
let(:database_yml) { YAML.safe_load(database_yml_file_content, aliases: true, symbolize_names: true) }
let(:default_database_settings) do
{
adapter: 'postgresql',
application_name: nil,
collation: nil,
connect_timeout: nil,
database: "gitlabhq_production",
encoding: "unicode",
host: "/var/opt/gitlab/postgresql",
keepalives: nil,
keepalives_count: nil,
keepalives_idle: nil,
keepalives_interval: nil,
load_balancing: {
hosts: []
},
password: nil,
port: 5432,
prepared_statements: false,
socket: nil,
sslca: nil,
sslcompression: 0,
sslmode: nil,
sslrootcert: nil,
statement_limit: 1000,
tcp_user_timeout: nil,
username: "gitlab",
variables: {
statement_timeout: nil
}
}
end
let(:default_content) do
{
main: default_database_settings.merge(database_tasks: true),
ci: default_database_settings.merge(database_tasks: false)
}
end
let(:default_geo_content) do
{
geo: {
adapter: 'postgresql',
application_name: nil,
collation: nil,
connect_timeout: nil,
database: "gitlabhq_geo_production",
database_tasks: true,
migrations_paths: "ee/db/geo/migrate",
schema_migrations_path: "ee/db/geo/schema_migrations",
encoding: "unicode",
host: "/var/opt/gitlab/geo-postgresql",
keepalives: nil,
keepalives_count: nil,
keepalives_idle: nil,
keepalives_interval: nil,
load_balancing: {
hosts: []
},
password: nil,
port: 5431,
prepared_statements: false,
socket: nil,
sslca: nil,
sslcompression: 0,
sslmode: nil,
sslrootcert: nil,
statement_limit: nil,
tcp_user_timeout: nil,
username: "gitlab_geo",
variables: {
statement_timeout: nil
}
}
}
end
before do
allow(Gitlab).to receive(:[]).and_call_original
end
shared_examples 'renders database.yml without geo database' do
context 'database.yml' do
it 'renders database.yml without geo database' do
expect(database_yml[:production].keys).not_to include(:geo)
end
context 'with geo database specified' do
before do
stub_gitlab_rb(
gitlab_rails: {
databases: {
geo: {
enable: true,
db_connect_timeout: 50
}
}
}
)
end
it 'renders database.yml without geo database' do
expect(database_yml[:production].keys).not_to include(:geo)
end
end
end
end
shared_examples 'renders database.yml with both main and geo databases' do
context 'database.yml' do
context 'with default settings' do
it 'renders database.yml with main stanza first' do
expect(database_yml_file_content).to match("production:\n main:")
end
it 'renders database.yml with both main and geo databases using default values' do
expected_output = default_content.merge(default_geo_content)
expect(database_yml[:production]).to eq(expected_output)
end
end
context 'with user provided settings' do
context "via top level geo_secondary['db_*'] keys" do
before do
stub_gitlab_rb(
geo_secondary: {
db_database: 'foo',
db_sslcompression: 1
}
)
end
it 'renders database.yml with user specified values for geo database' do
expect(database_yml[:production][:geo][:database]).to eq('foo')
expect(database_yml[:production][:geo][:sslcompression]).to eq(1)
end
end
context "via gitlab_rails['databases']['geo'] settings" do
before do
stub_gitlab_rb(
gitlab_rails: {
databases: {
geo: {
enable: true,
db_database: 'bar',
db_sslcompression: 1
}
}
}
)
end
it 'renders database.yml with user specified values for geo database' do
expect(database_yml[:production][:geo][:database]).to eq('bar')
expect(database_yml[:production][:geo][:sslcompression]).to eq(1)
end
end
end
context 'with geo database specified but not enabled' do
before do
stub_gitlab_rb(
gitlab_rails: {
databases: {
main: {
db_connect_timeout: 30
},
geo: {
db_connect_timeout: 50
}
}
}
)
end
it 'renders database.yml without geo database' do
expect(database_yml[:production].keys).not_to include(:geo)
end
end
context 'dependent services' do
let(:templatesymlink) { chef_run.templatesymlink('Add the geo database settings to database.yml and create a symlink to Rails root') }
it 'triggers dependent services notifications' do
expect(templatesymlink).to notify('ruby_block[Restart geo-secondary dependent services]').to(:run).delayed
end
end
end
end
describe 'when geo_secondary_role is disabled' do
before do
stub_gitlab_rb(geo_secondary_role: { enable: false })
end
it_behaves_like 'renders database.yml without geo database'
end
describe 'when geo_secondary_role is disabled but geo-postgresql enabled' do
before do
stub_gitlab_rb(geo_secondary_role: { enable: false },
geo_postgresql: { enable: true })
end
it_behaves_like 'renders database.yml without geo database'
end
describe 'when gitlab_rails is disabled, but geo_secondary_role is enabled' do
let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') }
before do
stub_gitlab_rb(geo_secondary_role: { enable: true },
gitlab_rails: { enable: false })
end
it 'does not render the geo-secondary files' do
expect(chef_run).not_to create_templatesymlink('Add the geo database settings to database.yml and create a symlink to Rails root')
end
end
describe 'when gitlab_rails is enabled' do
let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') }
before do
stub_gitlab_rb(geo_secondary_role: { enable: true },
geo_postgresql: { enable: true },
puma: { enable: false },
sidekiq: { enable: false },
geo_logcursor: { enable: false },
gitlab_rails: { enable: true })
end
it 'allows gitlab_rails to be overriden' do
expect(chef_run.node['gitlab']['gitlab_rails']['enable']).to be true
end
end
context 'when gitaly is enabled' do
describe 'when gitlab_rails enable is not set' do
let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') }
before do
stub_gitlab_rb(geo_secondary_role: { enable: true },
geo_secondary: { enable: true },
geo_postgresql: { enable: false },
puma: { enable: false },
sidekiq: { enable: false },
geo_logcursor: { enable: false },
gitaly: { enable: true })
end
it 'ensures gitlab_rails is enabled' do
chef_run
expect(Gitlab['gitlab_rails']['enable']).to be true
end
end
describe 'when gitlab_rails enable is provided' do
let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') }
before do
stub_gitlab_rb(geo_secondary_role: { enable: true },
geo_secondary: { enable: true },
geo_postgresql: { enable: false },
puma: { enable: false },
sidekiq: { enable: false },
geo_logcursor: { enable: false },
gitaly: { enable: true },
gitlab_rails: { enable: false })
end
it 'does not override gitlab_rails enable' do
chef_run
expect(Gitlab['gitlab_rails']['enable']).to be false
end
end
end
context 'when geo_secondary_role is enabled but geo-postgresql is disabled' do
before do
stub_gitlab_rb(geo_secondary_role: { enable: true },
geo_secondary: { db_host: '/var/opt/gitlab/geo-postgresql' },
geo_postgresql: { enable: false })
end
context 'migrations' do
let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') }
it 'includes the database migration recipe' do
expect(chef_run).to include_recipe('gitlab-ee::geo_database_migrations')
end
end
it_behaves_like 'renders database.yml with both main and geo databases'
end
context 'when geo_secondary_role is enabled' do
before do
stub_gitlab_rb(geo_secondary_role: { enable: true },
geo_postgresql: { enable: true, dir: '/tmp/geo-postgresql' })
end
it 'sets geo-secondary db_host to the value of geo_postgresql socket directory ' do
expect(database_yml[:production][:geo][:host]).to eq('/tmp/geo-postgresql')
end
end
context 'when geo_secondary_role is enabled and geo_secondary db_host is set' do
before do
stub_gitlab_rb(geo_secondary_role: { enable: true },
geo_secondary: { db_host: '/some_test_directory/geo-postgresql' },
geo_postgresql: { enable: true, dir: '/tmp/geo-postgresql' })
end
it 'sets geo-secondary db_host to the specified db_host' do
expect(database_yml[:production][:geo][:host]).to eq('/some_test_directory/geo-postgresql')
end
end
context 'when geo_secondary_role is enabled, and geo-postgresql is enabled, but geo-postgresql dir is not set' do
before do
stub_gitlab_rb(geo_secondary_role: { enable: true },
geo_postgresql: { enable: true })
end
it 'sets geo-secondary db_host to the default' do
expect(database_yml[:production][:geo][:host]).to eq('/var/opt/gitlab/geo-postgresql')
end
end
context 'when geo_secondary_role is enabled' do
before do
stub_gitlab_rb(geo_secondary_role: { enable: true })
# Make sure other calls to `File.symlink?` are allowed.
allow(File).to receive(:symlink?).and_call_original
%w(
alertmanager
gitlab-exporter
gitlab-workhorse
logrotate
nginx
node-exporter
postgres-exporter
postgresql
prometheus
redis
redis-exporter
sidekiq
puma
gitaly
geo-postgresql
gitlab-pages
geo-logcursor
patroni
).map { |svc| stub_should_notify?(svc, true) }
end
it_behaves_like 'renders database.yml with both main and geo databases'
describe 'PostgreSQL gitlab-geo.conf' do
let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') }
let(:geo_conf) { '/var/opt/gitlab/postgresql/data/gitlab-geo.conf' }
let(:postgresql_conf) { '/var/opt/gitlab/postgresql/data/postgresql.conf' }
context 'when postgresql enabled on the node' do
it 'renders gitlab-geo.conf' do
expect(chef_run).to render_file(geo_conf)
end
end
context 'when postgresql disabled on the node' do
before { stub_gitlab_rb(postgresql: { enable: false }) }
it 'does not render gitlab-geo.conf' do
expect(chef_run).not_to render_file(geo_conf)
end
end
end
describe 'restart geo-secondary dependent services' do
let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') }
let(:ruby_block) { chef_run.ruby_block('Restart geo-secondary dependent services') }
it 'does not run' do
expect(chef_run).not_to run_ruby_block('Restart geo-secondary dependent services')
expect(ruby_block).to do_nothing
end
it 'ruby_block triggers dependent services notifications' do
allow(ruby_block).to receive(:notifies)
ruby_block.block.call
%w(
puma
geo-logcursor
).each do |svc|
expect(ruby_block).to have_received(:notifies).with(:restart, "runit_service[#{svc}]")
end
expect(ruby_block).to have_received(:notifies).with(:restart, "sidekiq_service[sidekiq]")
end
end
describe 'include_recipe' do
let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') }
it 'includes the Geo tracking DB recipe' do
expect(chef_run).to include_recipe('gitlab-ee::geo-postgresql')
end
it 'includes the Geo secondary recipes for Rails' do
expect(chef_run).to include_recipe('gitlab-ee::geo-secondary')
expect(chef_run).to include_recipe('gitlab-ee::geo_database_migrations')
end
it 'does not include the Geo database migrations recipe if Rails not needed' do
stub_gitlab_rb(geo_secondary_role: { enable: true },
nginx: { enable: false },
puma: { enable: false },
sidekiq: { enable: false },
gitaly: { enable: false },
postgresql: { enable: false },
geo_logcursor: { enable: false },
redis: { enable: true })
expect(chef_run).not_to include_recipe('gitlab-ee::geo_database_migrations')
end
end
describe 'migrations' do
let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab-ee::default') }
it 'runs the migrations' do
expect(chef_run).to run_rails_migration('gitlab-geo tracking')
end
end
describe 'rails_needed?' do
let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab-ee::default') }
context 'manually enabled services' do
before do
stub_gitlab_rb(
# Everything but puma is disabled
puma: { enable: true },
sidekiq: { enable: false },
geo_logcursor: { enable: false },
gitaly: { enable: false }
)
end
it 'should need rails' do
expect(chef_run).to include_recipe('gitlab::gitlab-rails')
end
end
context 'manually disabled services' do
before do
stub_gitlab_rb(
gitaly: { enable: false },
puma: { enable: false },
sidekiq: { enable: false },
geo_logcursor: { enable: false }
)
end
it 'should not need rails' do
expect(chef_run).not_to include_recipe('gitlab::gitlab-rails')
end
end
end
end
context 'puma worker_processes' do
let(:chef_run) do
ChefSpec::SoloRunner.new do |node|
node.automatic['cpu']['total'] = 16
node.automatic['memory']['total'] = '8388608KB' # 8GB
end.converge('gitlab-ee::default')
end
it 'reduces the number of puma workers on secondary node' do
stub_gitlab_rb(geo_secondary_role: { enable: true })
expect(chef_run.node['gitlab']['puma']['worker_processes']).to eq 5
end
it 'uses the specified number of puma workers' do
stub_gitlab_rb(geo_secondary_role: { enable: true },
puma: { worker_processes: 1 })
expect(chef_run.node['gitlab']['puma']['worker_processes']).to eq 1
end
it 'does not reduce the number of puma workers on primary node' do
stub_gitlab_rb(geo_primary_role: { enable: true })
expect(chef_run.node['gitlab']['puma']['worker_processes']).to eq 6
end
end
end