spec/chef/cookbooks/praefect/recipes/praefect_spec.rb (425 lines of code) (raw):

require 'chef_helper' RSpec.describe 'praefect' do let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service env_dir)).converge('gitlab::default') } let(:prometheus_grpc_latency_buckets) do [0.001, 0.005, 0.025, 0.1, 0.5, 1.0, 10.0, 30.0, 60.0, 300.0, 1500.0] end before do allow(Gitlab).to receive(:[]).and_call_original end context 'when the defaults are used' do it_behaves_like 'disabled runit service', 'praefect' end context 'when praefect is enabled' do let(:config_path) { '/var/opt/gitlab/praefect/config.toml' } let(:env_dir) { '/opt/gitlab/etc/praefect/env' } let(:auth_transitioning) { false } before do stub_gitlab_rb(praefect: { enable: true, auth_transitioning: auth_transitioning, }) end it 'creates expected directories with correct permissions' do expect(chef_run).to create_directory('/var/opt/gitlab/praefect').with(user: 'git', mode: '0700') end it 'creates a default VERSION file and sends hup to service' do expect(chef_run).to create_version_file('Create Praefect version file').with( version_file_path: '/var/opt/gitlab/praefect/VERSION', version_check_cmd: '/opt/gitlab/embedded/bin/praefect --version' ) expect(chef_run.version_file('Create Praefect version file')).to notify('runit_service[praefect]').to(:hup) end it 'renders the config.toml' do rendered = { 'auth' => { 'transitioning' => false }, 'listen_addr' => 'localhost:2305', 'logging' => { 'format' => 'json' }, 'prometheus_listen_addr' => 'localhost:9652', 'failover' => { 'enabled' => true }, } expect(chef_run).to render_file(config_path).with_content { |content| expect(Tomlrb.parse(content)).to eq(rendered) } expect(chef_run).not_to render_file(config_path) .with_content(%r{\[prometheus\]\s+grpc_latency_buckets =}) end it 'renders the env dir files' do expect(chef_run).to render_file(File.join(env_dir, "GITALY_PID_FILE")) .with_content('/var/opt/gitlab/praefect/praefect.pid') expect(chef_run).to render_file(File.join(env_dir, "WRAPPER_JSON_LOGGING")) .with_content('true') expect(chef_run).to render_file(File.join(env_dir, "SSL_CERT_DIR")) .with_content('/opt/gitlab/embedded/ssl/certs/') end it 'renders the service run file with wrapper' do expect(chef_run).to render_file('/opt/gitlab/sv/praefect/run') .with_content('/opt/gitlab/embedded/bin/gitaly-wrapper /opt/gitlab/embedded/bin/praefect') .with_content('exec chpst -e /opt/gitlab/etc/praefect/env') end context 'with defaults overridden with custom configuration' do before do stub_gitlab_rb( { praefect: { enable: true, configuration: { listen_addr: 'custom_listen_addr:5432', prometheus_listen_addr: 'custom_prometheus_listen_addr:5432', logging: { format: 'custom_format', has_no_default: 'should get output' }, auth: { transitioning: true }, failover: { enabled: false }, virtual_storage: [ { name: 'default', node: [ { storage: 'praefect1', address: 'tcp://node2.internal', token: 'praefect2-token' }, { storage: 'praefect2', address: 'tcp://node2.internal', token: 'praefect2-token' } ] }, { name: 'virtual-storage-2', node: [ { storage: 'praefect3', address: 'tcp://node3.internal', token: 'praefect3-token' }, { storage: 'praefect4', address: 'tcp://node4.internal', token: 'praefect4-token' } ] } ] } } } ) end it 'renders config.toml' do expect(chef_run).to render_file(config_path).with_content { |content| expect(Tomlrb.parse(content)).to eq( { 'auth' => { 'transitioning' => true }, 'failover' => { 'enabled' => false }, 'listen_addr' => 'custom_listen_addr:5432', 'logging' => { 'format' => 'custom_format', 'has_no_default' => 'should get output' }, 'prometheus_listen_addr' => 'custom_prometheus_listen_addr:5432', 'virtual_storage' => [ { 'name' => 'default', 'node' => [ { 'storage' => 'praefect1', 'address' => 'tcp://node2.internal', 'token' => 'praefect2-token' }, { 'storage' => 'praefect2', 'address' => 'tcp://node2.internal', 'token' => 'praefect2-token' } ] }, { 'name' => 'virtual-storage-2', 'node' => [ { 'storage' => 'praefect3', 'address' => 'tcp://node3.internal', 'token' => 'praefect3-token' }, { 'storage' => 'praefect4', 'address' => 'tcp://node4.internal', 'token' => 'praefect4-token' } ] } ] } ) } end end context 'with custom settings' do let(:dir) { nil } let(:socket_path) { '/var/opt/gitlab/praefect/praefect.socket' } let(:auth_token) { 'secrettoken123' } let(:auth_transitioning) { false } let(:sentry_dsn) { 'https://my_key:my_secret@sentry.io/test_project' } let(:sentry_environment) { 'production' } let(:listen_addr) { 'localhost:4444' } let(:tls_listen_addr) { 'localhost:5555' } let(:certificate_path) { '/path/to/cert.pem' } let(:key_path) { '/path/to/key.pem' } let(:prom_addr) { 'localhost:1234' } let(:log_level) { 'debug' } let(:log_format) { 'text' } let(:log_group) { 'fugee' } let(:primaries) { %w[praefect1 praefect2] } let(:virtual_storage) do [ { "default_replication_factor" => 2, "name" => "default", "node" => [ { "address" => "tcp://node1.internal", "storage" => "praefect1", "token" => "praefect1-token" }, { "address" => "tcp://node2.internal", "storage" => "praefect2", "token" => "praefect2-token" }, { "address" => "tcp://node3.internal", "storage" => "praefect3", "token" => "praefect3-token" }, { "address" => "tcp://node4.internal", "storage" => "praefect4", "token" => "praefect4-token" } ] } ] end let(:failover_enabled) { true } let(:database_host) { 'pg.external' } let(:database_port) { 2234 } let(:database_user) { 'praefect-pg' } let(:database_password) { 'praefect-pg-pass' } let(:database_dbname) { 'praefect_production' } let(:database_sslmode) { 'require' } let(:database_sslcert) { '/path/to/client-cert' } let(:database_sslkey) { '/path/to/client-key' } let(:database_sslrootcert) { '/path/to/rootcert' } let(:database_direct_host) { 'pg.internal' } let(:database_direct_port) { 1234 } let(:reconciliation_scheduling_interval) { '1m' } let(:reconciliation_histogram_buckets) { [1.0, 2.0] } let(:user) { 'user123' } let(:password) { 'password321' } let(:ca_file) { '/path/to/ca_file' } let(:ca_path) { '/path/to/ca_path' } let(:read_timeout) { 123 } let(:graceful_stop_timeout) { '3m' } before do stub_gitlab_rb(praefect: { enable: true, dir: dir, log_group: log_group, failover_enabled: failover_enabled, # Sanity check that the configuration values get templated out as TOML. configuration: { string_value: 'value', graceful_stop_timeout: graceful_stop_timeout, listen_addr: listen_addr, socket_path: socket_path, auth: { token: auth_token, transitioning: auth_transitioning }, logging: { format: log_format, level: log_level }, background_verification: { verification_interval: '168h', delete_invalid_records: true, }, prometheus: { grpc_latency_buckets: prometheus_grpc_latency_buckets }, reconciliation: { scheduling_interval: reconciliation_scheduling_interval, histogram_buckets: reconciliation_histogram_buckets, }, sentry: { sentry_dsn: sentry_dsn, sentry_environment: sentry_environment }, tls: { certificate_path: certificate_path, key_path: key_path, }, tls_listen_addr: tls_listen_addr, virtual_storage: virtual_storage, database: { host: database_host, port: database_port, user: database_user, password: database_password, dbname: database_dbname, sslmode: database_sslmode, sslcert: database_sslcert, sslkey: database_sslkey, sslrootcert: database_sslrootcert, session_pooled: { host: database_direct_host, port: database_direct_port, } }, prometheus_listen_addr: prom_addr, subsection: { array_value: [1, 2] }, } } ) end it 'renders the config.toml' do expect(chef_run).to render_file(config_path).with_content { |content| expect(Tomlrb.parse(content)).to eq( { 'auth' => { 'token' => 'secrettoken123', 'transitioning' => false, }, 'database' => { 'dbname' => 'praefect_production', 'host' => 'pg.external', 'password' => 'praefect-pg-pass', 'port' => 2234, 'sslcert' => '/path/to/client-cert', 'sslkey' => '/path/to/client-key', 'sslmode' => 'require', 'sslrootcert' => '/path/to/rootcert', 'user' => 'praefect-pg', 'session_pooled' => { 'host' => 'pg.internal', 'port' => 1234, } }, 'failover' => { 'enabled' => true, }, 'logging' => { 'format' => 'text', 'level' => 'debug' }, 'listen_addr' => 'localhost:4444', 'prometheus' => { 'grpc_latency_buckets' => [0.001, 0.005, 0.025, 0.1, 0.5, 1.0, 10.0, 30.0, 60.0, 300.0, 1500.0] }, 'reconciliation' => { 'histogram_buckets' => [1.0, 2.0], 'scheduling_interval' => '1m' }, 'background_verification' => { 'verification_interval' => '168h', 'delete_invalid_records' => true }, 'sentry' => { 'sentry_dsn' => 'https://my_key:my_secret@sentry.io/test_project', 'sentry_environment' => 'production' }, 'prometheus_listen_addr' => 'localhost:1234', 'socket_path' => '/var/opt/gitlab/praefect/praefect.socket', 'string_value' => 'value', 'subsection' => { 'array_value' => [1, 2] }, 'tls' => { 'certificate_path' => '/path/to/cert.pem', 'key_path' => '/path/to/key.pem' }, 'tls_listen_addr' => 'localhost:5555', 'virtual_storage' => [ { 'name' => 'default', 'default_replication_factor' => 2, 'node' => [ { 'address' => 'tcp://node1.internal', 'storage' => 'praefect1', 'token' => 'praefect1-token' }, { 'address' => 'tcp://node2.internal', 'storage' => 'praefect2', 'token' => 'praefect2-token' }, { 'address' => 'tcp://node3.internal', 'storage' => 'praefect3', 'token' => 'praefect3-token' }, { 'address' => 'tcp://node4.internal', 'storage' => 'praefect4', 'token' => 'praefect4-token' } ] } ], 'graceful_stop_timeout' => graceful_stop_timeout } ) } end it 'renders the env dir files correctly' do expect(chef_run).to render_file(File.join(env_dir, "WRAPPER_JSON_LOGGING")) .with_content('false') end end describe 'database migrations' do it 'runs the migrations' do expect(chef_run).to run_bash('migrate praefect database') end context 'with auto_migrate off' do before { stub_gitlab_rb(praefect: { auto_migrate: false }) } it 'skips running the migrations' do expect(chef_run).not_to run_bash('migrate praefect database') end end end context 'log directory and runit group' do context 'default values' do before do stub_gitlab_rb(praefect: { enable: true }) end it_behaves_like 'enabled logged service', 'praefect', true, { log_directory_owner: 'git' } end context 'custom values' do before do stub_gitlab_rb( praefect: { enable: true, log_group: 'fugee' } ) end it_behaves_like 'enabled logged service', 'praefect', true, { log_directory_owner: 'git', log_group: 'fugee' } end end include_examples "consul service discovery", "praefect", "praefect" end end