Skip to content

Commit 0e1b259

Browse files
Robert Marshallbalasankarc
andcommitted
Merge branch 'refactor-redis-helper' into 'master'
Add Redis settings specific to Workhorse Closes #8300 See merge request https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/7269 Merged-by: Robert Marshall <[email protected]> Approved-by: Andrew Patterson <[email protected]> Approved-by: Robert Marshall <[email protected]> Reviewed-by: Balasankar 'Balu' C <[email protected]> Co-authored-by: Balasankar "Balu" C <[email protected]>
2 parents a0ba0e2 + a708e06 commit 0e1b259

File tree

9 files changed

+558
-4
lines changed

9 files changed

+558
-4
lines changed

files/gitlab-config-template/gitlab.rb.template

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,6 +1128,28 @@ external_url 'GENERATED_EXTERNAL_URL'
11281128
##! Semantic metadata used when registering GitLab Workhorse as a Consul service
11291129
# gitlab_workhorse['consul_service_meta'] = {}
11301130

1131+
##! Redis settings specific for GitLab Workhorse
1132+
##! To be used when Workhorse is supposed to use a different Redis instance than
1133+
##! other components. The settings specified here should match
1134+
##! `gitlab_rails['redis_workhorse_*']` settings, if specified. If not specified,
1135+
##! they are inferred from the below values. `gitlab_rails['redis_workhorse_*']`
1136+
##! settings tell the Rails app which Redis has channels to publish messages to,
1137+
##! and `gitlab_workhorse['redis_*']` tells Workhorse which Redis has channels to
1138+
##! subscribe to. Hence, the requirement of the settings to match.
1139+
# gitlab_workhorse['redis_socket'] = "/var/opt/gitlab/redis/redis.socket"
1140+
# gitlab_workhorse['redis_host'] = "127.0.0.1"
1141+
# gitlab_workhorse['redis_port'] = nil
1142+
# gitlab_workhorse['redis_database'] = nil
1143+
# gitlab_workhorse['redis_username'] = nil
1144+
# gitlab_workhorse['redis_password'] = nil
1145+
# gitlab_workhorse['redis_ssl'] = false
1146+
# gitlab_workhorse['redis_cluster_nodes'] = []
1147+
# gitlab_workhorse['redis_sentinels'] = []
1148+
# gitlab_workhorse['redis_sentinels_password'] = nil
1149+
# gitlab_workhorse['redis_sentinel_master'] = nil
1150+
# gitlab_workhorse['redis_sentinel_master_ip'] = nil
1151+
# gitlab_workhorse['redis_sentinel_master_port'] = nil
1152+
11311153
################################################################################
11321154
## GitLab User Settings
11331155
##! Modify default git user.

files/gitlab-cookbooks/gitlab/attributes/default.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,20 @@
817817
default['gitlab']['gitlab_workhorse']['consul_service_name'] = 'workhorse'
818818
default['gitlab']['gitlab_workhorse']['consul_service_meta'] = nil
819819

820+
default['gitlab']['gitlab_workhorse']['redis_socket'] = "/var/opt/gitlab/redis/redis.socket"
821+
default['gitlab']['gitlab_workhorse']['redis_host'] = "127.0.0.1"
822+
default['gitlab']['gitlab_workhorse']['redis_port'] = nil
823+
default['gitlab']['gitlab_workhorse']['redis_database'] = nil
824+
default['gitlab']['gitlab_workhorse']['redis_username'] = nil
825+
default['gitlab']['gitlab_workhorse']['redis_password'] = nil
826+
default['gitlab']['gitlab_workhorse']['redis_ssl'] = false
827+
default['gitlab']['gitlab_workhorse']['redis_cluster_nodes'] = []
828+
default['gitlab']['gitlab_workhorse']['redis_sentinels'] = []
829+
default['gitlab']['gitlab_workhorse']['redis_sentinels_password'] = nil
830+
default['gitlab']['gitlab_workhorse']['redis_sentinel_master'] = nil
831+
default['gitlab']['gitlab_workhorse']['redis_sentinel_master_ip'] = nil
832+
default['gitlab']['gitlab_workhorse']['redis_sentinel_master_port'] = nil
833+
820834
####
821835
# mailroom
822836
####

files/gitlab-cookbooks/gitlab/libraries/gitlab_workhorse.rb

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
# limitations under the License.
1616
#
1717

18+
require_relative './redis_uri'
19+
require_relative '../../package/libraries/helpers/new_redis_helper'
20+
1821
module GitlabWorkhorse
1922
class << self
2023
def parse_variables
@@ -30,13 +33,123 @@ def parse_variables
3033
network = user_network || default_network
3134

