diplomat 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 09c6058fde667219b0e08ed9118dc49000da12c9
4
- data.tar.gz: b13753886f7406977b20c336093a0c259fa848bc
3
+ metadata.gz: 8c4fdef4cc41c9b67236ee64307e8f156ffcc790
4
+ data.tar.gz: 3506d1b24c567796d5fa397ae3055da5efbac41f
5
5
  SHA512:
6
- metadata.gz: 28e5c5a6251a9dbfd8d3962fdd2eab0f7fc7bf53a085ff1ac35e00584bc90a6bc48c37cec1981265fd0fcc8a34fe99388543284bdd40b71ed76dd76a4a349bdb
7
- data.tar.gz: 86db1e94a2adca8ab6d55e05daabcff8ecef6cb64a285a3f4ca6dd21921d938b3b211c6e28a1736f582cef5e33350780bc2906027c8f611ff3d7a7505d9485e4
6
+ metadata.gz: 837c53a701c4a1d3d1c2d8972bde7895530319dd056dcb9d5d1d9567c0c59ea130e3dca260df277f3f145bec30d18891987a9519e74551e21682090e95fc237d
7
+ data.tar.gz: 8a0d0aab7c784b5af4bb39a6d7aeb24b717d9c49134293542dc5ba5aa19a53f373bf067502ff0b30a8b7dff40951a49deb39c6cb935e56a1fedf33e4af380a84
data/README.md CHANGED
@@ -118,6 +118,39 @@ Release a lock:
118
118
  Diplomat::Lock.release("/key/to/lock", sessionid )
