redis 4.1.3 → 4.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +49 -0
- data/README.md +10 -5
- data/lib/redis.rb +388 -348
- data/lib/redis/client.rb +71 -69
- data/lib/redis/cluster.rb +10 -4
- data/lib/redis/cluster/node.rb +3 -0
- data/lib/redis/cluster/node_key.rb +3 -7
- data/lib/redis/cluster/option.rb +27 -14
- data/lib/redis/cluster/slot.rb +30 -13
- data/lib/redis/cluster/slot_loader.rb +4 -4
- data/lib/redis/connection.rb +2 -0
- data/lib/redis/connection/command_helper.rb +3 -2
- data/lib/redis/connection/hiredis.rb +4 -3
- data/lib/redis/connection/registry.rb +2 -1
- data/lib/redis/connection/ruby.rb +92 -109
- data/lib/redis/connection/synchrony.rb +9 -4
- data/lib/redis/distributed.rb +109 -57
- data/lib/redis/errors.rb +2 -0
- data/lib/redis/hash_ring.rb +15 -14
- data/lib/redis/pipeline.rb +16 -3
- data/lib/redis/subscribe.rb +11 -12
- data/lib/redis/version.rb +3 -1
- metadata +14 -9
data/lib/redis/client.rb
CHANGED
|
@@ -6,24 +6,30 @@ require "cgi"
|
|
|
6
6
|
|
|
7
7
|
class Redis
|
|
8
8
|
class Client
|
|
9
|
-
|
|
9
|
+
# Defaults are also used for converting string keys to symbols.
|
|
10
10
|
DEFAULTS = {
|
|
11
|
-
:
|
|
12
|
-
:
|
|
13
|
-
:
|
|
14
|
-
:
|
|
15
|
-
:
|
|
16
|
-
:
|
|
17
|
-
:
|
|
18
|
-
:
|
|
19
|
-
:
|
|
20
|
-
:
|
|
21
|
-
:
|
|
22
|
-
:
|
|
23
|
-
:
|
|
24
|
-
:
|
|
25
|
-
:
|
|
26
|
-
|
|
11
|
+
url: -> { ENV["REDIS_URL"] },
|
|
12
|
+
scheme: "redis",
|
|
13
|
+
host: "127.0.0.1",
|
|
14
|
+
port: 6379,
|
|
15
|
+
path: nil,
|
|
16
|
+
read_timeout: nil,
|
|
17
|
+
write_timeout: nil,
|
|
18
|
+
connect_timeout: nil,
|
|
19
|
+
timeout: 5.0,
|
|
20
|
+
password: nil,
|
|
21
|
+
db: 0,
|
|
22
|
+
driver: nil,
|
|
23
|
+
id: nil,
|
|
24
|
+
tcp_keepalive: 0,
|
|
25
|
+
reconnect_attempts: 1,
|
|
26
|
+
reconnect_delay: 0,
|
|
27
|
+
reconnect_delay_max: 0.5,
|
|
28
|
+
inherit_socket: false,
|
|
29
|
+
logger: nil,
|
|
30
|
+
sentinels: nil,
|
|
31
|
+
role: nil
|
|
32
|
+
}.freeze
|
|
27
33
|
|
|
28
34
|
attr_reader :options
|
|
29
35
|
|
|
@@ -89,7 +95,7 @@ class Redis
|
|
|
89
95
|
@pending_reads = 0
|
|
90
96
|
|
|
91
97
|
@connector =
|
|
92
|
-
if options.
|
|
98
|
+
if !@options[:sentinels].nil?
|
|
93
99
|
Connector::Sentinel.new(@options)
|
|
94
100
|
elsif options.include?(:connector) && options[:connector].respond_to?(:new)
|
|
95
101
|
options.delete(:connector).new(@options)
|
|
@@ -166,6 +172,7 @@ class Redis
|
|
|
166
172
|
end
|
|
167
173
|
rescue ConnectionError => e
|
|
168
174
|
return nil if pipeline.shutdown?
|
|
175
|
+
|
|
169
176
|
# Assume the pipeline was sent in one piece, but execution of
|
|
170
177
|
# SHUTDOWN caused none of the replies for commands that were executed
|
|
171
178
|
# prior to it from coming back around.
|
|
@@ -244,12 +251,13 @@ class Redis
|
|
|
244
251
|
end
|
|
245
252
|
|
|
246
253
|
def connected?
|
|
247
|
-
!!
|
|
254
|
+
!!(connection && connection.connected?)
|
|
248
255
|
end
|
|
249
256
|
|
|
250
257
|
def disconnect
|
|
251
258
|
connection.disconnect if connected?
|
|
252
259
|
end
|
|
260
|
+
alias close disconnect
|
|
253
261
|
|
|
254
262
|
def reconnect
|
|
255
263
|
disconnect
|
|
@@ -300,30 +308,27 @@ class Redis
|
|
|
300
308
|
with_socket_timeout(0, &blk)
|
|
301
309
|
end
|
|
302
310
|
|
|
303
|
-
def with_reconnect(val=true)
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
@reconnect = original
|
|
309
|
-
end
|
|
311
|
+
def with_reconnect(val = true)
|
|
312
|
+
original, @reconnect = @reconnect, val
|
|
313
|
+
yield
|
|
314
|
+
ensure
|
|
315
|
+
@reconnect = original
|
|
310
316
|
end
|
|
311
317
|
|
|
312
318
|
def without_reconnect(&blk)
|
|
313
319
|
with_reconnect(false, &blk)
|
|
314
320
|
end
|
|
315
321
|
|
|
316
|
-
|
|
322
|
+
protected
|
|
317
323
|
|
|
318
324
|
def logging(commands)
|
|
319
|
-
return yield unless @logger
|
|
325
|
+
return yield unless @logger&.debug?
|
|
320
326
|
|
|
321
327
|
begin
|
|
322
328
|
commands.each do |name, *args|
|
|
323
329
|
logged_args = args.map do |a|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
when a.respond_to?(:to_s) then a.to_s
|
|
330
|
+
if a.respond_to?(:inspect) then a.inspect
|
|
331
|
+
elsif a.respond_to?(:to_s) then a.to_s
|
|
327
332
|
else
|
|
328
333
|
# handle poorly-behaved descendants of BasicObject
|
|
329
334
|
klass = a.instance_exec { (class << self; self end).superclass }
|
|
@@ -357,9 +362,9 @@ class Redis
|
|
|
357
362
|
Errno::ENETUNREACH,
|
|
358
363
|
Errno::ENOENT,
|
|
359
364
|
Errno::ETIMEDOUT,
|
|
360
|
-
Errno::EINVAL
|
|
365
|
+
Errno::EINVAL => error
|
|
361
366
|
|
|
362
|
-
raise CannotConnectError, "Error connecting to Redis on #{location} (#{
|
|
367
|
+
raise CannotConnectError, "Error connecting to Redis on #{location} (#{error.class})"
|
|
363
368
|
end
|
|
364
369
|
|
|
365
370
|
def ensure_connected
|
|
@@ -373,9 +378,9 @@ class Redis
|
|
|
373
378
|
if connected?
|
|
374
379
|
unless inherit_socket? || Process.pid == @pid
|
|
375
380
|
raise InheritedError,
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
381
|
+
"Tried to use a connection from a child process without reconnecting. " \
|
|
382
|
+
"You need to reconnect to Redis after forking " \
|
|
383
|
+
"or set :inherit_socket to true."
|
|
379
384
|
end
|
|
380
385
|
else
|
|
381
386
|
connect
|
|
@@ -386,7 +391,7 @@ class Redis
|
|
|
386
391
|
disconnect
|
|
387
392
|
|
|
388
393
|
if attempts <= @options[:reconnect_attempts] && @reconnect
|
|
389
|
-
sleep_t = [(@options[:reconnect_delay] * 2**(attempts-1)),
|
|
394
|
+
sleep_t = [(@options[:reconnect_delay] * 2**(attempts - 1)),
|
|
390
395
|
@options[:reconnect_delay_max]].min
|
|
391
396
|
|
|
392
397
|
Kernel.sleep(sleep_t)
|
|
@@ -408,16 +413,14 @@ class Redis
|
|
|
408
413
|
|
|
409
414
|
defaults.keys.each do |key|
|
|
410
415
|
# Fill in defaults if needed
|
|
411
|
-
if defaults[key].respond_to?(:call)
|
|
412
|
-
defaults[key] = defaults[key].call
|
|
413
|
-
end
|
|
416
|
+
defaults[key] = defaults[key].call if defaults[key].respond_to?(:call)
|
|
414
417
|
|
|
415
418
|
# Symbolize only keys that are needed
|
|
416
|
-
options[key] = options[key.to_s] if options.
|
|
419
|
+
options[key] = options[key.to_s] if options.key?(key.to_s)
|
|
417
420
|
end
|
|
418
421
|
|
|
419
422
|
url = options[:url]
|
|
420
|
-
url = defaults[:url] if url
|
|
423
|
+
url = defaults[:url] if url.nil?
|
|
421
424
|
|
|
422
425
|
# Override defaults from URL if given
|
|
423
426
|
if url
|
|
@@ -426,7 +429,7 @@ class Redis
|
|
|
426
429
|
uri = URI(url)
|
|
427
430
|
|
|
428
431
|
if uri.scheme == "unix"
|
|
429
|
-
defaults[:path]
|
|
432
|
+
defaults[:path] = uri.path
|
|
430
433
|
elsif uri.scheme == "redis" || uri.scheme == "rediss"
|
|
431
434
|
defaults[:scheme] = uri.scheme
|
|
432
435
|
defaults[:host] = uri.host if uri.host
|
|
@@ -457,7 +460,7 @@ class Redis
|
|
|
457
460
|
options[:port] = options[:port].to_i
|
|
458
461
|
end
|
|
459
462
|
|
|
460
|
-
if options.
|
|
463
|
+
if options.key?(:timeout)
|
|
461
464
|
options[:connect_timeout] ||= options[:timeout]
|
|
462
465
|
options[:read_timeout] ||= options[:timeout]
|
|
463
466
|
options[:write_timeout] ||= options[:timeout]
|
|
@@ -476,7 +479,7 @@ class Redis
|
|
|
476
479
|
|
|
477
480
|
case options[:tcp_keepalive]
|
|
478
481
|
when Hash
|
|
479
|
-
[
|
|
482
|
+
%i[time intvl probes].each do |key|
|
|
480
483
|
unless options[:tcp_keepalive][key].is_a?(Integer)
|
|
481
484
|
raise "Expected the #{key.inspect} key in :tcp_keepalive to be an Integer"
|
|
482
485
|
end
|
|
@@ -484,13 +487,13 @@ class Redis
|
|
|
484
487
|
|
|
485
488
|
when Integer
|
|
486
489
|
if options[:tcp_keepalive] >= 60
|
|
487
|
-
options[:tcp_keepalive] = {:
|
|
490
|
+
options[:tcp_keepalive] = { time: options[:tcp_keepalive] - 20, intvl: 10, probes: 2 }
|
|
488
491
|
|
|
489
492
|
elsif options[:tcp_keepalive] >= 30
|
|
490
|
-
options[:tcp_keepalive] = {:
|
|
493
|
+
options[:tcp_keepalive] = { time: options[:tcp_keepalive] - 10, intvl: 5, probes: 2 }
|
|
491
494
|
|
|
492
495
|
elsif options[:tcp_keepalive] >= 5
|
|
493
|
-
options[:tcp_keepalive] = {:
|
|
496
|
+
options[:tcp_keepalive] = { time: options[:tcp_keepalive] - 2, intvl: 2, probes: 1 }
|
|
494
497
|
end
|
|
495
498
|
end
|
|
496
499
|
|
|
@@ -502,14 +505,14 @@ class Redis
|
|
|
502
505
|
def _parse_driver(driver)
|
|
503
506
|
driver = driver.to_s if driver.is_a?(Symbol)
|
|
504
507
|
|
|
505
|
-
if driver.
|
|
508
|
+
if driver.is_a?(String)
|
|
506
509
|
begin
|
|
507
510
|
require_relative "connection/#{driver}"
|
|
508
|
-
rescue LoadError, NameError
|
|
511
|
+
rescue LoadError, NameError
|
|
509
512
|
begin
|
|
510
513
|
require "connection/#{driver}"
|
|
511
|
-
rescue LoadError, NameError =>
|
|
512
|
-
raise
|
|
514
|
+
rescue LoadError, NameError => error
|
|
515
|
+
raise "Cannot load driver #{driver.inspect}: #{error.message}"
|
|
513
516
|
end
|
|
514
517
|
end
|
|
515
518
|
|
|
@@ -528,8 +531,7 @@ class Redis
|
|
|
528
531
|
@options
|
|
529
532
|
end
|
|
530
533
|
|
|
531
|
-
def check(client)
|
|
532
|
-
end
|
|
534
|
+
def check(client); end
|
|
533
535
|
|
|
534
536
|
class Sentinel < Connector
|
|
535
537
|
def initialize(options)
|
|
@@ -538,7 +540,7 @@ class Redis
|
|
|
538
540
|
@options[:db] = DEFAULTS.fetch(:db)
|
|
539
541
|
|
|
540
542
|
@sentinels = @options.delete(:sentinels).dup
|
|
541
|
-
@role = @options
|
|
543
|
+
@role = (@options[:role] || "master").to_s
|
|
542
544
|
@master = @options[:host]
|
|
543
545
|
end
|
|
544
546
|
|
|
@@ -561,13 +563,13 @@ class Redis
|
|
|
561
563
|
|
|
562
564
|
def resolve
|
|
563
565
|
result = case @role
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
566
|
+
when "master"
|
|
567
|
+
resolve_master
|
|
568
|
+
when "slave"
|
|
569
|
+
resolve_slave
|
|
570
|
+
else
|
|
571
|
+
raise ArgumentError, "Unknown instance role #{@role}"
|
|
572
|
+
end
|
|
571
573
|
|
|
572
574
|
result || (raise ConnectionError, "Unable to fetch #{@role} via Sentinel.")
|
|
573
575
|
end
|
|
@@ -575,11 +577,11 @@ class Redis
|
|
|
575
577
|
def sentinel_detect
|
|
576
578
|
@sentinels.each do |sentinel|
|
|
577
579
|
client = Client.new(@options.merge({
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
580
|
+
host: sentinel[:host] || sentinel["host"],
|
|
581
|
+
port: sentinel[:port] || sentinel["port"],
|
|
582
|
+
password: sentinel[:password] || sentinel["password"],
|
|
583
|
+
reconnect_attempts: 0
|
|
584
|
+
}))
|
|
583
585
|
|
|
584
586
|
begin
|
|
585
587
|
if result = yield(client)
|
|
@@ -601,7 +603,7 @@ class Redis
|
|
|
601
603
|
def resolve_master
|
|
602
604
|
sentinel_detect do |client|
|
|
603
605
|
if reply = client.call(["sentinel", "get-master-addr-by-name", @master])
|
|
604
|
-
{:
|
|
606
|
+
{ host: reply[0], port: reply[1] }
|
|
605
607
|
end
|
|
606
608
|
end
|
|
607
609
|
end
|
|
@@ -619,7 +621,7 @@ class Redis
|
|
|
619
621
|
slave = slaves.sample
|
|
620
622
|
{
|
|
621
623
|
host: slave.fetch('ip'),
|
|
622
|
-
port: slave.fetch('port')
|
|
624
|
+
port: slave.fetch('port')
|
|
623
625
|
}
|
|
624
626
|
end
|
|
625
627
|
end
|
data/lib/redis/cluster.rb
CHANGED
|
@@ -80,6 +80,7 @@ class Redis
|
|
|
80
80
|
def call_pipeline(pipeline)
|
|
81
81
|
node_keys, command_keys = extract_keys_in_pipeline(pipeline)
|
|
82
82
|
raise CrossSlotPipeliningError, command_keys if node_keys.size > 1
|
|
83
|
+
|
|
83
84
|
node = find_node(node_keys.first)
|
|
84
85
|
try_send(node, :call_pipeline, pipeline)
|
|
85
86
|
end
|
|
@@ -112,12 +113,11 @@ class Redis
|
|
|
112
113
|
node = Node.new(option.per_node_key)
|
|
113
114
|
available_slots = SlotLoader.load(node)
|
|
114
115
|
node_flags = NodeLoader.load_flags(node)
|
|
115
|
-
|
|
116
|
-
option.update_node(available_node_urls)
|
|
116
|
+
option.update_node(available_slots.keys.map { |k| NodeKey.optionize(k) })
|
|
117
117
|
[Node.new(option.per_node_key, node_flags, option.use_replica?),
|
|
118
118
|
Slot.new(available_slots, node_flags, option.use_replica?)]
|
|
119
119
|
ensure
|
|
120
|
-
node
|
|
120
|
+
node&.each(&:disconnect)
|
|
121
121
|
end
|
|
122
122
|
|
|
123
123
|
def fetch_command_details(nodes)
|
|
@@ -216,9 +216,14 @@ class Redis
|
|
|
216
216
|
node.public_send(method_name, *args, &block)
|
|
217
217
|
rescue CommandError => err
|
|
218
218
|
if err.message.start_with?('MOVED')
|
|
219
|
-
|
|
219
|
+
raise if retry_count <= 0
|
|
220
|
+
|
|
221
|
+
node = assign_redirection_node(err.message)
|
|
222
|
+
retry_count -= 1
|
|
223
|
+
retry
|
|
220
224
|
elsif err.message.start_with?('ASK')
|
|
221
225
|
raise if retry_count <= 0
|
|
226
|
+
|
|
222
227
|
node = assign_asking_node(err.message)
|
|
223
228
|
node.call(%i[asking])
|
|
224
229
|
retry_count -= 1
|
|
@@ -264,6 +269,7 @@ class Redis
|
|
|
264
269
|
|
|
265
270
|
def find_node(node_key)
|
|
266
271
|
return @node.sample if node_key.nil?
|
|
272
|
+
|
|
267
273
|
@node.find_by(node_key)
|
|
268
274
|
rescue Node::ReloadNeeded
|
|
269
275
|
update_cluster_info!(node_key)
|
data/lib/redis/cluster/node.rb
CHANGED
|
@@ -39,6 +39,7 @@ class Redis
|
|
|
39
39
|
def call_master(command, &block)
|
|
40
40
|
try_map do |node_key, client|
|
|
41
41
|
next if slave?(node_key)
|
|
42
|
+
|
|
42
43
|
client.call(command, &block)
|
|
43
44
|
end.values
|
|
44
45
|
end
|
|
@@ -48,6 +49,7 @@ class Redis
|
|
|
48
49
|
|
|
49
50
|
try_map do |node_key, client|
|
|
50
51
|
next if master?(node_key)
|
|
52
|
+
|
|
51
53
|
client.call(command, &block)
|
|
52
54
|
end.values
|
|
53
55
|
end
|
|
@@ -97,6 +99,7 @@ class Redis
|
|
|
97
99
|
end
|
|
98
100
|
|
|
99
101
|
return results if errors.empty?
|
|
102
|
+
|
|
100
103
|
raise CommandErrorCollection, errors
|
|
101
104
|
end
|
|
102
105
|
end
|
|
@@ -6,17 +6,13 @@ class Redis
|
|
|
6
6
|
# It is different from node id.
|
|
7
7
|
# Node id is internal identifying code in Redis Cluster.
|
|
8
8
|
module NodeKey
|
|
9
|
-
DEFAULT_SCHEME = 'redis'
|
|
10
|
-
SECURE_SCHEME = 'rediss'
|
|
11
9
|
DELIMITER = ':'
|
|
12
10
|
|
|
13
11
|
module_function
|
|
14
12
|
|
|
15
|
-
def
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
.map { |k| k.split(DELIMITER) }
|
|
19
|
-
.map { |k| URI::Generic.build(scheme: scheme, host: k[0], port: k[1].to_i).to_s }
|
|
13
|
+
def optionize(node_key)
|
|
14
|
+
host, port = split(node_key)
|
|
15
|
+
{ host: host, port: port }
|
|
20
16
|
end
|
|
21
17
|
|
|
22
18
|
def split(node_key)
|
data/lib/redis/cluster/option.rb
CHANGED
|
@@ -15,36 +15,35 @@ class Redis
|
|
|
15
15
|
def initialize(options)
|
|
16
16
|
options = options.dup
|
|
17
17
|
node_addrs = options.delete(:cluster)
|
|
18
|
-
@
|
|
18
|
+
@node_opts = build_node_options(node_addrs)
|
|
19
19
|
@replica = options.delete(:replica) == true
|
|
20
|
+
add_common_node_option_if_needed(options, @node_opts, :scheme)
|
|
21
|
+
add_common_node_option_if_needed(options, @node_opts, :password)
|
|
20
22
|
@options = options
|
|
21
23
|
end
|
|
22
24
|
|
|
23
25
|
def per_node_key
|
|
24
|
-
@
|
|
26
|
+
@node_opts.map { |opt| [NodeKey.build_from_host_port(opt[:host], opt[:port]), @options.merge(opt)] }
|
|
25
27
|
.to_h
|
|
26
28
|
end
|
|
27
29
|
|
|
28
|
-
def secure?
|
|
29
|
-
@node_uris.any? { |uri| uri.scheme == SECURE_SCHEME } || @options[:ssl_params] || false
|
|
30
|
-
end
|
|
31
|
-
|
|
32
30
|
def use_replica?
|
|
33
31
|
@replica
|
|
34
32
|
end
|
|
35
33
|
|
|
36
34
|
def update_node(addrs)
|
|
37
|
-
@
|
|
35
|
+
@node_opts = build_node_options(addrs)
|
|
38
36
|
end
|
|
39
37
|
|
|
40
38
|
def add_node(host, port)
|
|
41
|
-
@
|
|
39
|
+
@node_opts << { host: host, port: port }
|
|
42
40
|
end
|
|
43
41
|
|
|
44
42
|
private
|
|
45
43
|
|
|
46
|
-
def
|
|
44
|
+
def build_node_options(addrs)
|
|
47
45
|
raise InvalidClientOptionError, 'Redis option of `cluster` must be an Array' unless addrs.is_a?(Array)
|
|
46
|
+
|
|
48
47
|
addrs.map { |addr| parse_node_addr(addr) }
|
|
49
48
|
end
|
|
50
49
|
|
|
@@ -53,7 +52,7 @@ class Redis
|
|
|
53
52
|
when String
|
|
54
53
|
parse_node_url(addr)
|
|
55
54
|
when Hash
|
|
56
|
-
|
|
55
|
+
parse_node_option(addr)
|
|
57
56
|
else
|
|
58
57
|
raise InvalidClientOptionError, 'Redis option of `cluster` must includes String or Hash'
|
|
59
58
|
end
|
|
@@ -62,15 +61,29 @@ class Redis
|
|
|
62
61
|
def parse_node_url(addr)
|
|
63
62
|
uri = URI(addr)
|
|
64
63
|
raise InvalidClientOptionError, "Invalid uri scheme #{addr}" unless VALID_SCHEMES.include?(uri.scheme)
|
|
65
|
-
|
|
64
|
+
|
|
65
|
+
db = uri.path.split('/')[1]&.to_i
|
|
66
|
+
{ scheme: uri.scheme, password: uri.password, host: uri.host, port: uri.port, db: db }.reject { |_, v| v.nil? }
|
|
66
67
|
rescue URI::InvalidURIError => err
|
|
67
68
|
raise InvalidClientOptionError, err.message
|
|
68
69
|
end
|
|
69
70
|
|
|
70
|
-
def
|
|
71
|
+
def parse_node_option(addr)
|
|
71
72
|
addr = addr.map { |k, v| [k.to_sym, v] }.to_h
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
if addr.values_at(:host, :port).any?(&:nil?)
|
|
74
|
+
raise InvalidClientOptionError, 'Redis option of `cluster` must includes `:host` and `:port` keys'
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
addr
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Redis cluster node returns only host and port information.
|
|
81
|
+
# So we should complement additional information such as:
|
|
82
|
+
# scheme, password and so on.
|
|
83
|
+
def add_common_node_option_if_needed(options, node_opts, key)
|
|
84
|
+
return options if options[key].nil? && node_opts.first[key].nil?
|
|
85
|
+
|
|
86
|
+
options[key] ||= node_opts.first[key]
|
|
74
87
|
end
|
|
75
88
|
end
|
|
76
89
|
end
|