etcd 0.2.0.alpha → 0.2.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MjEwNDYyYjk0OWMyNzU2M2NhMjk2N2QxYmVmYTFhMmI2NjhlNzdiOQ==
4
+ YjhkNjNmODJjMmU3YTg3NDliYTU4ODk3MjY0NDYyYjNjODg1Y2QwZg==
5
5
  data.tar.gz: !binary |-
6
- NDdkYzdhMzM4ZmFhZWU1YTkxZTM3ZWEwYTA1MDE1YjI5NWZmODgxZg==
6
+ NzEyZjMxMTcwYmFiOWY3ZWU0OTg0YzdiMDFlNTNlNGJiM2Q2NjAwNw==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- OGI3MTA1YmM5MjA4ZjUzYmE4Mjc0MTVmYWEzODU1NDI0ZjhiMzNlZDM3MWQ1
10
- Mjk1NTIxNmY2MzA4MGYyZTEyZTRiZjExOTFiZTRkNGI3MzAyZjFkOWY3Yjdm
11
- MGY5NGNmYjU5NDEwOTVjZDNjMzBiOWU5YTA5YzNmOTUwNGEzMDA=
9
+ YTM3YWI4NDQ1YWJhY2E1NjgwYmJlMjkyNGZiYmQ4YWNmYjQ3NDlmNDlmNGIy
10
+ ZjUwYTA0MWNjMGVkZWE1Y2Y3MTliNWNjYzMzODc5YzQwNjA2YzVhNzVhMDRh
11
+ MTk4MDYxYWI0NjAwYzEyYTVhYzc3YzI3ZDM2YmI3OWVkMWJlZjU=
12
12
  data.tar.gz: !binary |-
