Skip to content

Commit

Permalink
Allow to configure sentinel client via url
Browse files Browse the repository at this point in the history
Fix: #117

```ruby
RedisClient.sentinel(
   url: 'redis://user:pass@mymaster/12',
   sentinels: [
      { host: "127.0.0.1", port: 26380 },
      { host: "127.0.0.1", port: 26381 },
    ]
)
```
  • Loading branch information
moofkit authored and byroot committed May 17, 2023
1 parent ff42581 commit ed821c0
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 29 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Unreleased

- Allow to configure sentinel client via url. #117.
- Fix sentinel to preverse the auth/password when refreshing the sentinel list. #107.

# 0.14.1
Expand Down
1 change: 1 addition & 0 deletions lib/redis_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require "redis_client/version"
require "redis_client/command_builder"
require "redis_client/url_config"
require "redis_client/config"
require "redis_client/pid_cache"
require "redis_client/sentinel_config"
Expand Down
38 changes: 11 additions & 27 deletions lib/redis_client/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -162,37 +162,21 @@ def initialize(
**kwargs
)
if url
uri = URI(url)
unless uri.scheme == "redis" || uri.scheme == "rediss"
raise ArgumentError, "Invalid URL: #{url.inspect}"
end

kwargs[:ssl] = uri.scheme == "rediss" unless kwargs.key?(:ssl)

kwargs[:username] ||= uri.user if uri.password && !uri.user.empty?

kwargs[:password] ||= if uri.user && !uri.password
URI.decode_www_form_component(uri.user)
elsif uri.user && uri.password
URI.decode_www_form_component(uri.password)
end

db_path = uri.path&.delete_prefix("/")
kwargs[:db] ||= Integer(db_path) if db_path && !db_path.empty?
url_config = URLConfig.new(url)
kwargs = {
ssl: url_config.ssl?,
username: url_config.username,
password: url_config.password,
db: url_config.db,
}.compact.merge(kwargs)
host ||= url_config.host
port ||= url_config.port
end

super(**kwargs)

@host = host
unless @host
uri_host = uri&.host
uri_host = nil if uri_host&.empty?
if uri_host
@host = uri_host&.sub(/\A\[(.*)\]\z/, '\1')
end
end
@host ||= DEFAULT_HOST
@port = Integer(port || uri&.port || DEFAULT_PORT)
@host = host || DEFAULT_HOST
@port = Integer(port || DEFAULT_PORT)
@path = path
end
end
Expand Down
20 changes: 18 additions & 2 deletions lib/redis_client/sentinel_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,28 @@ class SentinelConfig
SENTINEL_DELAY = 0.25
DEFAULT_RECONNECT_ATTEMPTS = 2

def initialize(name:, sentinels:, role: :master, **client_config)
attr_reader :name

def initialize(sentinels:, role: :master, name: nil, url: nil, **client_config)
unless %i(master replica slave).include?(role)
raise ArgumentError, "Expected role to be either :master or :replica, got: #{role.inspect}"
end

if url
url_config = URLConfig.new(url)
client_config = {
username: url_config.username,
password: url_config.password,
db: url_config.db,
}.compact.merge(client_config)
name ||= url_config.host
end

@name = name
unless @name
raise ArgumentError, "RedisClient::SentinelConfig requires either a name or an url with a host"
end

@to_list_of_hash = @to_hash = nil
@extra_config = {}
if client_config[:protocol] == 2
Expand All @@ -25,7 +42,6 @@ def initialize(name:, sentinels:, role: :master, **client_config)
end
end

@name = name
@sentinels = {}.compare_by_identity
@role = role
@mutex = Mutex.new
Expand Down
53 changes: 53 additions & 0 deletions lib/redis_client/url_config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# frozen_string_literal: true

require "uri"

class RedisClient
class URLConfig
DEFAULT_SCHEMA = "redis"
SSL_SCHEMA = "rediss"

attr_reader :url, :uri

def initialize(url)
@url = url
@uri = URI(url)
unless uri.scheme == DEFAULT_SCHEMA || uri.scheme == SSL_SCHEMA
raise ArgumentError, "Invalid URL: #{url.inspect}"
end
end

def ssl?
@uri.scheme == SSL_SCHEMA
end

def db
db_path = uri.path&.delete_prefix("/")
Integer(db_path) if db_path && !db_path.empty?
end

def username
uri.user if uri.password && !uri.user.empty?
end

def password
if uri.user && !uri.password
URI.decode_www_form_component(uri.user)
elsif uri.user && uri.password
URI.decode_www_form_component(uri.password)
end
end

def host
return if uri.host.nil? || uri.host.empty?

uri.host.sub(/\A\[(.*)\]\z/, '\1')
end

def port
return unless uri.port

Integer(uri.port)
end
end
end
9 changes: 9 additions & 0 deletions test/sentinel/sentinel_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,15 @@ def test_sentinel_refresh_password
end
end

def test_config_from_url
config = new_config(url: "redis://george:hunter2@cache/10", name: nil)
assert_equal "hunter2", config.password
assert_equal "george", config.username
assert_equal "cache", config.name
assert_equal 10, config.db
assert_equal [Servers::REDIS.host, Servers::REDIS.port], [config.host, config.port]
end

private

def response_hash(hash)
Expand Down
1 change: 1 addition & 0 deletions test/support/servers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ def generate_conf
sentinel down-after-milliseconds #{SENTINEL_NAME} 10
sentinel failover-timeout #{SENTINEL_NAME} 2000
sentinel parallel-syncs #{SENTINEL_NAME} 1
user george on allcommands allkeys >hunter2
EOS
end

Expand Down

0 comments on commit ed821c0

Please sign in to comment.