files/gitlab-cookbooks/gitlab-ee/libraries/sentinel_helper.rb (107 lines of code) (raw):

class SentinelHelper MYID_PATTERN ||= /^[0-9a-f]{40}$/.freeze JSON_FILE ||= '/etc/gitlab/gitlab-sentinel.json'.freeze def initialize(node) @node = node end def myid if sentinel.key?('myid') && sentinel['myid'] restore_from_node else restore_or_generate_from_file end end def use_hostnames # Detect if user is overriding what we want to calculate here return sentinel['use_hostnames'] ? 'yes' : 'no' unless sentinel['use_hostnames'].nil? return 'yes' if redis['announce_ip_from_hostname'] # Enable hostnames if a non-IP address value is provided in announce_ip return 'yes' if sentinel['announce_ip'] && !Regexp.union([Resolv::IPv4::Regex, Resolv::IPv6::Regex]).match(sentinel['announce_ip']) 'no' end def running_version return unless OmnibusHelper.new(@node).service_up?('sentinel') command = "/opt/gitlab/embedded/bin/redis-cli #{redis_cli_connect_options} INFO" env = if sentinel['password'] { 'REDISCLI_AUTH' => sentinel['password'] } else {} end command_output = VersionHelper.version(command, env: env) 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 def installed_version return unless OmnibusHelper.new(@node).service_up?('sentinel') command = '/opt/gitlab/embedded/bin/redis-sentinel --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 private # Restore from node definition (gitlab.rb) def restore_from_node raise 'Sentinel myid must be exactly 40 hex-characters lowercase' unless MYID_PATTERN.match?(sentinel['myid']) sentinel['myid'] end # Restore from local JSON file or create a new myid def restore_or_generate_from_file existing_data = load_from_file if existing_data && existing_data['myid'] existing_data['myid'] else myid = generate_myid save_to_file({ 'myid' => myid }) myid end end def sentinel @node['gitlab']['sentinel'] end def redis @node['redis'] end # Load from local JSON file def load_from_file Chef::JSONCompat.from_json(File.read(JSON_FILE)) if File.exist?(JSON_FILE) end # Save to local JSON file def save_to_file(data) return unless File.directory?('/etc/gitlab') File.open(JSON_FILE, 'w', 0600) do |f| f.puts(Chef::JSONCompat.to_json_pretty(data)) f.chmod(0600) # update existing file end end def generate_myid SecureRandom.hex(20) # size will be n*2 -> 40 characters end def redis_cli_connect_options args = ["-h #{sentinel['bind']}"] port = sentinel['port'].to_i if port.zero? redis_cli_tls_options(args) else args << "-p #{port}" end args.join(' ') end def redis_cli_tls_options(args) tls_port = sentinel['tls_port'].to_i raise "No Sentinel port available: sentinel['port'] or sentinel['tls_port'] must be non-zero" if tls_port.zero? args << "--tls" args << "-p #{tls_port}" args << "--cacert '#{sentinel['tls_ca_cert_file']}'" if sentinel['tls_ca_cert_file'] args << "--cacertdir '#{sentinel['tls_ca_cert_dir']}'" if sentinel['tls_ca_cert_dir'] return unless client_certs_required? raise "Sentinel TLS client authentication requires sentinel['tls_cert_file'] and sentinel['tls_key_file'] options" unless client_cert_and_key_available? args << "--cert '#{sentinel['tls_cert_file']}'" args << "--key '#{sentinel['tls_key_file']}'" end def client_certs_required? sentinel['tls_auth_clients'] == 'yes' end def client_cert_and_key_available? sentinel['tls_cert_file'] && !sentinel['tls_cert_file'].empty? && sentinel['tls_key_file'] && !sentinel['tls_key_file'].empty? end end