Skip to content

Commit d21527d

Browse files
authored
Merge pull request #856 from supercaracal/fix-sentinel-auth
Fix Sentinel authentication
2 parents 64fea92 + 0559b50 commit d21527d

File tree

3 files changed

+75
-38
lines changed

3 files changed

+75
-38
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,15 @@ but a few so that if one is down the client will try the next one. The client
9595
is able to remember the last Sentinel that was able to reply correctly and will
9696
use it for the next requests.
9797

98+
If you want to [authenticate](https://redis.io/topics/sentinel#configuring-sentinel-instances-with-authentication) Sentinel itself, you must specify the `password` option per instance.
99+
100+
```ruby
101+
SENTINELS = [{ host: '127.0.0.1', port: 26380, password: 'mysecret' },
102+
{ host: '127.0.0.1', port: 26381, password: 'mysecret' }]
103+
104+
redis = Redis.new(host: 'mymaster', sentinels: SENTINELS, role: :master)
105+
```
106+
98107
## Cluster support
99108

100109
`redis-rb` supports [clustering](https://redis.io/topics/cluster-spec).

lib/redis/client.rb

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,7 @@ def sentinel_detect
577577
client = Client.new(@options.merge({
578578
:host => sentinel[:host],
579579
:port => sentinel[:port],
580+
password: sentinel[:password],
580581
:reconnect_attempts => 0,
581582
}))
582583

@@ -595,11 +596,6 @@ def sentinel_detect
595596
end
596597

597598
raise CannotConnectError, "No sentinels available."
598-
rescue Redis::CommandError => err
599-
# this feature is only available starting with Redis 5.0.1
600-
raise if err.message !~ /ERR unknown command (`|')auth(`|')/
601-
@options[:password] = DEFAULTS.fetch(:password)
602-
retry
603599
end
604600

605601
def resolve_master

test/sentinel_test.rb

Lines changed: 65 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -129,63 +129,56 @@ def test_sentinel_failover_prioritize_healthy_sentinel
129129
end
130130

131131
def test_sentinel_with_non_sentinel_options
132-
sentinels = [{:host => "127.0.0.1", :port => 26381}]
133-
134-
commands = {
135-
:s1 => [],
136-
:m1 => []
137-
}
132+
commands = { s1: [], m1: [] }
138133

139134
sentinel = lambda do |port|
140135
{
141-
:auth => lambda do |pass|
142-
commands[:s1] << ["auth", pass]
136+
auth: lambda do |pass|
137+
commands[:s1] << ['auth', pass]
143138
'+OK'
144139
end,
145-
:select => lambda do |db|
146-
commands[:s1] << ["select", db]
140+
select: lambda do |db|
141+
commands[:s1] << ['select', db]
147142
"-ERR unknown command 'select'"
148143
end,
149-
:sentinel => lambda do |command, *args|
144+
sentinel: lambda do |command, *args|
150145
commands[:s1] << [command, *args]
151-
["127.0.0.1", port.to_s]
146+
['127.0.0.1', port.to_s]
152147
end
153148
}
154149
end
155150

156151
master = {
157-
:auth => lambda do |pass|
158-
commands[:m1] << ["auth", pass]
159-
"+OK"
152+
auth: lambda do |pass|
153+
commands[:m1] << ['auth', pass]
154+
'+OK'
160155
end,
161-
:role => lambda do
162-
commands[:m1] << ["role"]
163-
["master"]
156+
role: lambda do
157+
commands[:m1] << ['role']
158+
['master']
164159
end
165160
}
166161

167162
RedisMock.start(master) do |master_port|
168163
RedisMock.start(sentinel.call(master_port)) do |sen_port|
169-
sentinels[0][:port] = sen_port
170-
redis = Redis.new(:url => "redis://:foo@master1/15", :sentinels => sentinels, :role => :master)
171-
164+
s = [{ host: '127.0.0.1', port: sen_port }]
165+
redis = Redis.new(url: 'redis://:foo@master1/15', sentinels: s, role: :master)
172166
assert redis.ping
173167
end
174168
end
175169

176-
assert_equal [%w[auth foo], %w[get-master-addr-by-name master1]], commands[:s1]
170+
assert_equal [%w[get-master-addr-by-name master1]], commands[:s1]
177171
assert_equal [%w[auth foo], %w[role]], commands[:m1]
178172
end
179173

180-
def test_sentinel_authentication_in_redis_prior_to_version_five
181-
sentinels = [{ host: '127.0.0.1', port: 26381 }]
174+
def test_authentication_for_sentinel
182175
commands = { s1: [], m1: [] }
183176

184177
sentinel = lambda do |port|
185178
{
186179
auth: lambda do |pass|
187180
commands[:s1] << ['auth', pass]
188-
'-ERR unknown command `auth`'
181+
'+OK'
189182
end,
190183
select: lambda do |db|
191184
commands[:s1] << ['select', db]
@@ -201,7 +194,7 @@ def test_sentinel_authentication_in_redis_prior_to_version_five
201194
master = {
202195
auth: lambda do |pass|
203196
commands[:m1] << ['auth', pass]
204-
'+OK'
197+
'-ERR Client sent AUTH, but no password is set'
205198
end,
206199
role: lambda do
207200
commands[:m1] << ['role']
@@ -211,18 +204,57 @@ def test_sentinel_authentication_in_redis_prior_to_version_five
211204

212205
RedisMock.start(master) do |master_port|
213206
RedisMock.start(sentinel.call(master_port)) do |sen_port|
214-
sentinels[0][:port] = sen_port
215-
redis = Redis.new(url: 'redis://master1',
216-
sentinels: sentinels,
217-
role: :master,
218-
password: 'foo')
207+
s = [{ host: '127.0.0.1', port: sen_port, password: 'foo' }]
208+
r = Redis.new(host: 'master1', sentinels: s, role: :master)
209+
assert r.ping
210+
end
211+
end
219212

220-
assert redis.ping
213+
assert_equal [%w[auth foo], %w[get-master-addr-by-name master1]], commands[:s1]
214+
assert_equal [%w[role]], commands[:m1]
215+
end
216+
217+
def test_authentication_for_sentinel_and_redis
218+
commands = { s1: [], m1: [] }
219+
220+
sentinel = lambda do |port|
221+
{
222+
auth: lambda do |pass|
223+
commands[:s1] << ['auth', pass]
224+
'+OK'
225+
end,
226+
select: lambda do |db|
227+
commands[:s1] << ['select', db]
228+
'-ERR unknown command `select`'
229+
end,
230+
sentinel: lambda do |command, *args|
231+
commands[:s1] << [command, *args]
232+
['127.0.0.1', port.to_s]
233+
end
234+
}
235+
end
236+
237+
master = {
238+
auth: lambda do |pass|
239+
commands[:m1] << ['auth', pass]
240+
'+OK'
241+
end,
242+
role: lambda do
243+
commands[:m1] << ['role']
244+
['master']
245+
end
246+
}
247+
248+
RedisMock.start(master) do |master_port|
249+
RedisMock.start(sentinel.call(master_port)) do |sen_port|
250+
s = [{ host: '127.0.0.1', port: sen_port, password: 'foo' }]
251+
r = Redis.new(host: 'master1', sentinels: s, role: :master, password: 'bar')
252+
assert r.ping
221253
end
222254
end
223255

224256
assert_equal [%w[auth foo], %w[get-master-addr-by-name master1]], commands[:s1]
225-
assert_equal [%w[auth foo], %w[role]], commands[:m1]
257+
assert_equal [%w[auth bar], %w[role]], commands[:m1]
226258
end
227259

228260
def test_sentinel_role_mismatch

0 commit comments

Comments
 (0)