diff --git a/files/gitlab-cookbooks/gitlab-kas/recipes/enable.rb b/files/gitlab-cookbooks/gitlab-kas/recipes/enable.rb index d771c852f690598b987cfbbd0a14e1dd9f955541_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIta2FzL3JlY2lwZXMvZW5hYmxlLnJi..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIta2FzL3JlY2lwZXMvZW5hYmxlLnJi 100644 --- a/files/gitlab-cookbooks/gitlab-kas/recipes/enable.rb +++ b/files/gitlab-cookbooks/gitlab-kas/recipes/enable.rb @@ -16,7 +16,7 @@ # account_helper = AccountHelper.new(node) omnibus_helper = OmnibusHelper.new(node) -redis_helper = NewRedisHelper::GitlabKAS.new(node) +redis_helper = RedisHelper::GitlabKAS.new(node) logfiles_helper = LogfilesHelper.new(node) logging_settings = logfiles_helper.logging_settings('gitlab-kas') diff --git a/files/gitlab-cookbooks/gitlab/libraries/gitlab_exporter.rb b/files/gitlab-cookbooks/gitlab/libraries/gitlab_exporter.rb index d771c852f690598b987cfbbd0a14e1dd9f955541_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL2dpdGxhYl9leHBvcnRlci5yYg==..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL2dpdGxhYl9leHBvcnRlci5yYg== 100644 --- a/files/gitlab-cookbooks/gitlab/libraries/gitlab_exporter.rb +++ b/files/gitlab-cookbooks/gitlab/libraries/gitlab_exporter.rb @@ -15,7 +15,7 @@ # limitations under the License. # require_relative 'redis_uri.rb' -require_relative '../../package/libraries/helpers/new_redis_helper/base' +require_relative '../../package/libraries/helpers/redis_helper/base' module GitlabExporter class << self @@ -30,7 +30,7 @@ # that. return if Gitlab['gitlab_exporter'].key?('probe_sidekiq') && !Gitlab['gitlab_exporter']['probe_sidekiq'].nil? - Gitlab['gitlab_exporter']['probe_sidekiq'] = !NewRedisHelper::Base.has_sentinels?(config: Gitlab['gitlab_rails']) + Gitlab['gitlab_exporter']['probe_sidekiq'] = !RedisHelper::Base.has_sentinels?(config: Gitlab['gitlab_rails']) end def validate_tls_config diff --git a/files/gitlab-cookbooks/gitlab/libraries/gitlab_rails.rb b/files/gitlab-cookbooks/gitlab/libraries/gitlab_rails.rb index d771c852f690598b987cfbbd0a14e1dd9f955541_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL2dpdGxhYl9yYWlscy5yYg==..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL2dpdGxhYl9yYWlscy5yYg== 100644 --- a/files/gitlab-cookbooks/gitlab/libraries/gitlab_rails.rb +++ b/files/gitlab-cookbooks/gitlab/libraries/gitlab_rails.rb @@ -17,7 +17,7 @@ require_relative 'nginx.rb' require_relative '../../gitaly/libraries/gitaly.rb' require_relative '../../package/libraries/settings_dsl.rb' -require_relative '../../package/libraries/helpers/new_redis_helper/gitlab_rails' +require_relative '../../package/libraries/helpers/redis_helper/gitlab_rails' module GitlabRails ALLOWED_DATABASES = %w[main ci geo embedding].freeze @@ -329,7 +329,7 @@ # This requires the parse_shared_dir to be executed before encrypted_settings_path = Gitlab['gitlab_rails']['encrypted_settings_path'] ||= File.join(Gitlab['gitlab_rails']['shared_path'], 'encrypted_settings') - NewRedisHelper::GitlabRails::REDIS_INSTANCES.each do |instance| + RedisHelper::GitlabRails::REDIS_INSTANCES.each do |instance| Gitlab['gitlab_rails']["redis_#{instance}_encrypted_settings_file"] ||= Gitlab['gitlab_rails']['redis_encrypted_settings_file'] || File.join(encrypted_settings_path, "redis.#{instance}.yml.enc") end @@ -347,7 +347,7 @@ end def parse_redis_extra_config_command - NewRedisHelper::GitlabRails::REDIS_INSTANCES.each do |instance| + RedisHelper::GitlabRails::REDIS_INSTANCES.each do |instance| Gitlab['gitlab_rails']["redis_#{instance}_extra_config_command"] ||= Gitlab['gitlab_rails']['redis_extra_config_command'] end end diff --git a/files/gitlab-cookbooks/gitlab/libraries/gitlab_workhorse.rb b/files/gitlab-cookbooks/gitlab/libraries/gitlab_workhorse.rb index d771c852f690598b987cfbbd0a14e1dd9f955541_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL2dpdGxhYl93b3JraG9yc2UucmI=..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL2dpdGxhYl93b3JraG9yc2UucmI= 100644 --- a/files/gitlab-cookbooks/gitlab/libraries/gitlab_workhorse.rb +++ b/files/gitlab-cookbooks/gitlab/libraries/gitlab_workhorse.rb @@ -16,7 +16,7 @@ # require_relative './redis_uri' -require_relative '../../package/libraries/helpers/new_redis_helper' +require_relative '../../package/libraries/helpers/redis_helper' module GitlabWorkhorse class << self @@ -76,7 +76,7 @@ # rubocop:disable Metrics/PerceivedComplexity def update_separate_redis_instance_settings if Gitlab['gitlab_workhorse']['redis_host'] - uri_from_workhorse = NewRedisHelper.build_redis_url( + uri_from_workhorse = RedisHelper.build_redis_url( ssl: Gitlab['gitlab_workhorse']['redis_ssl'] || Gitlab['node']['gitlab']['gitlab_workhorse']['redis_ssl'], host: Gitlab['gitlab_workhorse']['redis_host'] || Gitlab['node']['gitlab']['gitlab_workhorse']['redis_host'], port: Gitlab['gitlab_workhorse']['redis_port'] || Gitlab['node']['gitlab']['gitlab_workhorse']['redis_port'], @@ -84,7 +84,7 @@ path: Gitlab['gitlab_workhorse']['redis_database'] || Gitlab['node']['gitlab']['gitlab_workhorse']['redis_database'] ).to_s - uri_from_rails = NewRedisHelper.build_redis_url( + uri_from_rails = RedisHelper.build_redis_url( ssl: Gitlab['gitlab_rails']['redis_ssl'] || Gitlab['node']['gitlab']['gitlab_rails']['redis_ssl'], host: Gitlab['gitlab_rails']['redis_host'] || Gitlab['node']['gitlab']['gitlab_rails']['redis_host'], port: Gitlab['gitlab_rails']['redis_port'] || Gitlab['node']['gitlab']['gitlab_rails']['redis_port'], diff --git a/files/gitlab-cookbooks/gitlab/libraries/redis.rb b/files/gitlab-cookbooks/gitlab/libraries/redis.rb index d771c852f690598b987cfbbd0a14e1dd9f955541_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JlZGlzLnJi..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JlZGlzLnJi 100644 --- a/files/gitlab-cookbooks/gitlab/libraries/redis.rb +++ b/files/gitlab-cookbooks/gitlab/libraries/redis.rb @@ -18,7 +18,7 @@ require 'open3' require_relative 'redis_uri.rb' -require_relative '../../package/libraries/helpers/new_redis_helper/gitlab_rails' +require_relative '../../package/libraries/helpers/redis_helper/gitlab_rails' module Redis CommandExecutionError = Class.new(StandardError) @@ -32,7 +32,7 @@ end def parse_redis_settings - redis_helper = NewRedisHelper::Base + redis_helper = RedisHelper::Base # node['redis'] need not reflect user's choice accurately here, since we # also set redis['port'] programmatically, and hence can't depend on it. # So we specify Gitlab['redis'] as the config to use. @@ -62,7 +62,7 @@ end def parse_redis_sentinel_settings - redis_helper = NewRedisHelper::GitlabRails + redis_helper = RedisHelper::GitlabRails return unless redis_helper.sentinel_daemon_enabled? Gitlab['gitlab_rails']['redis_sentinels_password'] ||= Gitlab['sentinel']['password'] diff --git a/files/gitlab-cookbooks/gitlab/recipes/gitlab-rails.rb b/files/gitlab-cookbooks/gitlab/recipes/gitlab-rails.rb index d771c852f690598b987cfbbd0a14e1dd9f955541_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMucmI=..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMucmI= 100644 --- a/files/gitlab-cookbooks/gitlab/recipes/gitlab-rails.rb +++ b/files/gitlab-cookbooks/gitlab/recipes/gitlab-rails.rb @@ -19,7 +19,7 @@ omnibus_helper = OmnibusHelper.new(node) consul_helper = ConsulHelper.new(node) mailroom_helper = MailroomHelper.new(node) -redis_helper = NewRedisHelper::GitlabRails.new(node) +redis_helper = RedisHelper::GitlabRails.new(node) logfiles_helper = LogfilesHelper.new(node) logging_settings = logfiles_helper.logging_settings('gitlab-rails') @@ -283,7 +283,7 @@ sensitive true end -NewRedisHelper::GitlabRails::REDIS_INSTANCES.each do |instance| +RedisHelper::GitlabRails::REDIS_INSTANCES.each do |instance| filename = "redis.#{instance}.yml" url = node['gitlab']['gitlab_rails']["redis_#{instance}_instance"] sentinels = node['gitlab']['gitlab_rails']["redis_#{instance}_sentinels"] diff --git a/files/gitlab-cookbooks/gitlab/recipes/gitlab-workhorse.rb b/files/gitlab-cookbooks/gitlab/recipes/gitlab-workhorse.rb index d771c852f690598b987cfbbd0a14e1dd9f955541_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItd29ya2hvcnNlLnJi..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItd29ya2hvcnNlLnJi 100644 --- a/files/gitlab-cookbooks/gitlab/recipes/gitlab-workhorse.rb +++ b/files/gitlab-cookbooks/gitlab/recipes/gitlab-workhorse.rb @@ -15,7 +15,7 @@ # limitations under the License. # account_helper = AccountHelper.new(node) -redis_helper = NewRedisHelper::GitlabWorkhorse.new(node) +redis_helper = RedisHelper::GitlabWorkhorse.new(node) workhorse_helper = GitlabWorkhorseHelper.new(node) logfiles_helper = LogfilesHelper.new(node) logging_settings = logfiles_helper.logging_settings('gitlab-workhorse') diff --git a/files/gitlab-cookbooks/monitoring/recipes/gitlab-exporter.rb b/files/gitlab-cookbooks/monitoring/recipes/gitlab-exporter.rb index d771c852f690598b987cfbbd0a14e1dd9f955541_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9tb25pdG9yaW5nL3JlY2lwZXMvZ2l0bGFiLWV4cG9ydGVyLnJi..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9tb25pdG9yaW5nL3JlY2lwZXMvZ2l0bGFiLWV4cG9ydGVyLnJi 100644 --- a/files/gitlab-cookbooks/monitoring/recipes/gitlab-exporter.rb +++ b/files/gitlab-cookbooks/monitoring/recipes/gitlab-exporter.rb @@ -16,7 +16,7 @@ # limitations under the License. # account_helper = AccountHelper.new(node) -redis_helper = NewRedisHelper::GitlabExporter.new(node) +redis_helper = RedisHelper::GitlabExporter.new(node) logfiles_helper = LogfilesHelper.new(node) logging_settings = logfiles_helper.logging_settings('gitlab-exporter') gitlab_user = account_helper.gitlab_user diff --git a/files/gitlab-cookbooks/monitoring/recipes/redis-exporter.rb b/files/gitlab-cookbooks/monitoring/recipes/redis-exporter.rb index d771c852f690598b987cfbbd0a14e1dd9f955541_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9tb25pdG9yaW5nL3JlY2lwZXMvcmVkaXMtZXhwb3J0ZXIucmI=..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9tb25pdG9yaW5nL3JlY2lwZXMvcmVkaXMtZXhwb3J0ZXIucmI= 100644 --- a/files/gitlab-cookbooks/monitoring/recipes/redis-exporter.rb +++ b/files/gitlab-cookbooks/monitoring/recipes/redis-exporter.rb @@ -43,7 +43,7 @@ end runtime_flags = PrometheusHelper.new(node).flags('redis_exporter') -redis_helper = NewRedisHelper::RedisExporter.new(node) +redis_helper = RedisHelper::RedisExporter.new(node) redis_url = redis_helper.formatted_redis_url runit_service 'redis-exporter' do diff --git a/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper.rb b/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper.rb deleted file mode 100644 index d771c852f690598b987cfbbd0a14e1dd9f955541_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9oZWxwZXJzL25ld19yZWRpc19oZWxwZXIucmI=..0000000000000000000000000000000000000000 --- a/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper.rb +++ /dev/null @@ -1,41 +0,0 @@ -require 'erb' -require_relative '../../../gitlab/libraries/redis_uri' - -module NewRedisHelper - class << self - def build_redis_url(ssl:, host:, port:, path:, password:) - scheme = ssl ? 'rediss:/' : 'redis:/' - uri = URI(scheme) - uri.host = host if host - uri.port = port if port - uri.path = path if path - # In case the password has non-alphanumeric passwords, be sure to encode it - uri.password = encode_redis_password(password) if password - - uri - end - - def build_sentinels_urls(sentinels:, password:) - return [] if sentinels.nil? || sentinels.empty? - - sentinels.map do |sentinel| - build_redis_url( - ssl: sentinel['ssl'], - host: sentinel['host'], - port: sentinel['port'], - path: '', - password: password - ) - end - end - - # RFC 3986 says that the userinfo value should be percent-encoded: - # https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1. - # Note that CGI.escape and URI.encode_www_form_component encodes - # a space as "+" instead of "%20". While this appears to be handled with - # the Ruby client, the Go client doesn't work with "+". - def encode_redis_password(password) - ERB::Util.url_encode(password) - end - end -end diff --git a/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/base.rb b/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/base.rb deleted file mode 100644 index d771c852f690598b987cfbbd0a14e1dd9f955541_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9oZWxwZXJzL25ld19yZWRpc19oZWxwZXIvYmFzZS5yYg==..0000000000000000000000000000000000000000 --- a/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/base.rb +++ /dev/null @@ -1,193 +0,0 @@ -require_relative '../../../../gitlab/libraries/redis_uri' - -module NewRedisHelper - class Base - attr_reader :node - - def initialize(node) - @node = node - end - - def redis_params - # Defined in each component that extends this class. Should return all - # the Redis related information required by the component to populate - # it's own config file - raise NotImplementedError - end - - def redis_credentials - params = if has_sentinels? - master = support_sentinel_groupname? ? master_name : master_ip - [master, master_port, redis_password] - else - [redis_host, redis_port, redis_password] - end - - { - host: params[0], - port: params[1], - password: params[2] - } - end - - def redis_url - socket = if connect_to_redis_over_tcp? - false - else - redis_socket - end - - params = redis_credentials - - if socket && !has_sentinels? - uri = URI("unix://") - uri.path = socket - - if params[:password] - password = NewRedisHelper.encode_redis_password(params[:password]) - uri.userinfo = ":#{password}" - end - else - uri = NewRedisHelper.build_redis_url( - ssl: redis_ssl, - host: params[:host], - port: params[:port], - password: params[:password], - path: "/#{redis_database}" - ) - end - - uri - end - - def connect_to_redis_over_tcp? - gitlab_rb_attr['redis_host'] - end - - # Few methods are used in both libraries and recipes. While used in - # recipes, they work on the `node` object, and can follow the regular - # pattern (by initializing an object of the relevant - # `NewRedisHelper::<Service>` class and calling the method). However, when - # used in libraries, they have to work on the `Gitlab` object, and should - # be accessible as class methods (without having to create an object). - # Hence, we make these methods class methods, with the config block they - # work on being passed as an argument. Then the instance methods just call - # these class methods with the correct arguments. The libraries can also - # directly call these class methods. - def has_sentinels? - self.class.has_sentinels?(config: node_attr) - end - - def redis_server_over_tcp? - self.class.redis_server_over_tcp?(config: redis) - end - - def redis_replica? - self.class.redis_replica?(config: redis) - end - - class << self - def has_sentinels?(config: nil) - config['redis_sentinels'] && !config['redis_sentinels'].empty? - end - - def redis_server_over_tcp?(config: nil) - config['port']&.positive? || config['tls_port']&.positive? - end - - def redis_replica?(config: nil) - config['master'] == false - end - - # The following methods doesn't work on node object at all and are - # independent of the Redis client service using them, and hence makes - # sense to be just class methods - def redis_master_role? - Gitlab['redis_master_role']['enable'] - end - - def redis_replica_role? - Gitlab['redis_replica_role']['enable'] - end - - def sentinel_daemon_enabled? - Services.enabled?('sentinel') - end - end - - private - - def node_access_keys - # List of keys to obtain the attributes of the service from node object. - # For example ['gitlab', 'gitlab_workhorse'] or ['gitlab_kas'] - raise NotImplementedError - end - - def gitlab_rb_attr - Gitlab[node_access_keys.last] - end - - def node_attr - @node.dig(*node_access_keys) - end - - def redis - @node['redis'] - end - - def master_name - node_attr['redis_sentinel_master'] - end - - def master_ip - node_attr['redis_sentinel_master_ip'] - end - - def master_port - node_attr['redis_sentinel_master_port'] - end - - def redis_sentinels_password - node_attr['redis_sentinels_password'] - end - - def redis_socket - node_attr['redis_socket'] - end - - def redis_host - node_attr['redis_host'] - end - - def redis_port - node_attr['redis_port'] - end - - def redis_password - node_attr['redis_password'] - end - - def redis_sentinels - node_attr['redis_sentinels'] - end - - def redis_ssl - !!node_attr['redis_ssl'] - end - - def redis_database - node_attr['redis_database'] - end - - def sentinel_urls - NewRedisHelper.build_sentinels_urls(sentinels: redis_sentinels, password: redis_sentinels_password)&.map(&:to_s) - end - - def support_sentinel_groupname? - # Defined in each component that extends this class. Should denote - # whether the component can connect to the primary Redis using a - # groupname instead of an IP - raise NotImplementedError - end - end -end diff --git a/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/gitlab_exporter.rb b/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/gitlab_exporter.rb deleted file mode 100644 index d771c852f690598b987cfbbd0a14e1dd9f955541_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9oZWxwZXJzL25ld19yZWRpc19oZWxwZXIvZ2l0bGFiX2V4cG9ydGVyLnJi..0000000000000000000000000000000000000000 --- a/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/gitlab_exporter.rb +++ /dev/null @@ -1,25 +0,0 @@ -module NewRedisHelper - class GitlabExporter < NewRedisHelper::Base - def redis_params - { - url: redis_url, - enable_client: enable_client - } - end - - private - - def enable_client - node_attr['redis_enable_client'] - end - - # GitLab Exporter uses the same Redis information as GitLab Rails - def node_access_keys - %w[gitlab gitlab_rails] - end - - def support_sentinel_groupname? - false - end - end -end diff --git a/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/gitlab_kas.rb b/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/gitlab_kas.rb deleted file mode 100644 index d771c852f690598b987cfbbd0a14e1dd9f955541_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9oZWxwZXJzL25ld19yZWRpc19oZWxwZXIvZ2l0bGFiX2thcy5yYg==..0000000000000000000000000000000000000000 --- a/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/gitlab_kas.rb +++ /dev/null @@ -1,37 +0,0 @@ -module NewRedisHelper - class GitlabKAS < NewRedisHelper::Base - def redis_params - { - network: redis_network, - address: redis_address, - password: redis_credentials[:password], - sentinels: redis_sentinels, - sentinelMaster: master_name, - sentinelPassword: redis_sentinels_password, - ssl: redis_ssl - } - end - - private - - def redis_network - redis_url.scheme == 'unix' ? 'unix' : 'tcp' - end - - def redis_address - redis_network == 'tcp' ? "#{redis_host}:#{redis_port || URI::Redis::DEFAULT_PORT}" : redis_socket - end - - def master_name - node_attr['redis_sentinels_master_name'] - end - - def node_access_keys - %w[gitlab_kas] - end - - def support_sentinel_groupname? - true - end - end -end diff --git a/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/gitlab_rails.rb b/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/gitlab_rails.rb deleted file mode 100644 index d771c852f690598b987cfbbd0a14e1dd9f955541_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9oZWxwZXJzL25ld19yZWRpc19oZWxwZXIvZ2l0bGFiX3JhaWxzLnJi..0000000000000000000000000000000000000000 --- a/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/gitlab_rails.rb +++ /dev/null @@ -1,32 +0,0 @@ -module NewRedisHelper - class GitlabRails < NewRedisHelper::Base - 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 redis_params - { - url: redis_url - } - end - - def validate_instance_shard_config(instance) - sentinels = node_attr["redis_#{instance}_sentinels"] - clusters = node_attr["redis_#{instance}_cluster_nodes"] - - return if clusters.empty? - - raise "Both sentinel and cluster configurations are defined for #{instance}" unless sentinels.empty? - raise "Cluster mode is not allowed for #{instance}" unless ALLOWED_REDIS_CLUSTER_INSTANCE.include?(instance) - end - - private - - def node_access_keys - %w[gitlab gitlab_rails] - end - - def support_sentinel_groupname? - true - end - end -end diff --git a/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/gitlab_workhorse.rb b/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/gitlab_workhorse.rb deleted file mode 100644 index d771c852f690598b987cfbbd0a14e1dd9f955541_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9oZWxwZXJzL25ld19yZWRpc19oZWxwZXIvZ2l0bGFiX3dvcmtob3JzZS5yYg==..0000000000000000000000000000000000000000 --- a/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/gitlab_workhorse.rb +++ /dev/null @@ -1,23 +0,0 @@ -module NewRedisHelper - class GitlabWorkhorse < NewRedisHelper::Base - def redis_params - { - url: redis_url.to_s, - password: redis_credentials[:password], - sentinels: sentinel_urls, - sentinelMaster: master_name, - sentinelPassword: redis_sentinels_password - } - end - - private - - def node_access_keys - %w[gitlab gitlab_workhorse] - end - - def support_sentinel_groupname? - true - end - end -end diff --git a/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/redis_exporter.rb b/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/redis_exporter.rb deleted file mode 100644 index d771c852f690598b987cfbbd0a14e1dd9f955541_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9oZWxwZXJzL25ld19yZWRpc19oZWxwZXIvcmVkaXNfZXhwb3J0ZXIucmI=..0000000000000000000000000000000000000000 --- a/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/redis_exporter.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -module NewRedisHelper - class RedisExporter < NewRedisHelper::Base - def redis_params - { - url: redis_url - } - end - - def formatted_redis_url - url = redis_url - - url.scheme == 'unix' ? "unix://#{url.path}" : url.to_s - end - - private - - def node_access_keys - %w[gitlab gitlab_rails] - end - - def support_sentinel_groupname? - false - end - end -end diff --git a/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/server.rb b/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/server.rb deleted file mode 100644 index d771c852f690598b987cfbbd0a14e1dd9f955541_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9oZWxwZXJzL25ld19yZWRpc19oZWxwZXIvc2VydmVyLnJi..0000000000000000000000000000000000000000 --- a/files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper/server.rb +++ /dev/null @@ -1,97 +0,0 @@ -# frozen_string_literal: true - -require_relative '../version_helper' - -module NewRedisHelper - class Server < NewRedisHelper::Base - def installed_version - return unless OmnibusHelper.new(@node).service_up?('redis') - - command = '/opt/gitlab/embedded/bin/redis-server --version' - - command_output = VersionHelper.version(command) - raise "Execution of the command `#{command}` failed" unless command_output - - version_match = command_output.match(/Redis server v=(?<redis_version>\d*\.\d*\.\d*)/) - raise "Execution of the command `#{command}` generated unexpected output `#{command_output.strip}`" unless version_match - - version_match['redis_version'] - end - - def running_version - return unless OmnibusHelper.new(@node).service_up?('redis') - - commands = ['/opt/gitlab/embedded/bin/redis-cli'] - commands << redis_cli_connect_options - commands << "INFO" - command = commands.join(" ") - - command_output = VersionHelper.version(command) - raise "Execution of the command `#{command}` failed" unless command_output - - version_match = command_output.match(/redis_version:(?<redis_version>\d*\.\d*\.\d*)/) - raise "Execution of the command `#{command}` generated unexpected output `#{command_output.strip}`" unless version_match - - version_match['redis_version'] - end - - private - - def redis_password - redis['extracted_password'] || redis['password'] - end - - def redis_cli_connect_options - args = [] - if redis_server_over_tcp? - args = redis_cli_tcp_connect_options(args) - else - args << "-s #{redis['unixsocket']}" - end - - args << "-a '#{redis_password}'" if redis_password - - args - end - - def redis_cli_tcp_connect_options(args) - args << ["-h #{redis['default_host']}"] - port = redis['port'].to_i - - if port.zero? - args = redis_cli_tls_options(args) - else - args << "-p #{port}" - end - - args - end - - def redis_cli_tls_options(args) - tls_port = redis['tls_port'].to_i - - args << "--tls" - args << "-p #{tls_port}" - args << "--cacert '#{redis['tls_ca_cert_file']}'" if redis['tls_ca_cert_file'] - args << "--cacertdir '#{redis['tls_ca_cert_dir']}'" if redis['tls_ca_cert_dir'] - - return args unless client_certs_required? - - raise "Redis TLS client authentication requires redis['tls_cert_file'] and redis['tls_key_file'] options" unless client_cert_and_key_available? - - args << "--cert '#{redis['tls_cert_file']}'" - args << "--key '#{redis['tls_key_file']}'" - - args - end - - def client_certs_required? - redis['tls_auth_clients'] == 'yes' - end - - def client_cert_and_key_available? - redis['tls_cert_file'] && !redis['tls_cert_file'].empty? && - redis['tls_key_file'] && !redis['tls_key_file'].empty? - end - end -end diff --git a/files/gitlab-cookbooks/package/libraries/helpers/redis_helper.rb b/files/gitlab-cookbooks/package/libraries/helpers/redis_helper.rb new file mode 100644 index 0000000000000000000000000000000000000000..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9oZWxwZXJzL3JlZGlzX2hlbHBlci5yYg== --- /dev/null +++ b/files/gitlab-cookbooks/package/libraries/helpers/redis_helper.rb @@ -0,0 +1,41 @@ +require 'erb' +require_relative '../../../gitlab/libraries/redis_uri' + +module RedisHelper + class << self + def build_redis_url(ssl:, host:, port:, path:, password:) + scheme = ssl ? 'rediss:/' : 'redis:/' + uri = URI(scheme) + uri.host = host if host + uri.port = port if port + uri.path = path if path + # In case the password has non-alphanumeric passwords, be sure to encode it + uri.password = encode_redis_password(password) if password + + uri + end + + def build_sentinels_urls(sentinels:, password:) + return [] if sentinels.nil? || sentinels.empty? + + sentinels.map do |sentinel| + build_redis_url( + ssl: sentinel['ssl'], + host: sentinel['host'], + port: sentinel['port'], + path: '', + password: password + ) + end + end + + # RFC 3986 says that the userinfo value should be percent-encoded: + # https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1. + # Note that CGI.escape and URI.encode_www_form_component encodes + # a space as "+" instead of "%20". While this appears to be handled with + # the Ruby client, the Go client doesn't work with "+". + def encode_redis_password(password) + ERB::Util.url_encode(password) + end + end +end diff --git a/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/base.rb b/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/base.rb new file mode 100644 index 0000000000000000000000000000000000000000..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9oZWxwZXJzL3JlZGlzX2hlbHBlci9iYXNlLnJi --- /dev/null +++ b/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/base.rb @@ -0,0 +1,193 @@ +require_relative '../../../../gitlab/libraries/redis_uri' + +module RedisHelper + class Base + attr_reader :node + + def initialize(node) + @node = node + end + + def redis_params + # Defined in each component that extends this class. Should return all + # the Redis related information required by the component to populate + # it's own config file + raise NotImplementedError + end + + def redis_credentials + params = if has_sentinels? + master = support_sentinel_groupname? ? master_name : master_ip + [master, master_port, redis_password] + else + [redis_host, redis_port, redis_password] + end + + { + host: params[0], + port: params[1], + password: params[2] + } + end + + def redis_url + socket = if connect_to_redis_over_tcp? + false + else + redis_socket + end + + params = redis_credentials + + if socket && !has_sentinels? + uri = URI("unix://") + uri.path = socket + + if params[:password] + password = RedisHelper.encode_redis_password(params[:password]) + uri.userinfo = ":#{password}" + end + else + uri = RedisHelper.build_redis_url( + ssl: redis_ssl, + host: params[:host], + port: params[:port], + password: params[:password], + path: "/#{redis_database}" + ) + end + + uri + end + + def connect_to_redis_over_tcp? + gitlab_rb_attr['redis_host'] + end + + # Few methods are used in both libraries and recipes. While used in + # recipes, they work on the `node` object, and can follow the regular + # pattern (by initializing an object of the relevant + # `RedisHelper::<Service>` class and calling the method). However, when + # used in libraries, they have to work on the `Gitlab` object, and should + # be accessible as class methods (without having to create an object). + # Hence, we make these methods class methods, with the config block they + # work on being passed as an argument. Then the instance methods just call + # these class methods with the correct arguments. The libraries can also + # directly call these class methods. + def has_sentinels? + self.class.has_sentinels?(config: node_attr) + end + + def redis_server_over_tcp? + self.class.redis_server_over_tcp?(config: redis) + end + + def redis_replica? + self.class.redis_replica?(config: redis) + end + + class << self + def has_sentinels?(config: nil) + config['redis_sentinels'] && !config['redis_sentinels'].empty? + end + + def redis_server_over_tcp?(config: nil) + config['port']&.positive? || config['tls_port']&.positive? + end + + def redis_replica?(config: nil) + config['master'] == false + end + + # The following methods doesn't work on node object at all and are + # independent of the Redis client service using them, and hence makes + # sense to be just class methods + def redis_master_role? + Gitlab['redis_master_role']['enable'] + end + + def redis_replica_role? + Gitlab['redis_replica_role']['enable'] + end + + def sentinel_daemon_enabled? + Services.enabled?('sentinel') + end + end + + private + + def node_access_keys + # List of keys to obtain the attributes of the service from node object. + # For example ['gitlab', 'gitlab_workhorse'] or ['gitlab_kas'] + raise NotImplementedError + end + + def gitlab_rb_attr + Gitlab[node_access_keys.last] + end + + def node_attr + @node.dig(*node_access_keys) + end + + def redis + @node['redis'] + end + + def master_name + node_attr['redis_sentinel_master'] + end + + def master_ip + node_attr['redis_sentinel_master_ip'] + end + + def master_port + node_attr['redis_sentinel_master_port'] + end + + def redis_sentinels_password + node_attr['redis_sentinels_password'] + end + + def redis_socket + node_attr['redis_socket'] + end + + def redis_host + node_attr['redis_host'] + end + + def redis_port + node_attr['redis_port'] + end + + def redis_password + node_attr['redis_password'] + end + + def redis_sentinels + node_attr['redis_sentinels'] + end + + def redis_ssl + !!node_attr['redis_ssl'] + end + + def redis_database + node_attr['redis_database'] + end + + def sentinel_urls + RedisHelper.build_sentinels_urls(sentinels: redis_sentinels, password: redis_sentinels_password)&.map(&:to_s) + end + + def support_sentinel_groupname? + # Defined in each component that extends this class. Should denote + # whether the component can connect to the primary Redis using a + # groupname instead of an IP + raise NotImplementedError + end + end +end diff --git a/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/gitlab_exporter.rb b/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/gitlab_exporter.rb new file mode 100644 index 0000000000000000000000000000000000000000..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9oZWxwZXJzL3JlZGlzX2hlbHBlci9naXRsYWJfZXhwb3J0ZXIucmI= --- /dev/null +++ b/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/gitlab_exporter.rb @@ -0,0 +1,25 @@ +module RedisHelper + class GitlabExporter < RedisHelper::Base + def redis_params + { + url: redis_url, + enable_client: enable_client + } + end + + private + + def enable_client + node_attr['redis_enable_client'] + end + + # GitLab Exporter uses the same Redis information as GitLab Rails + def node_access_keys + %w[gitlab gitlab_rails] + end + + def support_sentinel_groupname? + false + end + end +end diff --git a/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/gitlab_kas.rb b/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/gitlab_kas.rb new file mode 100644 index 0000000000000000000000000000000000000000..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9oZWxwZXJzL3JlZGlzX2hlbHBlci9naXRsYWJfa2FzLnJi --- /dev/null +++ b/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/gitlab_kas.rb @@ -0,0 +1,37 @@ +module RedisHelper + class GitlabKAS < RedisHelper::Base + def redis_params + { + network: redis_network, + address: redis_address, + password: redis_credentials[:password], + sentinels: redis_sentinels, + sentinelMaster: master_name, + sentinelPassword: redis_sentinels_password, + ssl: redis_ssl + } + end + + private + + def redis_network + redis_url.scheme == 'unix' ? 'unix' : 'tcp' + end + + def redis_address + redis_network == 'tcp' ? "#{redis_host}:#{redis_port || URI::Redis::DEFAULT_PORT}" : redis_socket + end + + def master_name + node_attr['redis_sentinels_master_name'] + end + + def node_access_keys + %w[gitlab_kas] + end + + def support_sentinel_groupname? + true + end + end +end diff --git a/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/gitlab_rails.rb b/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/gitlab_rails.rb new file mode 100644 index 0000000000000000000000000000000000000000..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9oZWxwZXJzL3JlZGlzX2hlbHBlci9naXRsYWJfcmFpbHMucmI= --- /dev/null +++ b/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/gitlab_rails.rb @@ -0,0 +1,32 @@ +module RedisHelper + class GitlabRails < RedisHelper::Base + 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 redis_params + { + url: redis_url + } + end + + def validate_instance_shard_config(instance) + sentinels = node_attr["redis_#{instance}_sentinels"] + clusters = node_attr["redis_#{instance}_cluster_nodes"] + + return if clusters.empty? + + raise "Both sentinel and cluster configurations are defined for #{instance}" unless sentinels.empty? + raise "Cluster mode is not allowed for #{instance}" unless ALLOWED_REDIS_CLUSTER_INSTANCE.include?(instance) + end + + private + + def node_access_keys + %w[gitlab gitlab_rails] + end + + def support_sentinel_groupname? + true + end + end +end diff --git a/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/gitlab_workhorse.rb b/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/gitlab_workhorse.rb new file mode 100644 index 0000000000000000000000000000000000000000..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9oZWxwZXJzL3JlZGlzX2hlbHBlci9naXRsYWJfd29ya2hvcnNlLnJi --- /dev/null +++ b/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/gitlab_workhorse.rb @@ -0,0 +1,23 @@ +module RedisHelper + class GitlabWorkhorse < RedisHelper::Base + def redis_params + { + url: redis_url.to_s, + password: redis_credentials[:password], + sentinels: sentinel_urls, + sentinelMaster: master_name, + sentinelPassword: redis_sentinels_password + } + end + + private + + def node_access_keys + %w[gitlab gitlab_workhorse] + end + + def support_sentinel_groupname? + true + end + end +end diff --git a/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/redis_exporter.rb b/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/redis_exporter.rb new file mode 100644 index 0000000000000000000000000000000000000000..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9oZWxwZXJzL3JlZGlzX2hlbHBlci9yZWRpc19leHBvcnRlci5yYg== --- /dev/null +++ b/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/redis_exporter.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module RedisHelper + class RedisExporter < RedisHelper::Base + def redis_params + { + url: redis_url + } + end + + def formatted_redis_url + url = redis_url + + url.scheme == 'unix' ? "unix://#{url.path}" : url.to_s + end + + private + + def node_access_keys + %w[gitlab gitlab_rails] + end + + def support_sentinel_groupname? + false + end + end +end diff --git a/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/server.rb b/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/server.rb new file mode 100644 index 0000000000000000000000000000000000000000..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9oZWxwZXJzL3JlZGlzX2hlbHBlci9zZXJ2ZXIucmI= --- /dev/null +++ b/files/gitlab-cookbooks/package/libraries/helpers/redis_helper/server.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true + +require_relative '../version_helper' + +module RedisHelper + class Server < RedisHelper::Base + def installed_version + return unless OmnibusHelper.new(@node).service_up?('redis') + + command = '/opt/gitlab/embedded/bin/redis-server --version' + + command_output = VersionHelper.version(command) + raise "Execution of the command `#{command}` failed" unless command_output + + version_match = command_output.match(/Redis server v=(?<redis_version>\d*\.\d*\.\d*)/) + raise "Execution of the command `#{command}` generated unexpected output `#{command_output.strip}`" unless version_match + + version_match['redis_version'] + end + + def running_version + return unless OmnibusHelper.new(@node).service_up?('redis') + + commands = ['/opt/gitlab/embedded/bin/redis-cli'] + commands << redis_cli_connect_options + commands << "INFO" + command = commands.join(" ") + + command_output = VersionHelper.version(command) + raise "Execution of the command `#{command}` failed" unless command_output + + version_match = command_output.match(/redis_version:(?<redis_version>\d*\.\d*\.\d*)/) + raise "Execution of the command `#{command}` generated unexpected output `#{command_output.strip}`" unless version_match + + version_match['redis_version'] + end + + private + + def redis_password + redis['extracted_password'] || redis['password'] + end + + def redis_cli_connect_options + args = [] + if redis_server_over_tcp? + args = redis_cli_tcp_connect_options(args) + else + args << "-s #{redis['unixsocket']}" + end + + args << "-a '#{redis_password}'" if redis_password + + args + end + + def redis_cli_tcp_connect_options(args) + args << ["-h #{redis['default_host']}"] + port = redis['port'].to_i + + if port.zero? + args = redis_cli_tls_options(args) + else + args << "-p #{port}" + end + + args + end + + def redis_cli_tls_options(args) + tls_port = redis['tls_port'].to_i + + args << "--tls" + args << "-p #{tls_port}" + args << "--cacert '#{redis['tls_ca_cert_file']}'" if redis['tls_ca_cert_file'] + args << "--cacertdir '#{redis['tls_ca_cert_dir']}'" if redis['tls_ca_cert_dir'] + + return args unless client_certs_required? + + raise "Redis TLS client authentication requires redis['tls_cert_file'] and redis['tls_key_file'] options" unless client_cert_and_key_available? + + args << "--cert '#{redis['tls_cert_file']}'" + args << "--key '#{redis['tls_key_file']}'" + + args + end + + def client_certs_required? + redis['tls_auth_clients'] == 'yes' + end + + def client_cert_and_key_available? + redis['tls_cert_file'] && !redis['tls_cert_file'].empty? && + redis['tls_key_file'] && !redis['tls_key_file'].empty? + end + end +end diff --git a/files/gitlab-cookbooks/redis/resources/service.rb b/files/gitlab-cookbooks/redis/resources/service.rb index d771c852f690598b987cfbbd0a14e1dd9f955541_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9yZWRpcy9yZXNvdXJjZXMvc2VydmljZS5yYg==..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9yZWRpcy9yZXNvdXJjZXMvc2VydmljZS5yYg== 100644 --- a/files/gitlab-cookbooks/redis/resources/service.rb +++ b/files/gitlab-cookbooks/redis/resources/service.rb @@ -4,7 +4,7 @@ property :dir, String, default: lazy { node['redis']['dir'] } property :account_helper, default: lazy { AccountHelper.new(node) }, sensitive: true property :omnibus_helper, default: lazy { OmnibusHelper.new(node) }, sensitive: true -property :redis_helper, default: lazy { NewRedisHelper::Server.new(node) }, sensitive: true +property :redis_helper, default: lazy { RedisHelper::Server.new(node) }, sensitive: true property :runit_sv_timeout, [Integer, nil], default: lazy { node['redis']['runit_sv_timeout'] } property :logfiles_helper, default: lazy { LogfilesHelper.new(node) }, sensitive: true diff --git a/spec/chef/cookbooks/gitlab/libraries/redis_spec.rb b/spec/chef/cookbooks/gitlab/libraries/redis_spec.rb index d771c852f690598b987cfbbd0a14e1dd9f955541_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JlZGlzX3NwZWMucmI=..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JlZGlzX3NwZWMucmI= 100644 --- a/spec/chef/cookbooks/gitlab/libraries/redis_spec.rb +++ b/spec/chef/cookbooks/gitlab/libraries/redis_spec.rb @@ -179,7 +179,7 @@ end it 'instance Sentinel passwords are nil' do - NewRedisHelper::GitlabRails::REDIS_INSTANCES.each do |instance| + RedisHelper::GitlabRails::REDIS_INSTANCES.each do |instance| expect(node['gitlab']['gitlab_rails']["redis_#{instance}_sentinels_password"]).to be_nil end end @@ -199,7 +199,7 @@ end it 'instance Sentinel passwords are autofilled based on Sentinel password' do - NewRedisHelper::GitlabRails::REDIS_INSTANCES.each do |instance| + RedisHelper::GitlabRails::REDIS_INSTANCES.each do |instance| expect(node['gitlab']['gitlab_rails']["redis_#{instance}_sentinels_password"]).to eq('sentinel pass!') end end diff --git a/spec/chef/cookbooks/gitlab/libraries/redis_uri_spec.rb b/spec/chef/cookbooks/gitlab/libraries/redis_uri_spec.rb index d771c852f690598b987cfbbd0a14e1dd9f955541_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JlZGlzX3VyaV9zcGVjLnJi..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JlZGlzX3VyaV9zcGVjLnJi 100644 --- a/spec/chef/cookbooks/gitlab/libraries/redis_uri_spec.rb +++ b/spec/chef/cookbooks/gitlab/libraries/redis_uri_spec.rb @@ -2,7 +2,7 @@ # are the ones we expect require 'spec_helper' -require_relative '../../../../../files/gitlab-cookbooks/package/libraries/helpers/new_redis_helper' +require_relative '../../../../../files/gitlab-cookbooks/package/libraries/helpers/redis_helper' RSpec.describe URI::Redis do subject { URI('redis://localhost') } @@ -50,7 +50,7 @@ context 'with non-alphanumeric password' do let(:password) { "&onBsidv6# XeKFd}=BDDyRrv/?@[]()<>*" } - let(:escaped) { NewRedisHelper.encode_redis_password(password) } + let(:escaped) { RedisHelper.encode_redis_password(password) } it 'rejects unencoded passwords' do expect { subject.password = password }.to raise_error(URI::InvalidComponentError) diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails_spec.rb index d771c852f690598b987cfbbd0a14e1dd9f955541_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHNfc3BlYy5yYg==..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHNfc3BlYy5yYg== 100644 --- a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails_spec.rb +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails_spec.rb @@ -4,7 +4,7 @@ using RSpec::Parameterized::TableSyntax let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink runit_service)).converge('gitlab::default') } - let(:redis_instances) { NewRedisHelper::GitlabRails::REDIS_INSTANCES } + let(:redis_instances) { RedisHelper::GitlabRails::REDIS_INSTANCES } let(:redis_cluster_instances) { %w(cache rate_limiting cluster_rate_limiting) } let(:config_dir) { '/var/opt/gitlab/gitlab-rails/etc/' } let(:default_vars) do @@ -420,7 +420,7 @@ context 'with allowed instances' do before do stub_gitlab_rb( - gitlab_rails: NewRedisHelper::GitlabRails::ALLOWED_REDIS_CLUSTER_INSTANCE.to_h do |inst| + gitlab_rails: RedisHelper::GitlabRails::ALLOWED_REDIS_CLUSTER_INSTANCE.to_h do |inst| ["redis_#{inst}_cluster_nodes", { 'host' => 'cluster1.example.com', 'port' => '12345' }] end ) diff --git a/spec/chef/cookbooks/package/libraries/new_redis_helper/gitlab_rails_spec.rb b/spec/chef/cookbooks/package/libraries/new_redis_helper/gitlab_rails_spec.rb deleted file mode 100644 index d771c852f690598b987cfbbd0a14e1dd9f955541_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9uZXdfcmVkaXNfaGVscGVyL2dpdGxhYl9yYWlsc19zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/cookbooks/package/libraries/new_redis_helper/gitlab_rails_spec.rb +++ /dev/null @@ -1,85 +0,0 @@ -require 'chef_helper' - -RSpec.describe NewRedisHelper::GitlabRails do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab::default') } - - subject { described_class.new(chef_run.node) } - - describe '#validate_instance_shard_config' do - before { allow(Gitlab).to receive(:[]).and_call_original } - - context 'with both sentinels and cluster declared' do - before do - stub_gitlab_rb( - gitlab_rails: { - redis_cache_sentinels: [ - { 'host' => 'sentinel1.example.com', 'port' => '12345' }, - { 'host' => 'sentinel2.example.com', 'port' => '12345' } - ], - redis_cache_cluster_nodes: [ - { 'host' => 'cluster1.example.com', 'port' => '12345' }, - { 'host' => 'cluster1.example.com', 'port' => '12345' } - ] - } - ) - end - - it 'raises error' do - expect { subject.validate_instance_shard_config('cache') }.to raise_error(RuntimeError) - end - end - - context 'with only sentinels declared' do - before do - stub_gitlab_rb( - gitlab_rails: { - redis_cache_sentinels: [ - { 'host' => 'sentinel1.example.com', 'port' => '12345' }, - { 'host' => 'sentinel2.example.com', 'port' => '12345' } - ] - } - ) - end - - it 'does not raise error' do - expect { subject.validate_instance_shard_config('cache') }.not_to raise_error(RuntimeError) - - subject - end - end - - context 'with only clusters declared' do - before do - stub_gitlab_rb( - gitlab_rails: { - redis_rate_limiting_cluster_nodes: [ - { 'host' => 'cluster1.example.com', 'port' => '12345' }, - { 'host' => 'cluster1.example.com', 'port' => '12345' } - ] - } - ) - end - - it 'does not raise error' do - expect { subject.validate_instance_shard_config('rate_limiting') }.not_to raise_error(RuntimeError) - end - end - - context 'with cluster declared for instances outside allowed list' do - before do - stub_gitlab_rb( - gitlab_rails: { - redis_sessions_cluster_nodes: [ - { 'host' => 'cluster1.example.com', 'port' => '12345' }, - { 'host' => 'cluster1.example.com', 'port' => '12345' } - ] - } - ) - end - - it 'raises error' do - expect { subject.validate_instance_shard_config('sessions') }.to raise_error(RuntimeError) - end - end - end -end diff --git a/spec/chef/cookbooks/package/libraries/new_redis_helper/gitlab_workhorse_spec.rb b/spec/chef/cookbooks/package/libraries/new_redis_helper/gitlab_workhorse_spec.rb deleted file mode 100644 index d771c852f690598b987cfbbd0a14e1dd9f955541_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9uZXdfcmVkaXNfaGVscGVyL2dpdGxhYl93b3JraG9yc2Vfc3BlYy5yYg==..0000000000000000000000000000000000000000 --- a/spec/chef/cookbooks/package/libraries/new_redis_helper/gitlab_workhorse_spec.rb +++ /dev/null @@ -1,237 +0,0 @@ -require 'chef_helper' - -RSpec.describe NewRedisHelper::GitlabWorkhorse do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab::default') } - let(:workhorse_redis_yml_template) { chef_run.template('/var/opt/gitlab/gitlab-rails/etc/redis.workhorse.yml') } - let(:workhorse_redis_yml_file_content) { ChefSpec::Renderer.new(chef_run, workhorse_redis_yml_template).content } - let(:workhorse_redis_yml) { YAML.safe_load(workhorse_redis_yml_file_content, aliases: true, symbolize_names: true) } - - subject { described_class.new(chef_run.node) } - - before do - allow(Gitlab).to receive(:[]).and_call_original - end - - context '#redis_params' do - context 'by default' do - it 'returns information about the default Redis instance' do - expect(subject.redis_params).to eq( - url: 'unix:///var/opt/gitlab/redis/redis.socket', - password: nil, - sentinels: [], - sentinelMaster: 'gitlab-redis', - sentinelPassword: nil - ) - end - end - - context 'with user specified values' do - context 'when password set for UNIX socket' do - before do - stub_gitlab_rb( - gitlab_rails: { - redis_password: 'redis-password' - } - ) - end - - it 'returns a UNIX socket URL with password' do - expect(subject.redis_params).to eq( - url: 'unix://:redis-password@/var/opt/gitlab/redis/redis.socket', - password: 'redis-password', - sentinels: [], - sentinelMaster: 'gitlab-redis', - sentinelPassword: nil - ) - end - end - - context 'when password set with a space' do - before do - stub_gitlab_rb( - gitlab_rails: { - redis_password: 'redis password' - } - ) - end - - it 'ensures the password is encoded with %20' do - expect(subject.redis_params).to eq( - url: 'unix://:redis%20password@/var/opt/gitlab/redis/redis.socket', - password: 'redis password', - sentinels: [], - sentinelMaster: 'gitlab-redis', - sentinelPassword: nil - ) - end - end - - context 'when settings specified via gitlab_rails' do - before do - stub_gitlab_rb( - gitlab_rails: { - redis_host: 'my.redis.host', - redis_password: 'redis-password' - } - ) - end - - it 'returns information about the provided Redis instance via gitlab_rails' do - expect(subject.redis_params).to eq( - url: 'redis://:redis-password@my.redis.host/', - password: 'redis-password', - sentinels: [], - sentinelMaster: 'gitlab-redis', - sentinelPassword: nil - ) - end - end - - context 'when settings specified via gitlab_rails for separate Redis instance' do - before do - stub_gitlab_rb( - gitlab_rails: { - redis_host: 'my.redis.host', - redis_password: 'redis-password', - redis_workhorse_instance: 'different.workhorse.redis.instance', - redis_workhorse_password: 'different-redis-password' - } - ) - end - - it 'returns information about the provided Redis instance via gitlab_rails workhorse instance' do - expect(subject.redis_params).to eq( - url: 'redis://:different-redis-password@different.workhorse.redis.instance/', - password: 'different-redis-password', - sentinels: [], - sentinelMaster: 'gitlab-redis', - sentinelPassword: nil - ) - end - end - - context 'when settings specified via gitlab_workhorse' do - context 'when pointing to different Redis instances' do - before do - stub_gitlab_rb( - gitlab_rails: { - redis_host: 'my.redis.host', - redis_password: 'redis-password', - redis_workhorse_instance: 'different.workhorse.redis.instance', - redis_workhorse_password: 'different-redis-password' - }, - gitlab_workhorse: { - redis_host: 'workhorse.redis.host', - redis_password: 'redis-workhorse-password' - } - ) - end - - it 'returns information about the provided Redis instance via gitlab_workhorse' do - expect(subject.redis_params).to eq( - url: 'redis://:redis-workhorse-password@workhorse.redis.host/', - password: 'redis-workhorse-password', - sentinels: [], - sentinelMaster: 'gitlab-redis', - sentinelPassword: nil - ) - end - - # TODO: When Workhorse recipe spec is cleaned up, this should ideally - # end up there. - it 'populates workhorse.redis.yml with values from gitlab_workhorse' do - expect(workhorse_redis_yml).to eq( - production: { - url: 'redis://:redis-workhorse-password@workhorse.redis.host/', - secret_file: '/var/opt/gitlab/gitlab-rails/shared/encrypted_settings/redis.workhorse.yml.enc' - } - ) - end - end - - context 'when pointing to same Redis instance' do - before do - stub_gitlab_rb( - gitlab_rails: { - redis_host: 'my.redis.host', - redis_password: 'redis-password', - }, - gitlab_workhorse: { - redis_host: 'my.redis.host', - redis_password: 'redis-password' - } - ) - end - - it 'returns information about the provided Redis instance via gitlab_workhorse' do - expect(subject.redis_params).to eq( - url: 'redis://:redis-password@my.redis.host/', - password: 'redis-password', - sentinels: [], - sentinelMaster: 'gitlab-redis', - sentinelPassword: nil - ) - end - - # TODO: When Workhorse recipe spec is cleaned up, this should ideally - # end up there. - it 'does not populate workhorse.redis.yml' do - expect(chef_run).not_to render_file('/var/opt/gitlab/gitlab-rails/etc/redis.workhorse.yml') - end - end - end - - context 'when sentinels are specified' do - context 'when Redis master settings are specified via redis key' do - before do - stub_gitlab_rb( - gitlab_workhorse: { - redis_host: 'workhorse.redis.host', - redis_sentinels: [ - { host: '10.0.0.1', port: 26379 }, - { host: '10.0.0.2', port: 26379 }, - { host: '10.0.0.3', port: 26379 } - ], - redis_sentinels_password: 'workhorse-sentinel-password' - }, - redis: { - master_name: 'redis-for-workhorse', - master_password: 'redis-password' - } - ) - end - - it 'returns information about the provided Redis instance via gitlab_workhorse' do - expect(subject.redis_params).to eq( - url: 'redis://:redis-password@redis-for-workhorse/', - password: 'redis-password', - sentinels: [ - "redis://:workhorse-sentinel-password@10.0.0.1:26379", - "redis://:workhorse-sentinel-password@10.0.0.2:26379", - "redis://:workhorse-sentinel-password@10.0.0.3:26379" - ], - sentinelMaster: 'redis-for-workhorse', - sentinelPassword: 'workhorse-sentinel-password' - ) - end - - # TODO: When Workhorse recipe spec is cleaned up, this should ideally - # end up there. - it 'populates workhorse.redis.yml with values from gitlab_workhorse' do - expect(workhorse_redis_yml).to eq( - production: { - url: 'redis://:redis-password@workhorse.redis.host/', - secret_file: '/var/opt/gitlab/gitlab-rails/shared/encrypted_settings/redis.workhorse.yml.enc', - sentinels: [ - { host: '10.0.0.1', port: 26379, password: 'workhorse-sentinel-password' }, - { host: '10.0.0.2', port: 26379, password: 'workhorse-sentinel-password' }, - { host: '10.0.0.3', port: 26379, password: 'workhorse-sentinel-password' }, - ] - } - ) - end - end - end - end - end -end diff --git a/spec/chef/cookbooks/package/libraries/new_redis_helper/server_spec.rb b/spec/chef/cookbooks/package/libraries/new_redis_helper/server_spec.rb deleted file mode 100644 index d771c852f690598b987cfbbd0a14e1dd9f955541_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9uZXdfcmVkaXNfaGVscGVyL3NlcnZlcl9zcGVjLnJi..0000000000000000000000000000000000000000 --- a/spec/chef/cookbooks/package/libraries/new_redis_helper/server_spec.rb +++ /dev/null @@ -1,142 +0,0 @@ -require 'chef_helper' - -RSpec.describe NewRedisHelper::Server do - let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab::default') } - - subject { described_class.new(chef_run.node) } - - describe '#running_version' do - let(:redis_cli_output) do - <<~MSG - # Server - redis_version:3.2.12 - redis_git_sha1:00000000 - redis_git_dirty:0 - redis_build_id:e16da30f4a0a7845 - redis_mode:standalone - os:Linux 4.15.0-58-generic x86_64 - MSG - end - - before do - # Un-doing the stub added in chef_helper - allow_any_instance_of(described_class).to receive(:running_version).and_call_original - allow(Gitlab).to receive(:[]).and_call_original - allow(VersionHelper).to receive(:version).with(/redis-cli.*INFO/).and_return(redis_cli_output) - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).and_call_original - end - - context 'when redis is not running' do - it 'returns nil' do - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('redis').and_return(false) - - expect(subject.running_version).to be_nil - end - end - - context 'when redis is running' do - before do - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('redis').and_return(true) - end - context 'over socket' do - it 'calls VersionHelper.version with correct arguments' do - expect(VersionHelper).to receive(:version).with('/opt/gitlab/embedded/bin/redis-cli -s /var/opt/gitlab/redis/redis.socket INFO') - - subject.running_version - end - end - - context 'over TCP' do - context 'on non-TLS port' do - before do - stub_gitlab_rb( - redis: { - bind: '0.0.0.0', - port: 6379 - } - ) - end - - it 'calls VersionHelper.version with correct arguments' do - expect(VersionHelper).to receive(:version).with('/opt/gitlab/embedded/bin/redis-cli -h 0.0.0.0 -p 6379 INFO') - - subject.running_version - end - end - - context 'on TLS port' do - before do - stub_gitlab_rb( - redis: { - bind: '0.0.0.0', - tls_port: 6380, - tls_cert_file: '/tmp/self_signed.crt', - tls_key_file: '/tmp/self_signed.key', - tls_auth_clients: 'yes' - } - ) - end - - it 'calls VersionHelper.version with correct arguments' do - expected_args = "-h 0.0.0.0 --tls -p 6380 --cacert '/opt/gitlab/embedded/ssl/certs/cacert.pem' --cacertdir '/opt/gitlab/embedded/ssl/certs/' --cert '/tmp/self_signed.crt' --key '/tmp/self_signed.key'" - expect(VersionHelper).to receive(:version).with("/opt/gitlab/embedded/bin/redis-cli #{expected_args} INFO") - - subject.running_version - end - end - end - - context 'with a Redis password specified' do - before do - stub_gitlab_rb( - redis: { - bind: '0.0.0.0', - port: 6379, - password: 'toomanysecrets' - } - ) - end - - it 'it passes password to the command' do - expect(VersionHelper).to receive(:version).with("/opt/gitlab/embedded/bin/redis-cli -h 0.0.0.0 -p 6379 -a 'toomanysecrets' INFO") - - subject.running_version - end - end - - it 'parses version from redis-cli output properly' do - expect(subject.running_version).to eq('3.2.12') - end - end - end - - describe '#installed_version' do - let(:redis_server_output) { 'Redis server v=3.2.12 sha=00000000:0 malloc=jemalloc-4.0.3 bits=64 build=e16da30f4a0a7845' } - - before do - # Un-doing the stub added in chef_helper - allow_any_instance_of(described_class).to receive(:installed_version).and_call_original - allow(Gitlab).to receive(:[]).and_call_original - allow(VersionHelper).to receive(:version).with(/redis-server --version/).and_return(redis_server_output) - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).and_call_original - end - - context 'when redis is not running' do - it 'returns nil' do - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('redis').and_return(false) - - expect(subject.installed_version).to be_nil - end - end - - context 'when redis is running' do - before do - allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('redis').and_return(true) - end - - it 'parses redis-server output properly' do - expect(subject.installed_version).to eq('3.2.12') - end - end - end -end diff --git a/spec/chef/cookbooks/package/libraries/redis_helper/gitlab_rails_spec.rb b/spec/chef/cookbooks/package/libraries/redis_helper/gitlab_rails_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9yZWRpc19oZWxwZXIvZ2l0bGFiX3JhaWxzX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/package/libraries/redis_helper/gitlab_rails_spec.rb @@ -0,0 +1,85 @@ +require 'chef_helper' + +RSpec.describe RedisHelper::GitlabRails do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab::default') } + + subject { described_class.new(chef_run.node) } + + describe '#validate_instance_shard_config' do + before { allow(Gitlab).to receive(:[]).and_call_original } + + context 'with both sentinels and cluster declared' do + before do + stub_gitlab_rb( + gitlab_rails: { + redis_cache_sentinels: [ + { 'host' => 'sentinel1.example.com', 'port' => '12345' }, + { 'host' => 'sentinel2.example.com', 'port' => '12345' } + ], + redis_cache_cluster_nodes: [ + { 'host' => 'cluster1.example.com', 'port' => '12345' }, + { 'host' => 'cluster1.example.com', 'port' => '12345' } + ] + } + ) + end + + it 'raises error' do + expect { subject.validate_instance_shard_config('cache') }.to raise_error(RuntimeError) + end + end + + context 'with only sentinels declared' do + before do + stub_gitlab_rb( + gitlab_rails: { + redis_cache_sentinels: [ + { 'host' => 'sentinel1.example.com', 'port' => '12345' }, + { 'host' => 'sentinel2.example.com', 'port' => '12345' } + ] + } + ) + end + + it 'does not raise error' do + expect { subject.validate_instance_shard_config('cache') }.not_to raise_error(RuntimeError) + + subject + end + end + + context 'with only clusters declared' do + before do + stub_gitlab_rb( + gitlab_rails: { + redis_rate_limiting_cluster_nodes: [ + { 'host' => 'cluster1.example.com', 'port' => '12345' }, + { 'host' => 'cluster1.example.com', 'port' => '12345' } + ] + } + ) + end + + it 'does not raise error' do + expect { subject.validate_instance_shard_config('rate_limiting') }.not_to raise_error(RuntimeError) + end + end + + context 'with cluster declared for instances outside allowed list' do + before do + stub_gitlab_rb( + gitlab_rails: { + redis_sessions_cluster_nodes: [ + { 'host' => 'cluster1.example.com', 'port' => '12345' }, + { 'host' => 'cluster1.example.com', 'port' => '12345' } + ] + } + ) + end + + it 'raises error' do + expect { subject.validate_instance_shard_config('sessions') }.to raise_error(RuntimeError) + end + end + end +end diff --git a/spec/chef/cookbooks/package/libraries/redis_helper/gitlab_workhorse_spec.rb b/spec/chef/cookbooks/package/libraries/redis_helper/gitlab_workhorse_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9yZWRpc19oZWxwZXIvZ2l0bGFiX3dvcmtob3JzZV9zcGVjLnJi --- /dev/null +++ b/spec/chef/cookbooks/package/libraries/redis_helper/gitlab_workhorse_spec.rb @@ -0,0 +1,237 @@ +require 'chef_helper' + +RSpec.describe RedisHelper::GitlabWorkhorse do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab::default') } + let(:workhorse_redis_yml_template) { chef_run.template('/var/opt/gitlab/gitlab-rails/etc/redis.workhorse.yml') } + let(:workhorse_redis_yml_file_content) { ChefSpec::Renderer.new(chef_run, workhorse_redis_yml_template).content } + let(:workhorse_redis_yml) { YAML.safe_load(workhorse_redis_yml_file_content, aliases: true, symbolize_names: true) } + + subject { described_class.new(chef_run.node) } + + before do + allow(Gitlab).to receive(:[]).and_call_original + end + + context '#redis_params' do + context 'by default' do + it 'returns information about the default Redis instance' do + expect(subject.redis_params).to eq( + url: 'unix:///var/opt/gitlab/redis/redis.socket', + password: nil, + sentinels: [], + sentinelMaster: 'gitlab-redis', + sentinelPassword: nil + ) + end + end + + context 'with user specified values' do + context 'when password set for UNIX socket' do + before do + stub_gitlab_rb( + gitlab_rails: { + redis_password: 'redis-password' + } + ) + end + + it 'returns a UNIX socket URL with password' do + expect(subject.redis_params).to eq( + url: 'unix://:redis-password@/var/opt/gitlab/redis/redis.socket', + password: 'redis-password', + sentinels: [], + sentinelMaster: 'gitlab-redis', + sentinelPassword: nil + ) + end + end + + context 'when password set with a space' do + before do + stub_gitlab_rb( + gitlab_rails: { + redis_password: 'redis password' + } + ) + end + + it 'ensures the password is encoded with %20' do + expect(subject.redis_params).to eq( + url: 'unix://:redis%20password@/var/opt/gitlab/redis/redis.socket', + password: 'redis password', + sentinels: [], + sentinelMaster: 'gitlab-redis', + sentinelPassword: nil + ) + end + end + + context 'when settings specified via gitlab_rails' do + before do + stub_gitlab_rb( + gitlab_rails: { + redis_host: 'my.redis.host', + redis_password: 'redis-password' + } + ) + end + + it 'returns information about the provided Redis instance via gitlab_rails' do + expect(subject.redis_params).to eq( + url: 'redis://:redis-password@my.redis.host/', + password: 'redis-password', + sentinels: [], + sentinelMaster: 'gitlab-redis', + sentinelPassword: nil + ) + end + end + + context 'when settings specified via gitlab_rails for separate Redis instance' do + before do + stub_gitlab_rb( + gitlab_rails: { + redis_host: 'my.redis.host', + redis_password: 'redis-password', + redis_workhorse_instance: 'different.workhorse.redis.instance', + redis_workhorse_password: 'different-redis-password' + } + ) + end + + it 'returns information about the provided Redis instance via gitlab_rails workhorse instance' do + expect(subject.redis_params).to eq( + url: 'redis://:different-redis-password@different.workhorse.redis.instance/', + password: 'different-redis-password', + sentinels: [], + sentinelMaster: 'gitlab-redis', + sentinelPassword: nil + ) + end + end + + context 'when settings specified via gitlab_workhorse' do + context 'when pointing to different Redis instances' do + before do + stub_gitlab_rb( + gitlab_rails: { + redis_host: 'my.redis.host', + redis_password: 'redis-password', + redis_workhorse_instance: 'different.workhorse.redis.instance', + redis_workhorse_password: 'different-redis-password' + }, + gitlab_workhorse: { + redis_host: 'workhorse.redis.host', + redis_password: 'redis-workhorse-password' + } + ) + end + + it 'returns information about the provided Redis instance via gitlab_workhorse' do + expect(subject.redis_params).to eq( + url: 'redis://:redis-workhorse-password@workhorse.redis.host/', + password: 'redis-workhorse-password', + sentinels: [], + sentinelMaster: 'gitlab-redis', + sentinelPassword: nil + ) + end + + # TODO: When Workhorse recipe spec is cleaned up, this should ideally + # end up there. + it 'populates workhorse.redis.yml with values from gitlab_workhorse' do + expect(workhorse_redis_yml).to eq( + production: { + url: 'redis://:redis-workhorse-password@workhorse.redis.host/', + secret_file: '/var/opt/gitlab/gitlab-rails/shared/encrypted_settings/redis.workhorse.yml.enc' + } + ) + end + end + + context 'when pointing to same Redis instance' do + before do + stub_gitlab_rb( + gitlab_rails: { + redis_host: 'my.redis.host', + redis_password: 'redis-password', + }, + gitlab_workhorse: { + redis_host: 'my.redis.host', + redis_password: 'redis-password' + } + ) + end + + it 'returns information about the provided Redis instance via gitlab_workhorse' do + expect(subject.redis_params).to eq( + url: 'redis://:redis-password@my.redis.host/', + password: 'redis-password', + sentinels: [], + sentinelMaster: 'gitlab-redis', + sentinelPassword: nil + ) + end + + # TODO: When Workhorse recipe spec is cleaned up, this should ideally + # end up there. + it 'does not populate workhorse.redis.yml' do + expect(chef_run).not_to render_file('/var/opt/gitlab/gitlab-rails/etc/redis.workhorse.yml') + end + end + end + + context 'when sentinels are specified' do + context 'when Redis master settings are specified via redis key' do + before do + stub_gitlab_rb( + gitlab_workhorse: { + redis_host: 'workhorse.redis.host', + redis_sentinels: [ + { host: '10.0.0.1', port: 26379 }, + { host: '10.0.0.2', port: 26379 }, + { host: '10.0.0.3', port: 26379 } + ], + redis_sentinels_password: 'workhorse-sentinel-password' + }, + redis: { + master_name: 'redis-for-workhorse', + master_password: 'redis-password' + } + ) + end + + it 'returns information about the provided Redis instance via gitlab_workhorse' do + expect(subject.redis_params).to eq( + url: 'redis://:redis-password@redis-for-workhorse/', + password: 'redis-password', + sentinels: [ + "redis://:workhorse-sentinel-password@10.0.0.1:26379", + "redis://:workhorse-sentinel-password@10.0.0.2:26379", + "redis://:workhorse-sentinel-password@10.0.0.3:26379" + ], + sentinelMaster: 'redis-for-workhorse', + sentinelPassword: 'workhorse-sentinel-password' + ) + end + + # TODO: When Workhorse recipe spec is cleaned up, this should ideally + # end up there. + it 'populates workhorse.redis.yml with values from gitlab_workhorse' do + expect(workhorse_redis_yml).to eq( + production: { + url: 'redis://:redis-password@workhorse.redis.host/', + secret_file: '/var/opt/gitlab/gitlab-rails/shared/encrypted_settings/redis.workhorse.yml.enc', + sentinels: [ + { host: '10.0.0.1', port: 26379, password: 'workhorse-sentinel-password' }, + { host: '10.0.0.2', port: 26379, password: 'workhorse-sentinel-password' }, + { host: '10.0.0.3', port: 26379, password: 'workhorse-sentinel-password' }, + ] + } + ) + end + end + end + end + end +end diff --git a/spec/chef/cookbooks/package/libraries/redis_helper/server_spec.rb b/spec/chef/cookbooks/package/libraries/redis_helper/server_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_c3BlYy9jaGVmL2Nvb2tib29rcy9wYWNrYWdlL2xpYnJhcmllcy9yZWRpc19oZWxwZXIvc2VydmVyX3NwZWMucmI= --- /dev/null +++ b/spec/chef/cookbooks/package/libraries/redis_helper/server_spec.rb @@ -0,0 +1,142 @@ +require 'chef_helper' + +RSpec.describe RedisHelper::Server do + let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink)).converge('gitlab::default') } + + subject { described_class.new(chef_run.node) } + + describe '#running_version' do + let(:redis_cli_output) do + <<~MSG + # Server + redis_version:3.2.12 + redis_git_sha1:00000000 + redis_git_dirty:0 + redis_build_id:e16da30f4a0a7845 + redis_mode:standalone + os:Linux 4.15.0-58-generic x86_64 + MSG + end + + before do + # Un-doing the stub added in chef_helper + allow_any_instance_of(described_class).to receive(:running_version).and_call_original + allow(Gitlab).to receive(:[]).and_call_original + allow(VersionHelper).to receive(:version).with(/redis-cli.*INFO/).and_return(redis_cli_output) + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).and_call_original + end + + context 'when redis is not running' do + it 'returns nil' do + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('redis').and_return(false) + + expect(subject.running_version).to be_nil + end + end + + context 'when redis is running' do + before do + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('redis').and_return(true) + end + context 'over socket' do + it 'calls VersionHelper.version with correct arguments' do + expect(VersionHelper).to receive(:version).with('/opt/gitlab/embedded/bin/redis-cli -s /var/opt/gitlab/redis/redis.socket INFO') + + subject.running_version + end + end + + context 'over TCP' do + context 'on non-TLS port' do + before do + stub_gitlab_rb( + redis: { + bind: '0.0.0.0', + port: 6379 + } + ) + end + + it 'calls VersionHelper.version with correct arguments' do + expect(VersionHelper).to receive(:version).with('/opt/gitlab/embedded/bin/redis-cli -h 0.0.0.0 -p 6379 INFO') + + subject.running_version + end + end + + context 'on TLS port' do + before do + stub_gitlab_rb( + redis: { + bind: '0.0.0.0', + tls_port: 6380, + tls_cert_file: '/tmp/self_signed.crt', + tls_key_file: '/tmp/self_signed.key', + tls_auth_clients: 'yes' + } + ) + end + + it 'calls VersionHelper.version with correct arguments' do + expected_args = "-h 0.0.0.0 --tls -p 6380 --cacert '/opt/gitlab/embedded/ssl/certs/cacert.pem' --cacertdir '/opt/gitlab/embedded/ssl/certs/' --cert '/tmp/self_signed.crt' --key '/tmp/self_signed.key'" + expect(VersionHelper).to receive(:version).with("/opt/gitlab/embedded/bin/redis-cli #{expected_args} INFO") + + subject.running_version + end + end + end + + context 'with a Redis password specified' do + before do + stub_gitlab_rb( + redis: { + bind: '0.0.0.0', + port: 6379, + password: 'toomanysecrets' + } + ) + end + + it 'it passes password to the command' do + expect(VersionHelper).to receive(:version).with("/opt/gitlab/embedded/bin/redis-cli -h 0.0.0.0 -p 6379 -a 'toomanysecrets' INFO") + + subject.running_version + end + end + + it 'parses version from redis-cli output properly' do + expect(subject.running_version).to eq('3.2.12') + end + end + end + + describe '#installed_version' do + let(:redis_server_output) { 'Redis server v=3.2.12 sha=00000000:0 malloc=jemalloc-4.0.3 bits=64 build=e16da30f4a0a7845' } + + before do + # Un-doing the stub added in chef_helper + allow_any_instance_of(described_class).to receive(:installed_version).and_call_original + allow(Gitlab).to receive(:[]).and_call_original + allow(VersionHelper).to receive(:version).with(/redis-server --version/).and_return(redis_server_output) + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).and_call_original + end + + context 'when redis is not running' do + it 'returns nil' do + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('redis').and_return(false) + + expect(subject.installed_version).to be_nil + end + end + + context 'when redis is running' do + before do + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('redis').and_return(true) + end + + it 'parses redis-server output properly' do + expect(subject.installed_version).to eq('3.2.12') + end + end + end +end diff --git a/spec/chef/cookbooks/redis/recipes/redis_spec.rb b/spec/chef/cookbooks/redis/recipes/redis_spec.rb index d771c852f690598b987cfbbd0a14e1dd9f955541_c3BlYy9jaGVmL2Nvb2tib29rcy9yZWRpcy9yZWNpcGVzL3JlZGlzX3NwZWMucmI=..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_c3BlYy9jaGVmL2Nvb2tib29rcy9yZWRpcy9yZWNpcGVzL3JlZGlzX3NwZWMucmI= 100644 --- a/spec/chef/cookbooks/redis/recipes/redis_spec.rb +++ b/spec/chef/cookbooks/redis/recipes/redis_spec.rb @@ -74,8 +74,8 @@ describe 'pending restart check' do context 'when running version is same as installed version' do before do - allow_any_instance_of(NewRedisHelper::Server).to receive(:running_version).and_return('3.2.12') - allow_any_instance_of(NewRedisHelper::Server).to receive(:installed_version).and_return('3.2.12') + allow_any_instance_of(RedisHelper::Server).to receive(:running_version).and_return('3.2.12') + allow_any_instance_of(RedisHelper::Server).to receive(:installed_version).and_return('3.2.12') end it 'does not raise a warning' do @@ -85,8 +85,8 @@ context 'when running version is different than installed version' do before do - allow_any_instance_of(NewRedisHelper::Server).to receive(:running_version).and_return('3.2.12') - allow_any_instance_of(NewRedisHelper::Server).to receive(:installed_version).and_return('5.0.9') + allow_any_instance_of(RedisHelper::Server).to receive(:running_version).and_return('3.2.12') + allow_any_instance_of(RedisHelper::Server).to receive(:installed_version).and_return('5.0.9') end it 'raises a warning' do diff --git a/spec/chef_helper.rb b/spec/chef_helper.rb index d771c852f690598b987cfbbd0a14e1dd9f955541_c3BlYy9jaGVmX2hlbHBlci5yYg==..151dfb74d13fdedbf5434cb7ff90e0409c12b35c_c3BlYy9jaGVmX2hlbHBlci5yYg== 100644 --- a/spec/chef_helper.rb +++ b/spec/chef_helper.rb @@ -70,8 +70,8 @@ allow(VersionHelper).to receive(:version).with('/opt/gitlab/embedded/bin/psql --version').and_return('fake_psql_version') allow(VersionHelper).to receive(:version).with('cat /opt/gitlab/embedded/service/mattermost/VERSION').and_return('foobar') allow(VersionHelper).to receive(:version).with(/-[-]?version/).and_return('foobar') - allow_any_instance_of(NewRedisHelper::Server).to receive(:installed_version).and_return('3.2.12') - allow_any_instance_of(NewRedisHelper::Server).to receive(:running_version).and_return('3.2.12') + allow_any_instance_of(RedisHelper::Server).to receive(:installed_version).and_return('3.2.12') + allow_any_instance_of(RedisHelper::Server).to receive(:running_version).and_return('3.2.12') allow_any_instance_of(ConsulHelper).to receive(:installed_version).and_return('1.9.6') allow_any_instance_of(ConsulHelper).to receive(:running_version).and_return('1.9.6') stub_command('/sbin/init --version | grep upstart')