etcdv3 0.1.3 → 0.1.4

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
2
  SHA1:
3
- metadata.gz: afad56a64c9b93a3438296356e13a9980625d193
4
- data.tar.gz: 6190434f69a1645f25f5336f3bebaf1efc8f3775
3
+ metadata.gz: 9275ec7e8408f87efd369493235bea9e7bd18a98
4
+ data.tar.gz: 9fcdd5d3199a60e6c0066758dc32e455e5125e8c
5
5
  SHA512:
6
- metadata.gz: 95d1672d8f0e0cd1843bf76d2998977c347711887ebacbae463d2d4aa86586b9c4ce663b9dd28e220c8a7cd87d55c94c7cb3381af98988c4ad232d2a733e9913
7
- data.tar.gz: 520c207ec53edc8a5ea840f138780c4b7a3e0f2a21180f135045dbb170e3bdaed9a2ebc2fb72b192e7de8295d67f83be9a5572cc9677196a4607b917c3a5f5e7
6
+ metadata.gz: f5755de875074e6268cd378568ba6633d141d71efeccffe197f59fa142c6fcaa553a4802d438b551f24808b524e07e68221d435f9cf20a434620850164bdf1e7
7
+ data.tar.gz: ee7cf3faf3e6694843a3c99ee553d3bbcf081c491fa463cf36223bc8e8eb6688c8fb2fe19a42c3d0737ed56d849504e9cea5215e65cc978380448502c4894a24
data/lib/etcdv3/auth.rb CHANGED
@@ -8,8 +8,8 @@ class Etcd
8
8
  'readwrite' => Authpb::Permission::Type::READWRITE
9
9
  }
10
10
 
11
- def initialize(hostname, port, credentials, metadata = {})
12
- @stub = Etcdserverpb::Auth::Stub.new("#{hostname}:#{port}", credentials)
11
+ def initialize(hostname, credentials, metadata = {})
12
+ @stub = Etcdserverpb::Auth::Stub.new(hostname, credentials)
13
13
  @metadata = metadata
14
14
  end
15
15
 
data/lib/etcdv3/kv.rb CHANGED
@@ -1,8 +1,8 @@
1
1
 
2
2
  class Etcd
3
3
  class KV
4
- def initialize(hostname, port, credentials, metadata={})
5
- @stub = Etcdserverpb::KV::Stub.new("#{hostname}:#{port}", credentials)
4
+ def initialize(hostname, credentials, metadata={})
5
+ @stub = Etcdserverpb::KV::Stub.new(hostname, credentials)
6
6
  @metadata = metadata
7
7
  end
8
8
 
data/lib/etcdv3/lease.rb CHANGED
@@ -1,8 +1,8 @@
1
1
 
2
2
  class Etcd
3
3
  class Lease
4
- def initialize(hostname, port, credentials, metadata={})
5
- @stub = Etcdserverpb::Lease::Stub.new("#{hostname}:#{port}", credentials)
4
+ def initialize(hostname, credentials, metadata={})
5
+ @stub = Etcdserverpb::Lease::Stub.new(hostname, credentials)
6
6
  @metadata = metadata
7
7
  end
8
8
 
@@ -14,8 +14,8 @@ class Etcd
14
14
  deactivate: 2
15
15
  }
16
16
 
17
- def initialize(hostname, port, credentials, metadata = {})
18
- @stub = Etcdserverpb::Maintenance::Stub.new("#{hostname}:#{port}", credentials)
17
+ def initialize(hostname, credentials, metadata = {})
18
+ @stub = Etcdserverpb::Maintenance::Stub.new(hostname, credentials)
19
19
  @metadata = metadata
20
20
  end
21
21
 