119
119
  ```
120
120
 
121
+ ### Events
122
+
123
+ Fire an event:
124
+
125
+ ```ruby
126
+ Diplomat::Event.fire('do_something', 'payload')
127
+ ```
128
+
129
+ List all events with a certain name received by the local agent:
130
+
131
+ ```ruby
132
+ Diplomat::Event.get_all('do_something')
133
+ ```
134
+
135
+ Get the latest event with a certain name received by the local agent:
136
+
137
+ ```ruby
138
+ Diplomat::Event.get('do_something')
139
+ ```
140
+
141
+ Iterate through the events with a certain name received by the local agent:
142
+
143
+ ```ruby
144
+ events = Enumerator.new do |y|
145
+ ret = {token: :first}
146
+ while ret = begin Diplomat::Event.get('do_something', ret[:token], :reject) rescue nil end
147
+ y.yield(ret[:value])
148
+ end
149
+ end
150
+
151
+ events.each{ |e| puts e }
152
+ ```
153
+
121
154
  ### Custom configuration
122
155
 
123
156
  You can create a custom configuration using the following syntax:
@@ -136,13 +169,18 @@ This is traditionally kept inside the `config/initializers` directory if you're
136
169
  ### Todo
137
170
 
138
171
  - [ ] Updating Docs with latest changes
172
+ - [ ] Using custom objects for response objects (instead of openStruct)
139
173
  - [ ] PUTing and DELETEing services
174
+ - [ ] Custom SSL Cert Middleware for faraday
140
175
  - [x] Allowing the custom configuration of the consul url to connect to
141
176
  - [x] Deleting Keys
142
177
  - [x] Listing available services
143
178
  - [x] Health
144
179
  - [x] Members
145
180
  - [x] Status
181
+ - [x] Datacenter support for services
182
+ - [x] Ruby 1.8 support
183
+ - [x] Events
146
184
 
147
185
 
148
186
  ## Enjoy!
data/lib/diplomat.rb CHANGED
@@ -20,7 +20,7 @@ module Diplomat
20
20
  self.root_path = File.expand_path "..", __FILE__
21
21
  self.lib_path = File.expand_path "../diplomat", __FILE__
22
22
 
23
- require_libs "configuration", "rest_client", "kv", "service", "members", "check", "health", "session", "lock", "error"
23
+ require_libs "configuration", "rest_client", "kv", "service", "members", "check", "health", "session", "lock", "error", "event"
24
24
  self.configuration ||= Diplomat::Configuration.new
25
25
 
26
26
  class << self
@@ -1,5 +1,7 @@
1
1
  module Diplomat
2
2
  class KeyNotFound < StandardError; end
3
3
  class KeyAlreadyExists < StandardError; end
4
+ class EventNotFound < StandardError; end
5
+ class EventAlreadyExists < StandardError; end
4
6
  class UnknownStatus < StandardError; end
5
7
  end
@@ -0,0 +1,163 @@
1
+ require 'faraday'
2
+
3
+ module Diplomat
4
+ class Event < Diplomat::RestClient
5
+
6
+ # Send an event
7
+ # @param name [String] the event name
8
+ # @param value [String] the payload of the event
9
+ # @param service [String] the target service name
10
+ # @param node [String] the target node name
11
+ # @param tag [String] the target tag name, must only be used with service
12
+ # @return [nil]
13
+ def fire name, value=nil, service=nil, node=nil, tag=nil
14
+ raw = @conn.put do |req|
15
+ url = [ "/v1/event/fire/#{name}" ]
16
+ url += use_named_parameter("service", service)
17
+ url += use_named_parameter("node", node)
18
+ url += use_named_parameter("tag", tag) if service
19
+ req.url concat_url url
20
+ req.body = value unless value.nil?
21
+ end
22
+ nil
23
+ end
24
+
25
+ # Get the list of events matching name
26
+ # @param name [String] the name of the event (regex)
27
+ # @param not_found [Symbol] behaviour if there are no events matching name;
28
+ # :reject with exception, or :wait for a non-empty list
29
+ # @param found [Symbol] behaviour if there are already events matching name;
30
+ # :reject with exception, :return its current value, or :wait for its next value
31
+ # @return [Array[hash]] The list of { :name, :payload } hashes
32
+ # @note
33
+ # Events are sent via the gossip protocol; there is no guarantee of delivery
34
+ # success or order, but the local agent will store up to 256 events that do
35
+ # arrive. This method lists those events.
36
+ # It has the same semantics as Kv::get, except the value returned is a list
37
+ # i.e. the current value is all events up until now, the next value is the
38
+ # current list plus the next event to arrive.
39
+ # To get a specific event in the sequence, @see #get
40
+ # When trying to get a list of events matching a name, there are two possibilities:
41
+ # - The list doesn't (yet) exist / is empty
42
+ # - The list exists / is non-empty
43
+ # The combination of not_found and found behaviour gives maximum possible
44
+ # flexibility. For X: reject, R: return, W: wait
45
+ # - X X - meaningless; never return a value
46
+ # - X R - "normal" non-blocking get operation. Default
47
+ # - X W - get the next value only (must have a current value)
48
+ # - W X - get the first value only (must not have a current value)
49
+ # - W R - get the first or current value; always return something, but
50
+ # block only when necessary
51
+ # - W W - get the first or next value; wait until there is an update
52
+ def get_all name=nil, not_found=:reject, found=:return
53
+ url = ["/v1/event/list"]
54
+ url += use_named_parameter("name", name)
55
+
56
+ # Event list never returns 404 or blocks, but may return an empty list
57
+ @raw = @conn.get concat_url url
58
+ if JSON.parse(@raw.body).count == 0
59
+ case not_found
60
+ when :reject
61
+ raise Diplomat::EventNotFound, name
62
+ when :wait
63
+ index = @raw.headers["x-consul-index"]
64
+ end
65
+ else
66
+ case found
67
+ when :reject
68
+ raise Diplomat::EventAlreadyExists, name
69
+ when :return
70
+ parse_body
71
+ return return_payload
72
+ when :wait
73
+ index = @raw.headers["x-consul-index"]
74
+ end
75
+ end
76
+
77
+ # Wait for first/next event
78
+ url += use_named_parameter("index", index)
79
+ @raw = @conn.get do |req|
80
+ req.url concat_url url
81
+ req.options.timeout = 86400
82
+ end
83
+ parse_body
84
+ return_payload
85
+ end
86
+
87
+ # Get a specific event in the sequence matching name
88
+ # @param name [String] the name of the event (regex)
89
+ # @param token [String|Symbol] the ordinate of the event in the sequence;
90
+ # String are tokens returned by previous calls to this function
91
+ # Symbols are the special tokens :first, :last, and :next
92
+ # @param not_found [Symbol] behaviour if there is no matching event;
93
+ # :reject with exception, or :wait for event
94
+ # @param found [Symbol] behaviour if there is a matching event;
95
+ # :reject with exception, or :return its current value
96
+ # @return [hash] A hash with keys :value and :token;
97
+ # :value is a further hash of the :name and :payload of the event,
98
+ # :token is the event's ordinate in the sequence and can be passed to future calls to get the subsequent event
99
+ # @note
100
+ # Whereas the consul API for events returns all past events that match
101
+ # name, this method allows retrieval of individual events from that
102
+ # sequence. However, because consul's API isn't conducive to this, we can
103
+ # offer first, last, next (last + 1) events, or arbitrary events in the
104
+ # middle, though these can only be identified relative to the preceding
105
+ # event. However, this is ideal for iterating through the sequence of
106
+ # events (while being sure that none are missed).
107
+ def get name=nil, token=:last, not_found=:wait, found=:return
108
+ url = ["/v1/event/list"]
109
+ url += use_named_parameter("name", name)
110
+ @raw = @conn.get concat_url url
111
+ body = JSON.parse(@raw.body)
112
+ # TODO: deal with unknown symbols, invalid indices (find_index will return nil)
113
+ idx = case token
114
+ when :first then 0
115
+ when :last then body.length - 1
116
+ when :next then body.length
117
+ else body.find_index { |e| e["ID"] == token } + 1
118
+ end
119
+ if idx == body.length then
120
+ case not_found
121
+ when :reject
122
+ raise Diplomat::EventNotFound, name
123
+ when :wait
124
+ # Wait for next event
125
+ index = @raw.headers["x-consul-index"]
126
+ url += use_named_parameter("index", index)
127
+ @raw = @conn.get do |req|
128
+ req.url concat_url url
129
+ req.options.timeout = 86400
130
+ end
131
+ body = JSON.parse(@raw.body)
132
+ event = body.last # If it's possible for two events to arrive at once, this needs to #find again
133
+ end
134
+ else
135
+ case found
136
+ when :reject
137
+ raise Diplomat::EventAlreadyExits, name
138
+ when :return
139
+ event = body[idx]
140
+ end
141
+ end
142
+
143
+ { :value => { :name => event["Name"], :payload => Base64.decode64(event["Payload"]) },
144
+ :token => event["ID"] }
145
+ end
146
+
147
+
148
+ # @note This is sugar, see (#fire)
149
+ def self.fire *args
150
+ Diplomat::Event.new.fire *args
151
+ end
152
+
153
+ # @note This is sugar, see (#get_all)
154
+ def self.get_all *args
155
+ Diplomat::Event.new.get_all *args
156
+ end
157
+
158
+ # @note This is sugar, see (#get)
159
+ def self.get *args
160
+ Diplomat::Event.new.get *args
161
+ end
162
+ end
163
+ end
data/lib/diplomat/kv.rb CHANGED
@@ -13,7 +13,7 @@ module Diplomat
13
13
  # @param not_found [Symbol] behaviour if the key doesn't exist;
14
14
  # :reject with exception, or :wait for it to appear
15
15
  # @param found [Symbol] behaviour if the key does exist;
16
- # :reject with exception, :wait for its next value, or :return its current value
16
+ # :reject with exception, :return its current value, or :wait for its next value
17
17
  # @return [String] The base64-decoded value associated with the key
18
18
  # @note
19
19
  # When trying to access a key, there are two possibilites:
@@ -33,8 +33,8 @@ module Diplomat
33
33
  @options = options
34
34
 
35
35
  url = ["/v1/kv/#{@key}"]
36
- url += check_acl_token unless check_acl_token.nil?
37
- url += use_consistency(@options) unless use_consistency(@options).nil?
36
+ url += check_acl_token
37
+ url += use_consistency(@options)
38
38
 
39
39
  # 404s OK using this connection
40
40
  raw = @conn_no_err.get concat_url url
@@ -61,10 +61,7 @@ module Diplomat
61
61
  end
62
62
 
63
63
  # Wait for first/next value
64
- url = ["/v1/kv/#{@key}"]
65
- url += check_acl_token unless check_acl_token.nil?
66
- url += use_consistency(@options) unless use_consistency(@options).nil?
67
- url += ["index=#{index}"]
64
+ url += use_named_parameter("index", index)
68
65
  @raw = @conn.get do |req|
69
66
  req.url concat_url url
70
67
  req.options.timeout = 86400
@@ -83,8 +80,8 @@ module Diplomat
83
80
  @options = options
84
81
  @raw = @conn.put do |req|
85
82
  url = ["/v1/kv/#{key}"]
86
- url += check_acl_token unless check_acl_token.nil?
87
- url += use_cas(@options) unless use_cas(@options).nil?
83
+ url += check_acl_token
84
+ url += use_cas(@options)
88
85
  req.url concat_url url
89
86
  req.body = value
90
87
  end
@@ -101,7 +98,7 @@ module Diplomat
101
98
  def delete key
102
99
  @key = key
103
100
  url = ["/v1/kv/#{@key}"]
104
- url += check_acl_token unless check_acl_token.nil?
101
+ url += check_acl_token
105
102
  @raw = @conn.delete concat_url url
106
103
  end
107
104
 
@@ -122,41 +119,16 @@ module Diplomat
122
119
 
123
120
  private
124
121
 
125
- # Parse the body, apply it to the raw attribute
126
- def parse_body
127
- @raw = JSON.parse(@raw.body)
128
- end
129
-
130
- # Get the key from the raw output
131
- def return_key
132
- @key = @raw["Key"]
133
- end
134
-
135
- # Get the value from the raw output
136
- def return_value
137
- if @raw.count == 1
138
- @value = @raw.first["Value"]
139
- @value = Base64.decode64(@value) unless @value.nil?
140
- else
141
- @value = @raw.map do |e|
142
- {
143
- :key => e["Key"],
144
- :value => e["Value"].nil? ? e["Value"] : Base64.decode64(e["Value"])
145
- }
146
- end
147
- end
148
- end
149
-
150
122
  def check_acl_token
151
- ["token=#{Diplomat.configuration.acl_token}"] if Diplomat.configuration.acl_token
123
+ use_named_parameter("token", Diplomat.configuration.acl_token)
152
124
  end
153
125
 
154
126
  def use_cas(options)
155
- ["cas=#{options[:cas]}"] if options && options[:cas]
127
+ if options then use_named_parameter("cas", options[:cas]) else [] end
156
128
  end
157
129
 
158
130
  def use_consistency(options)
159
- ["#{options[:consistency]}"] if options && options[:consistency]
131
+ if options && options[:consistency] then ["#{options[:consistency]}"] else [] end
160
132
  end
161
133
  end
162
134
  end
@@ -8,12 +8,16 @@ module Diplomat
8
8
  start_connection api_connection
9
9
  end
10
10
 
11
+ def use_named_parameter(name, value)
12
+ if value then ["#{name}=#{value}"] else [] end
13
+ end
14
+
11
15
  def concat_url parts
12
- if parts.length > 1 then
13
- parts.first << '?' << parts.drop(1).join('&')
14
- else
15
- parts.first
16
- end
16
+ if parts.length > 1 then
17
+ parts.first + '?' + parts.drop(1).join('&')
18
+ else
19
+ parts.first
20
+ end
17
21
  end
18
22
 
19
23
  private
@@ -41,5 +45,33 @@ module Diplomat
41
45
  end
42
46
  end
43
47
 
48
+ # Parse the body, apply it to the raw attribute
49
+ def parse_body
50
+ @raw = JSON.parse(@raw.body)
51
+ end
52
+
53
+ # Get the key/value(s) from the raw output
54
+ def return_value
55
+ if @raw.count == 1
56
+ @value = @raw.first["Value"]
57
+ @value = Base64.decode64(@value) unless @value.nil?
58
+ else
59
+ @value = @raw.map do |e|
60
+ {
61
+ :key => e["Key"],
62
+ :value => (Base64.decode64(e["Value"]) unless e["Value"].nil?)
63
+ }
64
+ end
65
+ end
66
+ end
67
+
68
+ # Get the name and payload(s) from the raw output
69
+ def return_payload
70
+ @value = @raw.map do |e|
71
+ { :name => e["Name"],
72
+ :payload => (Base64.decode64(e["Payload"]) unless e["Payload"].nil?) }
73
+ end
74
+ end
75
+
44
76
  end
45
77
  end
@@ -1,3 +1,3 @@
1
1
  module Diplomat
2
- VERSION = "0.7.0"
2
+ VERSION = "0.8.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: diplomat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Hamelink
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-04 00:00:00.000000000 Z
11
+ date: 2015-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -180,6 +180,7 @@ files:
180
180
  - lib/diplomat/check.rb
181
181
  - lib/diplomat/configuration.rb
182
182
  - lib/diplomat/error.rb
183
+ - lib/diplomat/event.rb
183
184
  - lib/diplomat/health.rb
184
185
  - lib/diplomat/kv.rb
185
186
  - lib/diplomat/lock.rb