3235
Gitlab['gitlab_workhorse']['listen_addr'] ||= File.join(sockets_dir, 'socket') if network == "unix"
36+
37+
parse_redis_settings
3338
end
3439

3540
def parse_secrets
3641
# gitlab-workhorse expects exactly 32 bytes, encoded with base64
3742
Gitlab['gitlab_workhorse']['secret_token'] ||= SecureRandom.base64(32)
3843
end
3944

45+
def parse_redis_settings
46+
gitlab_workhorse_redis_configured = Gitlab['gitlab_workhorse'].key?('redis_socket') ||
47+
Gitlab['gitlab_workhorse'].key?('redis_host')
48+
49+
rails_workhorse_redis_configured =
50+
Gitlab['gitlab_rails']['redis_workhorse_instance'] ||
51+
(Gitlab['gitlab_rails']['redis_workhorse_sentinels'] &&
52+
!Gitlab['gitlab_rails']['redis_workhorse_sentinels'].empty?)
53+
54+
if gitlab_workhorse_redis_configured
55+
# Parse settings from `redis['master_*']` first.
56+
parse_redis_master_settings
57+
# If gitlab_workhorse settings are specified, populate
58+
# gitlab_rails['redis_workhorse_*'] settings from it.
59+
update_separate_redis_instance_settings
60+
elsif rails_workhorse_redis_configured
61+
# If user has specified a separate Redis host for Workhorse via
62+
# `gitlab_rails['redis_workhorse_*']` settings, copy them to
63+
# `gitlab_workhorse['redis_*']`.
64+
parse_separate_redis_instance_settings
65+
parse_redis_master_settings
66+
else
67+
# If user hasn't specified any separate Redis settings for Workhorse,
68+
# copy the global settings from GitLab Rails
69+
parse_global_rails_redis_settings
70+
parse_redis_master_settings
71+
end
72+
end
73+
74+
# rubocop:disable Metrics/CyclomaticComplexity
75+
# rubocop:disable Metrics/AbcSize
76+
# rubocop:disable Metrics/PerceivedComplexity
77+
def update_separate_redis_instance_settings
78+
if Gitlab['gitlab_workhorse']['redis_host']
79+
uri_from_workhorse = NewRedisHelper.build_redis_url(
80+
ssl: Gitlab['gitlab_workhorse']['redis_ssl'] || Gitlab['node']['gitlab']['gitlab_workhorse']['redis_ssl'],
81+
host: Gitlab['gitlab_workhorse']['redis_host'] || Gitlab['node']['gitlab']['gitlab_workhorse']['redis_host'],
82+
port: Gitlab['gitlab_workhorse']['redis_port'] || Gitlab['node']['gitlab']['gitlab_workhorse']['redis_port'],
83+
password: Gitlab['gitlab_workhorse']['redis_password'] || Gitlab['node']['gitlab']['gitlab_workhorse']['redis_password'],
84+
path: Gitlab['gitlab_workhorse']['redis_database'] || Gitlab['node']['gitlab']['gitlab_workhorse']['redis_database']
85+
).to_s
86+
87+
uri_from_rails = NewRedisHelper.build_redis_url(
88+
ssl: Gitlab['gitlab_rails']['redis_ssl'] || Gitlab['node']['gitlab']['gitlab_rails']['redis_ssl'],
89+
host: Gitlab['gitlab_rails']['redis_host'] || Gitlab['node']['gitlab']['gitlab_rails']['redis_host'],
90+
port: Gitlab['gitlab_rails']['redis_port'] || Gitlab['node']['gitlab']['gitlab_rails']['redis_port'],
91+
password: Gitlab['gitlab_rails']['redis_password'] || Gitlab['node']['gitlab']['gitlab_rails']['redis_password'],
92+
path: Gitlab['gitlab_rails']['redis_database'] || Gitlab['node']['gitlab']['gitlab_rails']['redis_database']
93+
).to_s
94+
Gitlab['gitlab_rails']['redis_workhorse_instance'] = uri_from_workhorse if uri_from_workhorse != uri_from_rails
95+
96+
else
97+
workhorse_redis_socket = Gitlab['gitlab_workhorse']['redis_socket'] || Gitlab['node']['gitlab']['gitlab_workhorse']['redis_socket']
98+
rails_redis_socket = Gitlab['gitlab_rails']['redis_socket'] || Gitlab['node']['gitlab']['gitlab_rails']['redis_socket']
99+
Gitlab['gitlab_rails']['redis_workhorse_instance'] = "unix://#{workhorse_redis_socket}" if workhorse_redis_socket != rails_redis_socket
100+
end
101+
102+
%w[username password cluster_nodes sentinels sentinel_master sentinels_password].each do |setting|
103+
Gitlab['gitlab_rails']["redis_workhorse_#{setting}"] ||= Gitlab['gitlab_workhorse']["redis_#{setting}"]
104+
end
105+
end
106+
# rubocop:enable Metrics/CyclomaticComplexity
107+
# rubocop:enable Metrics/AbcSize
108+
# rubocop:enable Metrics/PerceivedComplexity
109+
110+
def parse_global_rails_redis_settings
111+
%w[ssl host socket port password database sentinels sentinels_password].each do |setting|
112+
Gitlab['gitlab_workhorse']["redis_#{setting}"] ||= Gitlab['gitlab_rails']["redis_#{setting}"]
113+
end
114+
end
115+
116+
def parse_separate_redis_instance_settings
117+
# If an individual Redis instance is specified for Workhorse, figure out
118+
# host, port, password, etc. from it
119+
if Gitlab['gitlab_rails']['redis_workhorse_instance']
120+
uri = URI(Gitlab['gitlab_rails']['redis_workhorse_instance'])
121+
122+
Gitlab['gitlab_workhorse']['redis_ssl'] = uri.scheme == 'rediss' unless Gitlab['gitlab_workhorse'].key?('redis_ssl')
123+
if uri.scheme == 'unix'
124+
Gitlab['gitlab_workhorse']['redis_socket'] = uri.path
125+
else
126+
Gitlab['gitlab_workhorse']['redis_host'] ||= if uri.path.start_with?('/')
127+
uri.host
128+
else
129+
uri.path
130+
end
131+
Gitlab['gitlab_workhorse']['redis_port'] ||= uri.port
132+
Gitlab['gitlab_workhorse']['redis_password'] ||= uri.password
133+
Gitlab['gitlab_workhorse']['redis_database'] ||= uri.path.delete_prefix('/') if uri.path.start_with?('/')
134+
end
135+
end
136+
137+
%w[username password cluster_nodes sentinels sentinel_master sentinels_password].each do |setting|
138+
Gitlab['gitlab_workhorse']["redis_#{setting}"] ||= Gitlab['gitlab_rails']["redis_workhorse_#{setting}"]
139+
end
140+
end
141+
142+
def parse_redis_master_settings
143+
# TODO: When GitLab rails gets it's own set if `redis_sentinel_master_*`
144+
# settings, update the following to use them instead of
145+
# `Gitlab['redis'][*]` settings. It can be then merged with
146+
# `parse_rails_redis_settings` method
147+
Gitlab['gitlab_workhorse']['redis_sentinel_master'] ||= Gitlab['redis']['master_name'] || Gitlab[:node]['redis']['master_name']
148+
Gitlab['gitlab_workhorse']['redis_sentinel_master_ip'] ||= Gitlab['redis']['master_ip'] || Gitlab[:node]['redis']['master_ip']
149+
Gitlab['gitlab_workhorse']['redis_sentinel_master_port'] ||= Gitlab['redis']['master_port'] || Gitlab[:node]['redis']['master_port']
150+
Gitlab['gitlab_workhorse']['redis_password'] ||= Gitlab['redis']['master_password'] || Gitlab[:node]['redis']['master_password']
151+
end
152+
40153
private
41154

