diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a3dd1a..7ada279 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/lib/redis_client.rb b/lib/redis_client.rb index f8b13c0..1a8ceb5 100644 --- a/lib/redis_client.rb +++ b/lib/redis_client.rb @@ -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" diff --git a/lib/redis_client/config.rb b/lib/redis_client/config.rb index 3e0d1a7..9e5ad55 100644 --- a/lib/redis_client/config.rb +++ b/lib/redis_client/config.rb @@ -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 diff --git a/lib/redis_client/sentinel_config.rb b/lib/redis_client/sentinel_config.rb index 663090a..7e88810 100644 --- a/lib/redis_client/sentinel_config.rb +++ b/lib/redis_client/sentinel_config.rb @@ -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 @@ -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 diff --git a/lib/redis_client/url_config.rb b/lib/redis_client/url_config.rb new file mode 100644 index 0000000..7175918 --- /dev/null +++ b/lib/redis_client/url_config.rb @@ -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 diff --git a/test/sentinel/sentinel_test.rb b/test/sentinel/sentinel_test.rb index e571007..4e7c541 100644 --- a/test/sentinel/sentinel_test.rb +++ b/test/sentinel/sentinel_test.rb @@ -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) diff --git a/test/support/servers.rb b/test/support/servers.rb index 56c8ceb..0f679fc 100644 --- a/test/support/servers.rb +++ b/test/support/servers.rb @@ -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