etcdv3 0.10.1 → 0.11.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: eea828b2c14cfeab2c0f9be8a84ea937131a5dc2
4
- data.tar.gz: a2e02f9d0155f35185994c07cd7abc5a6d23d5df
2
+ SHA256:
3
+ metadata.gz: 03fa84076fc6d31e895bc80d55abb4501398a0be305cefa1f492b5e217e52277
4
+ data.tar.gz: 020aa912711d848ceb60a842a2d04bc1d5d3aacbf2714a380782cefd37c3b9c4
5
5
  SHA512:
6
- metadata.gz: bff96b426657924163a26df9ca1322b5600b33607453d4648affffe2e66bf7c7ef20e5fe2d0443435de71b74d9ceec637f968196315837426294d26dd1b84e78
7
- data.tar.gz: 1a10f332e633b1010aba3a355b05ef82e2546e37413222de7553bcdb2461d0d2b368573af75a33e927349c4f461c81564d80adbe149ea7f6e5ac8b478158cc1e
6
+ metadata.gz: 7541f20326b9577fa7b595e371a0e94713b0b0d83ced62eaae120b5fe77c853625020425258ed941f49b345d40025f7c9d8398ba7bbb136e666f13d1b64e181b
7
+ data.tar.gz: 3e1226dcf9a6f7a3fcfce148148fcdcabe3f07bf92f24335a7a624b8af3e3b07176162a8f52f606b80dc9df864574ce8e430b95c456a54e79444f6466d3b37b4
data/.gitignore CHANGED
@@ -1,9 +1,10 @@
1
- Gemfile.lock
2
- Guardfile
3
- *.sh
4
1
  *.gem
5
- /tmp/
2
+ *.sh
3
+ *.swp
4
+ /.bundle
6
5
  /.idea