42155
def auth_socket_specified?

files/gitlab-cookbooks/gitlab/recipes/gitlab-workhorse.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# limitations under the License.
1616
#
1717
account_helper = AccountHelper.new(node)
18-
redis_helper = RedisHelper.new(node)
18+
redis_helper = NewRedisHelper::GitlabWorkhorse.new(node)
1919
workhorse_helper = GitlabWorkhorseHelper.new(node)
2020
logfiles_helper = LogfilesHelper.new(node)
2121
logging_settings = logfiles_helper.logging_settings('gitlab-workhorse')
@@ -89,7 +89,7 @@
8989
alt_document_root = node['gitlab']['gitlab_workhorse']['alt_document_root']
9090
shutdown_timeout = node['gitlab']['gitlab_workhorse']['shutdown_timeout']
9191
workhorse_keywatcher = node['gitlab']['gitlab_workhorse']['workhorse_keywatcher']
92-
redis_params = redis_helper.workhorse_params
92+
redis_params = redis_helper.redis_params
9393
config_file_path = File.join(working_dir, "config.toml")
9494
image_scaler_max_procs = node['gitlab']['gitlab_workhorse']['image_scaler_max_procs']
9595
image_scaler_max_filesize = node['gitlab']['gitlab_workhorse']['image_scaler_max_filesize']
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
require 'cgi'
2+
require_relative '../../../gitlab/libraries/redis_uri'
3+
4+
module NewRedisHelper
5+
class << self
6+
def build_redis_url(ssl:, host:, port:, path:, password:)
7+
scheme = ssl ? 'rediss:/' : 'redis:/'
8+
uri = URI(scheme)
9+
uri.host = host if host
10+
uri.port = port if port
11+
uri.path = path if path
12+
# In case the password has non-alphanumeric passwords, be sure to encode it
13+
uri.password = CGI.escape(password) if password
14+
15+
uri
16+
end
17+
18+
def build_sentinels_urls(sentinels:, password:)
19+
return [] if sentinels.nil? || sentinels.empty?
20+
21+
sentinels.map do |sentinel|
22+
build_redis_url(
23+
ssl: sentinel['ssl'],
24+
host: sentinel['host'],
25+
port: sentinel['port'],
26+
path: '',
27+
password: password
28+
)
29+
end
30+
end
31+
end
32+
end
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
require_relative '../../../../gitlab/libraries/redis_uri'
2+
3+
module NewRedisHelper
4+
class Base
5+
attr_reader :node
6+
7+
def initialize(node)
8+
@node = node
9+
end
10+
11+
def redis_params
12+
# Defined in each component that extends this class. Should return all
13+
# the Redis related information required by the component to populate
14+
# it's own config file
15+
raise NotImplementedError
16+
end
17+
18+
def redis_credentials
19+
params = if has_sentinels?
20+
master = support_sentinel_groupname? ? master_name : master_ip
21+
[master, master_port, redis_password]
22+
else
23+
[redis_host, redis_port, redis_password]
24+
end
25+
26+
{
27+
host: params[0],
28+
port: params[1],
29+
password: params[2]
30+
}
31+
end
32+
33+
def redis_url
34+
socket = if connect_to_redis_over_tcp?
35+
false
36+
else
37+
redis_socket
38+
end
39+
40+
if socket && !has_sentinels?
41+
uri = URI('unix:/')
42+
uri.path = socket
43+
else
44+
params = redis_credentials
45+
46+
uri = NewRedisHelper.build_redis_url(
47+
ssl: redis_ssl,
48+
host: params[:host],
49+
port: params[:port],
50+
password: params[:password],
51+
path: "/#{redis_database}"
52+
)
53+
end
54+
55+
uri.to_s
56+
end
57+
58+
private
59+
60+
def node_access_keys
61+
# List of keys to obtain the attributes of the service from node object.
62+
# For example ['gitlab', 'gitlab_workhorse'] or ['gitlab_kas']
63+
raise NotImplementedError
64+
end
65+
66+
def gitlab_rb_attr
67+
Gitlab[node_access_keys.last]
68+
end
69+
70+
def node_attr
71+
@node.dig(*node_access_keys)
72+
end
73+
74+
def redis
75+
@node['redis']
76+
end
77+
78+
def redis_server_over_tcp?
79+
redis['port']&.positive? || redis['tls_port']&.positive?
80+
end
81+
82+
def connect_to_redis_over_tcp?
83+
gitlab_rb_attr['redis_host']
84+
end
85+
86+
def redis_replica?
87+
redis['master'] == false
88+
end
89+
90+
def sentinel_daemon_enabled?
91+
Services.enabled?('sentinel')
92+
end
93+
94+
def master_name
95+
node_attr['redis_sentinel_master']
96+
end
97+
98+
def master_ip
99+
node_attr['redis_sentinel_master_ip']
100+
end
101+
102+
def master_port
103+
node_attr['redis_sentinel_master_port']
104+
end
105+
106+
def redis_sentinels_password
107+
node_attr['redis_sentinels_password']
108+
end
109+
110+
def redis_socket
111+
node_attr['redis_socket']
112+
end
113+
114+
def redis_host
115+
node_attr['redis_host']
116+
end
117+
118+
def redis_port
119+
node_attr['redis_port']
120+
end
121+
122+
def redis_password
123+
node_attr['redis_password']
124+
end
125+
126+
def redis_sentinels
127+
node_attr['redis_sentinels']
128+
end
129+
130+
def redis_ssl
131+
!!node_attr['redis_ssl']
132+
end
133+
134+
def redis_database
135+
node_attr['redis_database']
136+
end
137+
138+
def has_sentinels?
139+
node_attr['redis_sentinels'] && !node_attr['redis_sentinels'].empty?
140+
end
141+
142+
def sentinel_urls
143+
NewRedisHelper.build_sentinels_urls(sentinels: redis_sentinels, password: redis_sentinels_password)&.map(&:to_s)
144+
end
145+
146+
def support_sentinel_groupname?
147+
# Defined in each component that extends this class. Should denote
148+
# whether the component can connect to the primary Redis using a
149+
# groupname instead of an IP
150+
raise NotImplementedError
151+
end
152+
end
153+
end

0 commit comments

Comments
 (0)