@@ -0,0 +1,41 @@
1
+ class Etcd
2
+ class Request
3
+
4
+ attr_reader :metacache
5
+
6
+ def initialize(hostname, credentials, metadata, metacache)
7
+ @hostname = hostname
8
+ @credentials = credentials
9
+ @metadata = metadata
10
+ @metacache = metacache
11
+ end
12
+
13
+ def handle(interface, method, method_args=[])
14
+ interface = resolve_interface(interface)
15
+ interface.send(method, *method_args)
16
+ end
17
+
18
+ private
19
+
20
+ def resolve_interface(interface)
21
+ self.send(interface)
22
+ end
23
+
24
+ def auth
25
+ @auth ||= Etcd::Auth.new(@hostname, @credentials, @metadata)
26
+ end
27
+
28
+ def kv
29
+ @kv ||= Etcd::KV.new(@hostname, @credentials, @metadata)
30
+ end
31
+
32
+ def maintenance
33
+ @maintenance ||= Etcd::Maintenance.new(@hostname, @credentials, @metadata)
34
+ end
35
+
36
+ def lease
37
+ @lease ||= Etcd::Lease.new(@hostname, @credentials, @metadata)
38
+ end
39
+
40
+ end
41
+ end
@@ -1,3 +1,3 @@
1
1
  class Etcd
2
- VERSION = '0.1.3'
2
+ VERSION = '0.1.4'.freeze
3
3
  end
data/lib/etcdv3.rb CHANGED
@@ -1,12 +1,14 @@
1
1
 
2
2
  require 'grpc'
3
3
  require 'uri'
4
+ require 'base64'
4
5
 
5
6
  require 'etcdv3/etcdrpc/rpc_services_pb'
6
7
  require 'etcdv3/auth'
7
8
  require 'etcdv3/kv'
8
9
  require 'etcdv3/maintenance'
9
10
  require 'etcdv3/lease'
11
+ require 'etcdv3/request'
10
12
 
11
13
  class Etcd
12
14
 
@@ -45,167 +47,165 @@ class Etcd
45
47
  @credentials = resolve_credentials
46
48
  @metadata = {}
47
49
  @metadata[:token] = auth.generate_token(user, password) unless user.nil?
50
+ @metacache = set_metacache
48
51
  end
49
52
 
50
53
  # Version of Etcd running on member
51
54
  def version
52
- maintenance.member_status.version
55
+ request.handle(:maintenance, 'member_status').version
53
56
  end
54
57
 
55
58
  # Store size in bytes.
56
59
  def db_size
57
- maintenance.member_status.dbSize
60
+ request.handle(:maintenance, 'member_status').dbSize
58
61
  end
59
62
 
60
63
  # Cluster leader id
61
64
  def leader_id
62
- maintenance.member_status.leader
65
+ request.handle(:maintenance, 'member_status').leader
63
66
  end
64
67
 
65
68
  # Inserts a new key.
66
69
  def put(key, value, lease_id: nil)
67
- kv.put(key, value, lease_id)
70
+ request.handle(:kv, 'put', [key, value, lease_id])
68
71
  end
69
72
 
70
73
  # Fetches key(s).
71
74
  def get(key, range_end='')
72
- kv.get(key, range_end)
75
+ request.handle(:get, 'get', [key, range_end])
73
76
  end
74
77
 
75
78
  # Grant a lease with a speified TTL
76
79
  def grant_lease(ttl)
77
- lease.grant_lease(ttl)
80
+ request.handle(:lease, 'grant_lease', [ttl])
78
81
  end
79
82
 
80
83
  # Revokes lease and delete all attached keys
81
84
  def revoke_lease(id)
82
- lease.revoke_lease(id)
85
+ request.handle(:lease, 'revoke_lease', [id])
83
86
  end
84
87
 
85
88
  # Returns information regarding the current state of the lease
86
89
  def lease_ttl(id)
87
- lease.lease_ttl(id)
90
+ request.handle(:lease, 'lease_ttl', [id])
88
91
  end
89
92
 
90
93
  # Creates new user.
91
94
  def add_user(user, password)
92
- auth.add_user(user, password)
95
+ request.handle(:auth, 'add_user', [user, password])
93
96
  end
