diplomat 0.7.0 → 0.8.0

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: 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