etcdv3 0.9.0 → 0.11.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 2c90b3d66d75efdf3c6c608cc1ccaf9673731070
4
- data.tar.gz: 7698e02e69968d2e6348dd01a7e6813d27d5d50e
2
+ SHA256:
3
+ metadata.gz: 18d76403d57c3e56b75e603931345900a4583b2e94d773b33ca04aa1547e304d
4
+ data.tar.gz: f1d1f9336f62f18b32fb9d3f055e0043bddb6076022b922b097c0104a7467c8b
5
5
  SHA512:
6
- metadata.gz: 634ec36c4eccf0b93dd69b51a25e6515c06a26a1677264fb02d035c793cf905feb739ad9bde6e40ae30617416aebe7b85706bb4a1060cbb3d26d8b68e42a702f
7
- data.tar.gz: 7043f69c71837cefc8168ff3d5c0225afc9109c4a1c5e9aab1bec7b5fcf56999c9866abac23c3a7b13e9de5422b1983ffae57448f85dafd65f5d369837d7d286
6
+ metadata.gz: 9633a47e982b7777ccda7e5472a663a661e4cf6368689cfbbd8d06a6ca862be47e7c3a75d075919b645611879513d4c88c7f59686b2a0eac15cc160e3ea7520f
7
+ data.tar.gz: 5a330de810e02ebdec73c279e33b7c2ece0c2aa7c4c877da89a2b285976ce68578ca20b3c51206d9a14c9fccbf18ef6310254010053eaefa59b06b898e1c195a
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,15 +1,14 @@
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
6
7
 
7
8
  env:
8
- - ETCD_VERSION=v3.1.20
9
- - ETCD_VERSION=v3.2.25
10
- # v3.3.10 is not working for whatever reason (at least in travis, spec passes
11
- # locally for me)
12
- # - ETCD_VERSION=v3.3.10
9
+ - ETCD_VERSION_TO_TEST=v3.1.20
10
+ - ETCD_VERSION_TO_TEST=v3.2.25
11
+ - ETCD_VERSION_TO_TEST=v3.3.10
13
12
 
14
13
  install:
15
14
  - bundle install
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
@@ -149,12 +189,15 @@ end
149
189
 
150
190
  ## Watch
151
191
  ```ruby
152
- # Watch for changes on a specified key and return
153
- events = conn.watch('foo')
192
+ # Watch for changes on a specified key for at most 10 seconds and return
193
+ events = conn.watch('foo', timeout: 10)
154
194
 
155
195
  # Watch for changes on a specified key range and return
156
196
  events = conn.watch('foo', range_end: 'fop')
157
197
 
198
+ # Watch for changes since a given revision
199
+ events = conn.watch('foo', start_revision: 42)
200
+
158
201
  # Watches for changes continuously until killed.
159
202
  event_count = 0
160
203
  conn.watch('boom') do |events|
data/Rakefile CHANGED
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- ETCD_VERSION = ENV["ETCD_VERSION"] || "v3.2.0"
4
- ETCD_URL = "https://github.com/coreos/etcd/releases/download/#{ETCD_VERSION}/etcd-#{ETCD_VERSION}-linux-amd64.tar.gz"
3
+ ETCD_VERSION = ENV["ETCD_VERSION_TO_TEST"] || "v3.2.0"
4
+ ETCD_URL = "https://github.com/coreos/etcd/releases/download/" \
5
+ "#{ETCD_VERSION}/etcd-#{ETCD_VERSION}-linux-amd64.tar.gz"
5
6
 
6
7
  require "tmpdir"
7
8
 
@@ -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
@@ -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, 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, 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.9.0'.freeze
2
+ VERSION = '0.11.4'.freeze
3
3
  end
data/lib/etcdv3/watch.rb CHANGED
@@ -1,16 +1,20 @@
1
1
  class Etcdv3
2
2
  class Watch
3
+ include GRPC::Core::TimeConsts
3
4
 
4
- def initialize(hostname, credentials, _timeout, metadata = {})
5
+ def initialize(hostname, credentials, timeout, metadata = {})
5
6
  @stub = Etcdserverpb::Watch::Stub.new(hostname, credentials)
7
+ @timeout = timeout
6
8
  @metadata = metadata
7
9
  end
8
10
 
9
- def watch(key, range_end, block)
10
- create_req = Etcdserverpb::WatchCreateRequest.new(key: key, range_end: range_end)
11
+ def watch(key, range_end, start_revision, block, timeout: nil)
12
+ create_req = Etcdserverpb::WatchCreateRequest.new(key: key)
13
+ create_req.range_end = range_end if range_end
14
+ create_req.start_revision = start_revision if start_revision
11
15
  watch_req = Etcdserverpb::WatchRequest.new(create_request: create_req)
12
16
  events = nil
13
- @stub.watch([watch_req], metadata: @metadata).each do |resp|
17
+ @stub.watch([watch_req], metadata: @metadata, deadline: deadline(timeout)).each do |resp|
14
18
  next if resp.events.empty?
15
19
  if block
16
20
  block.call(resp.events)
@@ -21,5 +25,9 @@ class Etcdv3
21
25
  end
22
26
  events
23
27
  end
28
+
29
+ def deadline(timeout)
30
+ from_relative_time(timeout || @timeout)
31
+ end
24
32
  end
25
33
  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
@@ -219,8 +233,8 @@ class Etcdv3
219
233
  end
220
234
 
221
235
  # Watches for changes on a specified key range.
222
- def watch(key, range_end: '', &block)
223
- @conn.handle(:watch, 'watch', [key, range_end, block])
236
+ def watch(key, range_end: nil, start_revision: nil, timeout: nil, &block)
237
+ @conn.handle(:watch, 'watch', [key, range_end, start_revision, block, timeout: timeout])
224
238
  end
225
239
 
226
240
  def transaction(timeout: nil, &block)