diff --git a/files/gitlab-config-template/gitlab.rb.template b/files/gitlab-config-template/gitlab.rb.template index 3eac49be9d0b76016ad458b726a85c1b296b0ca2_ZmlsZXMvZ2l0bGFiLWNvbmZpZy10ZW1wbGF0ZS9naXRsYWIucmIudGVtcGxhdGU=..221553a16e33b0f79d1a961dda043abcd1e85bf2_ZmlsZXMvZ2l0bGFiLWNvbmZpZy10ZW1wbGF0ZS9naXRsYWIucmIudGVtcGxhdGU= 100644 --- a/files/gitlab-config-template/gitlab.rb.template +++ b/files/gitlab-config-template/gitlab.rb.template @@ -858,7 +858,14 @@ # gitlab_rails['redis_repository_cache_username'] = nil # gitlab_rails['redis_repository_cache_password'] = nil # gitlab_rails['redis_repository_cache_cluster_nodes'] = nil - +# gitlab_rails['redis_workhorse_instance'] = nil +# gitlab_rails['redis_workhorse_sentinels'] = nil +# gitlab_rails['redis_workhorse_sentinels_password'] = nil +# gitlab_rails['redis_workhorse_username'] = nil +# gitlab_rails['redis_workhorse_password'] = nil +# gitlab_rails['redis_workhorse_cluster_nodes'] = nil + +# gitlab_rails['redis_workhorse_sentinel_master'] = nil # gitlab_rails['redis_yml_override'] = nil diff --git a/files/gitlab-cookbooks/gitlab/attributes/default.rb b/files/gitlab-cookbooks/gitlab/attributes/default.rb index 3eac49be9d0b76016ad458b726a85c1b296b0ca2_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvYXR0cmlidXRlcy9kZWZhdWx0LnJi..221553a16e33b0f79d1a961dda043abcd1e85bf2_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvYXR0cmlidXRlcy9kZWZhdWx0LnJi 100644 --- a/files/gitlab-cookbooks/gitlab/attributes/default.rb +++ b/files/gitlab-cookbooks/gitlab/attributes/default.rb @@ -497,6 +497,15 @@ default['gitlab']['gitlab_rails']['redis_cluster_rate_limiting_username'] = nil default['gitlab']['gitlab_rails']['redis_cluster_rate_limiting_password'] = nil default['gitlab']['gitlab_rails']['redis_cluster_rate_limiting_cluster_nodes'] = [] +default['gitlab']['gitlab_rails']['redis_workhorse_instance'] = nil +default['gitlab']['gitlab_rails']['redis_workhorse_sentinels'] = [] +default['gitlab']['gitlab_rails']['redis_workhorse_sentinels_password'] = nil +default['gitlab']['gitlab_rails']['redis_workhorse_username'] = nil +default['gitlab']['gitlab_rails']['redis_workhorse_password'] = nil +default['gitlab']['gitlab_rails']['redis_workhorse_cluster_nodes'] = [] + +# used by workhorse to connect to a separate external redis instead of the omnibus-gitlab redis +default['gitlab']['gitlab_rails']['redis_workhorse_sentinel_master'] = nil default['gitlab']['gitlab_rails']['redis_yml_override'] = nil diff --git a/files/gitlab-cookbooks/gitlab/libraries/redis_helper.rb b/files/gitlab-cookbooks/gitlab/libraries/redis_helper.rb index 3eac49be9d0b76016ad458b726a85c1b296b0ca2_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JlZGlzX2hlbHBlci5yYg==..221553a16e33b0f79d1a961dda043abcd1e85bf2_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JlZGlzX2hlbHBlci5yYg== 100644 --- a/files/gitlab-cookbooks/gitlab/libraries/redis_helper.rb +++ b/files/gitlab-cookbooks/gitlab/libraries/redis_helper.rb @@ -2,7 +2,7 @@ require 'cgi' class RedisHelper - REDIS_INSTANCES = %w[cache queues shared_state trace_chunks rate_limiting sessions repository_cache cluster_rate_limiting].freeze + REDIS_INSTANCES = %w[cache queues shared_state trace_chunks rate_limiting sessions repository_cache cluster_rate_limiting workhorse].freeze ALLOWED_REDIS_CLUSTER_INSTANCE = %w[cache rate_limiting cluster_rate_limiting].freeze def initialize(node) @@ -51,6 +51,27 @@ uri end + def workhorse_params + gitlab_rails = @node['gitlab']['gitlab_rails'] + if gitlab_rails['redis_workhorse_instance'] || !gitlab_rails['redis_workhorse_sentinels'].empty? + { + url: gitlab_rails['redis_workhorse_instance'], + password: gitlab_rails['redis_workhorse_password'], + sentinels: redis_sentinel_urls('redis_workhorse_sentinels'), + sentinelMaster: gitlab_rails['redis_workhorse_sentinel_master'], + sentinelPassword: gitlab_rails['redis_workhorse_sentinels_password'] + } + else + { + url: redis_url, + password: redis_params.last, + sentinels: redis_sentinel_urls('redis_sentinels'), + sentinelMaster: @node['redis']['master_name'], + sentinelPassword: @node['redis']['master_password'] + } + end + end + def validate_instance_shard_config(instance) gitlab_rails = @node['gitlab']['gitlab_rails'] diff --git a/files/gitlab-cookbooks/gitlab/recipes/gitlab-workhorse.rb b/files/gitlab-cookbooks/gitlab/recipes/gitlab-workhorse.rb index 3eac49be9d0b76016ad458b726a85c1b296b0ca2_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItd29ya2hvcnNlLnJi..221553a16e33b0f79d1a961dda043abcd1e85bf2_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItd29ya2hvcnNlLnJi 100644 --- a/files/gitlab-cookbooks/gitlab/recipes/gitlab-workhorse.rb +++ b/files/gitlab-cookbooks/gitlab/recipes/gitlab-workhorse.rb @@ -88,5 +88,4 @@ alt_document_root = node['gitlab']['gitlab_workhorse']['alt_document_root'] shutdown_timeout = node['gitlab']['gitlab_workhorse']['shutdown_timeout'] -_redis_host, _redis_port, redis_password = redis_helper.redis_params workhorse_keywatcher = node['gitlab']['gitlab_workhorse']['workhorse_keywatcher'] @@ -92,8 +91,5 @@ workhorse_keywatcher = node['gitlab']['gitlab_workhorse']['workhorse_keywatcher'] -redis_url = redis_helper.redis_url.to_s -redis_sentinel_urls = redis_helper.redis_sentinel_urls('redis_sentinels') -redis_sentinel_master = node['redis']['master_name'] -redis_sentinel_master_password = node['redis']['master_password'] +redis_params = redis_helper.workhorse_params config_file_path = File.join(working_dir, "config.toml") image_scaler_max_procs = node['gitlab']['gitlab_workhorse']['image_scaler_max_procs'] image_scaler_max_filesize = node['gitlab']['gitlab_workhorse']['image_scaler_max_filesize'] @@ -108,8 +104,9 @@ variables( alt_document_root: alt_document_root, workhorse_keywatcher: workhorse_keywatcher, - redis_url: redis_url, - password: redis_password, - sentinels: redis_sentinel_urls, - sentinel_master: redis_sentinel_master, + redis_url: redis_params[:url], + password: redis_params[:password], + sentinels: redis_params[:sentinels], + sentinel_master: redis_params[:sentinelMaster], + master_password: redis_params[:sentinelPassword], shutdown_timeout: shutdown_timeout, @@ -115,5 +112,4 @@ shutdown_timeout: shutdown_timeout, - master_password: redis_sentinel_master_password, image_scaler_max_procs: image_scaler_max_procs, image_scaler_max_filesize: image_scaler_max_filesize, trusted_cidrs_for_propagation: trusted_cidrs_for_propagation, diff --git a/spec/chef/cookbooks/gitlab/libraries/redis_helper_spec.rb b/spec/chef/cookbooks/gitlab/libraries/redis_helper_spec.rb index 3eac49be9d0b76016ad458b726a85c1b296b0ca2_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JlZGlzX2hlbHBlcl9zcGVjLnJi..221553a16e33b0f79d1a961dda043abcd1e85bf2_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JlZGlzX2hlbHBlcl9zcGVjLnJi 100644 --- a/spec/chef/cookbooks/gitlab/libraries/redis_helper_spec.rb +++ b/spec/chef/cookbooks/gitlab/libraries/redis_helper_spec.rb @@ -139,6 +139,100 @@ end end + describe '#workhorse_params' do + let(:baseline_config_with_sentinel) do + { + gitlab_rails: { + redis_host: 'redis.example.com', + redis_port: 8888, + redis_password: 'mypass', + redis_sentinels: [ + { 'host' => 'sentinel1.example.com', 'port' => '12345' }, + { 'host' => 'sentinel2.example.com', 'port' => '12345' } + ] + }, + redis: { + master_name: 'master_from.redis.master_name', + master_password: 'password_from.redis.master_password' + } + } + end + + before { allow(Gitlab).to receive(:[]).and_call_original } + + context 'without external workhorse redis' do + context 'when no sentinels are configured' do + it 'renders parameters for workhorse redis' do + stub_gitlab_rb( + gitlab_rails: { + redis_host: 'redis.example.com', + redis_port: 8888, + redis_password: 'mypass' + } + ) + + params = subject.workhorse_params + expect(params[:password]).to eq('mypass') + expect(params[:sentinels]).to eq([]) + expect(params[:url].to_s).to eq('redis://:mypass@redis.example.com:8888/') + end + end + + context 'when sentinels are configured' do + it 'renders parameters for workhorse redis' do + stub_gitlab_rb(baseline_config_with_sentinel) + + params = subject.workhorse_params + expect(params[:password]).to eq('password_from.redis.master_password') + expect(params[:sentinels].map(&:to_s)).to eq(%w[redis://sentinel1.example.com:12345 redis://sentinel2.example.com:12345]) + expect(params[:sentinelMaster]).to eq('master_from.redis.master_name') + expect(params[:sentinelPassword]).to eq('password_from.redis.master_password') + expect(params[:url].to_s).to eq("redis://:password_from.redis.master_password@master_from.redis.master_name/") + end + end + end + + context 'with external workhorse redis' do + context 'when no sentinels are configured' do + it 'renders parameters for workhorse redis' do + stub_gitlab_rb(baseline_config_with_sentinel.merge({ + gitlab_rails: { + redis_workhorse_instance: "redis://:redis.workhorse.com:8888", + redis_workhorse_password: "workhorse.password" + } + })) + + params = subject.workhorse_params + expect(params[:password]).to eq('workhorse.password') + expect(params[:sentinels]).to eq([]) + expect(params[:url].to_s).to eq('redis://:redis.workhorse.com:8888') + end + end + + context 'when sentinels are configured' do + it 'renders parameters for workhorse redis' do + stub_gitlab_rb(baseline_config_with_sentinel.merge({ + gitlab_rails: { + redis_workhorse_sentinels: [ + { 'host' => 'sentinel1.workhorse.com', 'port' => '12345' }, + { 'host' => 'sentinel2.workhorse.com', 'port' => '12345' } + ], + redis_workhorse_sentinels_password: "workhorse.password", + redis_workhorse_sentinel_master: "workhorse.master" + } + })) + + params = subject.workhorse_params + expect(params[:password]).to be_nil + expect(params[:sentinels].map(&:to_s)).to eq(%w[redis://:workhorse.password@sentinel1.workhorse.com:12345 redis://:workhorse.password@sentinel2.workhorse.com:12345]) + expect(params[:sentinelMaster]).to eq('workhorse.master') + expect(params[:sentinelPassword]).to eq('workhorse.password') + expect(params[:url]).to eq(nil) + end + end + end + end + describe '#running_version' do let(:redis_cli_output) do <<~MSG diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-workhorse_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-workhorse_spec.rb index 3eac49be9d0b76016ad458b726a85c1b296b0ca2_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItd29ya2hvcnNlX3NwZWMucmI=..221553a16e33b0f79d1a961dda043abcd1e85bf2_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItd29ya2hvcnNlX3NwZWMucmI= 100644 --- a/spec/chef/cookbooks/gitlab/recipes/gitlab-workhorse_spec.rb +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-workhorse_spec.rb @@ -619,6 +619,71 @@ end end + context 'with separate workhorse redis details specified' do + context 'with standalone redis defined' do + before do + stub_gitlab_rb( + gitlab_rails: { + redis_sentinels: [ + { 'host' => '127.0.0.1', 'port' => 26379 }, + { 'host' => '127.0.8.1', 'port' => 12345 } + ], + redis_workhorse_instance: "unix:/home/random/path.socket", + redis_workhorse_password: 'some pass' + }, + redis: { + master_name: 'examplemaster', + master_password: 'examplepassword' + } + ) + end + + it 'should generate config file with the specified values' do + content_url = 'URL = "unix:/home/random/path.socket"' + content_password = 'Password = "some pass"' + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_url) + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_password) + expect(chef_run).not_to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(/Sentinel/) + expect(chef_run).not_to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(/SentinelMaster/) + end + end + + context 'with sentinels defined' do + before do + stub_gitlab_rb( + gitlab_rails: { + redis_sentinels: [ + { 'host' => '127.0.0.1', 'port' => 26379 }, + { 'host' => '127.0.8.1', 'port' => 12345 } + ], + redis_sentinels_password: 'some pass', + redis_workhorse_sentinels: [ + { 'host' => '127.0.0.2', 'port' => 26379 }, + { 'host' => '127.0.8.2', 'port' => 12345 } + ], + redis_workhorse_sentinels_password: 'some workhorse pass', + redis_workhorse_sentinel_master: 'worhorse.master' + }, + redis: { + master_name: 'examplemaster', + master_password: 'examplepassword' + } + ) + end + + it 'should generate config file with the specified values' do + content = 'Sentinel = ["redis://:some+workhorse+pass@127.0.0.2:26379","redis://:some+workhorse+pass@127.0.8.2:12345"]' + content_sentinel_master = 'SentinelMaster = "worhorse.master"' + content_sentinel_password = 'Password = "some workhorse pass"' + content_url = 'URL =' + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content) + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_sentinel_master) + expect(chef_run).to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_sentinel_password) + expect(chef_run).not_to render_file("/var/opt/gitlab/gitlab-workhorse/config.toml").with_content(content_url) + end + end + end + context 'image scaler' do context 'with default values' do it 'sets the default maximum file size' do