94
97
 
95
98
  # Fetch specified user
96
99
  def get_user(user)
97
- auth.get_user(user)
100
+ request.handle(:auth, 'get_user', [user])
98
101
  end
99
102
 
100
103
  # Delete specified user.
101
104
  def delete_user(user)
102
- auth.delete_user(user)
105
+ request.handle(:auth, 'delete_user', [user])
103
106
  end
104
107
 
105
108
  # Changes the specified users password.
106
109
  def change_user_password(user, new_password)
107
- auth.change_user_password(user, new_password)
110
+ request.handle(:auth, 'change_user_password', [user, new_password])
108
111
  end
109
112
 
110
113
  # List all users.
111
114
  def user_list
112
- auth.user_list
113
- end
114
-
115
- # Authenticate using specified user and password.
116
- # On successful authentication, an auth token will be assigned to the instance.
117
- def authenticate(user, password)
118
- token = auth.generate_token(user, password)
119
- return false unless token
120
- @metadata[:token] = token
121
- @options[:user] = user
122
- @options[:password] = password
123
-
124
- true
115
+ request.handle(:auth, 'user_list')
125
116
  end
126
117
 
127
118
  # List all roles.
128
119
  def role_list
129
- auth.role_list
120
+ request.handle(:auth, 'role_list')
130
121
  end
131
122
 
132
123
  # Add role with specified name.
133
124
  def add_role(name)
134
- auth.add_role(name)
125
+ request.handle(:auth, 'add_role', [name])
135
126
  end
136
127
 
137
128
  # Fetches a specified role.
138
129
  def get_role(name)
139
- auth.get_role(name)
130
+ request.handle(:auth, 'get_role', [name])
140
131
  end
141
132
 
142
133
  # Delete role.
143
134
  def delete_role(name)
144
- auth.delete_role(name)
135
+ request.handle(:auth, 'delete_role', [name])
145
136
  end
146
137
 
147
138
  # Grants role to an existing user.
148
139
  def grant_role_to_user(user, role)
149
- auth.grant_role_to_user(user, role)
140
+ request.handle(:auth, 'grant_role_to_user', [user, role])
150
141
  end
151
142
 
152
143
  # Revokes role from a specified user.
153
144
  def revoke_role_from_user(user, role)
154
- auth.revoke_role_from_user(user, role)
145
+ request.handle(:auth, 'revoke_role_from_user', [user, role])
155
146
  end
156
147
 
157
148
  # Grants a new permission to an existing role.
158
149
  def grant_permission_to_role(name, permission, key, range_end='')
159
- auth.grant_permission_to_role(name, permission, key, range_end)
150
+ request.handle(:auth, 'grant_permission_to_role', [name, permission, key, range_end])
160
151
  end
161
152
 
162
153
  def revoke_permission_from_role(name, permission, key, range_end='')
163
- auth.revoke_permission_from_role(name, permission, key, range_end)
154
+ request.handle(:auth, 'revoke_permission_from_role', [name, permission, key, range_end])
155
+ end
156
+
157
+ # List active alarms
158
+ def alarm_list
159
+ request.handle(:maintenance, 'alarms', [:get, leader_id])
160
+ end
161
+
162
+ # Disarm alarms on a specified member.
163
+ def deactivate_alarms
164
+ request.handle(:maintenance, 'alarms', [leader_id])
164
165
  end
165
166
 
166
167
  # Enables authentication.
167
168
  def enable_auth
168
- auth.enable_auth
169
+ request.handle(:auth, 'enable_auth')
169
170
  end
170
171
 
171
172
  # Disables authentication.
172
173
  # This will clear any active auth / token data.
173
174
  def disable_auth
174
- response = auth.disable_auth
175
+ response = request.handle(:auth, 'disable_auth')
175
176
  if response
176
177
  @metadata.delete(:token)
177
178
  @options[:user] = nil
