spec/chef/cookbooks/gitlab/recipes/puma_spec.rb (388 lines of code) (raw):

require 'chef_helper' RSpec.describe 'gitlab::puma with Ubuntu 16.04' do let(:chef_run) do runner = ChefSpec::SoloRunner.new( step_into: %w(runit_service puma_config), path: 'spec/chef/fixtures/fauxhai/ubuntu/16.04.json' ) runner.converge('gitlab::default') end before do allow(Gitlab).to receive(:[]).and_call_original stub_default_should_notify?(true) stub_should_notify?('puma', true) end context 'when puma is enabled' do it_behaves_like 'enabled runit service', 'puma', 'root', 'root' it 'creates runtime directories' do expect(chef_run).to create_directory('/opt/gitlab/var/puma').with( owner: 'git', group: nil, mode: '0700' ) expect(chef_run).to create_directory('/var/opt/gitlab/gitlab-rails/sockets').with( owner: 'git', group: 'gitlab-www', mode: '0750' ) end context 'log directory and runit group' do context 'default values' do it_behaves_like 'enabled logged service', 'puma', true, { log_directory_owner: 'git' } end context 'custom values' do before do stub_gitlab_rb( puma: { log_group: 'fugee' } ) end it_behaves_like 'configured logrotate service', 'puma', 'git', 'fugee' it_behaves_like 'enabled logged service', 'puma', true, { log_directory_owner: 'git', log_group: 'fugee' } end end it 'renders the runit configuration with expected configuration' do expect(chef_run).to render_file('/opt/gitlab/sv/puma/run') .with_content { |content| expect(content).not_to match(/export prometheus_run_dir=\'\'/) expect(content).not_to match(/rm \/run\/gitlab\/puma/) expect(content).to match(/-u git:git/) expect(content).to match(/-U git:git/) expect(content).to match(/mkdir -p \/run\/gitlab\/puma/) expect(content).to match(/chmod 0700 \/run\/gitlab\/puma/) expect(content).to match(/chown git \/run\/gitlab\/puma/) expect(content).to match(/export prometheus_run_dir=\'\/run\/gitlab\/puma\'/) expect(content).to match(/rubyopt=\"-W:no-experimental\"/) expect(content).to include(%(RUBYOPT="${rubyopt}")) expect(content).to match(%r(/opt/gitlab/embedded/bin/bundle exec puma -C /var/opt/gitlab/gitlab-rails/etc/puma.rb)) } end it 'renders the puma.rb file' do expect(chef_run).to create_puma_config('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with( tag: 'gitlab-puma-worker', rackup: 'config.ru', environment: 'production', pid: '/opt/gitlab/var/puma/puma.pid', state_path: '/opt/gitlab/var/puma/puma.state', listen_socket: '/var/opt/gitlab/gitlab-rails/sockets/gitlab.socket', listen_tcp: '127.0.0.1:8080', working_directory: '/var/opt/gitlab/gitlab-rails/working', worker_processes: 2, min_threads: 4, max_threads: 4 ) expect(chef_run).to render_file('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with_content { |content| expect(content).to match(/lowlevel_error_handler/) expect(content).to include('require "/opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/puma/error_handler"') } end it 'creates sysctl files' do expect(chef_run).to create_gitlab_sysctl('net.core.somaxconn').with_value(2048) end end context 'with custom Puma settings' do before do stub_gitlab_rb( puma: { worker_timeout: 120, worker_processes: 4, min_threads: 5, max_threads: 10, listen: '10.0.0.1', port: 9000, socket: '/tmp/puma.socket', state_path: '/tmp/puma.state', per_worker_max_memory_mb: 1000 } ) end it 'renders the puma.rb file' do expect(chef_run).to create_puma_config('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with( state_path: '/tmp/puma.state', listen_socket: '/tmp/puma.socket', listen_tcp: '10.0.0.1:9000', worker_processes: 4, min_threads: 5, max_threads: 10, per_worker_max_memory_mb: 1000 ) expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content { |content| expect(content).to match(%r(-authSocket /tmp/puma.socket)) } expect(Gitlab['gitlab_workhorse']['auth_socket']).to eq('/tmp/puma.socket') end end context 'with SSL enabled' do let(:base_params) do { puma: { worker_timeout: 120, worker_processes: 4, min_threads: 5, max_threads: 10, socket: '/tmp/puma.socket', listen: '10.0.0.1', port: 9000, ssl_listen: '192.168.0.1', ssl_port: 9999, ssl_certificate: '/tmp/test.crt', ssl_certificate_key: '/tmp/test.key', ssl_key_password_command: 'echo mypassword' } } end let(:params) { base_params } before do stub_gitlab_rb(params) end it 'renders the puma.rb file' do expect(chef_run).to create_puma_config('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with( ssl_certificate: '/tmp/test.crt', ssl_certificate_key: '/tmp/test.key', ssl_key_password_command: 'echo mypassword', ssl_listen_host: '192.168.0.1', ssl_port: 9999, listen_socket: '/tmp/puma.socket', listen_tcp: '10.0.0.1:9000', worker_processes: 4, min_threads: 5, max_threads: 10 ) expect(chef_run).to render_file('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with_content { |content| expect(content).to match(/ssl_bind '192.168.0.1', 9999, {\n cert: '\/tmp\/test.crt',\n key: '\/tmp\/test.key',\n key_password_command: 'echo mypassword',\n verify_mode: 'none'\n}/m) } end it 'uses HTTPS instead of a UNIX socket for the Workhorse auth backend' do expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-workhorse/run").with_content { |content| expect(content).to match(%r(-authBackend https://192.168.0.1:9999)) expect(content).not_to match(%r(-authSocket /tmp/puma.socket)) } expect(Gitlab['gitlab_workhorse']['auth_socket']).to be_nil expect(Gitlab['gitlab_workhorse']['auth_backend']).to eq('https://192.168.0.1:9999') end context 'with UNIX socket and HTTP disabled' do let(:params) do { puma: { worker_timeout: 120, worker_processes: 4, min_threads: 5, max_threads: 10, socket: '', listen: '', port: 9000, ssl_listen: '192.168.0.1', ssl_port: 9999, ssl_certificate: '/tmp/test.crt', ssl_certificate_key: '/tmp/test.key', ssl_key_password_command: nil } } end it 'omits the UNIX socket and TCP binds from the Puma config' do expect(chef_run).to create_puma_config('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with( ssl_certificate: '/tmp/test.crt', ssl_certificate_key: '/tmp/test.key', ssl_key_password_command: nil, ssl_listen_host: '192.168.0.1', ssl_port: 9999, listen_socket: '', listen_tcp: nil, worker_processes: 4, min_threads: 5, max_threads: 10 ) expect(chef_run).to render_file('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with_content { |content| expect(content).not_to match(%r(bind 'unix://)) expect(content).not_to match(%r(bind 'tcp://)) expect(content).to match(/ssl_bind '192.168.0.1', 9999, {\n cert: '\/tmp\/test.crt',\n key: '\/tmp\/test.key',\n verify_mode: 'none'\n}/m) } end end context 'with other SSL options configured' do let(:filter) { '!aNULL:AES+SHA' } let(:client_cert) { '/tmp/client.crt' } let(:params) do base_params.tap do |config| config[:puma][:ssl_client_certificate] = client_cert config[:puma][:ssl_cipher_filter] = filter config[:puma][:ssl_key_password_command] = nil config[:puma][:ssl_verify_mode] = 'peer' end end it 'renders the puma.rb file' do expect(chef_run).to create_puma_config('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with( ssl_listen_host: '192.168.0.1', ssl_port: 9999, listen_socket: '/tmp/puma.socket', listen_tcp: '10.0.0.1:9000', ssl_client_certificate: client_cert, ssl_cipher_filter: filter, worker_processes: 4, min_threads: 5, max_threads: 10 ) expect(chef_run).to render_file('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with_content { |content| expect(content).to match(/ssl_bind '192.168.0.1', 9999, {\n cert: '\/tmp\/test.crt',\n key: '\/tmp\/test.key',\n ca: '#{client_cert}',\n ssl_cipher_filter: '#{Regexp.escape(filter)}',\n verify_mode: 'peer'\n}/m) } end end end context 'with custom user and group' do before do stub_gitlab_rb( user: { username: 'foo', group: 'bar' } ) end it_behaves_like 'enabled runit service', 'puma', 'root', 'root' end context 'with custom runtime_dir' do before do stub_gitlab_rb( runtime_dir: '/tmp/test-dir' ) end it 'uses the user-specific runtime_dir' do expect(chef_run).to render_file('/opt/gitlab/sv/puma/run') .with_content { |content| expect(content).to match(%r(export prometheus_run_dir='/tmp/test-dir/gitlab/puma')) expect(content).to match(%r(mkdir -p /tmp/test-dir/gitlab/puma)) } end end context 'with ActionCable enabled' do before do stub_gitlab_rb( actioncable: { worker_pool_size: 7 } ) end it 'renders the runit configuration with ActionCable environment variables' do expect(chef_run).to render_file('/opt/gitlab/sv/puma/run') .with_content { |content| expect(content).to match(/ACTION_CABLE_WORKER_POOL_SIZE=7/) expect(content).to match(%r(/opt/gitlab/embedded/bin/bundle exec puma -C /var/opt/gitlab/gitlab-rails/etc/puma.rb)) } end end include_examples "consul service discovery", "puma", "rails" end RSpec.describe 'gitlab::puma Ubuntu 16.04 with no tmpfs' do let(:chef_run) do runner = ChefSpec::SoloRunner.new( path: 'spec/chef/fixtures/fauxhai/ubuntu/16.04-no-run-tmpfs.json', step_into: %w(runit_service) ) runner.converge('gitlab::default') end before do allow(Gitlab).to receive(:[]).and_call_original end context 'when puma is enabled on a node with no /run or /dev/shm tmpfs' do it_behaves_like 'enabled runit service', 'puma', 'root', 'root' it 'populates the files with expected configuration' do expect(chef_run).to render_file('/opt/gitlab/sv/puma/run') .with_content { |content| expect(content).to match(/export prometheus_run_dir=\'\'/) expect(content).not_to match(/mkdir -p \/run\/gitlab\/puma/) } end end end RSpec.describe 'gitlab::puma Ubuntu 16.04 Docker' do let(:chef_run) do runner = ChefSpec::SoloRunner.new( path: 'spec/chef/fixtures/fauxhai/ubuntu/16.04-docker.json', step_into: %w(runit_service) ) runner.converge('gitlab::default') end before do allow(Gitlab).to receive(:[]).and_call_original end context 'when puma is enabled on a node with a /dev/shm tmpfs' do it_behaves_like 'enabled runit service', 'puma', 'root', 'root' it 'populates the files with expected configuration' do expect(chef_run).to render_file('/opt/gitlab/sv/puma/run') .with_content { |content| expect(content).to match(/export prometheus_run_dir=\'\/dev\/shm\/gitlab\/puma\'/) expect(content).to match(/mkdir -p \/dev\/shm\/gitlab\/puma/) } end end end RSpec.describe 'gitlab::puma with more CPUs' do let(:chef_run) do runner = ChefSpec::SoloRunner.new( step_into: %w(runit_service), path: 'spec/chef/fixtures/fauxhai/ubuntu/16.04-more-cpus.json' ) runner.converge('gitlab::default') end before do allow(Gitlab).to receive(:[]).and_call_original end context 'when puma is enabled' do it 'renders the puma.rb file' do expect(chef_run).to create_puma_config('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with( environment: 'production', pid: '/opt/gitlab/var/puma/puma.pid', state_path: '/opt/gitlab/var/puma/puma.state', listen_socket: '/var/opt/gitlab/gitlab-rails/sockets/gitlab.socket', listen_tcp: '127.0.0.1:8080', working_directory: '/var/opt/gitlab/gitlab-rails/working', worker_processes: 16 ) end end end RSpec.describe 'gitlab::puma with no total CPUs' do let(:chef_run) do runner = ChefSpec::SoloRunner.new( step_into: %w(runit_service), path: 'spec/chef/fixtures/fauxhai/ubuntu/16.04-no-total-cpus.json' ) runner.converge('gitlab::default') end before do allow(Gitlab).to receive(:[]).and_call_original end context 'when puma is enabled' do it 'renders the puma.rb file' do expect(chef_run).to create_puma_config('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with( environment: 'production', pid: '/opt/gitlab/var/puma/puma.pid', state_path: '/opt/gitlab/var/puma/puma.state', listen_socket: '/var/opt/gitlab/gitlab-rails/sockets/gitlab.socket', listen_tcp: '127.0.0.1:8080', working_directory: '/var/opt/gitlab/gitlab-rails/working', worker_processes: 16 ) end end end RSpec.describe 'gitlab::puma with Raspberry Pi 4' do let(:chef_run) do runner = ChefSpec::SoloRunner.new( step_into: %w(runit_service), path: 'spec/chef/fixtures/fauxhai/ubuntu/22.04-rpi4.json' ) runner.converge('gitlab::default') end before do allow(Gitlab).to receive(:[]).and_call_original end context 'when puma is enabled' do it 'renders the puma.rb file' do expect(chef_run).to create_puma_config('/var/opt/gitlab/gitlab-rails/etc/puma.rb').with( environment: 'production', pid: '/opt/gitlab/var/puma/puma.pid', state_path: '/opt/gitlab/var/puma/puma.state', listen_socket: '/var/opt/gitlab/gitlab-rails/sockets/gitlab.socket', listen_tcp: '127.0.0.1:8080', working_directory: '/var/opt/gitlab/gitlab-rails/working', worker_processes: 2 ) end end end