omnibus-gitlab/spec/chef/cookbooks/gitaly/recipes/gitaly_spec.rb

891 lines
34 KiB
Ruby

require 'chef_helper'
RSpec.describe 'gitaly' do
let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service)).converge('gitlab::default') }
let(:config_path) { '/var/opt/gitlab/gitaly/config.toml' }
let(:gitaly_config) { chef_run.template(config_path) }
let(:internal_socket_dir) { '/var/opt/gitlab/gitaly/user_defined/internal_sockets' }
let(:socket_path) { '/tmp/gitaly.socket' }
let(:listen_addr) { 'localhost:7777' }
let(:tls_listen_addr) { 'localhost:8888' }
let(:certificate_path) { '/path/to/cert.pem' }
let(:key_path) { '/path/to/key.pem' }
let(:prometheus_listen_addr) { 'localhost:9000' }
let(:logging_level) { 'warn' }
let(:logging_format) { 'default' }
let(:logging_sentry_dsn) { 'https://my_key:my_secret@sentry.io/test_project' }
let(:logging_ruby_sentry_dsn) { 'https://my_key:my_secret@sentry.io/test_project-ruby' }
let(:logging_sentry_environment) { 'production' }
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
let(:auth_token) { '123secret456' }
let(:auth_transitioning) { true }
let(:ruby_max_rss) { 1000000 }
let(:graceful_restart_timeout) { '20m' }
let(:ruby_graceful_restart_timeout) { '30m' }
let(:ruby_restart_delay) { '10m' }
let(:ruby_num_workers) { 5 }
let(:ruby_rugged_git_config_search_path) { '/path/to/opt/gitlab/embedded/etc' }
let(:git_catfile_cache_size) { 50 }
let(:git_bin_path) { '/path/to/usr/bin/git' }
let(:use_bundled_git) { true }
let(:open_files_ulimit) { 10000 }
let(:default_vars) do
{
'SSL_CERT_DIR' => '/opt/gitlab/embedded/ssl/certs/',
'TZ' => ':/etc/localtime',
'HOME' => '/var/opt/gitlab',
'PATH' => '/opt/gitlab/bin:/opt/gitlab/embedded/bin:/bin:/usr/bin',
'ICU_DATA' => '/opt/gitlab/embedded/share/icu/current',
'PYTHONPATH' => '/opt/gitlab/embedded/lib/python3.9/site-packages',
'WRAPPER_JSON_LOGGING' => 'true',
"GITALY_PID_FILE" => '/var/opt/gitlab/gitaly/gitaly.pid',
}
end
let(:gitlab_url) { 'http://localhost:3000' }
let(:workhorse_addr) { 'localhost:4000' }
let(:gitaly_custom_hooks_dir) { '/path/to/gitaly/custom/hooks' }
let(:gitlab_shell_custom_hooks_dir) { '/path/to/gitlab-shell/custom/hooks' }
let(:user) { 'user123' }
let(:password) { 'password321' }
let(:ca_file) { '/path/to/ca_file' }
let(:ca_path) { '/path/to/ca_path' }
let(:self_signed_cert) { true }
let(:read_timeout) { 123 }
let(:daily_maintenance_start_hour) { 21 }
let(:daily_maintenance_start_minute) { 9 }
let(:daily_maintenance_duration) { '45m' }
let(:daily_maintenance_storages) { ["default"] }
let(:daily_maintenance_disabled) { false }
let(:cgroups_count) { 10 }
let(:cgroups_mountpoint) { '/sys/fs/cgroup' }
let(:cgroups_hierarchy_root) { 'gitaly' }
let(:cgroups_memory_enabled) { true }
let(:cgroups_memory_limit) { 1048576 }
let(:cgroups_cpu_enabled) { true }
let(:cgroups_cpu_shares) { 512 }
let(:pack_objects_cache_enabled) { true }
let(:pack_objects_cache_dir) { '/pack-objects-cache' }
let(:pack_objects_cache_max_age) { '10m' }
before do
allow(Gitlab).to receive(:[]).and_call_original
end
context 'by default' do
it_behaves_like "enabled runit service", "gitaly", "root", "root"
it 'creates expected directories with correct permissions' do
expect(chef_run).to create_directory('/var/opt/gitlab/gitaly').with(user: 'git', mode: '0700')
expect(chef_run).to create_directory('/var/log/gitlab/gitaly').with(user: 'git', mode: '0700')
end
it 'creates a default VERSION file and restarts service' do
expect(chef_run).to create_version_file('Create version file for Gitaly').with(
version_file_path: '/var/opt/gitlab/gitaly/VERSION',
version_check_cmd: "/opt/gitlab/embedded/bin/ruby -rdigest/sha2 -e 'puts %(sha256:) + Digest::SHA256.file(%(/opt/gitlab/embedded/bin/gitaly)).hexdigest'"
)
expect(chef_run.version_file('Create version file for Gitaly')).to notify('runit_service[gitaly]').to(:hup)
end
it 'creates a default RUBY_VERSION file and restarts service' do
expect(chef_run).to create_version_file('Create Ruby version file for Gitaly').with(
version_file_path: '/var/opt/gitlab/gitaly/RUBY_VERSION',
version_check_cmd: '/opt/gitlab/embedded/bin/ruby --version'
)
expect(chef_run.version_file('Create Ruby version file for Gitaly')).to notify('runit_service[gitaly]').to(:hup)
end
it 'populates gitaly config.toml with defaults' do
expect(chef_run).to render_file(config_path).with_content { |content|
expect(content).to include("socket_path = '/var/opt/gitlab/gitaly/gitaly.socket'")
expect(content).to include("internal_socket_dir = '/var/opt/gitlab/gitaly/internal_sockets'")
expect(content).to include("bin_dir = '/opt/gitlab/embedded/bin'")
expect(content).to include(%(rugged_git_config_search_path = "/opt/gitlab/embedded/etc"))
}
expect(chef_run).not_to render_file(config_path)
.with_content("listen_addr = '#{listen_addr}'")
expect(chef_run).not_to render_file(config_path)
.with_content("tls_listen_addr =")
expect(chef_run).not_to render_file(config_path)
.with_content("certificate_path =")
expect(chef_run).not_to render_file(config_path)
.with_content("key_path =")
expect(chef_run).not_to render_file(config_path)
.with_content("prometheus_listen_addr = '#{prometheus_listen_addr}'")
expect(chef_run).not_to render_file(config_path)
.with_content(%r{\[logging\]\s+level = '#{logging_level}'\s+format = '#{logging_format}'\s+sentry_dsn = '#{logging_sentry_dsn}'})
expect(chef_run).not_to render_file(config_path)
.with_content(%r{\[logging\]\s+level = '#{logging_level}'\s+format = '#{logging_format}'\s+ruby_sentry_dsn = '#{logging_ruby_sentry_dsn}'})
expect(chef_run).not_to render_file(config_path)
.with_content(%r{\[logging\]\s+level = '#{logging_level}'\s+format = '#{logging_format}'\s+sentry_environment = '#{logging_sentry_environment}'})
expect(chef_run).not_to render_file(config_path)
.with_content(%r{\[prometheus\]\s+grpc_latency_buckets = #{Regexp.escape(prometheus_grpc_latency_buckets)}})
expect(chef_run).not_to render_file(config_path)
.with_content(%r{\[auth\]\s+token = })
expect(chef_run).not_to render_file(config_path)
.with_content('transitioning =')
expect(chef_run).not_to render_file(config_path)
.with_content('max_rss =')
expect(chef_run).not_to render_file(config_path)
.with_content('graceful_restart_timeout =')
expect(chef_run).not_to render_file(config_path)
.with_content('restart_delay =')
expect(chef_run).not_to render_file(config_path)
.with_content('num_workers =')
expect(chef_run).not_to render_file(config_path)
.with_content(%r{\[logging\]\s+level})
expect(chef_run).not_to render_file(config_path)
.with_content(%r{catfile_cache_size})
expect(chef_run).not_to render_file(config_path)
.with_content('[pack_objects_cache]')
end
it 'populates gitaly config.toml with default git binary path' do
expect(chef_run).to render_file(config_path)
.with_content("bin_path = '/opt/gitlab/embedded/bin/git'")
end
it 'populates gitaly config.toml with default storages' do
expect(chef_run).to render_file(config_path)
.with_content(%r{\[\[storage\]\]\s+name = 'default'\s+path = '/var/opt/gitlab/git-data/repositories'})
end
it 'renders the runit run script with defaults' do
expect(chef_run).to render_file('/opt/gitlab/sv/gitaly/run')
.with_content(%r{ulimit -n 15000})
end
it 'does not append timestamp in logs if logging format is json' do
expect(chef_run).to render_file('/opt/gitlab/sv/gitaly/log/run')
.with_content(/exec svlogd \/var\/log\/gitlab\/gitaly/)
end
it 'populates gitaly config.toml with gitlab-shell values' do
expect(chef_run).to render_file(config_path)
.with_content(%r{\[gitlab-shell\]\s+dir = "/opt/gitlab/embedded/service/gitlab-shell"})
end
it 'populates gitaly config.toml with gitlab-workhorse socket' do
expect(chef_run).to render_file(config_path)
.with_content(%r{\[gitlab\]\s+url = 'http\+unix://%2Fvar%2Fopt%2Fgitlab%2Fgitlab-workhorse%2Fsockets%2Fsocket'\s+relative_url_root = ''})
end
end
context 'with user settings' do
before do
stub_gitlab_rb(
gitaly: {
socket_path: socket_path,
internal_socket_dir: internal_socket_dir,
listen_addr: listen_addr,
tls_listen_addr: tls_listen_addr,
certificate_path: certificate_path,
key_path: key_path,
prometheus_listen_addr: prometheus_listen_addr,
logging_level: logging_level,
logging_format: logging_format,
logging_sentry_dsn: logging_sentry_dsn,
logging_ruby_sentry_dsn: logging_ruby_sentry_dsn,
logging_sentry_environment: logging_sentry_environment,
prometheus_grpc_latency_buckets: prometheus_grpc_latency_buckets,
auth_token: auth_token,
auth_transitioning: auth_transitioning,
graceful_restart_timeout: graceful_restart_timeout,
ruby_max_rss: ruby_max_rss,
ruby_graceful_restart_timeout: ruby_graceful_restart_timeout,
ruby_restart_delay: ruby_restart_delay,
ruby_num_workers: ruby_num_workers,
git_catfile_cache_size: git_catfile_cache_size,
git_bin_path: git_bin_path,
use_bundled_git: true,
open_files_ulimit: open_files_ulimit,
ruby_rugged_git_config_search_path: ruby_rugged_git_config_search_path,
daily_maintenance_start_hour: daily_maintenance_start_hour,
daily_maintenance_start_minute: daily_maintenance_start_minute,
daily_maintenance_duration: daily_maintenance_duration,
daily_maintenance_storages: daily_maintenance_storages,
daily_maintenance_disabled: daily_maintenance_disabled,
cgroups_count: cgroups_count,
cgroups_mountpoint: cgroups_mountpoint,
cgroups_hierarchy_root: cgroups_hierarchy_root,
cgroups_memory_enabled: cgroups_memory_enabled,
cgroups_memory_limit: cgroups_memory_limit,
cgroups_cpu_enabled: cgroups_cpu_enabled,
cgroups_cpu_shares: cgroups_cpu_shares,
pack_objects_cache_enabled: pack_objects_cache_enabled,
pack_objects_cache_dir: pack_objects_cache_dir,
pack_objects_cache_max_age: pack_objects_cache_max_age,
custom_hooks_dir: gitaly_custom_hooks_dir
},
gitlab_rails: {
internal_api_url: gitlab_url
},
gitlab_shell: {
http_settings: {
read_timeout: read_timeout,
user: user,
password: password,
ca_file: ca_file,
ca_path: ca_path,
self_signed_cert: self_signed_cert
}
},
gitlab_workhorse: {
listen_network: 'tcp',
listen_addr: workhorse_addr,
},
user: {
username: 'foo',
group: 'bar'
}
)
end
it_behaves_like "enabled runit service", "gitaly", "root", "root"
it 'creates expected directories with correct permissions' do
expect(chef_run).to create_directory(internal_socket_dir).with(user: 'foo', mode: '0700')
end
it 'populates gitaly config.toml with custom values' do
expect(chef_run).to render_file(config_path)
.with_content("socket_path = '#{socket_path}'")
expect(chef_run).to render_file(config_path)
.with_content("internal_socket_dir = '#{internal_socket_dir}'")
expect(chef_run).to render_file(config_path)
.with_content("bin_dir = '/opt/gitlab/embedded/bin'")
expect(chef_run).to render_file(config_path)
.with_content("listen_addr = 'localhost:7777'")
expect(chef_run).to render_file(config_path)
.with_content("graceful_restart_timeout = '#{graceful_restart_timeout}'")
expect(chef_run).to render_file(config_path)
.with_content("bin_path = '#{git_bin_path}'")
expect(chef_run).to render_file(config_path)
.with_content("use_bundled_binaries = true")
gitaly_logging_section = Regexp.new([
%r{\[logging\]},
%r{level = '#{logging_level}'},
%r{format = '#{logging_format}'},
%r{sentry_dsn = '#{logging_sentry_dsn}'},
%r{ruby_sentry_dsn = '#{logging_ruby_sentry_dsn}'},
%r{sentry_environment = '#{logging_sentry_environment}'},
].map(&:to_s).join('\s+'))
gitaly_ruby_section = Regexp.new([
%r{\[gitaly-ruby\]},
%r{dir = "/opt/gitlab/embedded/service/gitaly-ruby"},
%r{max_rss = #{ruby_max_rss}},
%r{graceful_restart_timeout = '#{Regexp.escape(ruby_graceful_restart_timeout)}'},
%r{restart_delay = '#{Regexp.escape(ruby_restart_delay)}'},
%r{num_workers = #{ruby_num_workers}},
].map(&:to_s).join('\s+'))
gitlab_shell_section = Regexp.new([
%r{\[gitlab-shell\]},
%r{dir = "/opt/gitlab/embedded/service/gitlab-shell"},
].map(&:to_s).join('\s+'))
gitlab_section = Regexp.new([
%r{\[gitlab\]},
%r{url = '#{Regexp.escape(gitlab_url)}'},
%r{\[gitlab.http-settings\]},
%r{read_timeout = #{read_timeout}},
%r{user = '#{Regexp.escape(user)}'},
%r{password = '#{Regexp.escape(password)}'},
%r{ca_file = '#{Regexp.escape(ca_file)}'},
%r{ca_path = '#{Regexp.escape(ca_path)}'},
%r{self_signed_cert = #{self_signed_cert}},
].map(&:to_s).join('\s+'))
hooks_section = Regexp.new([
%r{\[hooks\]},
%r{custom_hooks_dir = '#{Regexp.escape(gitaly_custom_hooks_dir)}'},
].map(&:to_s).join('\s+'))
maintenance_section = Regexp.new([
%r{\[daily_maintenance\]},
%r{start_hour = #{daily_maintenance_start_hour}},
%r{start_minute = #{daily_maintenance_start_minute}},
%r{duration = '#{daily_maintenance_duration}'},
%r{storages = #{Regexp.escape(daily_maintenance_storages.to_s)}},
].map(&:to_s).join('\s+'))
cgroups_section = Regexp.new([
%r{\[cgroups\]},
%r{count = #{cgroups_count}},
%r{mountpoint = '#{cgroups_mountpoint}'},
%r{hierarchy_root = '#{cgroups_hierarchy_root}'},
].map(&:to_s).join('\s+'))
cgroups_memory_section = Regexp.new([
%r{\[cgroups\.memory\]},
%r{enabled = #{cgroups_memory_enabled}},
%r{limit = #{cgroups_memory_limit}},
].map(&:to_s).join('\s+'))
cgroups_cpu_section = Regexp.new([
%r{\[cgroups\.cpu\]},
%r{enabled = #{cgroups_cpu_enabled}},
%r{shares = #{cgroups_cpu_shares}},
].map(&:to_s).join('\s+'))
pack_objects_cache_section = Regexp.new([
%r{\[pack_objects_cache\]\nenabled = true},
%r{dir = '#{pack_objects_cache_dir}'},
%r{max_age = '#{pack_objects_cache_max_age}'},
].map(&:to_s).join('\s+'))
expect(chef_run).to render_file(config_path).with_content { |content|
expect(content).to include("tls_listen_addr = 'localhost:8888'")
expect(content).to include("certificate_path = '/path/to/cert.pem'")
expect(content).to include("key_path = '/path/to/key.pem'")
expect(content).to include(%(rugged_git_config_search_path = "#{ruby_rugged_git_config_search_path}"))
expect(content).to include("prometheus_listen_addr = 'localhost:9000'")
expect(content).to match(gitaly_logging_section)
expect(content).to match(%r{\[prometheus\]\s+grpc_latency_buckets = #{Regexp.escape(prometheus_grpc_latency_buckets)}})
expect(content).to match(%r{\[auth\]\s+token = '#{Regexp.escape(auth_token)}'\s+transitioning = true})
expect(content).to match(gitaly_ruby_section)
expect(content).to match(%r{\[git\]\s+catfile_cache_size = 50})
expect(content).to match(gitlab_shell_section)
expect(content).to match(gitlab_section)
expect(content).to match(hooks_section)
expect(content).to match(maintenance_section)
expect(content).to match(cgroups_section)
expect(content).to match(cgroups_memory_section)
expect(content).to match(cgroups_cpu_section)
expect(content).to match(pack_objects_cache_section)
}
end
it 'populates gitaly config.toml with custom deprecated values' do
stub_gitlab_rb(
gitlab_shell: {
custom_hooks_dir: gitlab_shell_custom_hooks_dir
}
)
hooks_section = Regexp.new([
%r{\[hooks\]},
%r{custom_hooks_dir = '#{Regexp.escape(gitlab_shell_custom_hooks_dir)}'},
].map(&:to_s).join('\s+'))
expect(chef_run).to render_file(config_path).with_content { |content|
expect(content).to match(hooks_section)
}
end
it 'renders the runit run script with custom values' do
expect(chef_run).to render_file('/opt/gitlab/sv/gitaly/run')
.with_content(%r{ulimit -n #{open_files_ulimit}})
end
it 'renders the runit run script with cgroup root creation' do
expect(chef_run).to render_file('/opt/gitlab/sv/gitaly/run').with_content { |content|
expect(content).to match(%r{mkdir -m 0700 -p #{cgroups_mountpoint}/memory/#{cgroups_hierarchy_root}})
expect(content).to match(%r{mkdir -m 0700 -p #{cgroups_mountpoint}/cpu/#{cgroups_hierarchy_root}})
expect(content).to match(%r{chown foo:bar #{cgroups_mountpoint}/memory/#{cgroups_hierarchy_root}})
expect(content).to match(%r{chown foo:bar #{cgroups_mountpoint}/cpu/#{cgroups_hierarchy_root}})
}
end
it 'populates sv related log files' do
expect(chef_run).to render_file('/opt/gitlab/sv/gitaly/log/run')
.with_content(/exec svlogd -tt \/var\/log\/gitlab\/gitaly/)
end
context 'when using multiple maintenance storage entries' do
let(:daily_maintenance_storages) { %w(storage0 storage1 storage2) }
it 'renders daily_maintenance with multiple storage entries' do
expect(chef_run).to render_file(config_path).with_content { |content|
expect(content).to include("storages = #{daily_maintenance_storages}")
}
end
end
context 'when maintenance is disabled' do
let(:daily_maintenance_disabled) { true }
it 'renders daily_maintenance with disabled set to true' do
expect(chef_run).to render_file(config_path).with_content { |content|
expect(content).to include("[daily_maintenance]\ndisabled = true\n\n")
}
end
end
context 'when using gitaly storage configuration' do
before do
stub_gitlab_rb(
gitaly: {
storage: [
{
'name' => 'default',
'path' => '/tmp/path-1'
},
{
'name' => 'nfs1',
'path' => '/mnt/nfs1'
}
]
}
)
end
it 'populates gitaly config.toml with custom storages' do
expect(chef_run).to render_file(config_path)
.with_content(%r{\[\[storage\]\]\s+name = 'default'\s+path = '/tmp/path-1'})
expect(chef_run).to render_file(config_path)
.with_content(%r{\[\[storage\]\]\s+name = 'nfs1'\s+path = '/mnt/nfs1'})
end
end
context 'when using git_data_dirs storage configuration' do
context 'using local gitaly' do
before do
stub_gitlab_rb(
{
git_data_dirs:
{
'default' => { 'path' => '/tmp/default/git-data' },
'nfs1' => { 'path' => '/mnt/nfs1' }
}
}
)
end
it 'populates gitaly config.toml with custom storages' do
expect(chef_run).to render_file(config_path)
.with_content(%r{\[\[storage\]\]\s+name = 'default'\s+path = '/tmp/default/git-data/repositories'})
expect(chef_run).to render_file(config_path)
.with_content(%r{\[\[storage\]\]\s+name = 'nfs1'\s+path = '/mnt/nfs1/repositories'})
expect(chef_run).not_to render_file(config_path)
.with_content('gitaly_address: "/var/opt/gitlab/gitaly/gitaly.socket"')
end
end
context 'using external gitaly' do
before do
stub_gitlab_rb(
{
git_data_dirs:
{
'default' => { 'gitaly_address' => 'tcp://gitaly.internal:8075' },
}
}
)
end
it 'populates gitaly config.toml with custom storages' do
expect(chef_run).to render_file(config_path)
.with_content(%r{\[\[storage\]\]\s+name = 'default'\s+path = '/var/opt/gitlab/git-data/repositories'})
expect(chef_run).not_to render_file(config_path)
.with_content('gitaly_address: "tcp://gitaly.internal:8075"')
end
end
end
end
context 'when gitaly is disabled' do
before do
stub_gitlab_rb(gitaly: { enable: false })
end
it_behaves_like "disabled runit service", "gitaly"
it 'does not create the gitaly directories' do
expect(chef_run).not_to create_directory('/var/opt/gitlab/gitaly')
expect(chef_run).not_to create_directory('/var/log/gitlab/gitaly')
expect(chef_run).not_to create_directory('/opt/gitlab/etc/gitaly')
expect(chef_run).not_to create_file('/var/opt/gitlab/gitaly/config.toml')
end
end
context 'when using concurrency configuration' do
before do
stub_gitlab_rb(
{
gitaly: {
concurrency: [
{
'rpc' => "/gitaly.SmartHTTPService/PostReceivePack",
'max_per_repo' => 20
}, {
'rpc' => "/gitaly.SSHService/SSHUploadPack",
'max_per_repo' => 5
}
]
}
}
)
end
it 'populates gitaly config.toml with custom concurrency configurations' do
expect(chef_run).to render_file(config_path)
.with_content(%r{\[\[concurrency\]\]\s+rpc = "/gitaly.SmartHTTPService/PostReceivePack"\s+max_per_repo = 20})
expect(chef_run).to render_file(config_path)
.with_content(%r{\[\[concurrency\]\]\s+rpc = "/gitaly.SSHService/SSHUploadPack"\s+max_per_repo = 5})
end
end
shared_examples 'empty concurrency configuration' do
it 'does not generate a gitaly concurrency configuration' do
expect(chef_run).not_to render_file(config_path)
.with_content(%r{\[\[concurrency\]\]})
end
end
context 'when not using concurrency configuration' do
context 'when concurrency configuration is not set' do
before do
stub_gitlab_rb(
{
gitaly: {
}
}
)
end
it_behaves_like 'empty concurrency configuration'
end
context 'when concurrency configuration is empty' do
before do
stub_gitlab_rb(
{
gitaly: {
concurrency: []
}
}
)
end
it_behaves_like 'empty concurrency configuration'
end
context 'when max_queue_size and max_queue_wait are empty' do
before do
stub_gitlab_rb(
{
gitaly: {
concurrency: [
{
'rpc' => "/gitaly.SmartHTTPService/PostReceivePack",
'max_per_repo' => 20,
}, {
'rpc' => "/gitaly.SSHService/SSHUploadPack",
'max_per_repo' => 5,
}
]
}
}
)
end
it 'populates gitaly config.toml without max_queue_size and max_queue_wait' do
expect(chef_run).to render_file(config_path).with_content { |content|
expect(content).not_to include("max_queue_size")
expect(content).not_to include("max_queue_wait")
}
end
end
context 'when max_per_repo is empty' do
before do
stub_gitlab_rb(
{
gitaly: {
concurrency: [
{
'rpc' => "/gitaly.SmartHTTPService/PostReceivePack",
'max_queue_size' => '10s'
}, {
'rpc' => "/gitaly.SSHService/SSHUploadPack",
'max_queue_size' => '10s'
}
]
}
}
)
end
it 'populates gitaly config.toml without max_per_repo' do
expect(chef_run).to render_file(config_path).with_content { |content|
expect(content).not_to include("max_per_repo")
}
end
end
context 'when max_queue_wait is set' do
before do
stub_gitlab_rb(
{
gitaly: {
concurrency: [
{
'rpc' => "/gitaly.SmartHTTPService/PostReceivePack",
'max_queue_wait' => "10s",
}
]
}
}
)
end
it 'populates gitaly config.toml with quoted max_queue_wait' do
expect(chef_run).to render_file(config_path)
.with_content(%r{\[\[concurrency\]\]\s+rpc = "/gitaly.SmartHTTPService/PostReceivePack"\s+max_queue_wait = "10s"})
end
end
end
context 'populates default env variables' do
it 'creates necessary env variable files' do
expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitaly/env').with_variables(default_vars)
end
end
context 'computes env variables based on other values' do
before do
stub_gitlab_rb(
{
user: {
home: "/my/random/path"
}
}
)
end
it 'creates necessary env variable files' do
expect(chef_run).to create_env_dir('/opt/gitlab/etc/gitaly/env').with_variables(
default_vars.merge(
{
'HOME' => '/my/random/path',
}
)
)
end
end
context 'with a non-default workhorse unix socket' do
context 'with only a listen address set' do
before do
stub_gitlab_rb(gitlab_workhorse: { listen_addr: '/fake/workhorse/socket' })
end
it 'create config file with provided values' do
expect(chef_run).to render_file(config_path)
.with_content(%r{\[gitlab\]\s+url = 'http\+unix://%2Ffake%2Fworkhorse%2Fsocket'\s+relative_url_root = ''})
end
end
context 'with only a socket directory set' do
before do
stub_gitlab_rb(gitlab_workhorse: { sockets_directory: '/fake/workhorse/sockets' })
end
it 'create config file with provided values' do
expect(chef_run).to render_file(config_path)
.with_content(%r{\[gitlab\]\s+url = 'http\+unix://%2Ffake%2Fworkhorse%2Fsockets%2Fsocket'\s+relative_url_root = ''})
end
end
context 'with a listen_address and a sockets_directory set' do
before do
stub_gitlab_rb(gitlab_workhorse: { listen_addr: '/sockets/in/the/wind', sockets_directory: '/sockets/in/the' })
end
it 'create config file with provided values' do
expect(chef_run).to render_file(config_path)
.with_content(%r{\[gitlab\]\s+url = 'http\+unix://%2Fsockets%2Fin%2Fthe%2Fwind'\s+relative_url_root = ''})
end
end
end
context 'with a tcp workhorse listener' do
before do
stub_gitlab_rb(
external_url: 'http://example.com/gitlab',
gitlab_workhorse: {
listen_network: 'tcp',
listen_addr: 'localhost:1234'
}
)
end
it 'create config file with only the URL set' do
expect(chef_run).to render_file(config_path).with_content { |content|
expect(content).to match(%r{\[gitlab\]\s+url = 'http://localhost:1234/gitlab'})
expect(content).not_to match(/relative_url_root/)
}
end
end
context 'with relative path in external_url' do
before do
stub_gitlab_rb(external_url: 'http://example.com/gitlab')
end
it 'create config file with the relative_url_root set' do
expect(chef_run).to render_file(config_path)
.with_content(%r{\[gitlab\]\s+url = 'http\+unix://%2Fvar%2Fopt%2Fgitlab%2Fgitlab-workhorse%2Fsockets%2Fsocket'\s+relative_url_root = '/gitlab'})
end
end
context 'with cgroups mountpoint and hierarchy_root' do
before do
stub_gitlab_rb(
gitaly: {
cgroups_mountpoint: '/sys/fs/cgroup',
cgroups_hierarchy_root: 'gitaly'
}
)
end
end
include_examples "consul service discovery", "gitaly", "gitaly"
end
RSpec.describe 'gitaly::git_data_dirs' do
let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') }
before do
allow(Gitlab).to receive(:[]).and_call_original
end
context 'when user has not specified git_data_dir' do
it 'defaults to correct path' do
expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages'])
.to eql('default' => { 'path' => '/var/opt/gitlab/git-data/repositories', 'gitaly_address' => 'unix:/var/opt/gitlab/gitaly/gitaly.socket' })
end
end
context 'when gitaly is set to use a listen_addr' do
before do
stub_gitlab_rb(git_data_dirs: {
'default' => {
'path' => '/tmp/user/git-data'
}
}, gitaly: {
socket_path: '',
listen_addr: 'localhost:8123'
})
end
it 'correctly sets the repository storage directories' do
expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages'])
.to eql('default' => { 'path' => '/tmp/user/git-data/repositories', 'gitaly_address' => 'tcp://localhost:8123' })
end
end
context 'when gitaly is set to use a tls_listen_addr' do
before do
stub_gitlab_rb(git_data_dirs: {
'default' => {
'path' => '/tmp/user/git-data'
}
}, gitaly: {
socket_path: '', tls_listen_addr: 'localhost:8123'
})
end
it 'correctly sets the repository storage directories' do
expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages'])
.to eql('default' => { 'path' => '/tmp/user/git-data/repositories', 'gitaly_address' => 'tls://localhost:8123' })
end
end
context 'when both tls and socket' do
before do
stub_gitlab_rb(git_data_dirs: {
'default' => {
'path' => '/tmp/user/git-data'
}
}, gitaly: {
socket_path: '/some/socket/path.socket', tls_listen_addr: 'localhost:8123'
})
end
it 'TlS should take precedence' do
expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages'])
.to eql('default' => { 'path' => '/tmp/user/git-data/repositories', 'gitaly_address' => 'tls://localhost:8123' })
end
end
context 'when git_data_dirs is set to multiple directories' do
before do
stub_gitlab_rb({
git_data_dirs: {
'default' => { 'path' => '/tmp/default/git-data' },
'overflow' => { 'path' => '/tmp/other/git-overflow-data' }
}
})
end
it 'correctly sets the repository storage directories' do
expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages']).to eql({
'default' => { 'path' => '/tmp/default/git-data/repositories', 'gitaly_address' => 'unix:/var/opt/gitlab/gitaly/gitaly.socket' },
'overflow' => { 'path' => '/tmp/other/git-overflow-data/repositories', 'gitaly_address' => 'unix:/var/opt/gitlab/gitaly/gitaly.socket' }
})
end
end
context 'when git_data_dirs is set to multiple directories with different gitaly addresses' do
before do
stub_gitlab_rb({
git_data_dirs: {
'default' => { 'path' => '/tmp/default/git-data' },
'overflow' => { 'path' => '/tmp/other/git-overflow-data', 'gitaly_address' => 'tcp://localhost:8123', 'gitaly_token' => '123secret456gitaly' }
}
})
end
it 'correctly sets the repository storage directories' do
expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages']).to eql({
'default' => { 'path' => '/tmp/default/git-data/repositories', 'gitaly_address' => 'unix:/var/opt/gitlab/gitaly/gitaly.socket' },
'overflow' => { 'path' => '/tmp/other/git-overflow-data/repositories', 'gitaly_address' => 'tcp://localhost:8123', 'gitaly_token' => '123secret456gitaly' }
})
end
end
context 'when path not defined in git_data_dirs' do
before do
stub_gitlab_rb(
{
git_data_dirs:
{
'default' => { 'gitaly_address' => 'tcp://gitaly.internal:8075' },
}
}
)
end
it 'correctly sets the repository storage directories' do
expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages']).to eql({ 'default' => { 'path' => '/var/opt/gitlab/git-data/repositories', 'gitaly_address' => 'tcp://gitaly.internal:8075' } })
end
end
context 'when git_data_dirs is set with symbol keys rather than string keys' do
before do
with_symbol_keys = {
default: { path: '/tmp/default/git-data' },
overflow: { path: '/tmp/other/git-overflow-data' }
}
allow(Gitlab).to receive(:[]).with('git_data_dirs').and_return(with_symbol_keys)
end
it 'correctly sets the repository storage directories' do
expect(chef_run.node['gitlab']['gitlab-rails']['repositories_storages']).to eql({
'default' => { 'path' => '/tmp/default/git-data/repositories', 'gitaly_address' => 'unix:/var/opt/gitlab/gitaly/gitaly.socket' },
'overflow' => { 'path' => '/tmp/other/git-overflow-data/repositories', 'gitaly_address' => 'unix:/var/opt/gitlab/gitaly/gitaly.socket' }
})
end
end
end