etcd 0.0.2 → 0.0.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 +15 -0
- data/.rspec +2 -0
- data/.travis.yml +14 -0
- data/README.md +7 -0
- data/Rakefile +9 -0
- data/etcd.gemspec +2 -2
- data/lib/etcd.rb +10 -0
- data/lib/etcd/client.rb +41 -16
- data/lib/etcd/lock.rb +48 -0
- data/lib/etcd/log.rb +2 -0
- data/lib/etcd/mixins/helpers.rb +22 -0
- data/lib/etcd/mixins/lockable.rb +16 -0
- data/lib/etcd/version.rb +1 -1
- data/spec/functional/client_spec.rb +55 -0
- data/spec/functional/lock_spec.rb +70 -0
- data/spec/functional/read_only_client_spec.rb +24 -0
- data/spec/functional/test_and_set_spec.rb +18 -0
- data/spec/functional/watch_spec.rb +25 -0
- data/spec/functional_spec_helpers.rb +56 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/unit/etcd/client_spec.rb +67 -0
- data/spec/unit/etcd/log_spec.rb +13 -0
- data/spec/unit/etcd/mixins/helpers_spec.rb +14 -0
- data/spec/unit/etcd_spec.rb +15 -0
- metadata +35 -30
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MzNhZWZiNTA1ZjAzOTQ0ZjQ4YjUzOTIyZTZiNjEwZGVkZWJkYzllZg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
Y2ZlOTY4YTcxYTNkNzRhZGRhZDBlNzRlYWUyNjk2Y2VmMzBjYWFmYg==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
YTU1YjdhMmVhNTM1ZWFlOTc3ZmQwN2ZlNDM2N2EyNTI0ZTVjN2FkNTljMzc2
|
10
|
+
ODZhYjdiNTU4YmE3NGE0NjdiY2M2MDhhM2VmNWEzZTQ0OTBmMjViYmI3MWVj
|
11
|
+
MTkxZjI1YWM4NTU5OWUzMmRiYmY5MzcyNzg0YzljN2Y2OWQ5Njg=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NjJlNWY1NjQyNmQ0YmU1Y2M1ZTkxYTY3NzRhOTc3Mjc0ODUxODYxOGE2NGY0
|
14
|
+
NDE3ZWFkZjMzMGFlNzZhYWIyZTRhMTE5NDUyMjZkNWE3Y2Y4MTI4NzQ4YmRh
|
15
|
+
ZGM0MGRlNDM5ZGQ4MzNkN2JhNmEzMTVhN2QzNWNhNjY1NjkyZWM=
|
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
before_install:
|
2
|
+
- sudo add-apt-repository ppa:duh/golang -y
|
3
|
+
- sudo apt-get update -y
|
4
|
+
- sudo apt-get install golang -y
|
5
|
+
- git clone https://github.com/coreos/etcd
|
6
|
+
- cd etcd && bash build
|
7
|
+
- bundle install --path .bundle
|
8
|
+
rvm:
|
9
|
+
- 1.9.3
|
10
|
+
- 2.0.0
|
11
|
+
branches:
|
12
|
+
only:
|
13
|
+
- master
|
14
|
+
script: "bundle exec rake spec"
|
data/README.md
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
[](http://travis-ci.org/ranjib/etcd-ruby)
|
1
2
|
# Etcd
|
2
3
|
|
3
4
|
A ruby client for [etcd](https://github.com/coreos/etcd)
|
@@ -65,6 +66,11 @@ client.machines
|
|
65
66
|
client.leader
|
66
67
|
```
|
67
68
|
|
69
|
+
## Contributors
|
70
|
+
* Ranjib Dey
|
71
|
+
* Jesse Nelson
|
72
|
+
|
73
|
+
|
68
74
|
|
69
75
|
## Contributing
|
70
76
|
|
@@ -73,3 +79,4 @@ client.leader
|
|
73
79
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
74
80
|
4. Push to the branch (`git push origin my-new-feature`)
|
75
81
|
5. Create new Pull Request
|
82
|
+
6. If applicable, update the README.md
|
data/Rakefile
CHANGED
data/etcd.gemspec
CHANGED
@@ -20,10 +20,10 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_dependency "mixlib-cli"
|
22
22
|
spec.add_dependency "mixlib-log"
|
23
|
-
spec.add_dependency "
|
23
|
+
spec.add_dependency "uuid"
|
24
24
|
|
25
25
|
spec.add_development_dependency "bundler"
|
26
26
|
spec.add_development_dependency "rake"
|
27
27
|
spec.add_development_dependency "rspec"
|
28
|
-
spec.add_development_dependency "
|
28
|
+
spec.add_development_dependency "simplecov"
|
29
29
|
end
|
data/lib/etcd.rb
CHANGED
@@ -1,5 +1,15 @@
|
|
1
|
+
|
2
|
+
##
|
3
|
+
# This module provides the Etcd:: name space for the gem and few
|
4
|
+
# factory methods for Etcd domain objects
|
1
5
|
require 'etcd/client'
|
2
6
|
module Etcd
|
7
|
+
|
8
|
+
##
|
9
|
+
# Create and return a Etcd::Client object. It takes a hash +opts+
|
10
|
+
# as an argument which gets passed to the Etcd::Client.new method
|
11
|
+
# directly
|
12
|
+
# If +opts+ is not passed default options are used, defined by Etcd::Client.new
|
3
13
|
def self.client(opts={})
|
4
14
|
Etcd::Client.new(opts)
|
5
15
|
end
|
data/lib/etcd/client.rb
CHANGED
@@ -1,16 +1,35 @@
|
|
1
1
|
require 'net/http'
|
2
2
|
require 'json'
|
3
|
-
require 'hashie'
|
4
3
|
require 'etcd/log'
|
4
|
+
require 'etcd/mixins/helpers'
|
5
|
+
require 'etcd/mixins/lockable'
|
6
|
+
require 'ostruct'
|
7
|
+
|
5
8
|
|
6
9
|
module Etcd
|
10
|
+
##
|
11
|
+
# This is the central ruby class for Etcd. It provides methods for all Etcd api calls.
|
12
|
+
# It also provides few additional methods beyond the core Etcd api, like Etcd::Client#lock
|
13
|
+
# and Etcd::Client#eternal_watch, they are defined in separate modules and included in this
|
14
|
+
# class
|
7
15
|
class Client
|
8
16
|
|
17
|
+
include Etcd::Helpers
|
18
|
+
include Etcd::Lockable
|
19
|
+
|
9
20
|
attr_reader :host, :port, :http, :allow_redirect
|
10
21
|
|
22
|
+
##
|
23
|
+
# Creates a new instance of Etcd::Client. It accepts a hash +opts+ as argument
|
24
|
+
#
|
25
|
+
# @param [Hash] opts The options for new Etcd::Client object
|
26
|
+
# @opts [String] :host IP address of the etcd server (default is '127.0.0.1')
|
27
|
+
# @opts [Fixnum] :port Port number of the etcd server (default is 4001)
|
28
|
+
|
11
29
|
def initialize(opts={})
|
12
30
|
@host = opts[:host] || '127.0.0.1'
|
13
31
|
@port = opts[:port] || 4001
|
32
|
+
@read_timeout = opts[:read_timeout] || 60
|
14
33
|
if opts.has_key?(:allow_redirect)
|
15
34
|
@allow_redirect = opts[:allow_redirect]
|
16
35
|
else
|
@@ -23,11 +42,11 @@ module Etcd
|
|
23
42
|
end
|
24
43
|
|
25
44
|
def machines
|
26
|
-
api_execute('/machines', :get).split(",")
|
45
|
+
api_execute( version_prefix + '/machines', :get).split(",")
|
27
46
|
end
|
28
47
|
|
29
48
|
def leader
|
30
|
-
api_execute('/leader', :get)
|
49
|
+
api_execute( version_prefix + '/leader', :get)
|
31
50
|
end
|
32
51
|
|
33
52
|
def key_endpoint
|
@@ -43,30 +62,26 @@ module Etcd
|
|
43
62
|
payload = {'value' => value, 'prevValue' => prevValue }
|
44
63
|
payload['ttl'] = ttl unless ttl.nil?
|
45
64
|
response = api_execute(path, :post, payload)
|
46
|
-
|
65
|
+
json2obj(response)
|
47
66
|
end
|
48
67
|
|
49
68
|
def set(key, value, ttl=nil)
|
50
69
|
path = key_endpoint + key
|
51
|
-
payload = {'value' => value}
|
70
|
+
payload = {'value' => value}
|
52
71
|
payload['ttl'] = ttl unless ttl.nil?
|
53
72
|
response = api_execute(path, :post, payload)
|
54
|
-
|
73
|
+
json2obj(response)
|
55
74
|
end
|
56
75
|
|
76
|
+
|
57
77
|
def delete(key)
|
58
78
|
response = api_execute(key_endpoint + key, :delete)
|
59
|
-
|
79
|
+
json2obj(response)
|
60
80
|
end
|
61
81
|
|
62
82
|
def get(key)
|
63
83
|
response = api_execute(key_endpoint + key, :get)
|
64
|
-
|
65
|
-
if obj.is_a?(Array)
|
66
|
-
obj.map{|e| Hashie::Mash.new(e)}
|
67
|
-
else
|
68
|
-
Hashie::Mash.new(obj)
|
69
|
-
end
|
84
|
+
json2obj(response)
|
70
85
|
end
|
71
86
|
|
72
87
|
def watch(key, index=nil)
|
@@ -75,7 +90,7 @@ module Etcd
|
|
75
90
|
else
|
76
91
|
api_execute(watch_endpoint + key, :post, {'index' => index})
|
77
92
|
end
|
78
|
-
|
93
|
+
json2obj(response)
|
79
94
|
end
|
80
95
|
|
81
96
|
def api_execute(path, method, params=nil)
|
@@ -87,9 +102,10 @@ module Etcd
|
|
87
102
|
else
|
88
103
|
Net::HTTP.new(host, port)
|
89
104
|
end
|
105
|
+
http.read_timeout = @read_timeout
|
90
106
|
|
91
107
|
case method
|
92
|
-
when :get
|
108
|
+
when :get
|
93
109
|
unless params.nil?
|
94
110
|
encoded_params = URI.encode_www_form(params)
|
95
111
|
path+= "?" + encoded_params
|
@@ -111,7 +127,7 @@ module Etcd
|
|
111
127
|
Log.debug("Invoking: '#{req.class}' against '#{path}")
|
112
128
|
res = http.request(req)
|
113
129
|
Log.debug("Response code: #{res.code}")
|
114
|
-
if res.is_a?(Net::HTTPSuccess)
|
130
|
+
if res.is_a?(Net::HTTPSuccess)
|
115
131
|
Log.debug("Http success")
|
116
132
|
res.body
|
117
133
|
elsif redirect?(res.code.to_i) and allow_redirect
|
@@ -128,5 +144,14 @@ module Etcd
|
|
128
144
|
def redirect?(code)
|
129
145
|
(code >= 300) and (code < 400)
|
130
146
|
end
|
147
|
+
|
148
|
+
def json2obj(json)
|
149
|
+
obj = JSON.parse(json)
|
150
|
+
if obj.is_a?(Array)
|
151
|
+
obj.map{|e| OpenStruct.new(e)}
|
152
|
+
else
|
153
|
+
OpenStruct.new(obj)
|
154
|
+
end
|
155
|
+
end
|
131
156
|
end
|
132
157
|
end
|
data/lib/etcd/lock.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'uuid'
|
2
|
+
|
3
|
+
module Etcd
|
4
|
+
class Lock
|
5
|
+
class AcqusitionFailure < StandardError; end
|
6
|
+
class ReleaseFailure < StandardError; end
|
7
|
+
|
8
|
+
|
9
|
+
attr_reader :lock_id, :key, :value, :client
|
10
|
+
attr_reader :retries, :retry_interval, :attempts
|
11
|
+
|
12
|
+
|
13
|
+
def initialize(opts={})
|
14
|
+
@client = opts[:client]
|
15
|
+
@key = opts[:key] || '/global/lock'
|
16
|
+
@value = opts[:value] || 0
|
17
|
+
@retries = opts[:retries] || 1
|
18
|
+
@retry_interval = opts[:retry_interval] || 1
|
19
|
+
@attempts = 0
|
20
|
+
end
|
21
|
+
|
22
|
+
def acquire
|
23
|
+
@lock_id = uuid.generate
|
24
|
+
begin
|
25
|
+
response = client.test_and_set(key, lock_id, value)
|
26
|
+
@attempts = 0
|
27
|
+
response
|
28
|
+
rescue Exception => e
|
29
|
+
@attempts += 1
|
30
|
+
raise AcqusitionFailure, e.message if attempts >= retries
|
31
|
+
sleep retry_interval
|
32
|
+
acquire
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def release
|
37
|
+
begin
|
38
|
+
response = client.test_and_set(key, value, lock_id)
|
39
|
+
rescue Exception => e
|
40
|
+
raise ReleaseFailure, e.message
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def uuid
|
45
|
+
@uuid ||= UUID.new
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/etcd/log.rb
CHANGED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'etcd/lock'
|
2
|
+
|
3
|
+
module Etcd
|
4
|
+
module Helpers
|
5
|
+
|
6
|
+
def has_key?(key)
|
7
|
+
begin
|
8
|
+
get(key)
|
9
|
+
true
|
10
|
+
rescue Net::HTTPServerException => e
|
11
|
+
false
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def eternal_watch(key, index=nil)
|
16
|
+
loop do
|
17
|
+
response = watch(key, index)
|
18
|
+
yield response
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/etcd/version.rb
CHANGED
@@ -0,0 +1,55 @@
|
|
1
|
+
$:<< "lib/"
|
2
|
+
|
3
|
+
require 'etcd'
|
4
|
+
require 'uuid'
|
5
|
+
|
6
|
+
ETCD_BIN = ENV['ETCD_BIN'] || './etcd/etcd'
|
7
|
+
|
8
|
+
require 'functional_spec_helpers'
|
9
|
+
require 'functional/lock_spec'
|
10
|
+
require 'functional/read_only_client_spec'
|
11
|
+
require 'functional/test_and_set_spec'
|
12
|
+
require 'functional/watch_spec'
|
13
|
+
|
14
|
+
include Etcd::FunctionalSpec::Helpers
|
15
|
+
|
16
|
+
describe "Functional Test Suite" do
|
17
|
+
|
18
|
+
before(:all) do
|
19
|
+
start_etcd_servers
|
20
|
+
end
|
21
|
+
|
22
|
+
after(:all) do
|
23
|
+
stop_etcd_servers
|
24
|
+
end
|
25
|
+
|
26
|
+
let(:client) do
|
27
|
+
Etcd.client
|
28
|
+
end
|
29
|
+
|
30
|
+
let(:read_only_client) do
|
31
|
+
Etcd.client(:allow_redirect=>false, :port=> 4004)
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
include_examples "read only client"
|
36
|
+
include_examples "lock"
|
37
|
+
include_examples "test_and_set"
|
38
|
+
include_examples "watch"
|
39
|
+
|
40
|
+
it "#set/#get" do
|
41
|
+
key = random_key
|
42
|
+
value = uuid.generate
|
43
|
+
client.set(key, value)
|
44
|
+
expect(client.get(key).value).to eq(value)
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
it "#leader" do
|
49
|
+
expect(client.leader).to eq('http://127.0.0.1:7001')
|
50
|
+
end
|
51
|
+
|
52
|
+
it "#machines" do
|
53
|
+
expect(client.machines).to include('http://127.0.0.1:4001')
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
shared_examples "lock" do
|
2
|
+
|
3
|
+
let(:other_client) do
|
4
|
+
Etcd.client
|
5
|
+
end
|
6
|
+
|
7
|
+
it "if the lock is already aquired then another lock acquisition should fail" do
|
8
|
+
key = random_key(4)
|
9
|
+
value = uuid.generate
|
10
|
+
# initialize the lock key
|
11
|
+
client.set(key, value)
|
12
|
+
thr = Thread.new do
|
13
|
+
client.lock(:key=>key, :value=>value) do
|
14
|
+
sleep 2
|
15
|
+
end
|
16
|
+
end
|
17
|
+
sleep 1
|
18
|
+
expect {
|
19
|
+
other_client.lock(:key=>key, :value=> value) do
|
20
|
+
puts "Do something"
|
21
|
+
end
|
22
|
+
}.to raise_error(Etcd::Lock::AcqusitionFailure)
|
23
|
+
thr.join
|
24
|
+
end
|
25
|
+
|
26
|
+
it "if the lock is not already aquired then new lock aquisition should pass" do
|
27
|
+
key = random_key(4)
|
28
|
+
value = uuid.generate
|
29
|
+
# initialize the lock key
|
30
|
+
client.set(key, value)
|
31
|
+
expect {
|
32
|
+
client.lock(:key=>key, :value=>value) do
|
33
|
+
:foo
|
34
|
+
end
|
35
|
+
}.to_not raise_error
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should release the lock even if the given block raises exception" do
|
39
|
+
key = random_key(4)
|
40
|
+
value = uuid.generate
|
41
|
+
client.set(key, value)
|
42
|
+
expect {
|
43
|
+
client.lock(:key=>key, :value=>value) do
|
44
|
+
raise StandardError
|
45
|
+
end
|
46
|
+
}.to raise_error(StandardError)
|
47
|
+
|
48
|
+
expect{
|
49
|
+
other_client.lock(:key=>key, :value=>value) {}
|
50
|
+
}.to_not raise_error
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should raise lock release exception if the lock key value is changed " do
|
54
|
+
key = random_key(4)
|
55
|
+
value = uuid.generate
|
56
|
+
# initialize the lock key
|
57
|
+
client.set(key, value)
|
58
|
+
thr = Thread.new do
|
59
|
+
expect{
|
60
|
+
client.lock(:key=>key, :value=>value) do
|
61
|
+
sleep 3
|
62
|
+
end
|
63
|
+
}.to raise_error(Etcd::Lock::ReleaseFailure)
|
64
|
+
|
65
|
+
end
|
66
|
+
sleep 1
|
67
|
+
other_client.set(key, uuid.generate)
|
68
|
+
thr.join
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
shared_examples "read only client" do
|
2
|
+
it "should not allow write" do
|
3
|
+
key= random_key
|
4
|
+
expect{
|
5
|
+
read_only_client.set(key, uuid.generate)
|
6
|
+
}.to raise_error(Net::HTTPRetriableError)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should allow reads" do
|
10
|
+
key = random_key
|
11
|
+
value = uuid.generate
|
12
|
+
client.set(key, value)
|
13
|
+
sleep 1
|
14
|
+
expect(read_only_client.get(key).value).to eq(value)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should allow watch" do
|
18
|
+
key = random_key
|
19
|
+
value = uuid.generate
|
20
|
+
index = client.set(key, value).index
|
21
|
+
expect(read_only_client.watch(key, index).value).to eq(value)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
shared_examples "test_and_set" do
|
2
|
+
it "should pass when prev value is correct" do
|
3
|
+
key = random_key(2)
|
4
|
+
old_value = uuid.generate
|
5
|
+
new_value = uuid.generate
|
6
|
+
client.set(key, old_value)
|
7
|
+
client.test_and_set(key, new_value, old_value)
|
8
|
+
expect(client.get(key).value).to eq(new_value)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should fail when prev value is incorrect" do
|
12
|
+
key = random_key(2)
|
13
|
+
value = uuid.generate
|
14
|
+
client.set(key, value)
|
15
|
+
expect{ client.test_and_set(key, 10, 2)}.to raise_error(Net::HTTPServerException)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
shared_examples "watch" do
|
2
|
+
it "without index, returns the value at a particular index" do
|
3
|
+
key = random_key(4)
|
4
|
+
value1 = uuid.generate
|
5
|
+
value2 = uuid.generate
|
6
|
+
|
7
|
+
index1 = client.set(key, value1).index
|
8
|
+
index2 = client.set(key, value2).index
|
9
|
+
|
10
|
+
expect(client.watch(key, index1).value).to eq(value1)
|
11
|
+
expect(client.watch(key, index2).value).to eq(value2)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "with index, waits and return when the key is updated" do
|
15
|
+
response = nil
|
16
|
+
key = random_key
|
17
|
+
value = uuid.generate
|
18
|
+
thr = Thread.new do
|
19
|
+
response = client.watch(key)
|
20
|
+
end
|
21
|
+
client.set(key, value)
|
22
|
+
thr.join
|
23
|
+
expect(response.value).to eq(value)
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'uuid'
|
2
|
+
|
3
|
+
module Etcd
|
4
|
+
module FunctionalSpec
|
5
|
+
module Helpers
|
6
|
+
def start_etcd_servers
|
7
|
+
@tmpdir = Dir.mktmpdir
|
8
|
+
pid = spawn_etcd_server(@tmpdir+'/leader')
|
9
|
+
@pids = Array(pid)
|
10
|
+
puts "Etcd leader process id :#{pid}"
|
11
|
+
leader = '127.0.0.1:7001'
|
12
|
+
|
13
|
+
4.times do |n|
|
14
|
+
client_port = 4002 + n
|
15
|
+
server_port = 7002 + n
|
16
|
+
pid = spawn_etcd_server(@tmpdir+client_port.to_s, client_port, server_port, leader)
|
17
|
+
@pids << pid
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def stop_etcd_servers
|
22
|
+
@pids.each do |pid|
|
23
|
+
Process.kill("HUP", pid)
|
24
|
+
puts "Killed #{pid}"
|
25
|
+
end
|
26
|
+
FileUtils.remove_entry_secure @tmpdir
|
27
|
+
end
|
28
|
+
|
29
|
+
def spawn_etcd_server(dir, client_port=4001, server_port=7001, leader = nil)
|
30
|
+
args = " -c 127.0.0.1:#{client_port} -s 127.0.0.1:#{server_port} -d #{dir} -n node_#{client_port}"
|
31
|
+
command = if leader.nil?
|
32
|
+
ETCD_BIN + args
|
33
|
+
else
|
34
|
+
ETCD_BIN + args + " -C #{leader}"
|
35
|
+
end
|
36
|
+
puts command
|
37
|
+
pid = spawn(command)
|
38
|
+
Process.detach(pid)
|
39
|
+
sleep 1
|
40
|
+
pid
|
41
|
+
end
|
42
|
+
|
43
|
+
def uuid
|
44
|
+
@uuid ||= UUID.new
|
45
|
+
end
|
46
|
+
|
47
|
+
def random_key(n=1)
|
48
|
+
key=''
|
49
|
+
n.times do
|
50
|
+
key << '/'+ uuid.generate
|
51
|
+
end
|
52
|
+
key
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'simplecov'
|
4
|
+
|
5
|
+
SimpleCov.start do
|
6
|
+
add_filter "/.bundle/"
|
7
|
+
add_filter "/spec/"
|
8
|
+
end
|
9
|
+
|
10
|
+
$:.unshift(File.expand_path("../lib", __FILE__))
|
11
|
+
$:.unshift(File.expand_path("../spec", __FILE__))
|
12
|
+
|
13
|
+
require 'etcd'
|
14
|
+
require 'functional_spec_helpers'
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'unit/etcd/mixins/helpers_spec'
|
3
|
+
|
4
|
+
describe Etcd::Client do
|
5
|
+
|
6
|
+
let(:client) do
|
7
|
+
Etcd::Client.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should use localhost and 4001 port by default" do
|
11
|
+
expect(client.host).to eq('127.0.0.1')
|
12
|
+
expect(client.port).to eq(4001)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "shlould follow redirection by default" do
|
16
|
+
expect(client.allow_redirect).to be_true
|
17
|
+
end
|
18
|
+
|
19
|
+
it "#machines should make /machines GET http request" do
|
20
|
+
client.should_receive(:api_execute).with('/v1/machines', :get).and_return('foobar')
|
21
|
+
expect(client.machines).to eq(['foobar'])
|
22
|
+
end
|
23
|
+
|
24
|
+
it "#leader should make /leader GET http request" do
|
25
|
+
client.should_receive(:api_execute).with('/v1/leader', :get).and_return('foobar')
|
26
|
+
expect(client.leader).to eq('foobar')
|
27
|
+
end
|
28
|
+
|
29
|
+
it "#get('/foo') should make /v1/keys/foo GET http request" do
|
30
|
+
client.should_receive(:api_execute).with('/v1/keys/foo', :get).and_return('{"value":1}')
|
31
|
+
expect(client.get('/foo').value).to eq(1)
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#set" do
|
35
|
+
it "set('/foo', 1) should invoke /v1/keys/foo POST http request" do
|
36
|
+
client.should_receive(:api_execute).with('/v1/keys/foo', :post, {'value'=>1}).and_return('{"value":1}')
|
37
|
+
expect(client.set('/foo', 1).value).to eq(1)
|
38
|
+
end
|
39
|
+
it "set('/foo', 1, 4) should invoke /v1/keys/foo POST http request and set the ttl to 4" do
|
40
|
+
client.should_receive(:api_execute).with('/v1/keys/foo', :post, {'value'=>1, 'ttl'=>4}).and_return('{"value":1}')
|
41
|
+
expect(client.set('/foo', 1, 4).value).to eq(1)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#test_and_set" do
|
46
|
+
it "test_and_set('/foo', 1, 4) should invoke /v1/keys/foo POST http request" do
|
47
|
+
client.should_receive(:api_execute).with('/v1/keys/foo', :post, {'value'=>1, 'prevValue'=>4}).and_return('{"value":1}')
|
48
|
+
expect(client.test_and_set('/foo', 1, 4).value).to eq(1)
|
49
|
+
end
|
50
|
+
it "test_and_set('/foo', 1, 4, 10) should invoke /v1/keys/foo POST http request and set the ttl to 10" do
|
51
|
+
client.should_receive(:api_execute).with('/v1/keys/foo', :post, {'value'=>1, 'prevValue'=>4, 'ttl'=>10}).and_return('{"value":1}')
|
52
|
+
expect(client.test_and_set('/foo', 1, 4, 10).value).to eq(1)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
it "#watch('/foo') should make /v1/watch/foo GET http request" do
|
57
|
+
client.should_receive(:api_execute).with('/v1/watch/foo', :get).and_return('{"value":1}')
|
58
|
+
expect(client.watch('/foo').value).to eq(1)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "#delete('/foo') should make /v1/keys/foo DELETE http request" do
|
62
|
+
client.should_receive(:api_execute).with('/v1/keys/foo', :delete).and_return('{"index":"1"}')
|
63
|
+
client.delete('/foo')
|
64
|
+
end
|
65
|
+
|
66
|
+
it_should_behave_like Etcd::Helpers
|
67
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Etcd::Log do
|
4
|
+
it "should support debug, warn, info logging" do
|
5
|
+
expect(Etcd::Log).to respond_to(:debug)
|
6
|
+
expect(Etcd::Log).to respond_to(:warn)
|
7
|
+
expect(Etcd::Log).to respond_to(:info)
|
8
|
+
end
|
9
|
+
it "should allow users to set a log levels" do
|
10
|
+
Etcd::Log.level = :warn
|
11
|
+
expect(Etcd::Log.level).to eq(:warn)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
shared_examples Etcd::Helpers do
|
4
|
+
describe "#has_key" do
|
5
|
+
it "should return true when the key is present" do
|
6
|
+
client.should_receive(:get).with('/foo/bar').and_return({})
|
7
|
+
expect(client.has_key?('/foo/bar')).to be_true
|
8
|
+
end
|
9
|
+
it "should return false when the key is absent" do
|
10
|
+
client.should_receive(:get).with('/foo/bar').and_raise(Net::HTTPServerException.new('',''))
|
11
|
+
expect(client.has_key?('/foo/bar')).to be_false
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Etcd do
|
5
|
+
describe "#client" do
|
6
|
+
it "should return a valid Etcd::Client object" do
|
7
|
+
expect(Etcd.client).to be_a_kind_of(Etcd::Client)
|
8
|
+
end
|
9
|
+
it "should pass the same options to Etcd::Client initilizer" do
|
10
|
+
opts = { :host => '10.10.10.10', :port=> 4001 }
|
11
|
+
Etcd::Client.should_receive(:new).with(opts)
|
12
|
+
Etcd.client(opts)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
metadata
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: etcd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.0.4
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Ranjib Dey
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-08-
|
11
|
+
date: 2013-08-26 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: mixlib-cli
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
17
|
- - ! '>='
|
20
18
|
- !ruby/object:Gem::Version
|
@@ -22,7 +20,6 @@ dependencies:
|
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
24
|
- - ! '>='
|
28
25
|
- !ruby/object:Gem::Version
|
@@ -30,7 +27,6 @@ dependencies:
|
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: mixlib-log
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
31
|
- - ! '>='
|
36
32
|
- !ruby/object:Gem::Version
|
@@ -38,15 +34,13 @@ dependencies:
|
|
38
34
|
type: :runtime
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
38
|
- - ! '>='
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
|
-
name:
|
42
|
+
name: uuid
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
45
|
- - ! '>='
|
52
46
|
- !ruby/object:Gem::Version
|
@@ -54,7 +48,6 @@ dependencies:
|
|
54
48
|
type: :runtime
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
52
|
- - ! '>='
|
60
53
|
- !ruby/object:Gem::Version
|
@@ -62,7 +55,6 @@ dependencies:
|
|
62
55
|
- !ruby/object:Gem::Dependency
|
63
56
|
name: bundler
|
64
57
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
58
|
requirements:
|
67
59
|
- - ! '>='
|
68
60
|
- !ruby/object:Gem::Version
|
@@ -70,7 +62,6 @@ dependencies:
|
|
70
62
|
type: :development
|
71
63
|
prerelease: false
|
72
64
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
65
|
requirements:
|
75
66
|
- - ! '>='
|
76
67
|
- !ruby/object:Gem::Version
|
@@ -78,7 +69,6 @@ dependencies:
|
|
78
69
|
- !ruby/object:Gem::Dependency
|
79
70
|
name: rake
|
80
71
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
72
|
requirements:
|
83
73
|
- - ! '>='
|
84
74
|
- !ruby/object:Gem::Version
|
@@ -86,7 +76,6 @@ dependencies:
|
|
86
76
|
type: :development
|
87
77
|
prerelease: false
|
88
78
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
79
|
requirements:
|
91
80
|
- - ! '>='
|
92
81
|
- !ruby/object:Gem::Version
|
@@ -94,7 +83,6 @@ dependencies:
|
|
94
83
|
- !ruby/object:Gem::Dependency
|
95
84
|
name: rspec
|
96
85
|
requirement: !ruby/object:Gem::Requirement
|
97
|
-
none: false
|
98
86
|
requirements:
|
99
87
|
- - ! '>='
|
100
88
|
- !ruby/object:Gem::Version
|
@@ -102,15 +90,13 @@ dependencies:
|
|
102
90
|
type: :development
|
103
91
|
prerelease: false
|
104
92
|
version_requirements: !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
93
|
requirements:
|
107
94
|
- - ! '>='
|
108
95
|
- !ruby/object:Gem::Version
|
109
96
|
version: '0'
|
110
97
|
- !ruby/object:Gem::Dependency
|
111
|
-
name:
|
98
|
+
name: simplecov
|
112
99
|
requirement: !ruby/object:Gem::Requirement
|
113
|
-
none: false
|
114
100
|
requirements:
|
115
101
|
- - ! '>='
|
116
102
|
- !ruby/object:Gem::Version
|
@@ -118,7 +104,6 @@ dependencies:
|
|
118
104
|
type: :development
|
119
105
|
prerelease: false
|
120
106
|
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
none: false
|
122
107
|
requirements:
|
123
108
|
- - ! '>='
|
124
109
|
- !ruby/object:Gem::Version
|
@@ -131,6 +116,8 @@ extensions: []
|
|
131
116
|
extra_rdoc_files: []
|
132
117
|
files:
|
133
118
|
- .gitignore
|
119
|
+
- .rspec
|
120
|
+
- .travis.yml
|
134
121
|
- Gemfile
|
135
122
|
- LICENSE.txt
|
136
123
|
- README.md
|
@@ -138,37 +125,55 @@ files:
|
|
138
125
|
- etcd.gemspec
|
139
126
|
- lib/etcd.rb
|
140
127
|
- lib/etcd/client.rb
|
128
|
+
- lib/etcd/lock.rb
|
141
129
|
- lib/etcd/log.rb
|
130
|
+
- lib/etcd/mixins/helpers.rb
|
131
|
+
- lib/etcd/mixins/lockable.rb
|
142
132
|
- lib/etcd/version.rb
|
133
|
+
- spec/functional/client_spec.rb
|
134
|
+
- spec/functional/lock_spec.rb
|
135
|
+
- spec/functional/read_only_client_spec.rb
|
136
|
+
- spec/functional/test_and_set_spec.rb
|
137
|
+
- spec/functional/watch_spec.rb
|
138
|
+
- spec/functional_spec_helpers.rb
|
139
|
+
- spec/spec_helper.rb
|
140
|
+
- spec/unit/etcd/client_spec.rb
|
141
|
+
- spec/unit/etcd/log_spec.rb
|
142
|
+
- spec/unit/etcd/mixins/helpers_spec.rb
|
143
|
+
- spec/unit/etcd_spec.rb
|
143
144
|
homepage: https://github.com/ranjib/etcd-ruby
|
144
145
|
licenses:
|
145
146
|
- MIT
|
147
|
+
metadata: {}
|
146
148
|
post_install_message:
|
147
149
|
rdoc_options: []
|
148
150
|
require_paths:
|
149
151
|
- lib
|
150
152
|
required_ruby_version: !ruby/object:Gem::Requirement
|
151
|
-
none: false
|
152
153
|
requirements:
|
153
154
|
- - ! '>='
|
154
155
|
- !ruby/object:Gem::Version
|
155
156
|
version: '0'
|
156
|
-
segments:
|
157
|
-
- 0
|
158
|
-
hash: 1991828162115001123
|
159
157
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
160
|
-
none: false
|
161
158
|
requirements:
|
162
159
|
- - ! '>='
|
163
160
|
- !ruby/object:Gem::Version
|
164
161
|
version: '0'
|
165
|
-
segments:
|
166
|
-
- 0
|
167
|
-
hash: 1991828162115001123
|
168
162
|
requirements: []
|
169
163
|
rubyforge_project:
|
170
|
-
rubygems_version:
|
164
|
+
rubygems_version: 2.0.3
|
171
165
|
signing_key:
|
172
|
-
specification_version:
|
166
|
+
specification_version: 4
|
173
167
|
summary: Ruby client library for etcd
|
174
|
-
test_files:
|
168
|
+
test_files:
|
169
|
+
- spec/functional/client_spec.rb
|
170
|
+
- spec/functional/lock_spec.rb
|
171
|
+
- spec/functional/read_only_client_spec.rb
|
172
|
+
- spec/functional/test_and_set_spec.rb
|
173
|
+
- spec/functional/watch_spec.rb
|
174
|
+
- spec/functional_spec_helpers.rb
|
175
|
+
- spec/spec_helper.rb
|
176
|
+
- spec/unit/etcd/client_spec.rb
|
177
|
+
- spec/unit/etcd/log_spec.rb
|
178
|
+
- spec/unit/etcd/mixins/helpers_spec.rb
|
179
|
+
- spec/unit/etcd_spec.rb
|