13
- OGYwZThkZjFmMDcxNzUyZTBjODBhMTgwZWYwMWVmZjU1NzAyZmVjNTg5YmY5
14
- ZGQ0ODgxYmMzNjBlMTFmMDZhOTI3MmI2MTgzMGE0NDQ3ODkxYWIzYWI0NDc0
15
- YzAzNzNjYzc3YzIwYzZiMGI5ZjEzN2ZjMWI5NmFlMDkzNDNlOWM=
13
+ YWUwNGJkYWIwOWZkOGVkMDRlNDg5NmQ1MzY5NWI1OGI1MGI3MjNjODc3ODNh
14
+ NzBhNjc2YzI2ZGI1ZDAzMGU1MDYzMmNjNDAxZTFkYjkzZTZlYmNiYjJlNDkz
15
+ N2ZkNDk5NTcyMGIyZDM0OTlkNDMxZDJkOGFiNzc1OGI5ZThlNmI=
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  -c
2
- -fs
2
+ -fd
3
+ --fail-fast
data/.rubocop.yml ADDED
@@ -0,0 +1,12 @@
1
+ AllCops:
2
+ Includes:
3
+ - Gemfile
4
+ - lib/**
5
+ Excludes:
6
+ - spec/**
7
+
8
+ Encoding:
9
+ Enabled: false
10
+
11
+ LineLength:
12
+ Max: 100
data/.travis.yml CHANGED
@@ -8,4 +8,4 @@ rvm:
8
8
  branches:
9
9
  only:
10
10
  - master
11
- script: "bundle exec rake spec"
11
+ script: "ETCD_BIN=./etcd/bin/etcd bundle exec rake spec"
data/Gemfile CHANGED
@@ -2,5 +2,10 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in etcd.gemspec
4
4
  gemspec
5
+
5
6
  gem 'coco'
6
7
  gem 'rubocop'
8
+ gem 'guard'
9
+ gem 'guard-rake'
10
+ # uuid gem isn't pulling in systemu
11
+ gem 'systemu'
data/Guardfile ADDED
@@ -0,0 +1,9 @@
1
+ # vim: set ft=ruby:
2
+ require 'guard/guard'
3
+ require 'guard/rake'
4
+
5
+ guard 'rake', :task => 'test:quick' do
6
+ watch(%r{^spec/.+_spec\.rb$})
7
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
8
+ watch('spec/spec_helper.rb') { "spec" }
9
+ end
data/README.md CHANGED
@@ -22,6 +22,7 @@ Or install it yourself as:
22
22
  client = Etcd.client # this will create a client against etcd server running on localhost on port 4001
23
23
  client = Etcd.client(port: 4002)
24
24
  client = Etcd.client(host: '127.0.0.1', port: 4003)
25
+ client = Etcd.client(:user_name => 'test', :password => 'pwd') # populates the authentication header for basic HTTP auth with user name and password (useful for proxied connections)
25
26
  client = Etcd.client(host: '127.0.0.1', port: 4003, allow_redirect: false) # wont let you run sensitive commands on non-leader machines, default is true
26
27
  ```
27
28
  ### Set a key
@@ -66,11 +67,14 @@ client.machines
66
67
  ```ruby
67
68
  client.leader
68
69
  ```
70
+ More examples and api details can be found in the [wiki](https://github.com/ranjib/etcd-ruby/wiki)
69
71
 
70
72
  ## Contributors
71
73
  * Ranjib Dey
72
- * Jesse Nelson
73
-
74
+ * [Jesse Nelson](https://github.com/spheromak)
75
+ * [Nilesh Bairagi](https://github.com/Bairagi)
76
+ * [Dr Nic Williams](https://github.com/drnic)
77
+ * [Eric Buth] (https://github.com/buth)
74
78
 
75
79
 
76
80
  ## Contributing
data/Rakefile CHANGED
@@ -1,10 +1,23 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
3
  require "rdoc/task"
4
+ require "rubocop/rake_task"
4
5
 
5
6
  RSpec::Core::RakeTask.new("spec")
6
7
 
8
+ Rubocop::RakeTask.new do |task|
9
+ task.fail_on_error = true
10
+ end
11
+
7
12
  RDoc::Task.new do |rdoc|
8
13
  rdoc.main = "README.rdoc"
9
14
  rdoc.rdoc_files.include("lib /*.rb")
10
15
  end
16
+
17
+ namespace :test do
18
+ desc 'Run all of the quick tests.'
19
+ task :quick do
20
+ Rake::Task['rubocop'].invoke
21
+ Rake::Task['spec'].invoke
22
+ end
23
+ end
data/lib/etcd.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'etcd/client'
4
4
  ##
5
- # This module provides the Etcd:: name space for the gem and few
5
+ # This module provides the Etcd:: name space for the gem and few
6
6
  # factory methods for Etcd domain objects
7
7
  module Etcd
8
8
  ##
@@ -10,7 +10,7 @@ module Etcd
10
10
  # as an argument which gets passed to the Etcd::Client.new method
11
11
  # directly
12
12
  # If +opts+ is not passed default options are used, defined by Etcd::Client.new
13
- def self.client(opts={})
13
+ def self.client(opts = {})
14
14
  Etcd::Client.new(opts)
15
15
  end
16
16
  end
data/lib/etcd/client.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # Encoding: utf-8
2
2
 
3
+ require 'openssl'
3
4
  require 'net/http'
4
5
  require 'json'
5
6
  require 'etcd/log'
@@ -16,10 +17,9 @@ module Etcd
16
17
  # etcd api, like Etcd::Client#lock and Etcd::Client#eternal_watch, they
17
18
  # are defined in separate modules and included in this class
18
19
  class Client
19
-
20
- HTTP_REDIRECT = ->(r){ r.is_a? Net::HTTPRedirection }
21
- HTTP_SUCCESS = ->(r){ r.is_a? Net::HTTPSuccess }
22
- HTTP_CLIENT_ERROR = ->(r){ r.is_a? Net::HTTPClientError }
20
+ HTTP_REDIRECT = ->(r) { r.is_a? Net::HTTPRedirection }
21
+ HTTP_SUCCESS = ->(r) { r.is_a? Net::HTTPSuccess }
22
+ HTTP_CLIENT_ERROR = ->(r) { r.is_a? Net::HTTPClientError }
23
23
 
24
24
  include Stats
25
25
  include Keys
@@ -28,6 +28,7 @@ module Etcd
28
28
 
29
29
  attr_reader :host, :port, :http, :allow_redirect
30
30
  attr_reader :use_ssl, :verify_mode, :read_timeout
31
+ attr_reader :user_name, :password
31
32
 
32
33
  ##
33
34
  # Creates an Etcd::Client object. It accepts a hash +opts+ as argument
@@ -36,6 +37,7 @@ module Etcd
36
37
  # @opts [String] :host IP address of the etcd server (default 127.0.0.1)
37
38
  # @opts [Fixnum] :port Port number of the etcd server (default 4001)
38
39
  # @opts [Fixnum] :read_timeout set HTTP read timeouts (default 60)
40
+ # rubocop:disable CyclomaticComplexity
39
41
  def initialize(opts = {})
40
42
  @host = opts[:host] || '127.0.0.1'
41
43
  @port = opts[:port] || 4001
@@ -43,7 +45,10 @@ module Etcd
43
45
  @allow_redirect = opts.key?(:allow_redirect) ? opts[:allow_redirect] : true
44
46
  @use_ssl = opts[:use_ssl] || false
45
47
  @verify_mode = opts.key?(:verify_mode) ? opts[:verify_mode] : OpenSSL::SSL::VERIFY_PEER
48
+ @user_name = opts[:user_name] || nil
49
+ @password = opts[:password] || nil
46
50
  end
51
+ # rubocop:enable CyclomaticComplexity
47
52
 
48
53
  # Returns the etcd api version that will be used for across API methods
49
54
  def version_prefix
@@ -71,6 +76,7 @@ module Etcd
71
76
  # * path - etcd server path (etcd server end point)
72
77
  # * method - the request method used
73
78
  # * options - any additional parameters used by request method (optional)
79
+ # rubocop:disable MethodLength, CyclomaticComplexity
74
80
  def api_execute(path, method, options = {})
75
81
  params = options[:params]
76
82
  case method
@@ -90,21 +96,26 @@ module Etcd
90
96
  http.read_timeout = timeout
91
97
  http.use_ssl = use_ssl
92
98
  http.verify_mode = verify_mode
99
+ req.basic_auth(user_name, password) if [user_name, password].all?
93
100
  Log.debug("Invoking: '#{req.class}' against '#{path}")
94
101
  res = http.request(req)
95
102
  Log.debug("Response code: #{res.code}")
96
- process_http_request(res)
103
+ process_http_request(res, req, params)
97
104
  end
98
105
 
99
- def process_http_request(res)
106
+ # need to ahve original request to process the response when it redirects
107
+ def process_http_request(res, req = nil, params = nil)
100
108
  case res
101
109
  when HTTP_SUCCESS
102
110
  Log.debug('Http success')
103
111
  res
104
112
  when HTTP_REDIRECT
105
113
  if allow_redirect
106
- Log.debug('Http redirect, following')
107
- api_execute(res['location'], method, params: params)
114
+ uri = URI(res['location'])
115
+ @host = uri.host
116
+ @port = uri.port
117
+ Log.debug("Http redirect, setting new host to: #{@host}:#{@port}, and retrying")
118
+ api_execute(uri.path, req.method.downcase.to_sym, params: params)
108
119
  else
109
120
  Log.debug('Http redirect not allowed')
110
121
  res.error!
@@ -117,6 +128,7 @@ module Etcd
117
128
  res.error!
118
129
  end
119
130
  end
131
+ # rubocop:enable MethodLength
120
132
 
121
133
  def build_http_request(klass, path, params = nil, body = nil)
122
134
  path += '?' + URI.encode_www_form(params) unless params.nil?
@@ -36,6 +36,7 @@ module Etcd
36
36
  class NotDir < Error; end
37
37
  class NodeExist < Error; end
38
38
  class KeyIsPreserved < Error; end
39
+ class DirNotEmpty < Error; end
39
40
 
40
41
  # Post form related error
41
42
  class ValueRequired < Error; end
@@ -60,6 +61,7 @@ module Etcd
60
61
  104 => NotDir,
61
62
  105 => NodeExist,
62
63
  106 => KeyIsPreserved,
64
+ 108 => DirNotEmpty,
63
65
 
64
66
  # Post form related error
65
67
  200 => ValueRequired,
data/lib/etcd/keys.rb CHANGED
@@ -16,8 +16,8 @@ module Etcd
16
16
  # Retrives a key with its associated data, if key is not present it will
17
17
  # return with message "Key Not Found"
18
18
  #
19
- # This method has following parameters as argument
20
- # * key - whose data to be retrive
19
+ # This method takes the following parameters as arguments
20
+ # * key - whose data is to be retrieved
21
21
  def get(key, opts = {})
22
22
  response = api_execute(key_endpoint + key, :get, params: opts)
23
23
  Response.from_http_response(response)
@@ -25,30 +25,16 @@ module Etcd
25
25
 
26
26
  # Create or update a new key
27
27
  #
28
- # This method has following parameters as argument
28
+ # This method takes the following parameters as arguments
29
29
  # * key - whose value to be set
30
30
  # * value - value to be set for specified key
31
- # * ttl - shelf life of a key (in secsonds) (optional)
32
- def set(key, value, opts = nil)
31
+ # * ttl - shelf life of a key (in seconds) (optional)
32
+ def set(key, opts = nil)
33
+ fail ArgumentError, 'Second argument must be a hash' unless opts.is_a?(Hash)
33
34
  path = key_endpoint + key
34
35
  payload = {}
35
- if value.is_a?(Hash) # directory
36
- opts = value.dup
37
- else
38
- payload['value'] = value
39
- end
40
- if opts.is_a? Fixnum
41
- warn '[DEPRECATION] Passing ttl as raw argument is deprecated \
42
- please use :ttl => value, this will be removed in next minor release'
43
- payload['ttl'] = opts
44
- elsif opts.is_a? Hash
45
- [:ttl, :dir, :prevExist, :prevValue, :prevIndex].each do |k|
46
- payload[k] = opts[k] if opts.key?(k)
47
- end
48
- elsif opts.nil?
49
- # do nothing
50
- else
51
- fail ArgumentError, "Dont know how to parse #{opts}"
36
+ [:ttl, :value, :dir, :prevExist, :prevValue, :prevIndex].each do |k|
37
+ payload[k] = opts[k] if opts.key?(k)
52
38
  end
53
39
  response = api_execute(path, :put, params: payload)
54
40
  Response.from_http_response(response)
@@ -56,7 +42,7 @@ module Etcd
56
42
 
57
43
  # Deletes a key (and its content)
58
44
  #
59
- # This method has following parameters as argument
45
+ # This method takes the following parameters as arguments
60
46
  # * key - key to be deleted
61
47
  def delete(key, opts = {})
62
48
  response = api_execute(key_endpoint + key, :delete, params: opts)
@@ -65,71 +51,63 @@ module Etcd
65
51
 
66
52
  # Set a new value for key if previous value of key is matched
67
53
  #
68
- # This method takes following parameters as argument
54
+ # This method takes the following parameters as arguments
69
55
  # * key - whose value is going to change if previous value is matched
70
56
  # * value - new value to be set for specified key
71
57
  # * prevValue - value of a key to compare with existing value of key
72
58
  # * ttl - shelf life of a key (in secsonds) (optional)
73
- def compare_and_swap(key, value, prevValue, ttl = nil)
74
- path = key_endpoint + key
75
- payload = { 'value' => value, 'prevValue' => prevValue }
76
- payload['ttl'] = ttl unless ttl.nil?
77
- response = api_execute(path, :put, params: payload)
78
- Response.from_http_response(response)
59
+ def compare_and_swap(key, opts = {})
60
+ fail ArgumentError, 'Second argument must be a hash' unless opts.is_a?(Hash)
61
+ fail ArgumentError, 'You must pass prevValue' unless opts.key?(:prevValue)
62
+ set(key, opts)
79
63
  end
80
64
 
81
65
  # Gives a notification when specified key changes
82
66
  #
83
- # This method has following parameters as argument
67
+ # This method takes the following parameters as arguments
84
68
  # @ key - key to be watched
85
69
  # @options [Hash] additional options for watching a key
86
70
  # @options [Fixnum] :index watch the specified key from given index
87
71
  # @options [Fixnum] :timeout specify http timeout
88
- def watch(key, options = {})
72
+ def watch(key, opts = {})
89
73
  params = { wait: true }
90
- timeout = options[:timeout] || @read_timeout
91
- index = options[:waitIndex] || options[:index]
74
+ fail ArgumentError, 'Second argument must be a hash' unless opts.is_a?(Hash)
75
+ timeout = opts[:timeout] || @read_timeout
76
+ index = opts[:waitIndex] || opts[:index]
92
77
  params[:waitIndex] = index unless index.nil?
93
- params[:consistent] = options[:consistent] if options.key?(:consistent)
78
+ params[:consistent] = opts[:consistent] if opts.key?(:consistent)
94
79
 
95
80
  response = api_execute(key_endpoint + key, :get,
96
81
  timeout: timeout, params: params)
97
82
  Response.from_http_response(response)
98
83
  end
99
84
 
100
- def create_in_order(dir, value, opts = {})
85
+ def create_in_order(dir, opts = {})
101
86
  path = key_endpoint + dir
102
- payload = { 'value' => value }
103
- payload['ttl'] = opts[:ttl] if opts[:ttl]
87
+ fail ArgumentError, 'Second argument must be a hash' unless opts.is_a?(Hash)
88
+ payload = {}
89
+ [:ttl, :value].each do |k|
90
+ payload[k] = opts[k] if opts.key?(k)
91
+ end
104
92
  response = api_execute(path, :post, params: payload)
105
93
  Response.from_http_response(response)
106
94
  end
107
95
 
108
96
  def exists?(key)
109
- begin
110
- Etcd::Log.debug("Checking if key:' #{key}' exists")
111
- get(key)
112
- true
113
- rescue KeyNotFound => e
114
- Etcd::Log.debug("Key does not exist #{e}")
115
- false
116
- end
97
+ Etcd::Log.debug("Checking if key:' #{key}' exists")
98
+ get(key)
99
+ true
100
+ rescue KeyNotFound => e
101
+ Etcd::Log.debug("Key does not exist #{e}")
102
+ false
117
103
  end
118
104
 
119
- def create(key, value, ttl = nil)
120
- path = key_endpoint + key
121
- payload = { value: value, prevExist: false }
122
- payload['ttl'] = ttl unless ttl.nil?
123
- response = api_execute(path, :put, params: payload)
124
- Response.from_http_response(response)
105
+ def create(key, opts = {})
106
+ set(key, opts.merge(prevExist: false))
125
107
  end
126
108
 
127
- def update(key, value, ttl = nil)
128
- path = key_endpoint + key
129
- payload = { value: value, prevExist: true }
130
- payload['ttl'] = ttl unless ttl.nil?
131
- response = api_execute(path, :put, params: payload)
132
- Response.from_http_response(response)
109
+ def update(key, opts = {})
110
+ set(key, opts.merge(prevExist: true))
133
111
  end
134
112
 
135
113
  def eternal_watch(key, index = nil)
@@ -4,15 +4,15 @@ require 'timeout'
4
4
 
5
5
  module Etcd
6
6
  module Mod
7
+ # Implemetn Etcd's Leader module
7
8
  module Leader
8
-
9
9
  def mod_leader_endpoint
10
10
  '/mod/v2/leader'
11
11
  end
12
12
 
13
13
  def set_leader(key, value, ttl)
14
14
  path = mod_leader_endpoint + "#{key}?ttl=#{ttl}"
15
- api_execute(path, :put, params:{name: value}).body
15
+ api_execute(path, :put, params: { name: value }).body
16
16
  end
17
17
 
18
18
  def get_leader(key)
data/lib/etcd/mod/lock.rb CHANGED
@@ -4,45 +4,46 @@ require 'timeout'
4
4
 
5
5
  module Etcd
6
6
  module Mod
7
+ # implement etcd lock module
7
8
  module Lock
8
-
9
9
  def mod_lock_endpoint
10
10
  '/mod/v2/lock'
11
11
  end
12
12
 
13
- def acquire_lock(key, ttl, opts={})
13
+ def acquire_lock(key, ttl, opts = {})
14
14
  path = mod_lock_endpoint + key + "?ttl=#{ttl}"
15
15
  timeout = opts[:timeout] || 60
16
- Timeout::timeout(timeout) do
17
- api_execute(path, :post, params:opts)
16
+ Timeout.timeout(timeout) do
17
+ api_execute(path, :post, params: opts)
18
18
  end
19
19
  end
20
20
 
21
- def renew_lock(key, ttl, opts={})
22
- unless opts.has_key?(:index) or opts.has_key?(:value)
23
- raise ArgumentError, 'You mast pass index or value'
21
+ def renew_lock(key, ttl, opts = {})
22
+ unless opts.key?(:index) || opts.key?(:value)
23
+ fail ArgumentError, 'You mast pass index or value'
24
24
  end
25
25
  path = mod_lock_endpoint + key + "?ttl=#{ttl}"
26
26
  timeout = opts[:timeout] || 60
27
- Timeout::timeout(timeout) do
28
- api_execute(path, :put, params:opts).body
27
+ Timeout.timeout(timeout) do
28
+ api_execute(path, :put, params: opts).body
29
29
  end
30
30
  end
31
31
 
32
- def get_lock(key, opts={})
33
- api_execute(mod_lock_endpoint + key, :get, params:opts).body
32
+ def get_lock(key, opts = {})
33
+ api_execute(mod_lock_endpoint + key, :get, params: opts).body
34
34
  end
35
35
 
36
- def delete_lock(key, opts={})
37
- unless opts.has_key?(:index) or opts.has_key?(:value)
38
- raise ArgumentError, 'You must pass index or value'
36
+ def delete_lock(key, opts = {})
37
+ unless opts.key?(:index) || opts.key?(:value)
38
+ fail ArgumentError, 'You must pass index or value'
39
39
  end
40
- api_execute(mod_lock_endpoint + key, :delete, params:opts)
40
+ api_execute(mod_lock_endpoint + key, :delete, params: opts)
41
41
  end
42
42
 
43
- def lock(key, ttl, opts={})
44
- acquire_lock('/'+key, ttl, opts)
45
- index= get_lock('/'+key, field: index)
43
+ # rubocop:disable RescueException
44
+ def lock(key, ttl, opts = {})
45
+ acquire_lock('/' + key, ttl, opts)
46
+ index = get_lock('/' + key, field: index)
46
47
  begin
47
48
  yield key
48
49
  rescue Exception => e
@@ -51,6 +52,7 @@ module Etcd
51
52
  delete_lock(key, index: index)
52
53
  end
53
54
  end
55
+ # rubocop:enable RescueException
54
56
 
55
57
  alias_method :retrive_lock, :get_lock
56
58
  alias_method :release_lock, :delete_lock