178
179
  @options[:password] = nil
180
+ @metacache = set_metacache
179
181
  end
180
182
  response
181
183
  end
182
184
 
183
- # List active alarms
184
- def alarm_list
185
- maintenance.alarms(:get, leader_id)
186
- end
185
+ # Authenticate using specified user and password.
186
+ # On successful authentication, an auth token will be assigned to the instance.
187
+ def authenticate(user, password)
188
+ token = request.handle(:auth, 'generate_token', [user, password])
189
+ return false unless token
190
+ @metadata[:token] = token
191
+ @options[:user] = user
192
+ @options[:password] = password
193
+ @metacache = set_metacache
187
194
 
188
- # Disarm alarms on a specified member.
189
- def deactivate_alarms
190
- maintenance.alarms(:deactivate, leader_id)
195
+ true
191
196
  end
192
197
 
193
198
  private
194
199
 
195
- def auth
196
- Etcd::Auth.new(hostname, port, @credentials, @metadata)
197
- end
198
-
199
- def kv
200
- Etcd::KV.new(hostname, port, @credentials, @metadata)
201
- end
202
-
203
- def maintenance
204
- Etcd::Maintenance.new(hostname, port, @credentials, @metadata)
200
+ def request
201
+ # Only re-initialize when metadata changes.
202
+ return @request if @request && @request.metacache == @metacache
203
+ @request = Request.new("#{hostname}:#{port}", @credentials, @metadata, @metacache)
205
204
  end
206
205
 
207
- def lease
208
- Etcd::Lease.new(hostname, port, @credentials, @metadata)
206
+ # Generates a new hash using a base64 of the metadata.
207
+ def set_metacache
208
+ Base64.strict_encode64(@metadata.to_s)
209
209
  end
210
210
 
211
211
  def resolve_credentials
data/spec/etcd_spec.rb CHANGED
@@ -2,14 +2,13 @@ require 'spec_helper'
2
2
 
3
3
  describe Etcd do
4
4
  context 'Insecure connection without Auth' do
5
- let(:conn) do
6
- Etcd.new(url: 'http://127.0.0.1:2379')
7
- end
5
+
6
+ let(:conn) { local_connection }
7
+
8
8
  describe '#initialize' do
9
9
  subject { conn }
10
10
  it { is_expected.to have_attributes(scheme: 'http') }
11
11
  it { is_expected.to have_attributes(hostname: '127.0.0.1') }
12
- it { is_expected.to have_attributes(port: 2379) }
13
12
  it { is_expected.to have_attributes(credentials: :this_channel_is_insecure) }
14
13
  it { is_expected.to have_attributes(token: nil) }
15
14
  it { is_expected.to have_attributes(user: nil) }
@@ -82,5 +81,32 @@ describe Etcd do
82
81
  end
83
82
  end
84
83
  end
84
+
85
+ describe '#metacache' do
86
+ context 'uses cached request object' do
87
+ let!(:object_id) { conn.send(:request).object_id }
88
+ before { conn.add_user('root', 'test') }
89
+ after { conn.delete_user('root') }
90
+ subject { conn.send(:request).object_id }
91
+ it { is_expected.to eq(object_id) }
92
+ end
93
+ context 'resets cache on auth' do
94
+ let!(:object_id) { conn.send(:request).object_id }
95
+ before do
96
+ conn.add_user('root', 'test')
97
+ conn.grant_role_to_user('root', 'root')
98
+ conn.enable_auth
99
+ conn.authenticate('root', 'test')
100
+ conn.add_user('boom', 'password')
101
+ end
102
+ after do
103
+ conn.disable_auth
104
+ conn.delete_user('root')
105
+ conn.delete_user('boom')
106
+ end
107
+ subject { conn.send(:request).object_id }
108
+ it { is_expected.to_not eq(object_id) }
109
+ end
110
+ end
85
111
  end
86
112
  end
@@ -2,9 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Etcd::Auth do
4
4
 