7
6
  /coverage/*
8
7
  /doc/
9
- *.swp
8
+ /tmp/
9
+ Gemfile.lock
10
+ Guardfile
data/.travis.yml CHANGED
@@ -1,5 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
+ - 3.0.2
3
4
  - 2.5.3
4
5
  - 2.4.5
5
6
  - 2.3.8
data/README.md CHANGED
@@ -26,6 +26,9 @@ conn = Etcdv3.new(endpoints: 'https://hostname:port')
26
26
  # Secure connection with Auth
27
27
  conn = Etcdv3.new(endpoints: 'https://hostname:port', user: 'root', password: 'mysecretpassword')
28
28
 
29
+ # Scope CRUD operations to a specific keyspace.
30
+ conn = Etcdv3.new(endpoints: 'https://hostname:port', namespace: "/target_keyspace/")
31
+
29
32
  # Secure connection specifying custom certificates
30
33
  # Coming soon...
31
34
 
@@ -34,6 +37,43 @@ conn = Etcdv3.new(endpoints: 'https://hostname:port', user: 'root', password: 'm
34
37
 
35
38
  In the event of a failure, the client will work to restore connectivity by cycling through the specified endpoints until a connection can be established. With that being said, it is encouraged to specify multiple endpoints when available.
36
39
 
40
+ However, sometimes this is not what you want. If you need more control over
41
+ failures, you can suppress this mechanism by using
42
+
43
+ ```ruby
44
+ conn = Etcdv3.new(endpoints: 'https://hostname:port', allow_reconnect: false)
45
+ ```
46
+
47
+ This will still rotate the endpoints, but it will raise an exception so you can
48
+ handle the failure yourself. On next call to the new endpoint (since they were
49
+ rotated) is tried. One thing you need to keep in mind if auth is enabled, you
50
+ need to take care of `GRPC::Unauthenticated` exception and manually re-authenticate
51
+ when token expires. To reiterate, you are responsible for handling the errors, so
52
+ some understanding of how this gem and etcd works is recommended.
53
+
54
+ ## Namespace support
55
+
56
+ Namespacing is a convenience feature used to scope CRUD based operations to a specific keyspace.
57
+
58
+ ```ruby
59
+ # Establish connection
60
+ conn = Etcdv3.new(endpoints: 'https://hostname:port', namespace: '/service-a/')
61
+
62
+ # Write key to /service-a/test_key
63
+ conn.put("test_key", "value").
64
+
65
+ # Get the key we just wrote.
66
+ conn.get("test_key")
67
+
68
+ # Retrieve everything under the namespace.
69
+ conn.get("", "\0")
70
+
71
+ # Delete everything under the namespace.
72
+ conn.del("", "\0")
73
+ ```
74
+
75
+ _Note: Namespaces are stripped from responses._
76
+
37
77
 
38
78
  ## Adding, Fetching and Deleting Keys
39
79
  ```ruby
@@ -1,6 +1,12 @@
1
1
  class Etcdv3
2
2
  class Connection
3
3
 
4
+ NAMESPACE_HANDLERS = {
5
+ kv: Etcdv3::Namespace::KV,
6
+ watch: Etcdv3::Namespace::Watch,
7
+ lock: Etcdv3::Namespace::Lock,
8
+ }
9
+
4
10
  HANDLERS = {
5
11
  auth: Etcdv3::Auth,
6
12
  kv: Etcdv3::KV,
@@ -10,18 +16,25 @@ class Etcdv3
10
16
  lock: Etcdv3::Lock,
11
17
  }
12
18
 
13
- attr_reader :endpoint, :hostname, :handlers, :credentials
19
+ attr_reader :endpoint, :hostname, :handlers, :credentials, :namespace
14
20
 
15
- def initialize(url, timeout, metadata={})
21
+ def initialize(url, timeout, namespace, metadata={})
16
22
  @endpoint = URI(url)
17
23
  @hostname = "#{@endpoint.hostname}:#{@endpoint.port}"
24
+ @namespace = namespace
18
25
  @credentials = resolve_credentials
19
26
  @timeout = timeout
20
27
  @handlers = handler_map(metadata)
21
28
  end
22
29
 
23
30
  def call(stub, method, method_args=[])
24
- @handlers.fetch(stub).send(method, *method_args)
31
+ *method_args, method_kwargs = method_args if method_args.last.class == Hash
32
+
33
+ if method_kwargs.nil?
34
+ @handlers.fetch(stub).send(method, *method_args)
35
+ else
36
+ @handlers.fetch(stub).send(method, *method_args, **method_kwargs)
37
+ end
25
38
  end
26
39
 
27
40
  def refresh_metadata(metadata)
@@ -31,11 +44,19 @@ class Etcdv3
31
44
  private
32
45
 
33
46
  def handler_map(metadata={})
34
- Hash[
47
+ handlers = Hash[
35
48
  HANDLERS.map do |key, klass|
36
- [key, klass.new("#{@hostname}", @credentials, @timeout, metadata)]
49
+ [key, klass.new(@hostname, @credentials, @timeout, metadata)]
37
50
  end
38
51
  ]
52
+ # Override any handlers that are namespace compatable.
53
+ if @namespace
54
+ NAMESPACE_HANDLERS.each do |key, klass|
55
+ handlers[key] = klass.new(@hostname, @credentials, @timeout, @namespace, metadata)
56
+ end
57
+ end
58
+
59
+ handlers
39
60
  end
40
61
 
41
62
  def resolve_credentials
@@ -3,31 +3,46 @@ class Etcdv3
3
3
 
4
4
  attr_accessor :connection, :endpoints, :user, :password, :token, :timeout
5
5
 
6
- def initialize(timeout, *endpoints)
6
+ def initialize(timeout, *endpoints, namespace, allow_reconnect)
7
7
  @user, @password, @token = nil, nil, nil
8
8
  @timeout = timeout
9
- @endpoints = endpoints.map{|endpoint| Etcdv3::Connection.new(endpoint, @timeout) }
9
+ @namespace = namespace
10
+ @endpoints = endpoints.map{|endpoint| Etcdv3::Connection.new(endpoint, @timeout, @namespace) }
11
+ @allow_reconnect = allow_reconnect
10
12
  @connection = @endpoints.first
11
13
  end
12
14
 
15
+ private def retry_or_raise(*args)
16
+ if @allow_reconnect
17
+ *args, kwargs = args if args.last.class == Hash
18
+
19
+ if kwargs.nil?
20
+ handle(*args)
21
+ else
22
+ handle(*args, **kwargs)
23
+ end
24
+ else
25
+ raise
26
+ end
27
+ end
28
+
13
29
  def handle(stub, method, method_args=[], retries: 1)
14
30
  @connection.call(stub, method, method_args)
15
31
 
16
- rescue GRPC::Unavailable, GRPC::Core::CallError => exception
32
+ rescue GRPC::Unavailable, GRPC::Core::CallError
17
33
  $stderr.puts("Failed to connect to endpoint '#{@connection.hostname}'")
18
34
  if @endpoints.size > 1
19
35
  rotate_connection_endpoint
20
- $stderr.puts("Failover event triggered. Failing over to '#{@connection.hostname}'")
21
- return handle(stub, method, method_args)
36
+ return retry_or_raise(stub, method, method_args)
22
37
  else
23
- return handle(stub, method, method_args)
38
+ return retry_or_raise(stub, method, method_args)
24
39
  end
25
40
  rescue GRPC::Unauthenticated => exception
26
41
  # Regenerate token in the event it expires.
27
42
  if exception.details == 'etcdserver: invalid auth token'
28
43
  if retries > 0
29
44
  authenticate(@user, @password)
30
- return handle(stub, method, method_args, retries: retries - 1)
45
+ return retry_or_raise(stub, method, method_args, retries: retries - 1)
31
46
  end
32
47
  end
33
48
  raise exception
data/lib/etcdv3/lock.rb CHANGED
@@ -10,12 +10,12 @@ class Etcdv3
10
10
 
11
11
  def lock(name, lease_id, timeout: nil)
12
12
  request = V3lockpb::LockRequest.new(name: name, lease: lease_id)
13
- @stub.lock(request, deadline: deadline(timeout))
13
+ @stub.lock(request, metadata: @metadata, deadline: deadline(timeout))
14
14
  end
15
15
 
16
16
  def unlock(key, timeout: nil)
17
17
  request = V3lockpb::UnlockRequest.new(key: key)
18
- @stub.unlock(request, deadline: deadline(timeout))
18
+ @stub.unlock(request, metadata: @metadata, deadline: deadline(timeout))
19
19
  end
20
20
 
21
21
  private
@@ -0,0 +1,54 @@
1
+ class Etcdv3::Namespace::KV
2
+ module Requests
3
+
4
+ SORT_TARGET = {
5
+ key: 0,
6
+ version: 1,
7
+ create: 2,
8
+ mod: 3,
9
+ value: 4
10
+ }
11
+
12
+ SORT_ORDER = {
13
+ none: 0,
14
+ ascend: 1,
15
+ descend: 2
16
+ }
17
+
18
+ def get_request(key, opts)
19
+ key = prepend_prefix(@namespace, key)
20
+ # In order to enforce the scope of the specified namespace, we are going to
21
+ # intercept the zero-byte reference and re-target everything under the given namespace.
22
+ if opts[:range_end] =~ /\x00/
23
+ opts[:range_end] = (@namespace[0..-2] + (@namespace[-1].ord + 1).chr)
24
+ else
25
+ opts[:range_end] = prepend_prefix(@namespace, opts[:range_end]) if opts[:range_end]
26
+ end
27
+ opts[:sort_order] = SORT_ORDER[opts[:sort_order]] \
28
+ if opts[:sort_order]
29
+ opts[:sort_target] = SORT_TARGET[opts[:sort_target]] \
30
+ if opts[:sort_target]
31
+ opts[:key] = key
32
+ Etcdserverpb::RangeRequest.new(opts)
33
+ end
34
+
35
+ def del_request(key, range_end=nil)
36
+ key = prepend_prefix(@namespace, key)
37
+ # In order to enforce the scope of the specified namespace, we are going to
38
+ # intercept the zero-byte reference and re-target everything under the given namespace.
39
+ if range_end =~ /\x00/
40
+ range_end = (@namespace[0..-2] + (@namespace[-1].ord + 1).chr)
41
+ else
42
+ range_end = prepend_prefix(@namespace, range_end) if range_end
43
+ end
44
+ Etcdserverpb::DeleteRangeRequest.new(key: key, range_end: range_end)
45
+ end
46
+
47
+ def put_request(key, value, lease=nil)
48
+ key = prepend_prefix(@namespace, key)
49
+ kv = Etcdserverpb::PutRequest.new(key: key, value: value)
50
+ kv.lease = lease if lease
51
+ kv
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,91 @@
1
+ class Etcdv3::Namespace::KV
2
+ class Transaction
3
+ include Etcdv3::Namespace::Utilities
4
+ include Etcdv3::Namespace::KV::Requests
5
+
6
+ # Available comparison identifiers.
7
+ COMPARISON_IDENTIFIERS = {
8
+ equal: 0,
9
+ greater: 1,
10
+ less: 2,
11
+ not_equal: 3
12
+ }
13
+
14
+ # Available targets to compare with.
15
+ TARGETS = {
16
+ version: 0,
17
+ create_revision: 1,
18
+ mod_revision: 2,
19
+ value: 3
20
+ }
21
+
22
+ attr_writer :compare, :success, :failure
23
+
24
+ def initialize(namespace)
25
+ @namespace = namespace
26
+ end
27
+
28
+ def compare
29
+ @compare ||= []
30
+ end
31
+
32
+ def success
33
+ @success ||= []
34
+ end
35
+
36
+ def failure
37
+ @failure ||=[]
38
+ end
39
+
40
+ # Request Operations
41
+
42
+ # txn.put('my', 'key', lease_id: 1)
43
+ def put(key, value, lease=nil)
44
+ put_request(key, value, lease)
45
+ end
46
+
47
+ # txn.get('key')
48
+ def get(key, opts={})
49
+ get_request(key, opts)
50
+ end
51
+
52
+ # txn.del('key')
53
+ def del(key, range_end='')
54
+ del_request(key, range_end)
55
+ end
56
+
57
+ ### Compare Operations
58
+
59
+ # txn.version('names', :greater, 0 )
60
+ def version(key, compare_type, value)
61
+ generate_compare(:version, key, compare_type, value)
62
+ end
63
+
64
+ # txn.value('names', :equal, 'wowza' )
65
+ def value(key, compare_type, value)
66
+ generate_compare(:value, key, compare_type, value)
67
+ end
68
+
69
+ # txn.mod_revision('names', :not_equal, 0)
70
+ def mod_revision(key, compare_type, value)
71
+ generate_compare(:mod_revision, key, compare_type, value)
72
+ end
73
+
74
+ # txn.create_revision('names', :less, 10)
75
+ def create_revision(key, compare_type, value)
76
+ generate_compare(:create_revision, key, compare_type, value)
77
+ end
78
+
79
+ private
80
+
81
+ def generate_compare(target_union, key, compare_type, value)
82
+ key = prepend_prefix(@namespace, key)
83
+ Etcdserverpb::Compare.new(
84
+ key: key,
85
+ result: COMPARISON_IDENTIFIERS[compare_type],
86
+ target: TARGETS[target_union],
87
+ target_union => value
88
+ )
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,62 @@
1
+ class Etcdv3::Namespace
2
+ class KV
3
+ include Etcdv3::Namespace::KV::Requests
4
+ include Etcdv3::Namespace::Utilities
5
+ include GRPC::Core::TimeConsts
6
+
7
+ def initialize(hostname, credentials, timeout, namespace, metadata={})
8
+ @stub = Etcdserverpb::KV::Stub.new(hostname, credentials)
9
+ @timeout = timeout
10
+ @namespace = namespace
11
+ @metadata = metadata
12
+ end
13
+
14
+ def get(key, opts={})
15
+ timeout = opts.delete(:timeout)
16
+ resp = @stub.range(get_request(key, opts), metadata: @metadata, deadline: deadline(timeout))
17
+ strip_prefix(@namespace, resp)
18
+ end
19
+
20
+ def del(key, range_end: '', timeout: nil)
21
+ resp = @stub.delete_range(del_request(key, range_end), metadata: @metadata, deadline: deadline(timeout))
22
+ strip_prefix(@namespace, resp)
23
+ end
24
+
25
+ def put(key, value, lease: nil, timeout: nil)
26
+ resp = @stub.put(put_request(key, value, lease), metadata: @metadata, deadline: deadline(timeout))
27
+ strip_prefix(@namespace, resp)
28
+ end
29
+
30
+ def transaction(block, timeout: nil)
31
+ txn = Etcdv3::Namespace::KV::Transaction.new(@namespace)
32
+ block.call(txn)
33
+ request = Etcdserverpb::TxnRequest.new(
34
+ compare: txn.compare,
35
+ success: generate_request_ops(txn.success),
36
+ failure: generate_request_ops(txn.failure),
37
+ )
38
+ resp = @stub.txn(request, metadata: @metadata, deadline: deadline(timeout))
39
+ strip_prefix(@namespace, resp)
40
+ end
41
+
42
+ private
43
+
44
+ def deadline(timeout)
45
+ from_relative_time(timeout || @timeout)
46
+ end
47
+
48
+ def generate_request_ops(requests)
49
+ requests.map do |request|
50
+ if request.is_a?(Etcdserverpb::RangeRequest)
51
+ Etcdserverpb::RequestOp.new(request_range: request)
52
+ elsif request.is_a?(Etcdserverpb::PutRequest)
53
+ Etcdserverpb::RequestOp.new(request_put: request)
54
+ elsif request.is_a?(Etcdserverpb::DeleteRangeRequest)
55
+ Etcdserverpb::RequestOp.new(request_delete_range: request)
56
+ else
57
+ raise "Invalid command. Not sure how you got here!"
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,32 @@
1
+ class Etcdv3::Namespace
2
+ class Lock
3
+ include GRPC::Core::TimeConsts
4
+ include Etcdv3::Namespace::Utilities
5
+
6
+ def initialize(hostname, credentials, timeout, namespace, metadata = {})
7
+ @stub = V3lockpb::Lock::Stub.new(hostname, credentials)
8
+ @timeout = timeout
9
+ @namespace = namespace
10
+ @metadata = metadata
11
+ end
12
+
13
+ def lock(name, lease_id, timeout: nil)
14
+ name = prepend_prefix(@namespace, name)
15
+ request = V3lockpb::LockRequest.new(name: name, lease: lease_id)
16
+ resp = @stub.lock(request, metadata: @metadata, deadline: deadline(timeout))
17
+ strip_prefix_from_lock(@namespace, resp)
18
+ end
19
+
20
+ def unlock(key, timeout: nil)
21
+ key = prepend_prefix(@namespace, key)
22
+ request = V3lockpb::UnlockRequest.new(key: key)
23
+ @stub.unlock(request, metadata: @metadata, deadline: deadline(timeout))
24
+ end
25
+
26
+ private
27
+
28
+ def deadline(timeout)
29
+ from_relative_time(timeout || @timeout)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,44 @@
1
+ class Etcdv3::Namespace
2
+ module Utilities
3
+
4
+ def prepend_prefix(prefix, key)
5
+ key = key.dup if key.frozen?
6
+ key.prepend(prefix)
7
+ end
8
+
9
+ def strip_prefix(prefix, resp)
10
+ [:kvs, :prev_kvs].each do |field|
11
+ if resp.respond_to?(field)
12
+ resp.send(field).each do |kv|
13
+ kv.key = delete_prefix(prefix, kv.key)
14
+ end
15
+ end
16
+ end
17
+ resp
18
+ end
19
+
20
+ def strip_prefix_from_lock(prefix, resp)
21
+ if resp.key
22
+ resp.key = delete_prefix(prefix, resp.key)
23
+ end
24
+ resp
25
+ end
26
+
27
+ def strip_prefix_from_events(prefix, events)
28
+ events.each do |event|
29
+ if event.kv
30
+ event.kv.key = delete_prefix(prefix, event.kv.key)
31
+ end
32
+ if event.prev_kv
33
+ event.prev_kv.key = delete_prefix(prefix, event.prev_kv.key)
34
+ end
35
+ event
36
+ end
37
+ end
38
+
39
+ def delete_prefix(prefix, str)
40
+ str.sub(/\A#{prefix}/, '')
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,38 @@
1
+ class Etcdv3::Namespace
2
+ class Watch
3
+ include GRPC::Core::TimeConsts
4
+ include Etcdv3::Namespace::Utilities
5
+
6
+ def initialize(hostname, credentials, timeout, namespace, metadata = {})
7
+ @stub = Etcdserverpb::Watch::Stub.new(hostname, credentials)
8
+ @timeout = timeout
9
+ @namespace = namespace
10
+ @metadata = metadata
11
+ end
12
+
13
+ def watch(key, range_end, start_revision, block, timeout: nil)
14
+ key = prepend_prefix(@namespace, key)
15
+ range_end = prepend_prefix(@namespace, range_end) if range_end
16
+ create_req = Etcdserverpb::WatchCreateRequest.new(key: key)
17
+ create_req.range_end = range_end if range_end
18
+ create_req.start_revision = start_revision if start_revision
19
+ watch_req = Etcdserverpb::WatchRequest.new(create_request: create_req)
20
+ events = nil
21
+ @stub.watch([watch_req], metadata: @metadata, deadline: deadline(timeout)).each do |resp|
22
+ next if resp.events.empty?
23
+ if block
24
+ block.call(strip_prefix_from_events(@namespace, resp.events))
25
+ else
26
+ events = strip_prefix_from_events(@namespace, resp.events)
27
+ break
28
+ end
29
+ end
30
+ events
31
+ end
32
+
33
+ def deadline(timeout)
34
+ from_relative_time(timeout || @timeout)
35
+ end
36
+
37
+ end
38
+ end
@@ -1,3 +1,3 @@
1
1
  class Etcdv3
2
- VERSION = '0.10.1'.freeze
2
+ VERSION = '0.11.5'.freeze
3
3
  end
data/lib/etcdv3.rb CHANGED
@@ -7,6 +7,14 @@ require 'etcdv3/auth'
7
7
  require 'etcdv3/kv/requests'
8
8
  require 'etcdv3/kv/transaction'
9
9
  require 'etcdv3/kv'
10
+
11
+ require 'etcdv3/namespace/utilities'
12
+ require 'etcdv3/namespace/kv/requests'
13
+ require 'etcdv3/namespace/kv/transaction'
14
+ require 'etcdv3/namespace/lock'
15
+ require 'etcdv3/namespace/kv'
16
+ require 'etcdv3/namespace/watch'
17
+
10
18
  require 'etcdv3/maintenance'
11
19
  require 'etcdv3/lease'
12
20
  require 'etcdv3/watch'
@@ -21,10 +29,16 @@ class Etcdv3
21
29
  attr_reader :conn, :credentials, :options
22
30
  DEFAULT_TIMEOUT = 120
23
31
 
24
- def initialize(options = {})
32
+ def initialize(**options)
25
33
  @options = options
26
34
  @timeout = options[:command_timeout] || DEFAULT_TIMEOUT
27
- @conn = ConnectionWrapper.new(@timeout, *sanitized_endpoints)
35
+ @namespace = options[:namespace]
36
+ @conn = ConnectionWrapper.new(
37
+ @timeout,
38
+ *sanitized_endpoints,
39
+ @namespace,
40
+ @options.fetch(:allow_reconnect, true),
41
+ )
28
42
  warn "WARNING: `url` is deprecated. Please use `endpoints` instead." if @options.key?(:url)
29
43
  authenticate(@options[:user], @options[:password]) if @options.key?(:user)
30
44
  end
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Etcdv3::Connection do
4
4
 
5
5
  describe '#initialize - without metadata' do
6
- subject { Etcdv3::Connection.new('http://localhost:2379', 10) }
6
+ subject { Etcdv3::Connection.new('http://localhost:2379', 10, nil) }
7
7
 
8
8
  it { is_expected.to have_attributes(endpoint: URI('http://localhost:2379')) }
9
9
  it { is_expected.to have_attributes(credentials: :this_channel_is_insecure) }
@@ -22,7 +22,7 @@ describe Etcdv3::Connection do
22
22
  end
23
23
 
24
24
  describe '#initialize - with metadata' do
25
- subject { Etcdv3::Connection.new('http://localhost:2379', 10, token: 'token123') }
25
+ subject { Etcdv3::Connection.new('http://localhost:2379', 10, nil, token: 'token123') }
26
26
 
27
27
  [:kv, :maintenance, :lease, :watch, :auth].each do |handler|
28
28
  let(:handler_stub) { subject.handlers[handler].instance_variable_get(:@stub) }
@@ -37,7 +37,7 @@ describe Etcdv3::Connection do
37
37
  end
38
38
 
39
39
  describe '#refresh_metadata' do
40
- subject { Etcdv3::Connection.new('http://localhost:2379', token: 'token123') }
40
+ subject { Etcdv3::Connection.new('http://localhost:2379', nil, token: 'token123') }
41
41
  before { subject.refresh_metadata(token: 'newtoken') }
42
42
  [:kv, :maintenance, :lease, :watch, :auth].each do |handler|
43
43
  let(:handler_metadata) { subject.handlers[handler].instance_variable_get(:@metadata) }