etcd 0.0.2 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Built on Travis](https://secure.travis-ci.org/ranjib/etcd-ruby.png?branch=master)](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
|