5
- let(:stub) do
6
- Etcd::Auth.new("127.0.0.1", 2379, :this_channel_is_insecure, {})
7
- end
5
+ let(:stub) { local_stub(Etcd::Auth) }
8
6
 
9
7
  describe '#add_user' do
10
8
  after { stub.delete_user('boom') }
@@ -1,14 +1,8 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Etcd::KV do
4
-
5
- let(:stub) do
6
- Etcd::KV.new("127.0.0.1", 2379, :this_channel_is_insecure, {})
7
- end
8
-
9
- let(:lease_stub) do
10
- Etcd::Lease.new("127.0.0.1", 2379, :this_channel_is_insecure, {})
11
- end
4
+ let(:stub) { local_stub(Etcd::KV) }
5
+ let(:lease_stub) { local_stub(Etcd::Lease) }
12
6
 
13
7
  describe '#put' do
14
8
  context 'without lease' do
@@ -2,9 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Etcd::Lease do
4
4
 
5
- let(:stub) do
6
- Etcd::Lease.new("127.0.0.1", 2379, :this_channel_is_insecure, {})
7
- end
5
+ let(:stub) { local_stub(Etcd::Lease) }
8
6
 
9
7
  describe '#grant_lease' do
10
8
  subject { stub.grant_lease(10) }
@@ -2,9 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Etcd::Maintenance do
4
4
 
5
- let(:stub) do
6
- Etcd::Maintenance.new("127.0.0.1", 2379, :this_channel_is_insecure, {})
7
- end
5
+ let(:stub) { local_stub(Etcd::Maintenance) }
8
6
 
9
7
  describe "#member_status" do
10
8
  subject { stub.member_status }
@@ -0,0 +1,21 @@
1
+ module Helpers
2
+ module Connections
3
+
4
+ def local_connection
5
+ Etcd.new(url: "http://#{local_url}")
6
+ end
7
+
8
+ def local_stub(interface)
9
+ interface.new(local_url, :this_channel_is_insecure, {})
10
+ end
11
+
12
+ def local_url
13
+ "127.0.0.1:#{port}"
14
+ end
15
+
16
+ def port
17
+ ENV.fetch('ETCD_TEST_PORT', 2379).to_i
18
+ end
19
+
20
+ end
21
+ end
@@ -1,8 +1,16 @@
1
1
  require 'fileutils'
2
2
  require 'tmpdir'
3
+ require 'socket'
4
+ require 'timeout'
5
+ require 'helpers/connections'
3
6
 
4
7
  module Helpers
5
8
  class TestInstance
9
+ include Helpers::Connections
10
+
11
+ class InvalidVersionException < StandardError; end
12
+ class PortInUseException < StandardError; end
13
+
6
14
  MINIMUM_VERSION = Gem::Version.new('3.0.0')
7
15
 
8
16
  def initialize
@@ -11,21 +19,28 @@ module Helpers
11
19
  @bin = discover_binary_path
12
20
  @version = discover_binary_version
13
21
 
14
- raise "Invalid Etcd Version: #{@version}. Must be running 3.0+" \
15
- if @version < MINIMUM_VERSION
22
+ raise InvalidVersionException if @version < MINIMUM_VERSION
23
+ raise PortInUseException if port_open?
24
+
25
+ rescue InvalidVersionException
26
+ puts "Invalid Etcd Version: #{@version}. Must be running 3.0+"
27
+ exit(1)
28
+ rescue PortInUseException
29
+ puts "Port #{port} is already in use. To choose a new port: `export ETCD_TEST_PORT=new_port`"
30
+ exit(1)
16
31
  end
17
32
 
18
33
  def start
19
34
  raise "Already running etcd servers(#{@pids.inspect})" unless @pids.empty?
20
- puts 'Starting up testing environment...'
35
+ puts "Starting up testing environment on port #{port}..."
21
36
  @pids << spawn_etcd_instance
22
37
  sleep(5)
23
38
  end
24
39
 
25
40
  def spawn_etcd_instance
26
- peer_url = 'http://127.0.0.1:2380'
27
- client_url = 'http://127.0.0.1:2379'
28
- cluster_url = 'node=http://127.0.0.1:2380'
41
+ peer_url = "http://127.0.0.1:#{port+1}"
42
+ client_url = "http://127.0.0.1:#{port}"
43
+ cluster_url = "node=http://127.0.0.1:#{port+1}"
29
44
  flags = ' --name=node'
30
45
  flags << " --initial-advertise-peer-urls=#{peer_url}"
31
46
  flags << " --listen-peer-urls=#{peer_url}"
@@ -47,6 +62,8 @@ module Helpers
47
62
  @pids.clear
48
63
  end
49
64
 
65
+ private
66
+
50
67
  def discover_binary_path
51
68
  'etcd'
52
69
  end
@@ -54,6 +71,23 @@ module Helpers
54
71
  def discover_binary_version
55
72
  result = `#{@bin} --version | grep "etcd Version"`
56
73
  Gem::Version.new(result.split(':').last.strip)
74
+ rescue
75
+ puts "The etcd binary is not in $PATH. Export it, and try again."
76
+ exit(1)
57
77
  end
78
+
79
+ def port_open?(seconds=1)
80
+ Timeout::timeout(seconds) do
81
+ begin
82
+ TCPSocket.new('127.0.0.1', port).close
83
+ true
84
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
85
+ false
86
+ end
87
+ end
88
+ rescue Timeout::Error
89
+ false
90
+ end
91
+
58
92
  end
59
93
  end
data/spec/spec_helper.rb CHANGED
@@ -3,8 +3,11 @@ $LOAD_PATH.unshift File.expand_path('./helpers', __FILE__)
3
3
 
4
4
  require 'etcdv3'
5
5
  require 'helpers/test_instance'
6
+ require 'helpers/connections'
6
7
 
7
8
  RSpec.configure do |config|
9
+ config.include(Helpers::Connections)
10
+
8
11
  config.expect_with :rspec do |expectations|
9
12
  expectations.include_chain_clauses_in_custom_matcher_descriptions = true
10
13
  end
@@ -12,6 +15,7 @@ RSpec.configure do |config|
12
15
  mocks.verify_partial_doubles = true
13
16
  end
14
17
  config.shared_context_metadata_behavior = :apply_to_host_groups
18
+
15
19
  instance = Helpers::TestInstance.new
16
20
  config.before(:suite) do
17
21
  instance.start
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: etcdv3
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shaun Davis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-06 00:00:00.000000000 Z
11
+ date: 2017-04-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: grpc
@@ -66,12 +66,14 @@ files:
66
66
  - lib/etcdv3/protos/http.proto
67
67
  - lib/etcdv3/protos/kv.proto
68
68
  - lib/etcdv3/protos/rpc.proto
69
+ - lib/etcdv3/request.rb
69
70
  - lib/etcdv3/version.rb
70
71
  - spec/etcd_spec.rb
71
72
  - spec/etcdv3/auth_spec.rb
72
73
  - spec/etcdv3/kv_spec.rb
73
74
  - spec/etcdv3/lease_spec.rb
74
75
  - spec/etcdv3/maintenance_spec.rb
76
+ - spec/helpers/connections.rb
75
77
  - spec/helpers/test_instance.rb
76
78
  - spec/spec_helper.rb
77
79
  homepage: https://github.com/davissp14/etcdv3-ruby
@@ -104,5 +106,6 @@ test_files:
104
106
  - spec/etcdv3/kv_spec.rb
105
107
  - spec/etcdv3/lease_spec.rb
106
108
  - spec/etcdv3/maintenance_spec.rb
109
+ - spec/helpers/connections.rb
107
110
  - spec/helpers/test_instance.rb
108
111
  - spec/spec_helper.rb